diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2018-11-15 09:24:46 +0100 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2018-11-15 09:24:46 +0100 |
commit | f087dfe25d093b1cb1665d12dc5a039f29f3af11 (patch) | |
tree | 14dbc119b902e8c2db22ed78ae4d7ed4e718790e /src/linda.c | |
parent | b0bb224ce6c4bd769a3f1a868e832d9d38f6e63e (diff) | |
download | lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.tar.gz lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.tar.bz2 lanes-f087dfe25d093b1cb1665d12dc5a039f29f3af11.zip |
split linda code in a separate file
Diffstat (limited to 'src/linda.c')
-rw-r--r-- | src/linda.c | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/src/linda.c b/src/linda.c new file mode 100644 index 0000000..98d2a8e --- /dev/null +++ b/src/linda.c | |||
@@ -0,0 +1,934 @@ | |||
1 | /* | ||
2 | * LINDA.C Copyright (c) 2018, Benoit Germain | ||
3 | * | ||
4 | * Linda deep userdata. | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | =============================================================================== | ||
9 | |||
10 | Copyright (C) 2018 benoit Germain <bnt.germain@gmail.com> | ||
11 | |||
12 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
13 | of this software and associated documentation files (the "Software"), to deal | ||
14 | in the Software without restriction, including without limitation the rights | ||
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
16 | copies of the Software, and to permit persons to whom the Software is | ||
17 | furnished to do so, subject to the following conditions: | ||
18 | |||
19 | The above copyright notice and this permission notice shall be included in | ||
20 | all copies or substantial portions of the Software. | ||
21 | |||
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
28 | THE SOFTWARE. | ||
29 | |||
30 | =============================================================================== | ||
31 | */ | ||
32 | |||
33 | #include <stdlib.h> | ||
34 | |||
35 | #include "threading.h" | ||
36 | #include "compat.h" | ||
37 | #include "tools.h" | ||
38 | #include "universe.h" | ||
39 | #include "keeper.h" | ||
40 | #include "deep.h" | ||
41 | #include "lanes_private.h" | ||
42 | |||
43 | /* | ||
44 | * Actual data is kept within a keeper state, which is hashed by the 's_Linda' | ||
45 | * pointer (which is same to all userdatas pointing to it). | ||
46 | */ | ||
47 | struct s_Linda | ||
48 | { | ||
49 | SIGNAL_T read_happened; | ||
50 | SIGNAL_T write_happened; | ||
51 | Universe* U; // the universe this linda belongs to | ||
52 | ptrdiff_t group; // a group to control keeper allocation between lindas | ||
53 | enum e_cancel_request simulate_cancel; | ||
54 | char name[1]; | ||
55 | }; | ||
56 | #define LINDA_KEEPER_HASHSEED( linda) (linda->group ? linda->group : (ptrdiff_t)linda) | ||
57 | |||
58 | static void* linda_id( lua_State*, DeepOp); | ||
59 | |||
60 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) | ||
61 | { | ||
62 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
63 | luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); | ||
64 | return linda; | ||
65 | } | ||
66 | |||
67 | static void check_key_types( lua_State* L, int start_, int end_) | ||
68 | { | ||
69 | int i; | ||
70 | for( i = start_; i <= end_; ++ i) | ||
71 | { | ||
72 | int t = lua_type( L, i); | ||
73 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
74 | { | ||
75 | continue; | ||
76 | } | ||
77 | (void) luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | LUAG_FUNC( linda_protected_call) | ||
82 | { | ||
83 | int rc = LUA_OK; | ||
84 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
85 | |||
86 | // acquire the keeper | ||
87 | Keeper* K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED(linda)); | ||
88 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
89 | if( KL == NULL) return 0; | ||
90 | |||
91 | // retrieve the actual function to be called and move it before the arguments | ||
92 | lua_pushvalue( L, lua_upvalueindex( 1)); | ||
93 | lua_insert( L, 1); | ||
94 | // do a protected call | ||
95 | rc = lua_pcall( L, lua_gettop( L) - 1, LUA_MULTRET, 0); | ||
96 | |||
97 | // release the keeper | ||
98 | keeper_release( K); | ||
99 | |||
100 | // if there was an error, forward it | ||
101 | if( rc != LUA_OK) | ||
102 | { | ||
103 | return lua_error( L); | ||
104 | } | ||
105 | // return whatever the actual operation provided | ||
106 | return lua_gettop( L); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * bool= linda_send( linda_ud, [timeout_secs=-1,] [linda.null,] key_num|str|bool|lightuserdata, ... ) | ||
111 | * | ||
112 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
113 | * | ||
114 | * Returns: 'true' if the value was queued | ||
115 | * 'false' for timeout (only happens when the queue size is limited) | ||
116 | * nil, CANCEL_ERROR if cancelled | ||
117 | */ | ||
118 | LUAG_FUNC( linda_send) | ||
119 | { | ||
120 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
121 | bool_t ret = FALSE; | ||
122 | enum e_cancel_request cancel = CANCEL_NONE; | ||
123 | int pushed; | ||
124 | time_d timeout = -1.0; | ||
125 | uint_t key_i = 2; // index of first key, if timeout not there | ||
126 | bool_t as_nil_sentinel; // if not NULL, send() will silently send a single nil if nothing is provided | ||
127 | |||
128 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
129 | { | ||
130 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
131 | ++ key_i; | ||
132 | } | ||
133 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
134 | { | ||
135 | ++ key_i; | ||
136 | } | ||
137 | |||
138 | as_nil_sentinel = equal_unique_key( L, key_i, NIL_SENTINEL); | ||
139 | if( as_nil_sentinel) | ||
140 | { | ||
141 | // the real key to send data to is after the NIL_SENTINEL marker | ||
142 | ++ key_i; | ||
143 | } | ||
144 | |||
145 | // make sure the key is of a valid type | ||
146 | check_key_types( L, key_i, key_i); | ||
147 | |||
148 | STACK_GROW( L, 1); | ||
149 | |||
150 | // make sure there is something to send | ||
151 | if( (uint_t)lua_gettop( L) == key_i) | ||
152 | { | ||
153 | if( as_nil_sentinel) | ||
154 | { | ||
155 | // send a single nil if nothing is provided | ||
156 | push_unique_key( L, NIL_SENTINEL); | ||
157 | } | ||
158 | else | ||
159 | { | ||
160 | return luaL_error( L, "no data to send"); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // convert nils to some special non-nil sentinel in sent values | ||
165 | keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); | ||
166 | |||
167 | { | ||
168 | bool_t try_again = TRUE; | ||
169 | Lane* const s = get_lane_from_registry( L); | ||
170 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
171 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | ||
172 | if( KL == NULL) return 0; | ||
173 | STACK_CHECK( KL); | ||
174 | for( ;;) | ||
175 | { | ||
176 | if( s != NULL) | ||
177 | { | ||
178 | cancel = s->cancel_request; | ||
179 | } | ||
180 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
181 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
182 | if( !try_again || cancel != CANCEL_NONE) | ||
183 | { | ||
184 | pushed = 0; | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | STACK_MID( KL, 0); | ||
189 | pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); | ||
190 | if( pushed < 0) | ||
191 | { | ||
192 | break; | ||
193 | } | ||
194 | ASSERT_L( pushed == 1); | ||
195 | |||
196 | ret = lua_toboolean( L, -1); | ||
197 | lua_pop( L, 1); | ||
198 | |||
199 | if( ret) | ||
200 | { | ||
201 | // Wake up ALL waiting threads | ||
202 | SIGNAL_ALL( &linda->write_happened); | ||
203 | break; | ||
204 | } | ||
205 | |||
206 | // instant timout to bypass the wait syscall | ||
207 | if( timeout == 0.0) | ||
208 | { | ||
209 | break; /* no wait; instant timeout */ | ||
210 | } | ||
211 | |||
212 | // storage limit hit, wait until timeout or signalled that we should try again | ||
213 | { | ||
214 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
215 | if( s != NULL) | ||
216 | { | ||
217 | // change status of lane to "waiting" | ||
218 | prev_status = s->status; // RUNNING, most likely | ||
219 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
220 | s->status = WAITING; | ||
221 | ASSERT_L( s->waiting_on == NULL); | ||
222 | s->waiting_on = &linda->read_happened; | ||
223 | } | ||
224 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
225 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); | ||
226 | if( s != NULL) | ||
227 | { | ||
228 | s->waiting_on = NULL; | ||
229 | s->status = prev_status; | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | STACK_END( KL, 0); | ||
234 | } | ||
235 | |||
236 | if( pushed < 0) | ||
237 | { | ||
238 | return luaL_error( L, "tried to copy unsupported types"); | ||
239 | } | ||
240 | |||
241 | switch( cancel) | ||
242 | { | ||
243 | case CANCEL_SOFT: | ||
244 | // if user wants to soft-cancel, the call returns lanes.cancel_error | ||
245 | push_unique_key( L, CANCEL_ERROR); | ||
246 | return 1; | ||
247 | |||
248 | case CANCEL_HARD: | ||
249 | // raise an error interrupting execution only in case of hard cancel | ||
250 | return cancel_error( L); // raises an error and doesn't return | ||
251 | |||
252 | default: | ||
253 | lua_pushboolean( L, ret); // true (success) or false (timeout) | ||
254 | return 1; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | |||
259 | /* | ||
260 | * 2 modes of operation | ||
261 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | ||
262 | * Consumes a single value from the Linda, in any key. | ||
263 | * Returns: received value (which is consumed from the slot), and the key which had it | ||
264 | |||
265 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) | ||
266 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. | ||
267 | * returns the actual consumed values, or nil if there weren't enough values to consume | ||
268 | * | ||
269 | */ | ||
270 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
271 | LUAG_FUNC( linda_receive) | ||
272 | { | ||
273 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
274 | int pushed, expected_pushed_min, expected_pushed_max; | ||
275 | enum e_cancel_request cancel = CANCEL_NONE; | ||
276 | keeper_api_t keeper_receive; | ||
277 | |||
278 | time_d timeout = -1.0; | ||
279 | uint_t key_i = 2; | ||
280 | |||
281 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | ||
282 | { | ||
283 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | ||
284 | ++ key_i; | ||
285 | } | ||
286 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key | ||
287 | { | ||
288 | ++ key_i; | ||
289 | } | ||
290 | |||
291 | // are we in batched mode? | ||
292 | { | ||
293 | int is_batched; | ||
294 | lua_pushliteral( L, BATCH_SENTINEL); | ||
295 | is_batched = lua501_equal( L, key_i, -1); | ||
296 | lua_pop( L, 1); | ||
297 | if( is_batched) | ||
298 | { | ||
299 | // no need to pass linda.batched in the keeper state | ||
300 | ++ key_i; | ||
301 | // make sure the keys are of a valid type | ||
302 | check_key_types( L, key_i, key_i); | ||
303 | // receive multiple values from a single slot | ||
304 | keeper_receive = KEEPER_API( receive_batched); | ||
305 | // we expect a user-defined amount of return value | ||
306 | expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1); | ||
307 | expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min); | ||
308 | // don't forget to count the key in addition to the values | ||
309 | ++ expected_pushed_min; | ||
310 | ++ expected_pushed_max; | ||
311 | if( expected_pushed_min > expected_pushed_max) | ||
312 | { | ||
313 | return luaL_error( L, "batched min/max error"); | ||
314 | } | ||
315 | } | ||
316 | else | ||
317 | { | ||
318 | // make sure the keys are of a valid type | ||
319 | check_key_types( L, key_i, lua_gettop( L)); | ||
320 | // receive a single value, checking multiple slots | ||
321 | keeper_receive = KEEPER_API( receive); | ||
322 | // we expect a single (value, key) pair of returned values | ||
323 | expected_pushed_min = expected_pushed_max = 2; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | { | ||
328 | bool_t try_again = TRUE; | ||
329 | Lane* const s = get_lane_from_registry( L); | ||
330 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
331 | if( K == NULL) return 0; | ||
332 | for( ;;) | ||
333 | { | ||
334 | if( s != NULL) | ||
335 | { | ||
336 | cancel = s->cancel_request; | ||
337 | } | ||
338 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
339 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
340 | if( !try_again || cancel != CANCEL_NONE) | ||
341 | { | ||
342 | pushed = 0; | ||
343 | break; | ||
344 | } | ||
345 | |||
346 | // all arguments of receive() but the first are passed to the keeper's receive function | ||
347 | pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); | ||
348 | if( pushed < 0) | ||
349 | { | ||
350 | break; | ||
351 | } | ||
352 | if( pushed > 0) | ||
353 | { | ||
354 | ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max); | ||
355 | // replace sentinels with real nils | ||
356 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
357 | // To be done from within the 'K' locking area | ||
358 | // | ||
359 | SIGNAL_ALL( &linda->read_happened); | ||
360 | break; | ||
361 | } | ||
362 | |||
363 | if( timeout == 0.0) | ||
364 | { | ||
365 | break; /* instant timeout */ | ||
366 | } | ||
367 | |||
368 | // nothing received, wait until timeout or signalled that we should try again | ||
369 | { | ||
370 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | ||
371 | if( s != NULL) | ||
372 | { | ||
373 | // change status of lane to "waiting" | ||
374 | prev_status = s->status; // RUNNING, most likely | ||
375 | ASSERT_L( prev_status == RUNNING); // but check, just in case | ||
376 | s->status = WAITING; | ||
377 | ASSERT_L( s->waiting_on == NULL); | ||
378 | s->waiting_on = &linda->write_happened; | ||
379 | } | ||
380 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
381 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout); | ||
382 | if( s != NULL) | ||
383 | { | ||
384 | s->waiting_on = NULL; | ||
385 | s->status = prev_status; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | } | ||
390 | |||
391 | if( pushed < 0) | ||
392 | { | ||
393 | return luaL_error( L, "tried to copy unsupported types"); | ||
394 | } | ||
395 | |||
396 | switch( cancel) | ||
397 | { | ||
398 | case CANCEL_SOFT: | ||
399 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | ||
400 | push_unique_key( L, CANCEL_ERROR); | ||
401 | return 1; | ||
402 | |||
403 | case CANCEL_HARD: | ||
404 | // raise an error interrupting execution only in case of hard cancel | ||
405 | return cancel_error( L); // raises an error and doesn't return | ||
406 | |||
407 | default: | ||
408 | return pushed; | ||
409 | } | ||
410 | } | ||
411 | |||
412 | |||
413 | /* | ||
414 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | ||
415 | * | ||
416 | * Set one or more value to Linda. | ||
417 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
418 | * | ||
419 | * Existing slot value is replaced, and possible queued entries removed. | ||
420 | */ | ||
421 | LUAG_FUNC( linda_set) | ||
422 | { | ||
423 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
424 | int pushed; | ||
425 | bool_t has_value = lua_gettop( L) > 2; | ||
426 | |||
427 | // make sure the key is of a valid type (throws an error if not the case) | ||
428 | check_key_types( L, 2, 2); | ||
429 | |||
430 | { | ||
431 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
432 | |||
433 | if( linda->simulate_cancel == CANCEL_NONE) | ||
434 | { | ||
435 | if( has_value) | ||
436 | { | ||
437 | // convert nils to some special non-nil sentinel in sent values | ||
438 | keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); | ||
439 | } | ||
440 | pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); | ||
441 | if( pushed >= 0) // no error? | ||
442 | { | ||
443 | ASSERT_L( pushed == 0 || pushed == 1); | ||
444 | |||
445 | if( has_value) | ||
446 | { | ||
447 | // we put some data in the slot, tell readers that they should wake | ||
448 | SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area | ||
449 | } | ||
450 | if( pushed == 1) | ||
451 | { | ||
452 | // the key was full, but it is no longer the case, tell writers they should wake | ||
453 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
454 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | else // linda is cancelled | ||
459 | { | ||
460 | // do nothing and return lanes.cancel_error | ||
461 | push_unique_key( L, CANCEL_ERROR); | ||
462 | pushed = 1; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | // must trigger any error after keeper state has been released | ||
467 | return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; | ||
468 | } | ||
469 | |||
470 | |||
471 | /* | ||
472 | * [val] = linda_count( linda_ud, [key [, ...]]) | ||
473 | * | ||
474 | * Get a count of the pending elements in the specified keys | ||
475 | */ | ||
476 | LUAG_FUNC( linda_count) | ||
477 | { | ||
478 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
479 | int pushed; | ||
480 | |||
481 | // make sure the keys are of a valid type | ||
482 | check_key_types( L, 2, lua_gettop( L)); | ||
483 | |||
484 | { | ||
485 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
486 | pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2); | ||
487 | if( pushed < 0) | ||
488 | { | ||
489 | return luaL_error( L, "tried to count an invalid key"); | ||
490 | } | ||
491 | } | ||
492 | return pushed; | ||
493 | } | ||
494 | |||
495 | |||
496 | /* | ||
497 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | ||
498 | * | ||
499 | * Get one or more values from Linda. | ||
500 | */ | ||
501 | LUAG_FUNC( linda_get) | ||
502 | { | ||
503 | struct s_Linda* const linda = lua_toLinda( L, 1); | ||
504 | int pushed; | ||
505 | lua_Integer count = luaL_optinteger( L, 3, 1); | ||
506 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | ||
507 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | ||
508 | |||
509 | // make sure the key is of a valid type (throws an error if not the case) | ||
510 | check_key_types( L, 2, 2); | ||
511 | { | ||
512 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
513 | |||
514 | if( linda->simulate_cancel == CANCEL_NONE) | ||
515 | { | ||
516 | pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); | ||
517 | if( pushed > 0) | ||
518 | { | ||
519 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper); | ||
520 | } | ||
521 | } | ||
522 | else // linda is cancelled | ||
523 | { | ||
524 | // do nothing and return lanes.cancel_error | ||
525 | push_unique_key( L, CANCEL_ERROR); | ||
526 | pushed = 1; | ||
527 | } | ||
528 | // an error can be raised if we attempt to read an unregistered function | ||
529 | if( pushed < 0) | ||
530 | { | ||
531 | return luaL_error( L, "tried to copy unsupported types"); | ||
532 | } | ||
533 | } | ||
534 | |||
535 | return pushed; | ||
536 | } | ||
537 | |||
538 | |||
539 | /* | ||
540 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) | ||
541 | * | ||
542 | * Set limit to 1 Linda keys. | ||
543 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | ||
544 | */ | ||
545 | LUAG_FUNC( linda_limit) | ||
546 | { | ||
547 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
548 | int pushed; | ||
549 | |||
550 | // make sure we got 3 arguments: the linda, a key and a limit | ||
551 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | ||
552 | // make sure we got a numeric limit | ||
553 | luaL_checknumber( L, 3); | ||
554 | // make sure the key is of a valid type | ||
555 | check_key_types( L, 2, 2); | ||
556 | |||
557 | { | ||
558 | Keeper* K = which_keeper( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
559 | |||
560 | if( linda->simulate_cancel == CANCEL_NONE) | ||
561 | { | ||
562 | pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2); | ||
563 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
564 | if( pushed == 1) | ||
565 | { | ||
566 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
567 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
568 | } | ||
569 | } | ||
570 | else // linda is cancelled | ||
571 | { | ||
572 | // do nothing and return lanes.cancel_error | ||
573 | push_unique_key( L, CANCEL_ERROR); | ||
574 | pushed = 1; | ||
575 | } | ||
576 | } | ||
577 | // propagate pushed boolean if any | ||
578 | return pushed; | ||
579 | } | ||
580 | |||
581 | |||
582 | /* | ||
583 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | ||
584 | * | ||
585 | * Signal linda so that waiting threads wake up as if their own lane was cancelled | ||
586 | */ | ||
587 | LUAG_FUNC( linda_cancel) | ||
588 | { | ||
589 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
590 | char const* who = luaL_optstring( L, 2, "both"); | ||
591 | |||
592 | // make sure we got 3 arguments: the linda, a key and a limit | ||
593 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); | ||
594 | |||
595 | linda->simulate_cancel = CANCEL_SOFT; | ||
596 | if( strcmp( who, "both") == 0) // tell everyone writers to wake up | ||
597 | { | ||
598 | SIGNAL_ALL( &linda->write_happened); | ||
599 | SIGNAL_ALL( &linda->read_happened); | ||
600 | } | ||
601 | else if( strcmp( who, "none") == 0) // reset flag | ||
602 | { | ||
603 | linda->simulate_cancel = CANCEL_NONE; | ||
604 | } | ||
605 | else if( strcmp( who, "read") == 0) // tell blocked readers to wake up | ||
606 | { | ||
607 | SIGNAL_ALL( &linda->write_happened); | ||
608 | } | ||
609 | else if( strcmp( who, "write") == 0) // tell blocked writers to wake up | ||
610 | { | ||
611 | SIGNAL_ALL( &linda->read_happened); | ||
612 | } | ||
613 | else | ||
614 | { | ||
615 | return luaL_error( L, "unknown wake hint '%s'", who); | ||
616 | } | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | |||
621 | /* | ||
622 | * lightuserdata= linda_deep( linda_ud ) | ||
623 | * | ||
624 | * Return the 'deep' userdata pointer, identifying the Linda. | ||
625 | * | ||
626 | * This is needed for using Lindas as key indices (timer system needs it); | ||
627 | * separately created proxies of the same underlying deep object will have | ||
628 | * different userdata and won't be known to be essentially the same deep one | ||
629 | * without this. | ||
630 | */ | ||
631 | LUAG_FUNC( linda_deep) | ||
632 | { | ||
633 | struct s_Linda* linda= lua_toLinda( L, 1); | ||
634 | lua_pushlightuserdata( L, linda); // just the address | ||
635 | return 1; | ||
636 | } | ||
637 | |||
638 | |||
639 | /* | ||
640 | * string = linda:__tostring( linda_ud) | ||
641 | * | ||
642 | * Return the stringification of a linda | ||
643 | * | ||
644 | * Useful for concatenation or debugging purposes | ||
645 | */ | ||
646 | |||
647 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | ||
648 | { | ||
649 | struct s_Linda* linda = (struct s_Linda*) luaG_todeep( L, linda_id, idx_); | ||
650 | if( !opt_) | ||
651 | { | ||
652 | luaL_argcheck( L, linda, idx_, "expecting a linda object"); | ||
653 | } | ||
654 | if( linda != NULL) | ||
655 | { | ||
656 | char text[128]; | ||
657 | int len; | ||
658 | if( linda->name[0]) | ||
659 | len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->name); | ||
660 | else | ||
661 | len = sprintf( text, "Linda: %p", linda); | ||
662 | lua_pushlstring( L, text, len); | ||
663 | return 1; | ||
664 | } | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | LUAG_FUNC( linda_tostring) | ||
669 | { | ||
670 | return linda_tostring( L, 1, FALSE); | ||
671 | } | ||
672 | |||
673 | |||
674 | /* | ||
675 | * string = linda:__concat( a, b) | ||
676 | * | ||
677 | * Return the concatenation of a pair of items, one of them being a linda | ||
678 | * | ||
679 | * Useful for concatenation or debugging purposes | ||
680 | */ | ||
681 | LUAG_FUNC( linda_concat) | ||
682 | { // linda1? linda2? | ||
683 | bool_t atLeastOneLinda = FALSE; | ||
684 | // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. | ||
685 | if( linda_tostring( L, 1, TRUE)) | ||
686 | { | ||
687 | atLeastOneLinda = TRUE; | ||
688 | lua_replace( L, 1); | ||
689 | } | ||
690 | if( linda_tostring( L, 2, TRUE)) | ||
691 | { | ||
692 | atLeastOneLinda = TRUE; | ||
693 | lua_replace( L, 2); | ||
694 | } | ||
695 | if( !atLeastOneLinda) // should not be possible | ||
696 | { | ||
697 | return luaL_error( L, "internal error: linda_concat called on non-Linda"); | ||
698 | } | ||
699 | lua_concat( L, 2); | ||
700 | return 1; | ||
701 | } | ||
702 | |||
703 | /* | ||
704 | * table = linda:dump() | ||
705 | * return a table listing all pending data inside the linda | ||
706 | */ | ||
707 | LUAG_FUNC( linda_dump) | ||
708 | { | ||
709 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
710 | ASSERT_L( linda->U == universe_get( L)); | ||
711 | return keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
712 | } | ||
713 | |||
714 | /* | ||
715 | * table = linda:dump() | ||
716 | * return a table listing all pending data inside the linda | ||
717 | */ | ||
718 | LUAG_FUNC( linda_towatch) | ||
719 | { | ||
720 | struct s_Linda* linda = lua_toLinda( L, 1); | ||
721 | int pushed; | ||
722 | ASSERT_L( linda->U == universe_get( L)); | ||
723 | pushed = keeper_push_linda_storage( linda->U, L, linda, LINDA_KEEPER_HASHSEED( linda)); | ||
724 | if( pushed == 0) | ||
725 | { | ||
726 | // if the linda is empty, don't return nil | ||
727 | pushed = linda_tostring( L, 1, FALSE); | ||
728 | } | ||
729 | return pushed; | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * Identity function of a shared userdata object. | ||
734 | * | ||
735 | * lightuserdata= linda_id( "new" [, ...] ) | ||
736 | * = linda_id( "delete", lightuserdata ) | ||
737 | * | ||
738 | * Creation and cleanup of actual 'deep' objects. 'luaG_...' will wrap them into | ||
739 | * regular userdata proxies, per each state using the deep data. | ||
740 | * | ||
741 | * tbl= linda_id( "metatable" ) | ||
742 | * | ||
743 | * Returns a metatable for the proxy objects ('__gc' method not needed; will | ||
744 | * be added by 'luaG_...') | ||
745 | * | ||
746 | * string= linda_id( "module") | ||
747 | * | ||
748 | * Returns the name of the module that a state should require | ||
749 | * in order to keep a handle on the shared library that exported the idfunc | ||
750 | * | ||
751 | * = linda_id( str, ... ) | ||
752 | * | ||
753 | * For any other strings, the ID function must not react at all. This allows | ||
754 | * future extensions of the system. | ||
755 | */ | ||
756 | static void* linda_id( lua_State* L, DeepOp op_) | ||
757 | { | ||
758 | switch( op_) | ||
759 | { | ||
760 | case eDO_new: | ||
761 | { | ||
762 | struct s_Linda* s; | ||
763 | size_t name_len = 0; | ||
764 | char const* linda_name = NULL; | ||
765 | unsigned long linda_group = 0; | ||
766 | // should have a string and/or a number of the stack as parameters (name and group) | ||
767 | switch( lua_gettop( L)) | ||
768 | { | ||
769 | default: // 0 | ||
770 | break; | ||
771 | |||
772 | case 1: // 1 parameter, either a name or a group | ||
773 | if( lua_type( L, -1) == LUA_TSTRING) | ||
774 | { | ||
775 | linda_name = lua_tolstring( L, -1, &name_len); | ||
776 | } | ||
777 | else | ||
778 | { | ||
779 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
780 | } | ||
781 | break; | ||
782 | |||
783 | case 2: // 2 parameters, a name and group, in that order | ||
784 | linda_name = lua_tolstring( L, -2, &name_len); | ||
785 | linda_group = (unsigned long) lua_tointeger( L, -1); | ||
786 | break; | ||
787 | } | ||
788 | |||
789 | /* The deep data is allocated separately of Lua stack; we might no | ||
790 | * longer be around when last reference to it is being released. | ||
791 | * One can use any memory allocation scheme. | ||
792 | * just don't use L's allocF because we don't know which state will get the honor of GCing the linda | ||
793 | */ | ||
794 | s = (struct s_Linda*) malloc( sizeof(struct s_Linda) + name_len); // terminating 0 is already included | ||
795 | if( s) | ||
796 | { | ||
797 | SIGNAL_INIT( &s->read_happened); | ||
798 | SIGNAL_INIT( &s->write_happened); | ||
799 | s->U = universe_get( L); | ||
800 | s->simulate_cancel = CANCEL_NONE; | ||
801 | s->group = linda_group << KEEPER_MAGIC_SHIFT; | ||
802 | s->name[0] = 0; | ||
803 | memcpy( s->name, linda_name, name_len ? name_len + 1 : 0); | ||
804 | } | ||
805 | return s; | ||
806 | } | ||
807 | |||
808 | case eDO_delete: | ||
809 | { | ||
810 | Keeper* K; | ||
811 | struct s_Linda* linda = lua_touserdata( L, 1); | ||
812 | ASSERT_L( linda); | ||
813 | |||
814 | // Clean associated structures in the keeper state. | ||
815 | K = keeper_acquire( linda->U->keepers, LINDA_KEEPER_HASHSEED( linda)); | ||
816 | if( K && K->L) // can be NULL if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) | ||
817 | { | ||
818 | // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... | ||
819 | keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); | ||
820 | } | ||
821 | keeper_release( K); | ||
822 | |||
823 | // There aren't any lanes waiting on these lindas, since all proxies have been gc'ed. Right? | ||
824 | SIGNAL_FREE( &linda->read_happened); | ||
825 | SIGNAL_FREE( &linda->write_happened); | ||
826 | free( linda); | ||
827 | return NULL; | ||
828 | } | ||
829 | |||
830 | case eDO_metatable: | ||
831 | { | ||
832 | |||
833 | STACK_CHECK( L); | ||
834 | lua_newtable( L); | ||
835 | // metatable is its own index | ||
836 | lua_pushvalue( L, -1); | ||
837 | lua_setfield( L, -2, "__index"); | ||
838 | |||
839 | // protect metatable from external access | ||
840 | lua_pushliteral( L, "Linda"); | ||
841 | lua_setfield( L, -2, "__metatable"); | ||
842 | |||
843 | lua_pushcfunction( L, LG_linda_tostring); | ||
844 | lua_setfield( L, -2, "__tostring"); | ||
845 | |||
846 | // Decoda __towatch support | ||
847 | lua_pushcfunction( L, LG_linda_towatch); | ||
848 | lua_setfield( L, -2, "__towatch"); | ||
849 | |||
850 | lua_pushcfunction( L, LG_linda_concat); | ||
851 | lua_setfield( L, -2, "__concat"); | ||
852 | |||
853 | // protected calls, to ensure associated keeper is always released even in case of error | ||
854 | // all function are the protected call wrapper, where the actual operation is provided as upvalue | ||
855 | // note that this kind of thing can break function lookup as we use the function pointer here and there | ||
856 | |||
857 | lua_pushcfunction( L, LG_linda_send); | ||
858 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
859 | lua_setfield( L, -2, "send"); | ||
860 | |||
861 | lua_pushcfunction( L, LG_linda_receive); | ||
862 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
863 | lua_setfield( L, -2, "receive"); | ||
864 | |||
865 | lua_pushcfunction( L, LG_linda_limit); | ||
866 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
867 | lua_setfield( L, -2, "limit"); | ||
868 | |||
869 | lua_pushcfunction( L, LG_linda_set); | ||
870 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
871 | lua_setfield( L, -2, "set"); | ||
872 | |||
873 | lua_pushcfunction( L, LG_linda_count); | ||
874 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
875 | lua_setfield( L, -2, "count"); | ||
876 | |||
877 | lua_pushcfunction( L, LG_linda_get); | ||
878 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
879 | lua_setfield( L, -2, "get"); | ||
880 | |||
881 | lua_pushcfunction( L, LG_linda_cancel); | ||
882 | lua_setfield( L, -2, "cancel"); | ||
883 | |||
884 | lua_pushcfunction( L, LG_linda_deep); | ||
885 | lua_setfield( L, -2, "deep"); | ||
886 | |||
887 | lua_pushcfunction( L, LG_linda_dump); | ||
888 | lua_pushcclosure( L, LG_linda_protected_call, 1); | ||
889 | lua_setfield( L, -2, "dump"); | ||
890 | |||
891 | // some constants | ||
892 | lua_pushliteral( L, BATCH_SENTINEL); | ||
893 | lua_setfield(L, -2, "batched"); | ||
894 | |||
895 | push_unique_key( L, NIL_SENTINEL); | ||
896 | lua_setfield(L, -2, "null"); | ||
897 | |||
898 | luaG_pushdeepversion( L); | ||
899 | STACK_END( L, 2); | ||
900 | return NULL; | ||
901 | } | ||
902 | |||
903 | case eDO_module: | ||
904 | // linda is a special case because we know lanes must be loaded from the main lua state | ||
905 | // to be able to ever get here, so we know it will remain loaded as long a the main state is around | ||
906 | // in other words, forever. | ||
907 | default: | ||
908 | { | ||
909 | return NULL; | ||
910 | } | ||
911 | } | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * ud = lanes.linda( [name[,group]]) | ||
916 | * | ||
917 | * returns a linda object, or raises an error if creation failed | ||
918 | */ | ||
919 | LUAG_FUNC( linda) | ||
920 | { | ||
921 | int const top = lua_gettop( L); | ||
922 | luaL_argcheck( L, top <= 2, top, "too many arguments"); | ||
923 | if( top == 1) | ||
924 | { | ||
925 | int const t = lua_type( L, 1); | ||
926 | luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); | ||
927 | } | ||
928 | else if( top == 2) | ||
929 | { | ||
930 | luaL_checktype( L, 1, LUA_TSTRING); | ||
931 | luaL_checktype( L, 2, LUA_TNUMBER); | ||
932 | } | ||
933 | return luaG_newdeepuserdata( L, linda_id); | ||
934 | } | ||