1
1
#include "main.h"
2
2
#include "utils.h"
3
+ #include <stdbool.h>
3
4
#include <erl_nif.h>
4
5
5
6
#define ERL_FUNCTION (FUNCTION_NAME ) static ERL_NIF_TERM FUNCTION_NAME(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
6
7
7
- #define ERL_FUNCTION_GETTER (NAME , GETTER ) \
8
- ERL_FUNCTION(NAME) \
9
- { \
10
- uintptr_t _handle = get_handle_from_term(env, argv[0]); \
11
- IF_ERROR(_handle == 0, "invalid first argument"); \
12
- uintptr_t _res = GETTER(_handle); \
13
- return get_handle_result(env, _res); \
8
+ #define ERL_FUNCTION_GETTER (NAME , RECV_TYPE , ATTR_TYPE , GETTER ) \
9
+ ERL_FUNCTION(NAME) \
10
+ { \
11
+ uintptr_t _handle = get_handle_from_term(env, RECV_TYPE, argv[0]); \
12
+ IF_ERROR(_handle == 0, "invalid first argument"); \
13
+ uintptr_t _res = GETTER(_handle); \
14
+ return get_handle_result(env, ATTR_TYPE, _res); \
14
15
}
15
16
16
17
#define IF_ERROR (COND , MSG ) \
19
20
return make_error_msg(env, (MSG)); \
20
21
}
21
22
22
- #define GET_HANDLE (TERM , NAME ) \
23
- ({ \
24
- uintptr_t _handle = get_handle_from_term(env, (TERM)); \
25
- IF_ERROR(_handle == 0, "invalid " NAME); \
26
- _handle; \
23
+ #define GET_HANDLE (TERM , TYPE ) \
24
+ ({ \
25
+ uintptr_t _handle = get_handle_from_term(env, (TYPE), ( TERM)); \
26
+ IF_ERROR(_handle == 0, "invalid " #TYPE); \
27
+ _handle; \
27
28
})
28
29
29
- #define NIF_ENTRY (FUNCTION_NAME , ARITY ) \
30
- { \
31
- #FUNCTION_NAME, ARITY, FUNCTION_NAME \
30
+ #define NIF_ENTRY (FUNCTION_NAME , ARITY , ...) \
31
+ { \
32
+ #FUNCTION_NAME, ARITY, FUNCTION_NAME, __VA_ARGS__ \
32
33
}
33
34
34
- const uint64_t PID_LENGTH = 1024 ;
35
35
const uint64_t BUFFER_SIZE = 4096 ;
36
36
37
+ /*************/
38
+ /* NIF Setup */
39
+ /*************/
40
+
41
+ ErlNifResourceType * Option ;
42
+ ErlNifResourceType * Host ;
43
+ ErlNifResourceType * Peerstore ;
44
+ ErlNifResourceType * peer_ID ;
45
+ ErlNifResourceType * Multiaddr_arr ;
46
+ ErlNifResourceType * Stream ;
47
+
48
+ // Resource type helpers
49
+ void handle_cleanup (ErlNifEnv * env , void * obj )
50
+ {
51
+ uintptr_t * handle = obj ;
52
+ DeleteHandle (* handle );
53
+ }
54
+
55
+ #define OPEN_RESOURCE_TYPE (NAME ) ((NAME) = enif_open_resource_type(env, NULL, (#NAME), handle_cleanup, flags, NULL))
56
+
57
+ static int open_resource_types (ErlNifEnv * env , ErlNifResourceFlags flags )
58
+ {
59
+ int failed = false;
60
+ failed |= NULL == OPEN_RESOURCE_TYPE (Option );
61
+ failed |= NULL == OPEN_RESOURCE_TYPE (Host );
62
+ failed |= NULL == OPEN_RESOURCE_TYPE (Peerstore );
63
+ failed |= NULL == OPEN_RESOURCE_TYPE (peer_ID );
64
+ failed |= NULL == OPEN_RESOURCE_TYPE (Multiaddr_arr );
65
+ failed |= NULL == OPEN_RESOURCE_TYPE (Stream );
66
+ return failed ;
67
+ }
68
+
69
+ static int load (ErlNifEnv * env , void * * priv_data , ERL_NIF_TERM load_info )
70
+ {
71
+ return open_resource_types (env , ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER );
72
+ }
73
+
74
+ static int upgrade (ErlNifEnv * env , void * * priv_data , void * * old_priv_data ,
75
+ ERL_NIF_TERM load_info )
76
+ {
77
+ return open_resource_types (env , ERL_NIF_RT_TAKEOVER );
78
+ }
79
+
37
80
/***********/
38
81
/* Helpers */
39
82
/***********/
40
83
41
- static uintptr_t get_handle_from_term (ErlNifEnv * env , ERL_NIF_TERM term )
84
+ static uintptr_t get_handle_from_term (ErlNifEnv * env , ErlNifResourceType * type , ERL_NIF_TERM term )
42
85
{
43
- uintptr_t handle ;
44
- return enif_get_uint64 (env , term , & handle ) ? handle : 0 ;
86
+ uintptr_t * obj ;
87
+ int result = enif_get_resource (env , term , type , (void * * )& obj );
88
+ return (!result || obj == NULL ) ? 0 : * obj ;
45
89
}
46
90
47
91
static ERL_NIF_TERM _make_error_msg (ErlNifEnv * env , uint len , const char * msg )
@@ -62,10 +106,32 @@ static ERL_NIF_TERM make_ok_tuple2(ErlNifEnv *env, ERL_NIF_TERM term)
62
106
return enif_make_tuple2 (env , enif_make_atom (env , "ok" ), term );
63
107
}
64
108
65
- static ERL_NIF_TERM get_handle_result (ErlNifEnv * env , uintptr_t handle )
109
+ static ERL_NIF_TERM get_handle_result (ErlNifEnv * env , ErlNifResourceType * type , uintptr_t handle )
66
110
{
67
111
IF_ERROR (handle == 0 , "invalid handle returned" );
68
- return make_ok_tuple2 (env , enif_make_uint64 (env , handle ));
112
+ uintptr_t * obj = enif_alloc_resource (type , sizeof (uintptr_t ));
113
+ IF_ERROR (obj == NULL , "couldn't create resource" );
114
+ * obj = handle ;
115
+ ERL_NIF_TERM term = enif_make_resource (env , obj );
116
+ // NOTE: we need to release our reference, so it can be GC'd
117
+ enif_release_resource (obj );
118
+ return make_ok_tuple2 (env , term );
119
+ }
120
+
121
+ void send_message (erl_pid_t _pid , uintptr_t stream_handle )
122
+ {
123
+ // Passed as void* to avoid including erl_nif.h in the header.
124
+ ErlNifPid * pid = (ErlNifPid * )_pid ;
125
+ ErlNifEnv * env = enif_alloc_env ();
126
+
127
+ ERL_NIF_TERM message = get_handle_result (env , Stream , stream_handle );
128
+
129
+ int result = enif_send (NULL , pid , env , message );
130
+ // On error, the env isn't freed by the function.
131
+ if (!result )
132
+ {
133
+ enif_free_env (env );
134
+ }
69
135
}
70
136
71
137
/*********/
@@ -80,7 +146,7 @@ ERL_FUNCTION(listen_addr_strings)
80
146
81
147
uintptr_t handle = ListenAddrStrings (listen_addr );
82
148
83
- return get_handle_result (env , handle );
149
+ return get_handle_result (env , Option , handle );
84
150
}
85
151
86
152
/****************/
@@ -97,23 +163,24 @@ ERL_FUNCTION(host_new)
97
163
while (!enif_is_empty_list (env , tail ) && i < MAX_OPTIONS )
98
164
{
99
165
enif_get_list_cell (env , tail , & head , & tail );
100
- options [i ++ ] = GET_HANDLE (head , "option" );
166
+ uintptr_t handle = GET_HANDLE (head , Option );
167
+ options [i ++ ] = handle ;
101
168
}
102
169
GoSlice go_options = {options , i , MAX_OPTIONS };
103
170
uintptr_t result = HostNew (go_options );
104
- return get_handle_result (env , result );
171
+ return get_handle_result (env , Host , result );
105
172
}
106
173
107
174
ERL_FUNCTION (host_close )
108
175
{
109
- uintptr_t host = GET_HANDLE (argv [0 ], "host" );
176
+ uintptr_t host = GET_HANDLE (argv [0 ], Host );
110
177
HostClose (host );
111
178
return enif_make_atom (env , "ok" );
112
179
}
113
180
114
181
ERL_FUNCTION (host_set_stream_handler )
115
182
{
116
- uintptr_t host = GET_HANDLE (argv [0 ], "host" );
183
+ uintptr_t host = GET_HANDLE (argv [0 ], Host );
117
184
118
185
ErlNifBinary bin ;
119
186
IF_ERROR (!enif_inspect_binary (env , argv [1 ], & bin ), "invalid protocol ID" );
@@ -124,41 +191,41 @@ ERL_FUNCTION(host_set_stream_handler)
124
191
125
192
IF_ERROR (!enif_self (env , pid ), "failed to get pid" );
126
193
127
- SetStreamHandler (host , proto_id , (void * )pid );
194
+ HostSetStreamHandler (host , proto_id , (void * )pid , send_message );
128
195
129
196
return enif_make_atom (env , "ok" );
130
197
}
131
198
132
199
ERL_FUNCTION (host_new_stream )
133
200
{
134
- uintptr_t host = GET_HANDLE (argv [0 ], "host" );
135
- uintptr_t id = GET_HANDLE (argv [1 ], "peer id" );
201
+ uintptr_t host = GET_HANDLE (argv [0 ], Host );
202
+ uintptr_t id = GET_HANDLE (argv [1 ], peer_ID );
136
203
137
204
ErlNifBinary bin ;
138
205
IF_ERROR (!enif_inspect_binary (env , argv [2 ], & bin ), "invalid protocol ID" );
139
206
GoString proto_id = {(const char * )bin .data , bin .size };
140
207
141
- int result = NewStream (host , id , proto_id );
142
- return get_handle_result (env , result );
208
+ uintptr_t result = HostNewStream (host , id , proto_id );
209
+ return get_handle_result (env , Stream , result );
143
210
}
144
211
145
- ERL_FUNCTION_GETTER (host_peerstore , Peerstore )
146
- ERL_FUNCTION_GETTER (host_id , ID )
147
- ERL_FUNCTION_GETTER (host_addrs , Addrs )
212
+ ERL_FUNCTION_GETTER (host_peerstore , Host , Peerstore , HostPeerstore )
213
+ ERL_FUNCTION_GETTER (host_id , Host , peer_ID , HostID )
214
+ ERL_FUNCTION_GETTER (host_addrs , Host , Multiaddr_arr , HostAddrs )
148
215
149
216
/*********************/
150
217
/* Peerstore methods */
151
218
/*********************/
152
219
153
220
ERL_FUNCTION (peerstore_add_addrs )
154
221
{
155
- uintptr_t ps = GET_HANDLE (argv [0 ], "peerstore" );
156
- uintptr_t id = GET_HANDLE (argv [1 ], "peer id" );
157
- uintptr_t addrs = GET_HANDLE (argv [2 ], "addrs" );
222
+ uintptr_t ps = GET_HANDLE (argv [0 ], Peerstore );
223
+ uintptr_t id = GET_HANDLE (argv [1 ], peer_ID );
224
+ uintptr_t addrs = GET_HANDLE (argv [2 ], Multiaddr_arr );
158
225
u_long ttl ;
159
226
IF_ERROR (!enif_get_uint64 (env , argv [3 ], & ttl ), "invalid TTL" );
160
227
161
- AddAddrs (ps , id , addrs , ttl );
228
+ PeerstoreAddAddrs (ps , id , addrs , ttl );
162
229
return enif_make_atom (env , "ok" );
163
230
}
164
231
@@ -168,7 +235,7 @@ ERL_FUNCTION(peerstore_add_addrs)
168
235
169
236
ERL_FUNCTION (stream_read )
170
237
{
171
- uintptr_t stream = GET_HANDLE (argv [0 ], "stream" );
238
+ uintptr_t stream = GET_HANDLE (argv [0 ], Stream );
172
239
173
240
char buffer [BUFFER_SIZE ];
174
241
GoSlice go_buffer = {buffer , BUFFER_SIZE , BUFFER_SIZE };
@@ -185,7 +252,7 @@ ERL_FUNCTION(stream_read)
185
252
186
253
ERL_FUNCTION (stream_write )
187
254
{
188
- uintptr_t stream = GET_HANDLE (argv [0 ], "stream" );
255
+ uintptr_t stream = GET_HANDLE (argv [0 ], Stream );
189
256
190
257
ErlNifBinary bin ;
191
258
IF_ERROR (!enif_inspect_binary (env , argv [1 ], & bin ), "invalid data" );
@@ -199,7 +266,7 @@ ERL_FUNCTION(stream_write)
199
266
200
267
ERL_FUNCTION (stream_close )
201
268
{
202
- uintptr_t stream = GET_HANDLE (argv [0 ], "stream" );
269
+ uintptr_t stream = GET_HANDLE (argv [0 ], Stream );
203
270
StreamClose (stream );
204
271
return enif_make_atom (env , "ok" );
205
272
}
@@ -209,14 +276,15 @@ static ErlNifFunc nif_funcs[] = {
209
276
NIF_ENTRY (host_new , 1 ),
210
277
NIF_ENTRY (host_close , 1 ),
211
278
NIF_ENTRY (host_set_stream_handler , 2 ),
212
- NIF_ENTRY (host_new_stream , 3 ),
279
+ // TODO: check if host_new_stream is truly dirty
280
+ NIF_ENTRY (host_new_stream , 3 , ERL_NIF_DIRTY_JOB_IO_BOUND ), // blocks negotiating protocol
213
281
NIF_ENTRY (host_peerstore , 1 ),
214
282
NIF_ENTRY (host_id , 1 ),
215
283
NIF_ENTRY (host_addrs , 1 ),
216
284
NIF_ENTRY (peerstore_add_addrs , 4 ),
217
- NIF_ENTRY (stream_read , 1 ),
218
- NIF_ENTRY (stream_write , 2 ),
285
+ NIF_ENTRY (stream_read , 1 , ERL_NIF_DIRTY_JOB_IO_BOUND ), // blocks until reading
286
+ NIF_ENTRY (stream_write , 2 , ERL_NIF_DIRTY_JOB_IO_BOUND ), // blocks when buffer is full
219
287
NIF_ENTRY (stream_close , 1 ),
220
288
};
221
289
222
- ERL_NIF_INIT (Elixir .Libp2p , nif_funcs , NULL , NULL , NULL , NULL )
290
+ ERL_NIF_INIT (Elixir .Libp2p , nif_funcs , load , NULL , upgrade , NULL )
0 commit comments