Skip to content

Commit 8c04c76

Browse files
committed
tcp entry points to try calling async lib from c
framework for "tcp" in rust called by c implement blocking connect, c test passes
1 parent bcc4373 commit 8c04c76

File tree

7 files changed

+357
-21
lines changed

7 files changed

+357
-21
lines changed

mylib/Cargo.lock

+251-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mylib/Cargo.toml

+2-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ edition = "2018"
99
name = "mylib"
1010
crate-type = ["lib", "staticlib"]
1111

12-
1312
[build-dependencies]
14-
cbindgen = "0.12"
15-
16-
13+
cbindgen = "0.13"
1714

1815
[dependencies]
19-
20-
16+
tokio = { version = "0.2", features = ["full"] }

mylib/cbindgen.toml

+7-5
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ no_includes = true
77

88

99

10-
[export]
11-
prefix = "my_"
12-
1310
[export.rename]
14-
"Thing_ExtThingStatus" = "thing_t"
15-
"StatusCallbackFunction" = "status_callback_t"
11+
"Thing_ExtThingStatus" = "my_thing_t"
12+
"StatusCallbackFunction" = "my_status_callback_t"
13+
14+
15+
16+
[fn]
17+
sort_by = "None"

mylib/mylib.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,28 @@
77

88
#include <stdint.h>
99

10+
typedef struct TcpConnection TcpConnection;
11+
1012
typedef struct my_thing_t my_thing_t;
1113

1214
typedef void (*my_status_callback_t)(int32_t code, void *user_data);
1315

16+
typedef void (*TcpStatusCallback)(int32_t code, void *user_data);
17+
1418
int32_t add(int32_t a, int32_t b);
1519

1620
my_thing_t *create_thing(int32_t num, my_status_callback_t callback_or_null, void *userdata);
1721

18-
void destroy_thing(my_thing_t *thing_ptr);
19-
2022
int32_t thing_num(my_thing_t *thing_ptr);
2123

2224
void thing_something(my_thing_t *thing_ptr);
2325

26+
void destroy_thing(my_thing_t *thing_ptr);
27+
28+
TcpConnection *create_tcp(TcpStatusCallback callback, void *user_data);
29+
30+
void tcp_connect_blocking(TcpConnection *tcp_ptr);
31+
32+
void destroy_tcp(TcpConnection *tcp_ptr);
33+
2434
#endif /* mylib_h */

mylib/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod thingc;
22
mod thing;
33
use thing::{Thing, StatusCallback, ThingStatus};
4+
mod tcp;
45

56
#[no_mangle]
67
pub extern "C" fn add(a: i32, b:i32) -> i32 {

mylib/src/tcp.rs

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use std::ffi::c_void;
2+
use tokio::runtime::Runtime;
3+
use tokio::net::TcpStream;
4+
// use std::time::Duration;
5+
6+
pub struct TcpConnection {
7+
runtime: Runtime,
8+
conn: Option<TcpStream>,
9+
callback: TcpStatusCallback,
10+
user_data: * mut c_void,
11+
}
12+
13+
pub type TcpStatusCallback = extern "C" fn(code: i32, user_data: * mut c_void) -> ();
14+
15+
impl TcpConnection {
16+
pub fn trigger_callback(&self, code: i32) {
17+
(self.callback)(code, self.user_data);
18+
}
19+
}
20+
21+
#[no_mangle]
22+
pub extern "C" fn create_tcp(callback: TcpStatusCallback, user_data: * mut c_void) -> * mut TcpConnection {
23+
assert!(!(callback as *mut c_void).is_null());
24+
let runtime = Runtime::new().unwrap();
25+
26+
Box::into_raw(Box::new(TcpConnection { runtime, conn: None, callback, user_data }))
27+
}
28+
29+
#[no_mangle]
30+
pub unsafe extern "C" fn tcp_connect_blocking(tcp_ptr: *mut TcpConnection) -> () {
31+
if tcp_ptr.is_null() {
32+
return; // should be an error, but just experimenting here
33+
}
34+
let tcp = &mut *tcp_ptr;
35+
36+
let mut code = -1;
37+
let conn = TcpStream::connect("127.0.0.1:80");
38+
39+
tcp.runtime.block_on(async {
40+
let result = conn.await;
41+
code = match result {
42+
Ok(_) => 200,
43+
Err(_) => 404,
44+
};
45+
});
46+
tcp.trigger_callback(code);
47+
48+
}
49+
50+
51+
#[no_mangle]
52+
pub unsafe extern fn destroy_tcp(tcp_ptr: *mut TcpConnection) {
53+
if tcp_ptr.is_null() {
54+
return;
55+
}
56+
let _tcp = Box::from_raw(tcp_ptr);
57+
// attempt to shutdown gracefully, but this API must have changed
58+
// tcp.runtime.shutdown_timeout(Duration::from_millis(100));
59+
// ^^^^^^^^^^^^^^^^ method not found in `tokio::runtime::Runtime`
60+
61+
62+
// memory associated with `tcp` that was allocated by Rust freed on fn exit
63+
}

test.c

+21-5
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,49 @@ int test_create_thing() {
2626
return 0;
2727
}
2828

29-
int called = 0;
3029
typedef struct {
3130
int counter;
32-
} MyData;
31+
} TestThingStatusData;
3332

3433
void status_callback(int32_t code, void *user_data) {
35-
called = 1;
36-
MyData* my_data = (MyData*)user_data;
34+
TestThingStatusData* my_data = (TestThingStatusData*)user_data;
3735
my_data->counter++;
3836
}
3937

4038
int test_thing_callback() {
41-
MyData data = {0};
39+
TestThingStatusData data = {0};
4240
my_thing_t* thing = create_thing(1, status_callback, &data);
4341
thing_something(thing);
4442
_assert(data.counter, 1);
4543
destroy_thing(thing);
4644
return 0;
4745
}
4846

47+
typedef struct {
48+
int counter;
49+
} TestTcpStatusData;
50+
51+
void tcp_status_callback(int32_t code, void *user_data) {
52+
TestTcpStatusData* my_data = (TestTcpStatusData*)user_data;
53+
my_data->counter++;
54+
}
55+
56+
int test_tcp_create_destroy() {
57+
TestTcpStatusData data = {0};
58+
TcpConnection* tcp = create_tcp(status_callback, &data);
59+
tcp_connect_blocking(tcp);
60+
_assert(data.counter, 1);
61+
destroy_tcp(tcp);
62+
return 0;
63+
}
4964

5065
// would be good to test memory leaks
5166
int all_tests() {
5267
_verify(add_positive_numbers);
5368
_verify(add_negative_numbers);
5469
_verify(test_create_thing);
5570
_verify(test_thing_callback);
71+
_verify(test_tcp_create_destroy);
5672
return 0;
5773
}
5874

0 commit comments

Comments
 (0)