diff options
author | Benoit Germain <bnt.germain@gmail.com> | 2011-04-18 08:25:23 +0200 |
---|---|---|
committer | Benoit Germain <bnt.germain@gmail.com> | 2011-04-18 08:25:23 +0200 |
commit | 05256f1e75f8583829bbaa7cdf1fb1518b93332a (patch) | |
tree | fc0d9a6855b6c05e06b06c933be8726bf546659c /src/lanes.c | |
parent | 2d5d798c24286f10c1bffd5ea4ac466c2c3a12fa (diff) | |
download | lanes-05256f1e75f8583829bbaa7cdf1fb1518b93332a.tar.gz lanes-05256f1e75f8583829bbaa7cdf1fb1518b93332a.tar.bz2 lanes-05256f1e75f8583829bbaa7cdf1fb1518b93332a.zip |
* linda uses a fast FIFO implementation to speed up data exchanges
* new linda:count() method
* new linda batched data read mode
* proper key type check in all linda methods
* fix setup-vc.cmd to support Visual Studio 2010 and Windows 7 64 bits
* bugfix: release keeper state mutex at desinit
Diffstat (limited to 'src/lanes.c')
-rw-r--r-- | src/lanes.c | 159 |
1 files changed, 106 insertions, 53 deletions
diff --git a/src/lanes.c b/src/lanes.c index e06d367..e514e6b 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -51,7 +51,7 @@ | |||
51 | * ... | 51 | * ... |
52 | */ | 52 | */ |
53 | 53 | ||
54 | const char *VERSION= "2.1.0"; | 54 | const char *VERSION= "2.2.0"; |
55 | 55 | ||
56 | /* | 56 | /* |
57 | =============================================================================== | 57 | =============================================================================== |
@@ -249,6 +249,20 @@ static void linda_id( lua_State*, char const * const which); | |||
249 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) | 249 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) |
250 | 250 | ||
251 | 251 | ||
252 | static void check_key_types( lua_State *L, int _start, int _end) | ||
253 | { | ||
254 | int i; | ||
255 | for( i = _start; i <= _end; ++ i) | ||
256 | { | ||
257 | int t = lua_type( L, i); | ||
258 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | ||
259 | { | ||
260 | continue; | ||
261 | } | ||
262 | luaL_error( L, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", i); | ||
263 | } | ||
264 | } | ||
265 | |||
252 | /* | 266 | /* |
253 | * bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) | 267 | * bool= linda_send( linda_ud, [timeout_secs=-1,] key_num|str|bool|lightuserdata, ... ) |
254 | * | 268 | * |
@@ -271,15 +285,18 @@ LUAG_FUNC( linda_send) | |||
271 | if( lua_isnumber(L, 2)) | 285 | if( lua_isnumber(L, 2)) |
272 | { | 286 | { |
273 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); | 287 | timeout= SIGNAL_TIMEOUT_PREPARE( lua_tonumber(L,2) ); |
274 | key_i++; | 288 | ++ key_i; |
275 | } | 289 | } |
276 | else if( lua_isnil( L, 2)) | 290 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key |
277 | { | 291 | { |
278 | key_i++; | 292 | ++ key_i; |
279 | } | 293 | } |
280 | 294 | ||
281 | if( lua_isnil( L, key_i)) | 295 | // make sure the keys are of a valid type |
282 | luaL_error( L, "nil key" ); | 296 | check_key_types( L, key_i, key_i); |
297 | |||
298 | // convert nils to some special non-nil sentinel in sent values | ||
299 | keeper_toggle_nil_sentinels( L, key_i + 1, 1); | ||
283 | 300 | ||
284 | STACK_GROW(L, 1); | 301 | STACK_GROW(L, 1); |
285 | { | 302 | { |
@@ -315,11 +332,11 @@ LUAG_FUNC( linda_send) | |||
315 | 332 | ||
316 | cancel = cancel_test( L); // testing here causes no delays | 333 | cancel = cancel_test( L); // testing here causes no delays |
317 | if (cancel) | 334 | if (cancel) |
335 | { | ||
318 | break; | 336 | break; |
337 | } | ||
319 | 338 | ||
320 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | 339 | // change status of lane to "waiting" |
321 | // | ||
322 | #if 1 | ||
323 | { | 340 | { |
324 | struct s_lane *s; | 341 | struct s_lane *s; |
325 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 342 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
@@ -338,6 +355,7 @@ LUAG_FUNC( linda_send) | |||
338 | ASSERT_L( s->waiting_on == NULL); | 355 | ASSERT_L( s->waiting_on == NULL); |
339 | s->waiting_on = &linda->read_happened; | 356 | s->waiting_on = &linda->read_happened; |
340 | } | 357 | } |
358 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
341 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | 359 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) |
342 | { | 360 | { |
343 | if( s) | 361 | if( s) |
@@ -353,12 +371,6 @@ LUAG_FUNC( linda_send) | |||
353 | s->status = prev_status; | 371 | s->status = prev_status; |
354 | } | 372 | } |
355 | } | 373 | } |
356 | #else | ||
357 | // K lock will be released for the duration of wait and re-acquired | ||
358 | // | ||
359 | if( !SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout)) | ||
360 | break; // timeout | ||
361 | #endif | ||
362 | } | 374 | } |
363 | STACK_END( KL, 0) | 375 | STACK_END( KL, 0) |
364 | keeper_release( K); | 376 | keeper_release( K); |
@@ -379,18 +391,23 @@ LUAG_FUNC( linda_send) | |||
379 | 391 | ||
380 | 392 | ||
381 | /* | 393 | /* |
382 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) | 394 | * 2 modes of operation |
383 | * | 395 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=-1], key_num|str|bool|lightuserdata [, ...] ) |
384 | * Receive a value from Linda, consuming it. | 396 | * Consumes a single value from the Linda, in any key. |
385 | * | 397 | * Returns: received value (which is consumed from the slot), and the key which had it |
386 | * Returns: value received (which is consumed from the slot) | 398 | |
387 | * key which had it | 399 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, COUNT) |
388 | */ | 400 | * Consumes COUNT values from the linda, from a single key. |
401 | * returns the COUNT consumed values, or nil if there weren't enough values to consume | ||
402 | * | ||
403 | */ | ||
404 | #define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" | ||
389 | LUAG_FUNC( linda_receive) | 405 | LUAG_FUNC( linda_receive) |
390 | { | 406 | { |
391 | struct s_Linda *linda = lua_toLinda( L, 1); | 407 | struct s_Linda *linda = lua_toLinda( L, 1); |
392 | int pushed; | 408 | int pushed, expected_pushed; |
393 | bool_t cancel = FALSE; | 409 | bool_t cancel = FALSE; |
410 | char *keeper_receive; | ||
394 | 411 | ||
395 | time_d timeout = -1.0; | 412 | time_d timeout = -1.0; |
396 | uint_t key_i = 2; | 413 | uint_t key_i = 2; |
@@ -400,26 +417,44 @@ LUAG_FUNC( linda_receive) | |||
400 | if( lua_isnumber( L, 2)) | 417 | if( lua_isnumber( L, 2)) |
401 | { | 418 | { |
402 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | 419 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); |
403 | key_i++; | 420 | ++ key_i; |
404 | } | 421 | } |
405 | else if( lua_isnil( L, 2)) | 422 | else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key |
406 | { | 423 | { |
407 | key_i++; | 424 | ++ key_i; |
408 | } | 425 | } |
409 | 426 | ||
427 | // make sure the keys are of a valid type | ||
428 | check_key_types( L, key_i, lua_gettop( L)); | ||
429 | |||
430 | // are we in batched mode? | ||
431 | lua_pushliteral( L, BATCH_SENTINEL); | ||
432 | if( lua_equal( L, key_i, -1)) | ||
433 | { | ||
434 | keeper_receive = "receive_batched"; | ||
435 | expected_pushed = (int)luaL_checkinteger( L, key_i + 2); | ||
436 | } | ||
437 | else | ||
438 | { | ||
439 | keeper_receive = "receive"; | ||
440 | expected_pushed = 2; | ||
441 | } | ||
442 | lua_pop( L, 1); | ||
443 | |||
410 | { | 444 | { |
411 | struct s_Keeper *K = keeper_acquire( linda); | 445 | struct s_Keeper *K = keeper_acquire( linda); |
412 | for( ;;) | 446 | for( ;;) |
413 | { | 447 | { |
414 | pushed = keeper_call( K->L, "receive", L, linda, key_i); | 448 | pushed = keeper_call( K->L, keeper_receive, L, linda, key_i); |
415 | if( pushed < 0) | 449 | if( pushed < 0) |
416 | { | 450 | { |
417 | break; | 451 | break; |
418 | } | 452 | } |
419 | if( pushed > 0) | 453 | if( pushed > 0) |
420 | { | 454 | { |
421 | ASSERT_L( pushed == 2); | 455 | ASSERT_L( pushed == expected_pushed); |
422 | 456 | // replace sentinels with real nils | |
457 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | ||
423 | // To be done from within the 'K' locking area | 458 | // To be done from within the 'K' locking area |
424 | // | 459 | // |
425 | SIGNAL_ALL( &linda->read_happened); | 460 | SIGNAL_ALL( &linda->read_happened); |
@@ -438,9 +473,7 @@ LUAG_FUNC( linda_receive) | |||
438 | break; | 473 | break; |
439 | } | 474 | } |
440 | 475 | ||
441 | // Bugfix by Benoit Germain Dec-2009: change status of lane to "waiting" | 476 | // change status of lane to "waiting" |
442 | // | ||
443 | #if 1 | ||
444 | { | 477 | { |
445 | struct s_lane *s; | 478 | struct s_lane *s; |
446 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 479 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
@@ -459,6 +492,7 @@ LUAG_FUNC( linda_receive) | |||
459 | ASSERT_L( s->waiting_on == NULL); | 492 | ASSERT_L( s->waiting_on == NULL); |
460 | s->waiting_on = &linda->write_happened; | 493 | s->waiting_on = &linda->write_happened; |
461 | } | 494 | } |
495 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
462 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | 496 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) |
463 | { | 497 | { |
464 | if( s) | 498 | if( s) |
@@ -474,12 +508,6 @@ LUAG_FUNC( linda_receive) | |||
474 | s->status = prev_status; | 508 | s->status = prev_status; |
475 | } | 509 | } |
476 | } | 510 | } |
477 | #else | ||
478 | // Release the K lock for the duration of wait, and re-acquire | ||
479 | // | ||
480 | if( !SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout)) | ||
481 | break; | ||
482 | #endif | ||
483 | } | 511 | } |
484 | keeper_release( K); | 512 | keeper_release( K); |
485 | } | 513 | } |
@@ -501,6 +529,7 @@ LUAG_FUNC( linda_receive) | |||
501 | * = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) | 529 | * = linda_set( linda_ud, key_num|str|bool|lightuserdata [,value] ) |
502 | * | 530 | * |
503 | * Set a value to Linda. | 531 | * Set a value to Linda. |
532 | * TODO: what do we do if we set to non-nil and limit is 0? | ||
504 | * | 533 | * |
505 | * Existing slot value is replaced, and possible queue entries removed. | 534 | * Existing slot value is replaced, and possible queue entries removed. |
506 | */ | 535 | */ |
@@ -510,9 +539,14 @@ LUAG_FUNC( linda_set) | |||
510 | bool_t has_value = !lua_isnil( L, 3); | 539 | bool_t has_value = !lua_isnil( L, 3); |
511 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 540 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
512 | 541 | ||
542 | // make sure the key is of a valid type | ||
543 | check_key_types( L, 2, 2); | ||
544 | |||
513 | { | 545 | { |
546 | int pushed; | ||
514 | struct s_Keeper *K = keeper_acquire( linda); | 547 | struct s_Keeper *K = keeper_acquire( linda); |
515 | int pushed = keeper_call( K->L, "set", L, linda, 2); | 548 | // no nil->sentinel toggling, we really clear the linda contents for the given key with a set() |
549 | pushed = keeper_call( K->L, "set", L, linda, 2); | ||
516 | if( pushed >= 0) // no error? | 550 | if( pushed >= 0) // no error? |
517 | { | 551 | { |
518 | ASSERT_L( pushed == 0); | 552 | ASSERT_L( pushed == 0); |
@@ -537,28 +571,28 @@ LUAG_FUNC( linda_set) | |||
537 | 571 | ||
538 | 572 | ||
539 | /* | 573 | /* |
540 | * [val]= linda_keys( linda_ud) | 574 | * [val] = linda_count( linda_ud, [key [, ...]]) |
541 | * | 575 | * |
542 | * Get the list of keys with pending data in the linda | 576 | * Get a count of the pending elements in the specified keys |
543 | */ | 577 | */ |
544 | LUAG_FUNC( linda_keys) | 578 | LUAG_FUNC( linda_count) |
545 | { | 579 | { |
546 | struct s_Linda *linda= lua_toLinda( L, 1); | 580 | struct s_Linda *linda= lua_toLinda( L, 1); |
547 | int pushed; | 581 | int pushed; |
582 | |||
548 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 583 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
584 | // make sure the keys are of a valid type | ||
585 | check_key_types( L, 2, lua_gettop( L)); | ||
549 | 586 | ||
550 | { | 587 | { |
551 | struct s_Keeper *K = keeper_acquire( linda); | 588 | struct s_Keeper *K = keeper_acquire( linda); |
552 | pushed = keeper_call( K->L, "keys", L, linda, 2); | 589 | pushed = keeper_call( K->L, "count", L, linda, 2); |
553 | ASSERT_L( pushed==0 || pushed==1 ); | 590 | keeper_release( K); |
554 | keeper_release(K); | ||
555 | // must trigger error after keeper state has been released | ||
556 | if( pushed < 0) | 591 | if( pushed < 0) |
557 | { | 592 | { |
558 | luaL_error( L, "tried to copy unsupported types"); | 593 | luaL_error( L, "tried to count an invalid key"); |
559 | } | 594 | } |
560 | } | 595 | } |
561 | |||
562 | return pushed; | 596 | return pushed; |
563 | } | 597 | } |
564 | 598 | ||
@@ -567,17 +601,25 @@ LUAG_FUNC( linda_keys) | |||
567 | * [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) | 601 | * [val]= linda_get( linda_ud, key_num|str|bool|lightuserdata ) |
568 | * | 602 | * |
569 | * Get a value from Linda. | 603 | * Get a value from Linda. |
604 | * TODO: add support to get multiple values? | ||
570 | */ | 605 | */ |
571 | LUAG_FUNC( linda_get) | 606 | LUAG_FUNC( linda_get) |
572 | { | 607 | { |
573 | struct s_Linda *linda= lua_toLinda( L, 1); | 608 | struct s_Linda *linda= lua_toLinda( L, 1); |
574 | int pushed; | 609 | int pushed; |
610 | |||
575 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 611 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
612 | // make sure the key is of a valid type | ||
613 | check_key_types( L, 2, 2); | ||
576 | 614 | ||
577 | { | 615 | { |
578 | struct s_Keeper *K = keeper_acquire( linda); | 616 | struct s_Keeper *K = keeper_acquire( linda); |
579 | pushed = keeper_call( K->L, "get", L, linda, 2); | 617 | pushed = keeper_call( K->L, "get", L, linda, 2); |
580 | ASSERT_L( pushed==0 || pushed==1 ); | 618 | ASSERT_L( pushed==0 || pushed==1 ); |
619 | if( pushed > 0) | ||
620 | { | ||
621 | keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, 0); | ||
622 | } | ||
581 | keeper_release(K); | 623 | keeper_release(K); |
582 | // must trigger error after keeper state has been released | 624 | // must trigger error after keeper state has been released |
583 | if( pushed < 0) | 625 | if( pushed < 0) |
@@ -598,7 +640,10 @@ LUAG_FUNC( linda_get) | |||
598 | LUAG_FUNC( linda_limit) | 640 | LUAG_FUNC( linda_limit) |
599 | { | 641 | { |
600 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 642 | struct s_Linda *linda= lua_toLinda( L, 1 ); |
643 | |||
601 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 644 | luaL_argcheck( L, linda, 1, "expected a linda object!"); |
645 | // make sure the key is of a valid type | ||
646 | check_key_types( L, 2, 2); | ||
602 | 647 | ||
603 | { | 648 | { |
604 | struct s_Keeper *K = keeper_acquire( linda); | 649 | struct s_Keeper *K = keeper_acquire( linda); |
@@ -775,21 +820,24 @@ static void linda_id( lua_State *L, char const * const which) | |||
775 | lua_pushcfunction( L, LG_linda_receive ); | 820 | lua_pushcfunction( L, LG_linda_receive ); |
776 | lua_setfield( L, -2, "receive" ); | 821 | lua_setfield( L, -2, "receive" ); |
777 | 822 | ||
778 | lua_pushcfunction( L, LG_linda_keys ); | ||
779 | lua_setfield( L, -2, "keys" ); | ||
780 | |||
781 | lua_pushcfunction( L, LG_linda_limit ); | 823 | lua_pushcfunction( L, LG_linda_limit ); |
782 | lua_setfield( L, -2, "limit" ); | 824 | lua_setfield( L, -2, "limit" ); |
783 | 825 | ||
784 | lua_pushcfunction( L, LG_linda_set ); | 826 | lua_pushcfunction( L, LG_linda_set ); |
785 | lua_setfield( L, -2, "set" ); | 827 | lua_setfield( L, -2, "set" ); |
786 | 828 | ||
829 | lua_pushcfunction( L, LG_linda_count ); | ||
830 | lua_setfield( L, -2, "count" ); | ||
831 | |||
787 | lua_pushcfunction( L, LG_linda_get ); | 832 | lua_pushcfunction( L, LG_linda_get ); |
788 | lua_setfield( L, -2, "get" ); | 833 | lua_setfield( L, -2, "get" ); |
789 | 834 | ||
790 | lua_pushcfunction( L, LG_linda_deep ); | 835 | lua_pushcfunction( L, LG_linda_deep ); |
791 | lua_setfield( L, -2, "deep" ); | 836 | lua_setfield( L, -2, "deep" ); |
792 | 837 | ||
838 | lua_pushliteral( L, BATCH_SENTINEL); | ||
839 | lua_setfield(L, -2, "batched"); | ||
840 | |||
793 | STACK_END(L,1) | 841 | STACK_END(L,1) |
794 | } | 842 | } |
795 | else if( strcmp( which, "module") == 0) | 843 | else if( strcmp( which, "module") == 0) |
@@ -803,6 +851,11 @@ static void linda_id( lua_State *L, char const * const which) | |||
803 | } | 851 | } |
804 | } | 852 | } |
805 | 853 | ||
854 | /* | ||
855 | * ud = lanes.linda() | ||
856 | * | ||
857 | * returns a linda object | ||
858 | */ | ||
806 | LUAG_FUNC( linda) | 859 | LUAG_FUNC( linda) |
807 | { | 860 | { |
808 | return luaG_deep_userdata( L, linda_id); | 861 | return luaG_deep_userdata( L, linda_id); |