/* receive a message from a lua process */ static int luaproc_receive( lua_State *L ) { int ret, nargs; channel *chan; luaproc *srclp, *self; const char *chname = luaL_checkstring( L, 1 ); /* get number of arguments passed to function */ nargs = lua_gettop( L ); chan = channel_locked_get( chname ); /* if channel is not found, return an error to Lua */ if ( chan == NULL ) { lua_pushnil( L ); lua_pushfstring( L, "channel '%s' does not exist", chname ); return 2; } /* remove first lua process, if any, from channels' send list */ srclp = list_remove( &chan->send ); if ( srclp != NULL ) { /* found a sender? */ /* try to move values between lua states' stacks */ ret = luaproc_copyvalues( srclp->lstate, L ); if ( ret == TRUE ) { /* was receive successful? */ lua_pushboolean( srclp->lstate, TRUE ); srclp->args = 1; } else { /* nil and error_msg already in stack */ srclp->args = 2; } if ( srclp->lstate == mainlp.lstate ) { /* if sending process is the parent (main) Lua state, unblock it */ pthread_mutex_lock( &mutex_mainls ); pthread_cond_signal( &cond_mainls_sendrecv ); pthread_mutex_unlock( &mutex_mainls ); } else { /* otherwise, schedule process for execution */ sched_queue_proc( srclp ); } /* unlock channel access */ luaproc_unlock_channel( chan ); /* disconsider channel name, async flag and any other args passed to the receive function when returning its results */ return lua_gettop( L ) - nargs; } else { /* otherwise test if receive was synchronous or asynchronous */ if ( lua_toboolean( L, 2 )) { /* asynchronous receive */ /* unlock channel access */ luaproc_unlock_channel( chan ); /* return an error */ lua_pushnil( L ); lua_pushfstring( L, "no senders waiting on channel '%s'", chname ); return 2; } else { /* synchronous receive */ if ( L == mainlp.lstate ) { /* receiving process is the parent (main) Lua state - block it */ mainlp.chan = chan; luaproc_queue_receiver( &mainlp ); luaproc_unlock_channel( chan ); pthread_mutex_lock( &mutex_mainls ); pthread_cond_wait( &cond_mainls_sendrecv, &mutex_mainls ); pthread_mutex_unlock( &mutex_mainls ); return mainlp.args; } else { /* receiving process is a standard luaproc - set status, block and yield */ self = luaproc_getself( L ); if ( self != NULL ) { self->status = LUAPROC_STATUS_BLOCKED_RECV; self->chan = chan; } /* yield. channel will be unlocked by the scheduler */ return lua_yield( L, lua_gettop( L )); } } } }
/* worker thread main function */ void *workermain( void *args ) { luaproc *lp; int procstat; /* main worker loop */ while ( TRUE ) { /* wait until instructed to wake up (because there's work to do or because workers must be destroyed) */ pthread_mutex_lock( &mutex_sched ); while (( list_count( &ready_lp_list ) == 0 ) && ( destroyworkers <= 0 )) { pthread_cond_wait( &cond_wakeup_worker, &mutex_sched ); } if ( destroyworkers > 0 ) { /* check whether workers should be destroyed */ destroyworkers--; /* decrease workers to be destroyed count */ workerscount--; /* decrease active workers count */ /* remove worker from workers table */ lua_getglobal( workerls, LUAPROC_SCHED_WORKERS_TABLE ); lua_pushlightuserdata( workerls, (void *)pthread_self( )); lua_pushnil( workerls ); lua_rawset( workerls, -3 ); lua_pop( workerls, 1 ); pthread_cond_signal( &cond_wakeup_worker ); /* wake other workers up */ pthread_mutex_unlock( &mutex_sched ); pthread_exit( NULL ); /* destroy itself */ } /* remove lua process from the ready queue */ lp = list_remove( &ready_lp_list ); pthread_mutex_unlock( &mutex_sched ); /* execute the lua code specified in the lua process struct */ procstat = luaproc_resume( luaproc_get_state( lp ), NULL, luaproc_get_numargs( lp )); /* reset the process argument count */ luaproc_set_numargs( lp, 0 ); /* has the lua process sucessfully finished its execution? */ if ( procstat == 0 ) { luaproc_set_status( lp, LUAPROC_STATUS_FINISHED ); luaproc_recycle_insert( lp ); /* try to recycle finished lua process */ sched_dec_lpcount(); /* decrease active lua process count */ } /* has the lua process yielded? */ else if ( procstat == LUA_YIELD ) { /* yield attempting to send a message */ if ( luaproc_get_status( lp ) == LUAPROC_STATUS_BLOCKED_SEND ) { luaproc_queue_sender( lp ); /* queue lua process on channel */ /* unlock channel */ luaproc_unlock_channel( luaproc_get_channel( lp )); } /* yield attempting to receive a message */ else if ( luaproc_get_status( lp ) == LUAPROC_STATUS_BLOCKED_RECV ) { luaproc_queue_receiver( lp ); /* queue lua process on channel */ /* unlock channel */ luaproc_unlock_channel( luaproc_get_channel( lp )); } /* yield on explicit coroutine.yield call */ else { /* re-insert the job at the end of the ready process queue */ pthread_mutex_lock( &mutex_sched ); list_insert( &ready_lp_list, lp ); pthread_mutex_unlock( &mutex_sched ); } } /* or was there an error executing the lua process? */ else { /* print error message */ fprintf( stderr, "close lua_State (error: %s)\n", luaL_checkstring( luaproc_get_state( lp ), -1 )); lua_close( luaproc_get_state( lp )); /* close lua state */ sched_dec_lpcount(); /* decrease active lua process count */ } } }