aboutsummaryrefslogtreecommitdiff
path: root/src/keeper.c
diff options
context:
space:
mode:
authorBenoit Germain <bnt period germain arrobase gmail period com>2014-02-26 11:53:30 +0100
committerBenoit Germain <bnt period germain arrobase gmail period com>2014-02-26 11:53:30 +0100
commitfe3c44e63f99538a02d42d2504ba405a6977ec0a (patch)
treeee369b7c44fb52b2030b784297e2b5ee788305c3 /src/keeper.c
parentd2bd8f65c678d898b6b7e5e92f76cb4dcce97b3c (diff)
downloadlanes-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.c158
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
189int keeper_push_linda_storage( lua_State* L, void* ptr, unsigned long magic_) 189int 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*/
580static struct s_Keeper *GKeepers = NULL;
581static int GNbKeepers = 0;
582 580
583void close_keepers( lua_State* L) 581// called as __gc for the keepers array userdata
582void 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 */
621int init_keepers( lua_State* L) 632void 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
705struct s_Keeper* keeper_acquire( unsigned long magic_) 724struct 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_)
730void keeper_release( struct s_Keeper* K) 750void 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
736void keeper_toggle_nil_sentinels( lua_State* L, int val_i_, enum eLookupMode mode_) 756void 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*/
773int keeper_call( lua_State* K, keeper_api_t func_, lua_State* L, void* linda, uint_t starting_index) 793int 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 }