diff options
-rw-r--r-- | CHANGES | 7 | ||||
-rw-r--r-- | docs/index.html | 14 | ||||
-rw-r--r-- | src/keeper.c | 2 | ||||
-rw-r--r-- | src/lanes.c | 160 | ||||
-rw-r--r-- | src/lanes.lua | 88 | ||||
-rw-r--r-- | tests/cancel.lua | 30 | ||||
-rw-r--r-- | tests/linda_perf.lua | 4 |
7 files changed, 178 insertions, 127 deletions
@@ -1,5 +1,12 @@ | |||
1 | CHANGES: | 1 | CHANGES: |
2 | 2 | ||
3 | CHANGE 98: BGe 13-Feb-14 | ||
4 | * version 3.8.5 | ||
5 | * linda:limit() returns lanes.cancel_error on a limited linda | ||
6 | * lanes.genlock() and lanes.genatomic() support cancelled lindas by returning lanes.cancel_error whenever appropriate | ||
7 | * fixed a possible Lua stack overflow when calling linda:dump() | ||
8 | * fixed cases where linda:send() and linda:receive() would not return lanes.cancel_error when they should | ||
9 | |||
3 | CHANGE 97: BGe 10-Feb-14 | 10 | CHANGE 97: BGe 10-Feb-14 |
4 | * version 3.8.4 | 11 | * version 3.8.4 |
5 | * new API linda:cancel("read"|"write"|"both"|"none") | 12 | * new API linda:cancel("read"|"write"|"both"|"none") |
diff --git a/docs/index.html b/docs/index.html index 1ad25a6..2468ad8 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -70,7 +70,7 @@ | |||
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 10-Feb-14, and applies to version <tt>3.8.4</tt>. | 73 | This document was revised on 13-Feb-14, and applies to version <tt>3.8.5</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -1039,7 +1039,7 @@ | |||
1039 | 1039 | ||
1040 | [key, val [, ...]]|[lanes.cancel_error] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) | 1040 | [key, val [, ...]]|[lanes.cancel_error] = h:receive( timeout, h.batched, key, n_uint_min[, n_uint_max]) |
1041 | 1041 | ||
1042 | [true] = h:limit( key, n_uint) | 1042 | [true|lanes.cancel_error] = h:limit( key, n_uint) |
1043 | </pre></td></tr></table> | 1043 | </pre></td></tr></table> |
1044 | 1044 | ||
1045 | <p> | 1045 | <p> |
@@ -1288,11 +1288,11 @@ events to a common Linda, but... :).</font> | |||
1288 | </p> | 1288 | </p> |
1289 | 1289 | ||
1290 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1290 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1291 | lock_func = lanes.genlock( linda_h, key [,N_uint=1]) | 1291 | lock_func|lanes.cancel_error = lanes.genlock( linda_h, key [,N_uint=1]) |
1292 | 1292 | ||
1293 | bool = lock_func( M_uint [, "try"] ) -- acquire | 1293 | bool|lanes.cancel_error = lock_func( M_uint [, "try"] ) -- acquire |
1294 | .. | 1294 | .. |
1295 | bool = lock_func( -M_uint) -- release | 1295 | bool|lanes.cancel_error = lock_func( -M_uint) -- release |
1296 | </pre></td></tr></table> | 1296 | </pre></td></tr></table> |
1297 | 1297 | ||
1298 | <p> | 1298 | <p> |
@@ -1314,9 +1314,9 @@ events to a common Linda, but... :).</font> | |||
1314 | <p> | 1314 | <p> |
1315 | 1315 | ||
1316 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1316 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1317 | atomic_func = lanes.genatomic( linda_h, key [,initial_num=0.0]) | 1317 | atomic_func|lanes.cancel_error = lanes.genatomic( linda_h, key [,initial_num=0.0]) |
1318 | 1318 | ||
1319 | new_num = atomic_func( [diff_num=+1.0]) | 1319 | new_num|lanes.cancel_error = atomic_func( [diff_num=+1.0]) |
1320 | </pre></td></tr></table> | 1320 | </pre></td></tr></table> |
1321 | 1321 | ||
1322 | <p> | 1322 | <p> |
diff --git a/src/keeper.c b/src/keeper.c index 1a696aa..c22bfed 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -190,6 +190,7 @@ int keeper_push_linda_storage( lua_State* L, void* ptr) | |||
190 | struct s_Keeper* K = keeper_acquire( ptr); | 190 | struct s_Keeper* K = keeper_acquire( ptr); |
191 | lua_State* KL = K ? K->L : NULL; | 191 | lua_State* KL = K ? K->L : NULL; |
192 | if( KL == NULL) return 0; | 192 | if( KL == NULL) return 0; |
193 | STACK_GROW( KL, 4); | ||
193 | STACK_CHECK( KL); | 194 | STACK_CHECK( KL); |
194 | lua_pushlightuserdata( KL, fifos_key); // fifos_key | 195 | lua_pushlightuserdata( KL, fifos_key); // fifos_key |
195 | lua_rawget( KL, LUA_REGISTRYINDEX); // fifos | 196 | lua_rawget( KL, LUA_REGISTRYINDEX); // fifos |
@@ -204,6 +205,7 @@ int keeper_push_linda_storage( lua_State* L, void* ptr) | |||
204 | } | 205 | } |
205 | // move data from keeper to destination state KEEPER MAIN | 206 | // move data from keeper to destination state KEEPER MAIN |
206 | lua_pushnil( KL); // storage nil | 207 | lua_pushnil( KL); // storage nil |
208 | STACK_GROW( L, 5); | ||
207 | STACK_CHECK( L); | 209 | STACK_CHECK( L); |
208 | lua_newtable( L); // out | 210 | lua_newtable( L); // out |
209 | while( lua_next( KL, -2)) // storage key fifo | 211 | while( lua_next( KL, -2)) // storage key fifo |
diff --git a/src/lanes.c b/src/lanes.c index cf88171..dbb0a82 100644 --- a/src/lanes.c +++ b/src/lanes.c | |||
@@ -52,7 +52,7 @@ | |||
52 | * ... | 52 | * ... |
53 | */ | 53 | */ |
54 | 54 | ||
55 | char const* VERSION = "3.8.4"; | 55 | char const* VERSION = "3.8.5"; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | =============================================================================== | 58 | =============================================================================== |
@@ -424,13 +424,17 @@ struct s_Linda | |||
424 | 424 | ||
425 | static void linda_id( lua_State*, char const * const which); | 425 | static void linda_id( lua_State*, char const * const which); |
426 | 426 | ||
427 | #define lua_toLinda(L,n) ((struct s_Linda *)luaG_todeep( L, linda_id, n )) | 427 | static inline struct s_Linda* lua_toLinda( lua_State* L, int idx_) |
428 | 428 | { | |
429 | struct s_Linda* linda = luaG_todeep( L, linda_id, idx_); | ||
430 | luaL_argcheck( L, linda != NULL, idx_, "expecting a linda object"); | ||
431 | return linda; | ||
432 | } | ||
429 | 433 | ||
430 | static void check_key_types( lua_State*L, int _start, int _end) | 434 | static void check_key_types( lua_State* L, int start_, int end_) |
431 | { | 435 | { |
432 | int i; | 436 | int i; |
433 | for( i = _start; i <= _end; ++ i) | 437 | for( i = start_; i <= end_; ++ i) |
434 | { | 438 | { |
435 | int t = lua_type( L, i); | 439 | int t = lua_type( L, i); |
436 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) | 440 | if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) |
@@ -459,8 +463,6 @@ LUAG_FUNC( linda_send) | |||
459 | time_d timeout = -1.0; | 463 | time_d timeout = -1.0; |
460 | uint_t key_i = 2; // index of first key, if timeout not there | 464 | uint_t key_i = 2; // index of first key, if timeout not there |
461 | 465 | ||
462 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
463 | |||
464 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 466 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion |
465 | { | 467 | { |
466 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L,2)); | 468 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L,2)); |
@@ -485,12 +487,26 @@ LUAG_FUNC( linda_send) | |||
485 | 487 | ||
486 | STACK_GROW( L, 1); | 488 | STACK_GROW( L, 1); |
487 | { | 489 | { |
490 | bool_t try_again = TRUE; | ||
491 | struct s_lane* const s = get_lane_from_registry( L); | ||
488 | struct s_Keeper* K = keeper_acquire( linda); | 492 | struct s_Keeper* K = keeper_acquire( linda); |
489 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' | 493 | lua_State* KL = K ? K->L : NULL; // need to do this for 'STACK_CHECK' |
490 | if( KL == NULL) return 0; | 494 | if( KL == NULL) return 0; |
491 | STACK_CHECK( KL); | 495 | STACK_CHECK( KL); |
492 | for( ;;) | 496 | for( ;;) |
493 | { | 497 | { |
498 | if( s != NULL) | ||
499 | { | ||
500 | cancel = s->cancel_request; | ||
501 | } | ||
502 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
503 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
504 | if( !try_again || cancel != CANCEL_NONE) | ||
505 | { | ||
506 | pushed = 0; | ||
507 | break; | ||
508 | } | ||
509 | |||
494 | STACK_MID( KL, 0); | 510 | STACK_MID( KL, 0); |
495 | pushed = keeper_call( KL, KEEPER_API( send), L, linda, key_i); | 511 | pushed = keeper_call( KL, KEEPER_API( send), L, linda, key_i); |
496 | if( pushed < 0) | 512 | if( pushed < 0) |
@@ -506,28 +522,19 @@ LUAG_FUNC( linda_send) | |||
506 | if( ret) | 522 | if( ret) |
507 | { | 523 | { |
508 | // Wake up ALL waiting threads | 524 | // Wake up ALL waiting threads |
509 | // | ||
510 | SIGNAL_ALL( &linda->write_happened); | 525 | SIGNAL_ALL( &linda->write_happened); |
511 | break; | 526 | break; |
512 | } | 527 | } |
528 | |||
529 | // instant timout to bypass the | ||
513 | if( timeout == 0.0) | 530 | if( timeout == 0.0) |
514 | { | 531 | { |
515 | break; /* no wait; instant timeout */ | 532 | break; /* no wait; instant timeout */ |
516 | } | 533 | } |
517 | /* limit faced; push until timeout */ | ||
518 | 534 | ||
535 | // storage limit hit, wait until timeout or signalled that we should try again | ||
519 | { | 536 | { |
520 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 537 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
521 | struct s_lane* const s = get_lane_from_registry( L); | ||
522 | if( s != NULL) | ||
523 | { | ||
524 | cancel = s->cancel_request; | ||
525 | } | ||
526 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
527 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without sending anything | ||
528 | { | ||
529 | break; | ||
530 | } | ||
531 | if( s != NULL) | 538 | if( s != NULL) |
532 | { | 539 | { |
533 | // change status of lane to "waiting" | 540 | // change status of lane to "waiting" |
@@ -537,22 +544,12 @@ LUAG_FUNC( linda_send) | |||
537 | ASSERT_L( s->waiting_on == NULL); | 544 | ASSERT_L( s->waiting_on == NULL); |
538 | s->waiting_on = &linda->read_happened; | 545 | s->waiting_on = &linda->read_happened; |
539 | } | 546 | } |
547 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
548 | try_again = SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout); | ||
549 | if( s != NULL) | ||
540 | { | 550 | { |
541 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | 551 | s->waiting_on = NULL; |
542 | bool_t const signalled = SIGNAL_WAIT( &linda->read_happened, &K->lock_, timeout); | 552 | s->status = prev_status; |
543 | if( s != NULL) | ||
544 | { | ||
545 | s->waiting_on = NULL; | ||
546 | s->status = prev_status; | ||
547 | // if a cancel request is pending, be sure to handle it as soon as possible | ||
548 | cancel = s->cancel_request; | ||
549 | } | ||
550 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
551 | if( !signalled || cancel != CANCEL_NONE) | ||
552 | { | ||
553 | // waiting returned after a timeout, or pending cancel: we are done | ||
554 | break; | ||
555 | } | ||
556 | } | 553 | } |
557 | } | 554 | } |
558 | } | 555 | } |
@@ -569,7 +566,7 @@ LUAG_FUNC( linda_send) | |||
569 | switch( cancel) | 566 | switch( cancel) |
570 | { | 567 | { |
571 | case CANCEL_SOFT: | 568 | case CANCEL_SOFT: |
572 | // if user wants to soft-cancel, the call returns CANCEL_ERROR | 569 | // if user wants to soft-cancel, the call returns lanes.cancel_error |
573 | lua_pushlightuserdata( L, CANCEL_ERROR); | 570 | lua_pushlightuserdata( L, CANCEL_ERROR); |
574 | return 1; | 571 | return 1; |
575 | 572 | ||
@@ -606,8 +603,6 @@ LUAG_FUNC( linda_receive) | |||
606 | time_d timeout = -1.0; | 603 | time_d timeout = -1.0; |
607 | uint_t key_i = 2; | 604 | uint_t key_i = 2; |
608 | 605 | ||
609 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
610 | |||
611 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion | 606 | if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion |
612 | { | 607 | { |
613 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); | 608 | timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); |
@@ -655,10 +650,24 @@ LUAG_FUNC( linda_receive) | |||
655 | } | 650 | } |
656 | 651 | ||
657 | { | 652 | { |
653 | bool_t try_again = TRUE; | ||
654 | struct s_lane* const s = get_lane_from_registry( L); | ||
658 | struct s_Keeper* K = keeper_acquire( linda); | 655 | struct s_Keeper* K = keeper_acquire( linda); |
659 | if( K == NULL) return 0; | 656 | if( K == NULL) return 0; |
660 | for( ;;) | 657 | for( ;;) |
661 | { | 658 | { |
659 | if( s != NULL) | ||
660 | { | ||
661 | cancel = s->cancel_request; | ||
662 | } | ||
663 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
664 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
665 | if( !try_again || cancel != CANCEL_NONE) | ||
666 | { | ||
667 | pushed = 0; | ||
668 | break; | ||
669 | } | ||
670 | |||
662 | // all arguments of receive() but the first are passed to the keeper's receive function | 671 | // all arguments of receive() but the first are passed to the keeper's receive function |
663 | pushed = keeper_call( K->L, keeper_receive, L, linda, key_i); | 672 | pushed = keeper_call( K->L, keeper_receive, L, linda, key_i); |
664 | if( pushed < 0) | 673 | if( pushed < 0) |
@@ -674,26 +683,16 @@ LUAG_FUNC( linda_receive) | |||
674 | // | 683 | // |
675 | SIGNAL_ALL( &linda->read_happened); | 684 | SIGNAL_ALL( &linda->read_happened); |
676 | break; | 685 | break; |
677 | |||
678 | } | 686 | } |
687 | |||
679 | if( timeout == 0.0) | 688 | if( timeout == 0.0) |
680 | { | 689 | { |
681 | break; /* instant timeout */ | 690 | break; /* instant timeout */ |
682 | } | 691 | } |
683 | /* nothing received; wait until timeout */ | ||
684 | 692 | ||
693 | // nothing received, wait until timeout or signalled that we should try again | ||
685 | { | 694 | { |
686 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings | 695 | enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings |
687 | struct s_lane* const s = get_lane_from_registry( L); | ||
688 | if( s != NULL) | ||
689 | { | ||
690 | cancel = s->cancel_request; | ||
691 | } | ||
692 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
693 | if( cancel != CANCEL_NONE) // if user wants to cancel, the call returns without providing anything | ||
694 | { | ||
695 | break; | ||
696 | } | ||
697 | if( s != NULL) | 696 | if( s != NULL) |
698 | { | 697 | { |
699 | // change status of lane to "waiting" | 698 | // change status of lane to "waiting" |
@@ -703,22 +702,12 @@ LUAG_FUNC( linda_receive) | |||
703 | ASSERT_L( s->waiting_on == NULL); | 702 | ASSERT_L( s->waiting_on == NULL); |
704 | s->waiting_on = &linda->write_happened; | 703 | s->waiting_on = &linda->write_happened; |
705 | } | 704 | } |
705 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
706 | try_again = SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout); | ||
707 | if( s != NULL) | ||
706 | { | 708 | { |
707 | // not enough data to read: wakeup when data was sent, or when timeout is reached | 709 | s->waiting_on = NULL; |
708 | bool_t const signalled = SIGNAL_WAIT( &linda->write_happened, &K->lock_, timeout); | 710 | s->status = prev_status; |
709 | if( s != NULL) | ||
710 | { | ||
711 | s->waiting_on = NULL; | ||
712 | s->status = prev_status; | ||
713 | // if a cancel request is pending, be sure to handle it as soon as possible | ||
714 | cancel = s->cancel_request; | ||
715 | } | ||
716 | cancel = (cancel != CANCEL_NONE) ? cancel : linda->simulate_cancel; | ||
717 | if( !signalled || cancel != CANCEL_NONE) | ||
718 | { | ||
719 | // waiting returned after a timeout, or pending cancel: we are done | ||
720 | break; | ||
721 | } | ||
722 | } | 711 | } |
723 | } | 712 | } |
724 | } | 713 | } |
@@ -761,7 +750,6 @@ LUAG_FUNC( linda_set) | |||
761 | struct s_Linda* const linda = lua_toLinda( L, 1); | 750 | struct s_Linda* const linda = lua_toLinda( L, 1); |
762 | int pushed; | 751 | int pushed; |
763 | bool_t has_value = lua_gettop( L) > 2; | 752 | bool_t has_value = lua_gettop( L) > 2; |
764 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
765 | 753 | ||
766 | // make sure the key is of a valid type (throws an error if not the case) | 754 | // make sure the key is of a valid type (throws an error if not the case) |
767 | check_key_types( L, 2, 2); | 755 | check_key_types( L, 2, 2); |
@@ -819,7 +807,6 @@ LUAG_FUNC( linda_count) | |||
819 | struct s_Linda* linda = lua_toLinda( L, 1); | 807 | struct s_Linda* linda = lua_toLinda( L, 1); |
820 | int pushed; | 808 | int pushed; |
821 | 809 | ||
822 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
823 | // make sure the keys are of a valid type | 810 | // make sure the keys are of a valid type |
824 | check_key_types( L, 2, lua_gettop( L)); | 811 | check_key_types( L, 2, lua_gettop( L)); |
825 | 812 | ||
@@ -848,7 +835,6 @@ LUAG_FUNC( linda_get) | |||
848 | int pushed; | 835 | int pushed; |
849 | int count = luaL_optint( L, 3, 1); | 836 | int count = luaL_optint( L, 3, 1); |
850 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); | 837 | luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); |
851 | luaL_argcheck( L, linda, 1, "expected a linda object"); | ||
852 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); | 838 | luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments"); |
853 | 839 | ||
854 | // make sure the key is of a valid type (throws an error if not the case) | 840 | // make sure the key is of a valid type (throws an error if not the case) |
@@ -897,7 +883,6 @@ LUAG_FUNC( linda_limit) | |||
897 | bool_t wake_writers = FALSE; | 883 | bool_t wake_writers = FALSE; |
898 | 884 | ||
899 | // make sure we got 3 arguments: the linda, a key and a limit | 885 | // make sure we got 3 arguments: the linda, a key and a limit |
900 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | ||
901 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); | 886 | luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); |
902 | // make sure we got a numeric limit | 887 | // make sure we got a numeric limit |
903 | luaL_checknumber( L, 3); | 888 | luaL_checknumber( L, 3); |
@@ -907,12 +892,22 @@ LUAG_FUNC( linda_limit) | |||
907 | { | 892 | { |
908 | struct s_Keeper* K = keeper_acquire( linda); | 893 | struct s_Keeper* K = keeper_acquire( linda); |
909 | if( K == NULL) return 0; | 894 | if( K == NULL) return 0; |
910 | pushed = keeper_call( K->L, KEEPER_API( limit), L, linda, 2); | 895 | |
911 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | 896 | if( linda->simulate_cancel == CANCEL_NONE) |
912 | if( pushed == 1) | 897 | { |
898 | pushed = keeper_call( K->L, KEEPER_API( limit), L, linda, 2); | ||
899 | ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads | ||
900 | if( pushed == 1) | ||
901 | { | ||
902 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | ||
903 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | ||
904 | } | ||
905 | } | ||
906 | else // linda is cancelled | ||
913 | { | 907 | { |
914 | ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1); | 908 | // do nothing and return lanes.cancel_error |
915 | SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area | 909 | lua_pushlightuserdata( L, CANCEL_ERROR); |
910 | pushed = 1; | ||
916 | } | 911 | } |
917 | keeper_release( K); | 912 | keeper_release( K); |
918 | } | 913 | } |
@@ -929,12 +924,11 @@ LUAG_FUNC( linda_limit) | |||
929 | LUAG_FUNC( linda_cancel) | 924 | LUAG_FUNC( linda_cancel) |
930 | { | 925 | { |
931 | struct s_Linda* linda = lua_toLinda( L, 1); | 926 | struct s_Linda* linda = lua_toLinda( L, 1); |
932 | char const* who = luaL_checkstring( L, 2); | 927 | char const* who = luaL_optstring( L, 2, "both"); |
933 | struct s_Keeper* K; | 928 | struct s_Keeper* K; |
934 | 929 | ||
935 | // make sure we got 3 arguments: the linda, a key and a limit | 930 | // make sure we got 3 arguments: the linda, a key and a limit |
936 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 931 | luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); |
937 | luaL_argcheck( L, lua_gettop( L) == 2, 2, "wrong number of arguments"); | ||
938 | 932 | ||
939 | // signalling must be done from inside the K locking area | 933 | // signalling must be done from inside the K locking area |
940 | K = keeper_acquire( linda); | 934 | K = keeper_acquire( linda); |
@@ -984,11 +978,11 @@ LUAG_FUNC( linda_cancel) | |||
984 | * different userdata and won't be known to be essentially the same deep one | 978 | * different userdata and won't be known to be essentially the same deep one |
985 | * without this. | 979 | * without this. |
986 | */ | 980 | */ |
987 | LUAG_FUNC( linda_deep ) { | 981 | LUAG_FUNC( linda_deep) |
988 | struct s_Linda *linda= lua_toLinda( L, 1 ); | 982 | { |
989 | luaL_argcheck( L, linda, 1, "expected a linda object!"); | 983 | struct s_Linda* linda= lua_toLinda( L, 1); |
990 | lua_pushlightuserdata( L, linda ); // just the address | 984 | lua_pushlightuserdata( L, linda); // just the address |
991 | return 1; | 985 | return 1; |
992 | } | 986 | } |
993 | 987 | ||
994 | 988 | ||
@@ -1002,10 +996,10 @@ LUAG_FUNC( linda_deep ) { | |||
1002 | 996 | ||
1003 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) | 997 | static int linda_tostring( lua_State* L, int idx_, bool_t opt_) |
1004 | { | 998 | { |
1005 | struct s_Linda* linda = lua_toLinda( L, idx_); | 999 | struct s_Linda* linda = luaG_todeep( L, linda_id, idx_); |
1006 | if( !opt_) | 1000 | if( !opt_) |
1007 | { | 1001 | { |
1008 | luaL_argcheck( L, linda, idx_, "expected a linda object!"); | 1002 | luaL_argcheck( L, linda, idx_, "expecting a linda object"); |
1009 | } | 1003 | } |
1010 | if( linda != NULL) | 1004 | if( linda != NULL) |
1011 | { | 1005 | { |
diff --git a/src/lanes.lua b/src/lanes.lua index 1286099..86dbe47 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -585,13 +585,25 @@ end | |||
585 | 585 | ||
586 | end -- settings.with_timers | 586 | end -- settings.with_timers |
587 | 587 | ||
588 | -- avoid pulling the whole core module as upvalue when cancel_error is enough | ||
589 | local cancel_error = assert( core.cancel_error) | ||
590 | |||
588 | ---=== Lock & atomic generators ===--- | 591 | ---=== Lock & atomic generators ===--- |
589 | 592 | ||
590 | -- These functions are just surface sugar, but make solutions easier to read. | 593 | -- These functions are just surface sugar, but make solutions easier to read. |
591 | -- Not many applications should even need explicit locks or atomic counters. | 594 | -- Not many applications should even need explicit locks or atomic counters. |
592 | 595 | ||
593 | -- | 596 | -- |
594 | -- lock_f= lanes.genlock( linda_h, key [,N_uint=1] ) | 597 | -- [true [, ...]= trues(uint) |
598 | -- | ||
599 | local function trues( n) | ||
600 | if n > 0 then | ||
601 | return true, trues( n - 1) | ||
602 | end | ||
603 | end | ||
604 | |||
605 | -- | ||
606 | -- lock_f = lanes.genlock( linda_h, key [,N_uint=1] ) | ||
595 | -- | 607 | -- |
596 | -- = lock_f( +M ) -- acquire M | 608 | -- = lock_f( +M ) -- acquire M |
597 | -- ...locked... | 609 | -- ...locked... |
@@ -602,16 +614,10 @@ end -- settings.with_timers | |||
602 | -- | 614 | -- |
603 | -- PUBLIC LANES API | 615 | -- PUBLIC LANES API |
604 | local genlock = function( linda, key, N) | 616 | local genlock = function( linda, key, N) |
605 | linda:set( key) -- clears existing data | 617 | -- clear existing data and set the limit |
606 | linda:limit( key, N) | 618 | N = N or 1 |
607 | 619 | if linda:set( key) == cancel_error or linda:limit( key, N) == cancel_error then | |
608 | -- | 620 | return cancel_error |
609 | -- [true [, ...]= trues(uint) | ||
610 | -- | ||
611 | local function trues( n) | ||
612 | if n > 0 then | ||
613 | return true, trues( n - 1) | ||
614 | end | ||
615 | end | 621 | end |
616 | 622 | ||
617 | -- use an optimized version for case N == 1 | 623 | -- use an optimized version for case N == 1 |
@@ -623,7 +629,8 @@ local genlock = function( linda, key, N) | |||
623 | return linda:send( timeout, key, true) -- suspends until been able to push them | 629 | return linda:send( timeout, key, true) -- suspends until been able to push them |
624 | else | 630 | else |
625 | local k = linda:receive( nil, key) | 631 | local k = linda:receive( nil, key) |
626 | return k and true or false | 632 | -- propagate cancel_error if we got it, else return true or false |
633 | return k and ((k ~= cancel_error) and true or k) or false | ||
627 | end | 634 | end |
628 | end | 635 | end |
629 | or | 636 | or |
@@ -634,34 +641,45 @@ local genlock = function( linda, key, N) | |||
634 | return linda:send( timeout, key, trues(M)) -- suspends until been able to push them | 641 | return linda:send( timeout, key, trues(M)) -- suspends until been able to push them |
635 | else | 642 | else |
636 | local k = linda:receive( nil, linda.batched, key, -M) | 643 | local k = linda:receive( nil, linda.batched, key, -M) |
637 | return k and true or false | 644 | -- propagate cancel_error if we got it, else return true or false |
645 | return k and ((k ~= cancel_error) and true or k) or false | ||
638 | end | 646 | end |
639 | end | 647 | end |
640 | end | 648 | end |
641 | 649 | ||
642 | 650 | ||
643 | -- | 651 | -- |
644 | -- atomic_f= lanes.genatomic( linda_h, key [,initial_num=0.0] ) | 652 | -- atomic_f = lanes.genatomic( linda_h, key [,initial_num=0.0]) |
645 | -- | 653 | -- |
646 | -- int= atomic_f( [diff_num=1.0] ) | 654 | -- int|cancel_error = atomic_f( [diff_num = 1.0]) |
647 | -- | 655 | -- |
648 | -- Returns an access function that allows atomic increment/decrement of the | 656 | -- Returns an access function that allows atomic increment/decrement of the |
649 | -- number in 'key'. | 657 | -- number in 'key'. |
650 | -- | 658 | -- |
651 | -- PUBLIC LANES API | 659 | -- PUBLIC LANES API |
652 | local function genatomic( linda, key, initial_val ) | 660 | local genatomic = function( linda, key, initial_val) |
653 | linda:limit(key,2) -- value [,true] | 661 | -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value |
654 | linda:set(key,initial_val or 0.0) -- clears existing data (also queue) | 662 | if linda:limit( key, 2) == cancel_error or linda:set( key, initial_val or 0.0) == cancel_error then |
655 | 663 | return cancel_error | |
656 | return | 664 | end |
657 | function(diff) | 665 | |
658 | -- 'nil' allows 'key' to be numeric | 666 | return function( diff) |
659 | linda:send( nil, key, true ) -- suspends until our 'true' is in | 667 | -- 'nil' allows 'key' to be numeric |
660 | local val= linda:get(key) + (diff or 1.0) | 668 | -- suspends until our 'true' is in |
661 | linda:set( key, val ) -- releases the lock, by emptying queue | 669 | if linda:send( nil, key, true) == cancel_error then |
662 | return val | 670 | return cancel_error |
663 | end | 671 | end |
664 | end | 672 | local val = linda:get( key) |
673 | if val ~= cancel_error then | ||
674 | val = val + (diff or 1.0) | ||
675 | -- set() releases the lock by emptying queue | ||
676 | if linda:set( key, val) == cancel_error then | ||
677 | val = cancel_error | ||
678 | end | ||
679 | end | ||
680 | return val | ||
681 | end | ||
682 | end | ||
665 | 683 | ||
666 | -- activate full interface | 684 | -- activate full interface |
667 | lanes.require = core.require | 685 | lanes.require = core.require |
diff --git a/tests/cancel.lua b/tests/cancel.lua index a5f1dab..6429487 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua | |||
@@ -2,6 +2,33 @@ local lanes = require "lanes" .configure{ with_timers = false} | |||
2 | 2 | ||
3 | local linda = lanes.linda() | 3 | local linda = lanes.linda() |
4 | 4 | ||
5 | --#################################################################### | ||
6 | print "\n\n####################################################################\nbegin genlock & genatomic cancel test\n" | ||
7 | |||
8 | -- get a lock and a atomic operator | ||
9 | local lock = lanes.genlock( linda, "lock", 1) | ||
10 | local atomic = lanes.genatomic( linda, "atomic") | ||
11 | |||
12 | -- check that cancelled lindas give cancel_error as they should | ||
13 | linda:cancel() | ||
14 | assert( linda:get( "empty") == lanes.cancel_error) | ||
15 | assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error) | ||
16 | assert( lanes.genatomic( linda, "any") == lanes.cancel_error) | ||
17 | |||
18 | -- check that lock and atomic functions return cancel_error if the linda was cancelled | ||
19 | assert( lock( 1) == lanes.cancel_error) | ||
20 | assert( lock( -1) == lanes.cancel_error) | ||
21 | assert( atomic( 1) == lanes.cancel_error) | ||
22 | |||
23 | -- reset the linda so that the other tests work | ||
24 | linda:cancel( "none") | ||
25 | linda:limit( "lock", -1) | ||
26 | linda:set( "lock") | ||
27 | linda:limit( "atomic", -1) | ||
28 | linda:set( "atomic") | ||
29 | |||
30 | --#################################################################### | ||
31 | |||
5 | local laneBody = function( timeout_) | 32 | local laneBody = function( timeout_) |
6 | set_finalizer( function( err, stk) | 33 | set_finalizer( function( err, stk) |
7 | if err == lanes.cancel_error then | 34 | if err == lanes.cancel_error then |
@@ -105,4 +132,7 @@ linda:cancel( "both") | |||
105 | print "wait 5s" | 132 | print "wait 5s" |
106 | linda:receive( 5, "yeah") | 133 | linda:receive( 5, "yeah") |
107 | 134 | ||
135 | --#################################################################### | ||
136 | |||
108 | print "\ndone" | 137 | print "\ndone" |
138 | |||
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua index 1d92c8e..1a44d07 100644 --- a/tests/linda_perf.lua +++ b/tests/linda_perf.lua | |||
@@ -31,8 +31,8 @@ local batched = function( l, loop, batch) | |||
31 | print( val) | 31 | print( val) |
32 | end | 32 | end |
33 | 33 | ||
34 | local lane_eater_gen = lanes.gen( "*", eater) | 34 | local lane_eater_gen = lanes.gen( "*", {priority = 3}, eater) |
35 | local lane_batched_gen = lanes.gen( "*", batched) | 35 | local lane_batched_gen = lanes.gen( "*", {priority = 3}, batched) |
36 | 36 | ||
37 | -- main thread writes data while a lane reads it | 37 | -- main thread writes data while a lane reads it |
38 | local function ziva( preloop, loop, batch) | 38 | local function ziva( preloop, loop, batch) |