diff options
author | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-02-26 11:53:30 +0100 |
---|---|---|
committer | Benoit Germain <bnt period germain arrobase gmail period com> | 2014-02-26 11:53:30 +0100 |
commit | fe3c44e63f99538a02d42d2504ba405a6977ec0a (patch) | |
tree | ee369b7c44fb52b2030b784297e2b5ee788305c3 /src/keeper.c | |
parent | d2bd8f65c678d898b6b7e5e92f76cb4dcce97b3c (diff) | |
download | lanes-fe3c44e63f99538a02d42d2504ba405a6977ec0a.tar.gz lanes-fe3c44e63f99538a02d42d2504ba405a6977ec0a.tar.bz2 lanes-fe3c44e63f99538a02d42d2504ba405a6977ec0a.zip |
Multiverse compatibility
* bumped version to 3.9.2
* Internal rework: the whole Lanes engine now works "per universe" to
allow concurrent Lanes execution in more than one embedded master state
* this universe is a full userdata created in the master state,
selfdestruct_gc is the __gc for this userdata
* most of what was initialized only once is now per-universe
* Fixed potential crashes at desinit if problems occur during keeper
states initialisation
* Fixed require() not always serialized properly
* Raise an error instead of crashing on deep userdata prelude memory
allocation failure
* Added forgotten mutex desinitialisation at universe shutdown
Diffstat (limited to 'src/keeper.c')
-rw-r--r-- | src/keeper.c | 158 |
1 files changed, 89 insertions, 69 deletions
diff --git a/src/keeper.c b/src/keeper.c index 0f54e13..9e5317b 100644 --- a/src/keeper.c +++ b/src/keeper.c | |||
@@ -186,9 +186,9 @@ static void push_table( lua_State* L, int idx) | |||
186 | STACK_END( L, 1); | 186 | STACK_END( L, 1); |
187 | } | 187 | } |
188 | 188 | ||
189 | int keeper_push_linda_storage( lua_State* L, void* ptr, unsigned long magic_) | 189 | int keeper_push_linda_storage( struct s_Universe* U, lua_State* L, void* ptr, unsigned long magic_) |
190 | { | 190 | { |
191 | struct s_Keeper* K = keeper_acquire( magic_); | 191 | struct s_Keeper* K = keeper_acquire( U->keepers, magic_); |
192 | lua_State* KL = K ? K->L : NULL; | 192 | lua_State* KL = K ? K->L : NULL; |
193 | if( KL == NULL) return 0; | 193 | if( KL == NULL) return 0; |
194 | STACK_GROW( KL, 4); | 194 | STACK_GROW( KL, 4); |
@@ -213,10 +213,10 @@ int keeper_push_linda_storage( lua_State* L, void* ptr, unsigned long magic_) | |||
213 | { | 213 | { |
214 | keeper_fifo* fifo = prepare_fifo_access( KL, -1); // storage key fifo | 214 | keeper_fifo* fifo = prepare_fifo_access( KL, -1); // storage key fifo |
215 | lua_pushvalue( KL, -2); // storage key fifo key | 215 | lua_pushvalue( KL, -2); // storage key fifo key |
216 | luaG_inter_move( KL, L, 1, eLM_FromKeeper); // storage key fifo // out key | 216 | luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key fifo // out key |
217 | STACK_MID( L, 2); | 217 | STACK_MID( L, 2); |
218 | lua_newtable( L); // out key keyout | 218 | lua_newtable( L); // out key keyout |
219 | luaG_inter_move( KL, L, 1, eLM_FromKeeper); // storage key // out key keyout fifo | 219 | luaG_inter_move( U, KL, L, 1, eLM_FromKeeper); // storage key // out key keyout fifo |
220 | lua_pushinteger( L, fifo->first); // out key keyout fifo first | 220 | lua_pushinteger( L, fifo->first); // out key keyout fifo first |
221 | STACK_MID( L, 5); | 221 | STACK_MID( L, 5); |
222 | lua_setfield( L, -3, "first"); // out key keyout fifo | 222 | lua_setfield( L, -3, "first"); // out key keyout fifo |
@@ -577,79 +577,107 @@ int keepercall_count( lua_State* L) | |||
577 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | 577 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the |
578 | * keepers randomly, by a hash. | 578 | * keepers randomly, by a hash. |
579 | */ | 579 | */ |
580 | static struct s_Keeper *GKeepers = NULL; | ||
581 | static int GNbKeepers = 0; | ||
582 | 580 | ||
583 | void close_keepers( lua_State* L) | 581 | // called as __gc for the keepers array userdata |
582 | void close_keepers( struct s_Universe* U, lua_State* L) | ||
584 | { | 583 | { |
585 | int i; | 584 | if( U->keepers != NULL) |
586 | int const nbKeepers = GNbKeepers; | ||
587 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | ||
588 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | ||
589 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | ||
590 | GNbKeepers = 0; | ||
591 | for( i = 0; i < nbKeepers; ++ i) | ||
592 | { | ||
593 | lua_State* L = GKeepers[i].L; | ||
594 | GKeepers[i].L = NULL; | ||
595 | lua_close( L); | ||
596 | } | ||
597 | for( i = 0; i < nbKeepers; ++ i) | ||
598 | { | 585 | { |
599 | MUTEX_FREE( &GKeepers[i].lock_); | 586 | int i; |
600 | } | 587 | int nbKeepers = U->keepers->nb_keepers; |
601 | if( GKeepers != NULL) | 588 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it |
602 | { | 589 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists |
603 | void* allocUD; | 590 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success |
604 | lua_Alloc allocF = lua_getallocf( L, &allocUD); | 591 | // which is early-outed with a U->keepers->nbKeepers null-check |
605 | allocF( allocUD, GKeepers, nbKeepers * sizeof( struct s_Keeper), 0); | 592 | U->keepers->nb_keepers = 0; |
593 | for( i = 0; i < nbKeepers; ++ i) | ||
594 | { | ||
595 | lua_State* K = U->keepers->keeper_array[i].L; | ||
596 | U->keepers->keeper_array[i].L = NULL; | ||
597 | if( K != NULL) | ||
598 | { | ||
599 | lua_close( K); | ||
600 | } | ||
601 | else | ||
602 | { | ||
603 | // detected partial init: destroy only the mutexes that got initialized properly | ||
604 | nbKeepers = i; | ||
605 | } | ||
606 | } | ||
607 | for( i = 0; i < nbKeepers; ++ i) | ||
608 | { | ||
609 | MUTEX_FREE( &U->keepers->keeper_array[i].keeper_cs); | ||
610 | } | ||
611 | // free the keeper bookkeeping structure | ||
612 | { | ||
613 | void* allocUD; | ||
614 | lua_Alloc allocF = lua_getallocf( L, &allocUD); | ||
615 | allocF( L, U->keepers, sizeof( struct s_Keepers) + (nbKeepers - 1) * sizeof(struct s_Keeper), 0); | ||
616 | U->keepers = NULL; | ||
617 | } | ||
606 | } | 618 | } |
607 | GKeepers = NULL; | ||
608 | } | 619 | } |
609 | 620 | ||
610 | /* | 621 | /* |
611 | * Initialize keeper states | 622 | * Initialize keeper states |
612 | * | 623 | * |
613 | * If there is a problem, return an error message (NULL for okay). | 624 | * If there is a problem, returns NULL and pushes the error message on the stack |
625 | * else returns the keepers bookkeeping structure. | ||
614 | * | 626 | * |
615 | * Note: Any problems would be design flaws; the created Lua state is left | 627 | * Note: Any problems would be design flaws; the created Lua state is left |
616 | * unclosed, because it does not really matter. In production code, this | 628 | * unclosed, because it does not really matter. In production code, this |
617 | * function never fails. | 629 | * function never fails. |
618 | * settings table is at position 1 on the stack | 630 | * settings table is at position 1 on the stack |
619 | * pushes an error string on the stack in case of problem | ||
620 | */ | 631 | */ |
621 | int init_keepers( lua_State* L) | 632 | void init_keepers( struct s_Universe* U, lua_State* L) |
622 | { | 633 | { |
623 | int i; | 634 | int i; |
635 | int nb_keepers; | ||
624 | void* allocUD; | 636 | void* allocUD; |
625 | lua_Alloc allocF = lua_getallocf( L, &allocUD); | 637 | lua_Alloc allocF = lua_getallocf( L, &allocUD); |
626 | 638 | ||
627 | STACK_CHECK( L); // L K | 639 | STACK_CHECK( L); // L K |
628 | lua_getfield( L, 1, "nb_keepers"); // nb_keepers | 640 | lua_getfield( L, 1, "nb_keepers"); // nb_keepers |
629 | GNbKeepers = (int) lua_tointeger( L, -1); | 641 | nb_keepers = (int) lua_tointeger( L, -1); |
630 | lua_pop( L, 1); // | 642 | lua_pop( L, 1); // |
631 | assert( GNbKeepers >= 1); | 643 | assert( nb_keepers >= 1); |
632 | 644 | ||
633 | GKeepers = (struct s_Keeper*) allocF( allocUD, NULL, 0, GNbKeepers * sizeof( struct s_Keeper)); | 645 | // struct s_Keepers contains an array of 1 s_Keeper, adjust for the actual number of keeper states |
634 | if( GKeepers == NULL) | ||
635 | { | 646 | { |
636 | lua_pushliteral( L, "init_keepers() failed while creating keeper array; out of memory"); | 647 | size_t const bytes = sizeof( struct s_Keepers) + (nb_keepers - 1) * sizeof(struct s_Keeper); |
637 | STACK_MID( L, 1); | 648 | U->keepers = (struct s_Keepers*) allocF( L, NULL, 0, bytes); |
638 | return 1; | 649 | if( U->keepers == NULL) |
650 | { | ||
651 | (void) luaL_error( L, "init_keepers() failed while creating keeper array; out of memory"); | ||
652 | return; | ||
653 | } | ||
654 | memset( U->keepers, 0, bytes); | ||
655 | U->keepers->nb_keepers = nb_keepers; | ||
639 | } | 656 | } |
640 | for( i = 0; i < GNbKeepers; ++ i) | 657 | for( i = 0; i < nb_keepers; ++ i) // keepersUD |
641 | { | 658 | { |
642 | lua_State* K = PROPAGATE_ALLOCF_ALLOC(); | 659 | lua_State* K = PROPAGATE_ALLOCF_ALLOC(); |
643 | if( K == NULL) | 660 | if( K == NULL) |
644 | { | 661 | { |
645 | lua_pushliteral( L, "init_keepers() failed while creating keeper states; out of memory"); | 662 | (void) luaL_error( L, "init_keepers() failed while creating keeper states; out of memory"); |
646 | STACK_MID( L, 1); | 663 | return; |
647 | return 1; | ||
648 | } | 664 | } |
665 | |||
666 | U->keepers->keeper_array[i].L = K; | ||
667 | // we can trigger a GC from inside keeper_call(), where a keeper is acquired | ||
668 | // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. | ||
669 | // therefore, we need a recursive mutex. | ||
670 | MUTEX_RECURSIVE_INIT( &U->keepers->keeper_array[i].keeper_cs); | ||
649 | STACK_CHECK( K); | 671 | STACK_CHECK( K); |
650 | 672 | ||
673 | // copy the universe pointer in the keeper itself | ||
674 | lua_pushlightuserdata( K, UNIVERSE_REGKEY); | ||
675 | lua_pushlightuserdata( K, U); | ||
676 | lua_rawset( K, LUA_REGISTRYINDEX); | ||
677 | STACK_MID( K, 0); | ||
678 | |||
651 | // make sure 'package' is initialized in keeper states, so that we have require() | 679 | // make sure 'package' is initialized in keeper states, so that we have require() |
652 | // this because this is needed when transfering deep userdata object | 680 | // this because this is needed when transferring deep userdata object |
653 | luaL_requiref( K, "package", luaopen_package, 1); // package | 681 | luaL_requiref( K, "package", luaopen_package, 1); // package |
654 | lua_pop( K, 1); // | 682 | lua_pop( K, 1); // |
655 | STACK_MID( K, 0); | 683 | STACK_MID( K, 0); |
@@ -657,16 +685,16 @@ int init_keepers( lua_State* L) | |||
657 | STACK_MID( K, 0); | 685 | STACK_MID( K, 0); |
658 | 686 | ||
659 | // copy package.path and package.cpath from the source state | 687 | // copy package.path and package.cpath from the source state |
660 | lua_getglobal( L, "package"); // package | 688 | lua_getglobal( L, "package"); // "..." keepersUD package |
661 | if( !lua_isnil( L, -1)) | 689 | if( !lua_isnil( L, -1)) |
662 | { | 690 | { |
663 | // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately | 691 | // when copying with mode eLM_ToKeeper, error message is pushed at the top of the stack, not raised immediately |
664 | if( luaG_inter_copy_package( L, K, -1, eLM_ToKeeper)) | 692 | if( luaG_inter_copy_package( U, L, K, -1, eLM_ToKeeper)) |
665 | { | 693 | { |
666 | // if something went wrong, the error message is at the top of the stack | 694 | // if something went wrong, the error message is at the top of the stack |
667 | lua_remove( L, -2); // error_msg | 695 | lua_remove( L, -2); // error_msg |
668 | STACK_MID( L, 1); | 696 | (void) lua_error( L); |
669 | return 1; | 697 | return; |
670 | } | 698 | } |
671 | } | 699 | } |
672 | lua_pop( L, 1); // | 700 | lua_pop( L, 1); // |
@@ -674,12 +702,8 @@ int init_keepers( lua_State* L) | |||
674 | 702 | ||
675 | // attempt to call on_state_create(), if we have one and it is a C function | 703 | // attempt to call on_state_create(), if we have one and it is a C function |
676 | // (only support a C function because we can't transfer executable Lua code in keepers) | 704 | // (only support a C function because we can't transfer executable Lua code in keepers) |
677 | if( call_on_state_create( K, L, eLM_ToKeeper)) | 705 | // will raise an error in L in case of problem |
678 | { | 706 | call_on_state_create( U, K, L, eLM_ToKeeper); |
679 | // if something went wrong, the error message is at the top of the stack | ||
680 | STACK_MID( L, 1); // error_msg | ||
681 | return 1; | ||
682 | } | ||
683 | 707 | ||
684 | // to see VM name in Decoda debugger | 708 | // to see VM name in Decoda debugger |
685 | lua_pushliteral( K, "Keeper #"); // "Keeper #" | 709 | lua_pushliteral( K, "Keeper #"); // "Keeper #" |
@@ -693,19 +717,15 @@ int init_keepers( lua_State* L) | |||
693 | lua_rawset( K, LUA_REGISTRYINDEX); // | 717 | lua_rawset( K, LUA_REGISTRYINDEX); // |
694 | 718 | ||
695 | STACK_END( K, 0); | 719 | STACK_END( K, 0); |
696 | // we can trigger a GC from inside keeper_call(), where a keeper is acquired | ||
697 | // from there, GC can collect a linda, which would acquire the keeper again, and deadlock the thread. | ||
698 | MUTEX_RECURSIVE_INIT( &GKeepers[i].lock_); | ||
699 | GKeepers[i].L = K; | ||
700 | } | 720 | } |
701 | STACK_END( L, 0); | 721 | STACK_END( L, 0); |
702 | return 0; // success | ||
703 | } | 722 | } |
704 | 723 | ||
705 | struct s_Keeper* keeper_acquire( unsigned long magic_) | 724 | struct s_Keeper* keeper_acquire( struct s_Keepers* keepers_, unsigned long magic_) |
706 | { | 725 | { |
726 | int const nbKeepers = keepers_->nb_keepers; | ||
707 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | 727 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) |
708 | if( GNbKeepers == 0) | 728 | if( nbKeepers == 0) |
709 | { | 729 | { |
710 | return NULL; | 730 | return NULL; |
711 | } | 731 | } |
@@ -718,10 +738,10 @@ struct s_Keeper* keeper_acquire( unsigned long magic_) | |||
718 | * Pointers are often aligned by 8 or so - ignore the low order bits | 738 | * Pointers are often aligned by 8 or so - ignore the low order bits |
719 | * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer | 739 | * have to cast to unsigned long to avoid compilation warnings about loss of data when converting pointer-to-integer |
720 | */ | 740 | */ |
721 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % GNbKeepers); | 741 | unsigned int i = (unsigned int)((magic_ >> KEEPER_MAGIC_SHIFT) % nbKeepers); |
722 | struct s_Keeper* K= &GKeepers[i]; | 742 | struct s_Keeper* K = &keepers_->keeper_array[i]; |
723 | 743 | ||
724 | MUTEX_LOCK( &K->lock_); | 744 | MUTEX_LOCK( &K->keeper_cs); |
725 | //++ K->count; | 745 | //++ K->count; |
726 | return K; | 746 | return K; |
727 | } | 747 | } |
@@ -730,16 +750,16 @@ struct s_Keeper* keeper_acquire( unsigned long magic_) | |||
730 | void keeper_release( struct s_Keeper* K) | 750 | void keeper_release( struct s_Keeper* K) |
731 | { | 751 | { |
732 | //-- K->count; | 752 | //-- K->count; |
733 | if( K) MUTEX_UNLOCK( &K->lock_); | 753 | if( K) MUTEX_UNLOCK( &K->keeper_cs); |
734 | } | 754 | } |
735 | 755 | ||
736 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) | 756 | void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) |
737 | { | 757 | { |
738 | int i, n = lua_gettop( L); | 758 | int i, n = lua_gettop( L); |
739 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe | 759 | /* We could use an empty table in 'keeper.lua' as the sentinel, but maybe |
740 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global of ours) | 760 | * checking for a lightuserdata is faster. (any unique value will do -> take the address of some global symbol of ours) |
741 | */ | 761 | */ |
742 | void* nil_sentinel = &GNbKeepers; | 762 | void* nil_sentinel = (void*) keeper_toggle_nil_sentinels; |
743 | for( i = val_i_; i <= n; ++ i) | 763 | for( i = val_i_; i <= n; ++ i) |
744 | { | 764 | { |
745 | if( mode_ == eLM_ToKeeper) | 765 | if( mode_ == eLM_ToKeeper) |
@@ -770,7 +790,7 @@ void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mod | |||
770 | * | 790 | * |
771 | * Returns: number of return values (pushed to 'L') or -1 in case of error | 791 | * Returns: number of return values (pushed to 'L') or -1 in case of error |
772 | */ | 792 | */ |
773 | int keeper_call( lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) | 793 | int keeper_call( struct s_Universe* U, lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) |
774 | { | 794 | { |
775 | int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; | 795 | int const args = starting_index ? (lua_gettop( L) - starting_index + 1) : 0; |
776 | int const Ktos = lua_gettop( K); | 796 | int const Ktos = lua_gettop( K); |
@@ -782,7 +802,7 @@ int keeper_call( lua_State* K, keeper_api_t func_, lua_State* L, void* linda, ui | |||
782 | 802 | ||
783 | lua_pushlightuserdata( K, linda); | 803 | lua_pushlightuserdata( K, linda); |
784 | 804 | ||
785 | if( (args == 0) || luaG_inter_copy( L, K, args, eLM_ToKeeper) == 0) // L->K | 805 | if( (args == 0) || luaG_inter_copy( U, L, K, args, eLM_ToKeeper) == 0) // L->K |
786 | { | 806 | { |
787 | lua_call( K, 1 + args, LUA_MULTRET); | 807 | lua_call( K, 1 + args, LUA_MULTRET); |
788 | 808 | ||
@@ -791,7 +811,7 @@ int keeper_call( lua_State* K, keeper_api_t func_, lua_State* L, void* linda, ui | |||
791 | // this may interrupt a lane, causing the destruction of the underlying OS thread | 811 | // this may interrupt a lane, causing the destruction of the underlying OS thread |
792 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function | 812 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function |
793 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) | 813 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) |
794 | if( (retvals > 0) && luaG_inter_move( K, L, retvals, eLM_FromKeeper) != 0) // K->L | 814 | if( (retvals > 0) && luaG_inter_move( U, K, L, retvals, eLM_FromKeeper) != 0) // K->L |
795 | { | 815 | { |
796 | retvals = -1; | 816 | retvals = -1; |
797 | } | 817 | } |