| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313 |
- /*
- _____ __ ___ __ ____ _ __
- / ___/__ ___ _ ___ / |/ /__ ___ / /_____ __ __/ __/_______(_)__ / /_
- / (_ / _ `/ ' \/ -_) /|_/ / _ \/ _ \/ '_/ -_) // /\ \/ __/ __/ / _ \/ __/
- \___/\_,_/_/_/_/\__/_/ /_/\___/_//_/_/\_\\__/\_, /___/\__/_/ /_/ .__/\__/
- /___/ /_/
-
- See Copyright Notice in gmMachine.h
- */
- #include "gmConfig.h"
- #include "gmMachineLib.h"
- #include "gmThread.h"
- #include "gmMachine.h"
- #include "gmUtil.h"
- //
- // machine
- //
- static int GM_CDECL gmVersion(gmThread * a_thread)
- {
- a_thread->PushNewString(GM_VERSION);
- return GM_OK;
- }
- static int GM_CDECL gmTypeId(gmThread * a_thread) // return int, or null
- {
- if(a_thread->GetNumParams() > 0)
- {
- a_thread->PushInt((gmptr) a_thread->Param(0).m_type);
- }
- return GM_OK;
- }
- static int GM_CDECL gmTypeName(gmThread * a_thread) // return string, or null
- {
- if(a_thread->GetNumParams() > 0)
- {
- const char * name = a_thread->GetMachine()->GetTypeName(a_thread->Param(0).m_type);
- a_thread->PushNewString(name);
- }
- return GM_OK;
- }
- static int GM_CDECL gmRegisterTypeOperator(gmThread * a_thread) // typeid, operatorname, function, returns true on success
- {
- GM_CHECK_NUM_PARAMS(3);
- GM_CHECK_INT_PARAM(typeId, 0);
- GM_CHECK_STRING_PARAM(operatorName, 1);
- GM_CHECK_FUNCTION_PARAM(function, 2);
- gmOperator op = gmGetOperator(operatorName);
- if(op != O_MAXOPERATORS)
- {
- a_thread->PushInt(a_thread->GetMachine()->RegisterTypeOperator((gmType) typeId, op, function) ? 1 : 0);
- return GM_OK;
- }
- a_thread->PushInt(0);
- return GM_OK;
- }
- static int GM_CDECL gmRegisterTypeVariable(gmThread * a_thread) // typeid, key (string), value
- {
- GM_CHECK_NUM_PARAMS(3);
- GM_CHECK_INT_PARAM(typeId, 0);
- GM_CHECK_STRING_PARAM(variable, 1);
- a_thread->GetMachine()->RegisterTypeVariable((gmType) typeId, variable, a_thread->Param(2));
-
- return GM_OK;
- }
- static int GM_CDECL gmCollectGarbage(gmThread * a_thread) // returns true if gc was run
- {
- GM_INT_PARAM(forceFullCollect, 0, false);
- a_thread->PushInt(a_thread->GetMachine()->CollectGarbage(forceFullCollect != 0) ? 1 : 0);
- return GM_OK;
- }
- static int GM_CDECL gmGetCurrentMemoryUsage(gmThread * a_thread) // returns current memory usage in bytes
- {
- a_thread->PushInt(a_thread->GetMachine()->GetCurrentMemoryUsage());
- return GM_OK;
- }
- static int GM_CDECL gmSetDesiredMemoryUsageHard(gmThread * a_thread) // mem usage in bytes
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_INT_PARAM(mem, 0);
- a_thread->GetMachine()->SetDesiredByteMemoryUsageHard(mem);
- return GM_OK;
- }
- static int GM_CDECL gmSetDesiredMemoryUsageSoft(gmThread * a_thread) // mem usage in bytes
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_INT_PARAM(mem, 0);
- a_thread->GetMachine()->SetDesiredByteMemoryUsageSoft(mem);
- return GM_OK;
- }
- static int GM_CDECL gmGetDesiredMemoryUsageHard(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->GetDesiredByteMemoryUsageHard());
- return GM_OK;
- }
- static int GM_CDECL gmGetDesiredMemoryUsageSoft(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->GetDesiredByteMemoryUsageSoft());
- return GM_OK;
- }
- static int GM_CDECL gmSetDesiredMemoryUsageAuto(gmThread * a_thread) // mem usage in bytes
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_INT_PARAM(autoEnable, 0);
- a_thread->GetMachine()->SetAutoMemoryUsage(autoEnable != 0);
- return GM_OK;
- }
- static int GM_CDECL gmSysGetStatsGCNumFullCollects(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumFullCollects());
- return GM_OK;
- }
- static int GM_CDECL gmSysGetStatsGCNumIncCollects(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumIncCollects());
- return GM_OK;
- }
- static int GM_CDECL gmSysGetStatsGCNumWarnings(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->GetStatsGCNumWarnings());
- return GM_OK;
- }
- static int GM_CDECL gmSysIsGCRunning(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetMachine()->IsGCRunning());
- return GM_OK;
- }
- static int GM_CDECL gmDoString(gmThread * a_thread) // string, now(int), returns thread id, null on error, exception on compile error
- {
- GM_CHECK_NUM_PARAMS(1); // Need at least 1 parameter
- GM_CHECK_STRING_PARAM(script, 0); // 1st param is script string
- GM_INT_PARAM(now, 1, 1); // 2nd param is execute now flag
- gmVariable paramThis = a_thread->Param(2, gmVariable::s_null); // 3rd param is 'this'
- int id = GM_INVALID_THREAD;
- if( script )
- {
- int errors = a_thread->GetMachine()->ExecuteString(script, &id, (now) ? true : false, NULL, ¶mThis);
- if( errors )
- {
- return GM_EXCEPTION;
- }
- a_thread->PushInt(id);
- }
- return GM_OK;
- }
- static int GM_CDECL gmGlobals(gmThread * a_thread) // return table
- {
- a_thread->PushTable(a_thread->GetMachine()->GetGlobals());
- return GM_OK;
- }
- static int GM_CDECL gmMachineTime(gmThread * a_thread) // return machine time
- {
- a_thread->PushInt(a_thread->GetMachine()->GetTime());
- return GM_OK;
- }
- //
- // thread
- //
- static int GM_CDECL gmSleep(gmThread * a_thread) // float\int param time in seconds
- {
- GM_CHECK_NUM_PARAMS(1);
- gmType type = a_thread->ParamType(0);
- gmuint32 ms = 0;
-
- if(type == GM_INT) ms = a_thread->Param(0).m_value.m_int * 1000;
- else if(type == GM_FLOAT) ms = (gmuint32) floorf(a_thread->Param(0).m_value.m_float * 1000.0f);
- a_thread->Sys_SetTimeStamp(a_thread->GetMachine()->GetTime() + ms);
- return GM_SYS_SLEEP;
- }
- static int GM_CDECL gmYield(gmThread * a_thread)
- {
- return GM_SYS_YIELD;
- }
- static int GM_CDECL gmThreadTime(gmThread * a_thread)
- {
- a_thread->PushInt(a_thread->GetThreadTime());
- return GM_OK;
- }
- static int GM_CDECL gmThreadId(gmThread * a_thread) // return thread id
- {
- a_thread->PushInt(a_thread->GetId());
- return GM_OK;
- }
- // Callback iteration function for gmThreadAllIds
- static bool gmThreadIdIter(gmThread * a_thread, void * a_context)
- {
- gmTableObject* table = (gmTableObject*)a_context;
- gmVariable threadId;
- threadId.SetInt(a_thread->GetId());
- table->Set(a_thread->GetMachine(), table->Count(), threadId);
- return true;
- }
-
- static int GM_CDECL gmThreadAllIds(gmThread * a_thread) // thread id
- {
- gmTableObject * threadIds = a_thread->PushNewTable();
- a_thread->GetMachine()->ForEachThread(gmThreadIdIter, threadIds);
- return GM_OK;
- }
- static int GM_CDECL gmExit(gmThread * a_thread)
- {
- return GM_SYS_KILL;
- }
- static int GM_CDECL gmKillThread(gmThread * a_thread) // thread id
- {
- GM_INT_PARAM(id, 0, GM_INVALID_THREAD); // 1 optional param, default is this thread
-
- // Kill this thread
- if( (id == GM_INVALID_THREAD) || (id == a_thread->GetId()) )
- {
- return GM_SYS_KILL; // Kill this thread
- }
- // Attempt to kill other thread by Id
- gmThread * thread = a_thread->GetMachine()->GetThread(id);
- if( thread )
- {
- thread->GetMachine()->Sys_SwitchState(thread, gmThread::KILLED); // Kill other thread
- }
- return GM_OK;
- }
- // Callback iteration function for gmKillAllThreads()
- // Kills all threads except current, current thread is passed in as context.
- static bool threadIterKill(gmThread * a_thread, void * a_context)
- {
- gmThread* caller = (gmThread*)a_context;
-
- if(a_thread != caller) // Ignore calling thread
- {
- switch(a_thread->GetState())
- {
- case gmThread::RUNNING:
- case gmThread::SLEEPING:
- case gmThread::BLOCKED:
- {
- // Kill the thread
- a_thread->GetMachine()->Sys_SwitchState(a_thread, gmThread::KILLED);
- break;
- }
- case gmThread::EXCEPTION:
- case gmThread::KILLED:
- default:
- {
- // Ignore threads of these states
- break;
- }
- }
- }
- return true;
- }
- static int GM_CDECL gmKillAllThreads(gmThread * a_thread) // thread id
- {
- GM_INT_PARAM(killCurrent, 0, 0);
- a_thread->GetMachine()->ForEachThread(threadIterKill, a_thread);
- if(killCurrent)
- {
- return GM_SYS_KILL;
- }
- return GM_OK;
- }
- static int GM_CDECL gmfThread(gmThread * a_thread) // fn, params, returns thread id (0) on error, else returns new thread id
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_FUNCTION_PARAM(function, 0);
- int id, i;
- gmThread * thread = a_thread->GetMachine()->CreateThread(&id);
- if(thread)
- {
- thread->Push(*a_thread->GetThis());
- thread->PushFunction(function);
- int numParameters = a_thread->GetNumParams() - 1;
- for(i = 0; i < numParameters; ++i)
- thread->Push(a_thread->Param(i + 1));
- thread->PushStackFrame(numParameters, 0);
- }
- a_thread->PushInt(id);
- return GM_OK;
- }
- static int GM_CDECL gmAssert(gmThread * a_thread)
- {
- if(a_thread->GetNumParams() > 0)
- {
- if(a_thread->Param(0).m_value.m_int)
- {
- return GM_OK;
- }
- }
- GM_STRING_PARAM(message, 1, "assert failed");
- a_thread->GetMachine()->GetLog().LogEntry("%s", message);
- return GM_EXCEPTION;
- }
- static gmType s_gmStateUserType = GM_NULL;
- struct gmStateUserType
- {
- gmFunctionObject * m_lastState; // last state
- gmFunctionObject * m_currentState; // current state
- gmFunctionObject * m_setExitState; // leave hanlder
- };
- static int GM_CDECL gmSetState(gmThread * a_thread) // fp, params
- {
- GM_CHECK_NUM_PARAMS(GM_STATE_NUM_PARAMS);
- GM_CHECK_FUNCTION_PARAM(function, 0);
- // make sure we have our state type.
- GM_ASSERT(s_gmStateUserType != GM_NULL);
- // save off the parameters to the new state
- gmVariable thisVar = *a_thread->GetThis();
- int i, numParameters = a_thread->GetNumParams() - GM_STATE_NUM_PARAMS;
- gmVariable * params = (gmVariable *) alloca(sizeof(gmVariable) * numParameters);
- for(i = 0; i < numParameters; ++i)
- {
- params[i] = a_thread->Param(i + GM_STATE_NUM_PARAMS);
- }
- // get the current state
- gmVariable newStateVariable;
- gmVariable * currentStateVariable = a_thread->GetBottom();
- if(currentStateVariable->m_type == s_gmStateUserType)
- {
- gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
- gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
- // call the on state leave if one exists.
- if(currentState->m_setExitState)
- {
- gmThread * thread = a_thread->GetMachine()->CreateThread(thisVar, gmVariable(GM_FUNCTION, currentState->m_setExitState->GetRef()));
- if(thread)
- {
- thread->Sys_Execute();
- }
- }
- currentState->m_setExitState = NULL;
- currentState->m_lastState = currentState->m_currentState;
- currentState->m_currentState = function;
- newStateVariable = *currentStateVariable;
- }
- else
- {
- gmStateUserType * state = (gmStateUserType *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmStateUserType));
- state->m_setExitState = NULL;
- state->m_currentState = function;
- state->m_lastState = NULL;
- // create a new state variable
- newStateVariable.SetUser(a_thread->GetMachine()->AllocUserObject(state, s_gmStateUserType));
- }
- // reset the stack. and push new state
- int user = a_thread->m_user;
- a_thread->Sys_Reset(a_thread->GetId());
- a_thread->m_user = user;
- a_thread->Sys_SetStartTime(a_thread->GetMachine()->GetTime());
- a_thread->Touch(4 + numParameters);
- a_thread->Push(newStateVariable);
- a_thread->Push(thisVar);
- a_thread->PushFunction(function);
- for(i = 0; i < numParameters; ++i)
- {
- a_thread->Push(params[i]);
- }
- return GM_SYS_STATE;
- }
- static int GM_CDECL gmSetStateOnThread(gmThread * a_thread) // (threadid, fp, params...) returns true or false.
- {
- GM_CHECK_NUM_PARAMS(2);
- GM_CHECK_INT_PARAM(threadId, 0);
- GM_CHECK_FUNCTION_PARAM(function, 1);
- // make sure we have our state type.
- GM_ASSERT(s_gmStateUserType != GM_NULL);
- // get the target thread
- gmThread * thread = a_thread->GetMachine()->GetThread(threadId);
- if(thread == a_thread)
- {
- a_thread->GetMachine()->GetLog().LogEntry("use setstate() on own thread");
- return GM_EXCEPTION;
- }
- if(thread == NULL)
- {
- return GM_OK;
- }
- // get the current state of the thread
- gmVariable newStateVariable;
- gmVariable thisVar = *thread->GetThis();
- gmVariable * currentStateVariable = thread->GetBottom();
- if(currentStateVariable->m_type == s_gmStateUserType)
- {
- gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
- gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
- // call the on state leave if one exists.
- if(currentState->m_setExitState)
- {
- gmThread * thread = a_thread->GetMachine()->CreateThread(thisVar, gmVariable(GM_FUNCTION, currentState->m_setExitState->GetRef()));
- if(thread)
- {
- thread->Sys_Execute();
- }
- }
- currentState->m_setExitState = NULL;
- currentState->m_lastState = currentState->m_currentState;
- currentState->m_currentState = function;
- newStateVariable = *currentStateVariable;
- }
- else
- {
- gmStateUserType * state = (gmStateUserType *) a_thread->GetMachine()->Sys_Alloc(sizeof(gmStateUserType));
- state->m_setExitState = NULL;
- state->m_currentState = function;
- state->m_lastState = NULL;
- // create a new state variable
- newStateVariable.SetUser(a_thread->GetMachine()->AllocUserObject(state, s_gmStateUserType));
- }
- // reset the stack. and push new state
- int numParameters = a_thread->GetNumParams() - 2;
-
- int user = thread->m_user;
- thread->Sys_Reset(thread->GetId());
- thread->m_user = user;
- thread->Sys_SetStartTime(thread->GetMachine()->GetTime());
- thread->Touch(4 + numParameters);
- thread->Push(newStateVariable);
- thread->Push(thisVar);
- thread->PushFunction(function);
- int i;
- for(i = 0; i < numParameters; ++i)
- {
- thread->Push(a_thread->Param(i+2));
- }
- thread->PushStackFrame(numParameters);
- a_thread->GetMachine()->Sys_SwitchState(thread, gmThread::RUNNING);
- return GM_OK;
- }
- static int GM_CDECL gmGetState(gmThread * a_thread) // return var
- {
- GM_ASSERT(s_gmStateUserType != GM_NULL);
- gmThread * testThread = a_thread;
-
- //Optional parameter, threadId
- if(a_thread->GetNumParams() >= 1)
- {
- GM_CHECK_INT_PARAM(testThreadId, 0);
- testThread = a_thread->GetMachine()->GetThread(testThreadId);
- if(!testThread)
- {
- a_thread->PushNull();
- return GM_OK;
- }
- }
-
- gmVariable * currentStateVariable = testThread->GetBottom();
- if(currentStateVariable->m_type == s_gmStateUserType)
- {
- gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
- gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
- a_thread->PushFunction(currentState->m_currentState);
- }
- return GM_OK;
- }
- static int GM_CDECL gmGetLastState(gmThread * a_thread) // return var
- {
- GM_ASSERT(s_gmStateUserType != GM_NULL);
-
- gmThread * testThread = a_thread;
-
- //Optional parameter, threadId
- if(a_thread->GetNumParams() >= 1)
- {
- GM_CHECK_INT_PARAM(testThreadId, 0);
- testThread = a_thread->GetMachine()->GetThread(testThreadId);
- if(!testThread)
- {
- a_thread->PushNull();
- return GM_OK;
- }
- }
- gmVariable * currentStateVariable = testThread->GetBottom();
- if(currentStateVariable->m_type == s_gmStateUserType)
- {
- gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
- gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
- if(currentState->m_lastState)
- {
- a_thread->PushFunction(currentState->m_lastState);
- }
- }
- return GM_OK;
- }
- static int GM_CDECL gmSetExitState(gmThread * a_thread) // function
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_FUNCTION_PARAM(function, 0);
- GM_ASSERT(s_gmStateUserType != GM_NULL);
- gmVariable * currentStateVariable = a_thread->GetBottom();
- if(currentStateVariable->m_type == s_gmStateUserType)
- {
- gmUserObject * userObj = (gmUserObject *) GM_OBJECT(currentStateVariable->m_value.m_ref);
- gmStateUserType * currentState = (gmStateUserType *) userObj->m_user;
- currentState->m_setExitState = function;
- }
- return GM_OK;
- }
- static int GM_CDECL gmSignal(gmThread * a_thread) // var, dest thread id
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_INT_PARAM(dstThreadId, 1, GM_INVALID_THREAD);
- a_thread->GetMachine()->Signal(a_thread->Param(0), dstThreadId, a_thread->GetId());
- return GM_OK;
- }
- static int GM_CDECL gmBlock(gmThread * a_thread) // var, ...
- {
- GM_CHECK_NUM_PARAMS(1);
- int res = a_thread->GetMachine()->Sys_Block(a_thread, a_thread->GetNumParams(), a_thread->GetBase());
- if(res == -1)
- {
- return GM_SYS_BLOCK;
- }
- else if(res == -2)
- {
- // Failed to block, so exception to prevent undefined or unexpected behavior
- GM_EXCEPTION_MSG("cannot block on null");
- return GM_EXCEPTION;
- }
- a_thread->Push(a_thread->Param(res));
- return GM_OK;
- }
- #if GM_USE_ENDON
- static int GM_CDECL gmEndOn(gmThread * a_thread)
- {
- GM_CHECK_NUM_PARAMS(1);
- int res = a_thread->GetMachine()->Sys_Block(a_thread, a_thread->GetNumParams(), a_thread->GetBase(), true);
- if(res == -1)
- {
- return GM_OK;
- }
- else if(res == -3)
- {
- return GM_EXCEPTION;
- }
- a_thread->Push(a_thread->Param(res));
- return GM_OK;
- }
- #endif //GM_USE_ENDON
- #if GM_USE_INCGC
- static void GM_CDECL gmGCDestructStateUserType(gmMachine * a_machine, gmUserObject* a_object)
- {
- gmStateUserType * state = (gmStateUserType *) a_object->m_user;
- a_machine->Sys_Free(state);
- }
- static bool GM_CDECL gmGCTraceStateUserType(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
- {
- gmStateUserType * state = (gmStateUserType *) a_object->m_user;
- if(state->m_currentState) a_gc->GetNextObject(state->m_currentState);
- if(state->m_lastState) a_gc->GetNextObject(state->m_lastState);
- if(state->m_setExitState) a_gc->GetNextObject(state->m_setExitState);
- a_workDone += 4; //contents + this
- return true;
- }
- #else //GM_USE_INCGC
- static void GM_CDECL gmGCStateUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
- {
- gmStateUserType * state = (gmStateUserType *) a_object->m_user;
- a_machine->Sys_Free(state);
- }
- static void GM_CDECL gmMarkStateUserType(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
- {
- gmStateUserType * state = (gmStateUserType *) a_object->m_user;
- if(state->m_currentState && state->m_currentState->NeedsMark(a_mark)) state->m_currentState->Mark(a_machine, a_mark);
- if(state->m_lastState && state->m_lastState->NeedsMark(a_mark)) state->m_lastState->Mark(a_machine, a_mark);
- if(state->m_setExitState && state->m_setExitState->NeedsMark(a_mark)) state->m_setExitState->Mark(a_machine, a_mark);
- }
- #endif //GM_USE_INCGC
- //
- // table
- //
- static int GM_CDECL gmTableCount(gmThread * a_thread)
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_TABLE_PARAM(table, 0);
- a_thread->PushInt(table->Count());
- return GM_OK;
- }
- static int GM_CDECL gmTableDuplicate(gmThread * a_thread)
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_TABLE_PARAM(table, 0);
- a_thread->PushTable(table->Duplicate(a_thread->GetMachine()));
- return GM_OK;
- }
- //
- // std
- //
- void gmConcat(gmMachine * a_machine, char * &a_dst, int &a_len, int &a_size, const char * a_src, int a_growBy = 32)
- {
- int len = (int)strlen(a_src);
- if((a_len + len + 1) >= a_size)
- {
- a_size = a_len + len + a_growBy + 1;
- char * str = (char *) a_machine->Sys_Alloc(a_size);
- if(a_dst != NULL)
- {
- memcpy(str, a_dst, a_len);
- a_machine->Sys_Free(a_dst);
- }
- a_dst = str;
- a_dst[a_len] = '\0';
- }
- memcpy(a_dst + a_len, a_src, len);
- a_len += len;
- a_dst[a_len] = '\0';
- }
- static int GM_CDECL gmPrint(gmThread * a_thread)
- {
- const int bufferSize = 256;
- int len = 0, size = 0, i;
- char * str = NULL, buffer[bufferSize];
- // build the string
- for(i = 0; i < a_thread->GetNumParams(); ++i)
- {
- gmConcat(a_thread->GetMachine(), str, len, size, a_thread->Param(i).AsString(a_thread->GetMachine(), buffer, bufferSize), 64);
- if(str)
- {
- GM_ASSERT(len < size);
- str[len++] = ' ';
- str[len] = '\0';
- }
- }
- // print the string
- if(str)
- {
- if(gmMachine::s_printCallback)
- {
- gmMachine::s_printCallback(a_thread->GetMachine(), str);
- }
- a_thread->GetMachine()->Sys_Free(str);
- }
- return GM_OK;
- }
- static int GM_CDECL gmfFormat(gmThread * a_thread) // string, params ...
- {
- GM_CHECK_NUM_PARAMS(1);
- GM_CHECK_STRING_PARAM(format, 0);
- int param = 1;
- int len = 0, size = 0;
- const int bufferSize = 1024;
- char * str = NULL, buffer[bufferSize] = {};
- while(*format)
- {
- if(*format == '%')
- {
- switch(format[1])
- {
- case 'S' :
- case 's' :
- {
- //GM_STRING_PARAM(pstr, param, "");
- const char *pstr = a_thread->Param(param).GetCStringSafe();
- if(!pstr)
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected string as param %d",param);
- return GM_EXCEPTION;
- }
- gmConcat(a_thread->GetMachine(), str, len, size, pstr, 64);
- ++param;
- break;
- }
- case 'C' :
- case 'c' :
- {
- //GM_INT_PARAM(ival, param, 0);
- if(!a_thread->Param(param).IsInt())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected int as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%c", a_thread->Param(param).GetInt());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'D' :
- case 'd' :
- {
- //GM_INT_PARAM(ival, param, 0);
- if(!a_thread->Param(param).IsInt())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected int as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%d", a_thread->Param(param).GetInt());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'U' :
- case 'u' :
- {
- //GM_INT_PARAM(ival, param, 0);
- if(!a_thread->Param(param).IsInt())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected int as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%u", a_thread->Param(param).GetInt());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'B' :
- case 'b' :
- {
- //GM_INT_PARAM(ival, param, 0);
- if(!a_thread->Param(param).IsInt())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected int as param %d",param);
- return GM_EXCEPTION;
- }
- gmItoa(a_thread->Param(param).GetInt(), buffer, 2);
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'X' :
- case 'x' :
- {
- //GM_INT_PARAM(ival, param, 0);
- if(!a_thread->Param(param).IsInt())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected int as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%x", a_thread->Param(param).GetInt());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'F' :
- case 'f' :
- {
- //GM_FLOAT_PARAM(fval, param, 0);
- if(!a_thread->Param(param).IsFloat())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected float as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%f", a_thread->Param(param).GetFloat());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'G' :
- case 'g' :
- {
- //GM_FLOAT_PARAM(fval, param, 0);
- if(!a_thread->Param(param).IsFloat())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected float as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%g", a_thread->Param(param).GetFloat());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case 'e' :
- case 'E' :
- {
- //GM_FLOAT_PARAM(fval, param, 0);
- if(!a_thread->Param(param).IsFloat())
- {
- a_thread->GetMachine()->Sys_Free(str);
- GM_EXCEPTION_MSG("expected float as param %d",param);
- return GM_EXCEPTION;
- }
- sprintf(buffer, "%e", a_thread->Param(param).GetFloat());
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- ++param;
- break;
- }
- case '%' :
- {
- if(len + 2 < size)
- str[len++] = '%';
- else
- gmConcat(a_thread->GetMachine(), str, len, size, "%", 64);
- break;
- }
- default :
- break;
- }
- format += 2;
- }
- else
- {
- if(len + 2 < size)
- str[len++] = *(format++);
- else
- {
- buffer[0] = *(format++);
- buffer[1] = '\0';
- gmConcat(a_thread->GetMachine(), str, len, size, buffer, 64);
- }
- }
- }
- if(str)
- {
- str[len] = '\0';
- a_thread->PushNewString(str);
- a_thread->GetMachine()->Sys_Free(str);
- }
- return GM_OK;
- }
- //
- // lib
- //
- static gmFunctionEntry s_binding[] =
- {
- /*gm
- \lib gm
- \brief functions in the gm lib are all global scope
- */
- /*gm
- \function gmVersion
- \brief gmVersion will return the gmMachine version string. version string is major type . minor type as a string
- and was added at version 1.1
- \return string
- */
- {"gmVersion", gmVersion},
- /*gm
- \function typeId
- \brief typeId will return the type id of the passed var
- \param var
- \return integer type
- */
- {"typeId", gmTypeId},
- /*gm
- \function typeName
- \brief typeName will return the type name of the passed var
- \param var
- \return string
- */
- {"typeName", gmTypeName},
- /*gm
- \function typeRegisterOperator
- \brief typeRegisterOperator will register an operator for a type
- \param int typeid
- \param string operator name is one of
- "getdot", "setdot", "getind", "setind", "add", "sub", "mul", "div", "mod",
- "inc", "dec", "bitor", "bitxor", "bitand", "shiftleft", "shiftright", "bitinv",
- "lt", "gt", "lte", "gte", "eq", "neq", "neg", "pos", "not"
- \param function
- \return 1 on success, otherwise 0
- */
- {"typeRegisterOperator", gmRegisterTypeOperator},
- /*gm
- \function typeRegisterVariable
- \brief typeRegisterVariable will register a variable with a type such that (type).varname will return the variable
- \param int typeid
- \param string var name
- \param var
- \return 1 on success, otherwise 0
- */
- {"typeRegisterVariable", gmRegisterTypeVariable},
- /*gm
- \function sysCollectGarbage
- \brief sysCollectGarbage will run the garbage collector iff the current mem used is over the desired mem used
- \param forceFullCollect (false) Optionally perform full garbage collection immediately if garbage collection is not disabled.
- \return 1 if the gc was run, 0 otherwise
- */
- {"sysCollectGarbage", gmCollectGarbage},
- /*gm
- \function sysGetMemoryUsage
- \brief sysGetMemoryUsage will return the current memory used in bytes
- \return int memory usage
- */
- {"sysGetMemoryUsage", gmGetCurrentMemoryUsage},
- /*gm
- \function sysSetDesiredMemoryUsageHard
- \brief sysSetDesiredMemoryUsageHard will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run.
- \param int desired mem usage in bytes
- */
- {"sysSetDesiredMemoryUsageHard", gmSetDesiredMemoryUsageHard},
- /*gm
- \function sysSetDesiredMemoryUsageSoft
- \brief sysSetDesiredMemoryUsageSoft will set the desired memory useage in bytes. when this is exceeded the garbage collector will be run.
- \param int desired mem usage in bytes
- */
- {"sysSetDesiredMemoryUsageSoft", gmSetDesiredMemoryUsageSoft},
- /*gm
- \function sysGetDesiredMemoryUsageHard
- \brief sysGetDesiredMemoryUsageHard will get the desired memory useage in bytes.
- Note that this value is used to start garbage collection, it is not a strict limit.
- \return int Desired memory usage in bytes.
- */
- {"sysGetDesiredMemoryUsageHard", gmGetDesiredMemoryUsageHard},
- /*gm
- \function sysGetDesiredMemoryUsageSoft
- \brief sysGetDesiredMemoryUsageSoft will get the desired memory useage in bytes.
- Note that this value is used to start garbage collection, it is not a strict limit.
- \return int Desired memory usage in bytes.
- */
- {"sysGetDesiredMemoryUsageSoft", gmGetDesiredMemoryUsageSoft},
- /*gm
- \function sysSetDesiredMemoryUsageAuto
- \brief sysSetDesiredMemoryUsageAuto will enable auto adjustment of the memory limit(s) for subsequent garbage collections.
- \param int enable or disable flag
- */
- {"sysSetDesiredMemoryUsageAuto", gmSetDesiredMemoryUsageAuto},
- /*gm
- \function sysGetStatsGCNumFullCollects
- \brief sysGetStatsGCNumFullCollects Return the number of times full garbage collection has occured.
- \return int Number of times full collect has occured.
- */
- {"sysGetStatsGCNumFullCollects", gmSysGetStatsGCNumFullCollects},
- /*gm
- \function sysGetStatsGCNumIncCollects
- \brief sysGetStatsGCNumIncCollects Return the number of times incremental garbage collection has occured.
- This number may increase in twos as the GC has multiple phases which appear as restarts.
- \return int Number of times incremental collect has occured.
- */
- {"sysGetStatsGCNumIncCollects", gmSysGetStatsGCNumIncCollects},
- /*gm
- \function sysGetStatsGCNumWarnings
- \brief sysGetStatsGCNumWarnings Return the number of warnings because the GC or VM thought the GC was poorly configured.
- If this number is large and growing rapidly, the GC soft and hard limits need to be configured better.
- Do not be concerned if this number grows slowly.
- \return int Number of warnings garbage collect has generated.
- */
- {"sysGetStatsGCNumWarnings", gmSysGetStatsGCNumWarnings},
- /*gm
- \function sysIsGCRunning
- \brief Returns true if GC is running a cycle.
- */
- {"sysIsGCRunning", gmSysIsGCRunning},
- /*gm
- \function sysTime
- \brief sysTime will return the machine time in milli seconds
- \return int
- */
- {"sysTime", gmMachineTime},
-
- /*gm
- \function doString
- \brief doString will execute the passed gm script
- \param string script
- \param int optional (1) set as true and the string will execute before returning to this thread
- \param ref optional (null) set 'this'
- \return int thread id of thread created for string execution
- */
- {"doString", gmDoString},
- /*gm
- \function globals
- \brief globals will return the globals table
- \return table containing all global variables
- */
- {"globals", gmGlobals},
- /*gm
- \function threadTime
- \brief threadTime will return the thread execution time in milliseconds
- \return int
- */
- {"threadTime", gmThreadTime},
- /*gm
- \function threadId
- \brief threadId will return the thread id of the current executing script
- \return int
- */
- {"threadId", gmThreadId},
- /*gm
- \function threadAllIds
- \brief threadIds returns a table of thread Ids
- \return table of thread Ids
- */
- {"threadAllIds", gmThreadAllIds},
- /*gm
- \function threadKill
- \brief threadKill will kill the thread with the given id
- \param int threadId optional (0) will kill this thread
- */
- {"threadKill", gmKillThread},
- /*gm
- \function threadKillAll
- \brief threadKillAll will kill all the threads except the current one
- \param bool optional (false) will kill this thread if true
- */
- {"threadKillAll", gmKillAllThreads},
- /*gm
- \function thread
- \brief thread will start a new thread
- \param function entry point of the thread
- \param ... parameters to pass to the entry function
- \return int threadid
- */
- {"thread", gmfThread},
- /*gm
- \function yield
- \brief yield will hand execution control to the next thread
- */
- {"yield", gmYield},
- /*gm
- \function exit
- \brief exit will kill this thread
- */
- {"exit", gmExit},
- /*gm
- \function assert
- \brief assert
- \param int expression if true, will do nothing, if false, will cause an exception
- */
- {"assert", gmAssert},
- /*gm
- \function sleep
- \brief sleep will sleep this thread for the given number of seconds
- \param int\float seconds
- */
- {"sleep", gmSleep},
- /*gm
- \function signal
- \brief signal will signal the given variable, this will unblock dest threads that are blocked on the same variable.
- \param var
- \param int destThreadId optional (0) 0 will signal all threads
- */
- {"signal", gmSignal},
- /*gm
- \function block
- \brief block will block on all passed vars, execution will halt until another thread signals one of the block variables. Will yield on null and return null.
- \param ... vars
- \return the unblocking var
- */
- {"block", gmBlock},
- #if GM_USE_ENDON
- /*gm
- \function endon
- \brief endon will kill the thread when a signal is sent from those matching the list
- \param ... vars
- \return the killing var
- */
- {"endon", gmEndOn},
- #endif //GM_USE_ENDON
- /*gm
- \function stateSet
- \brief stateSet will collapse the stack to nothing, and push the passed functions.
- \param function new state function to execute
- \param ... params for new state function
- */
- {"stateSet", gmSetState},
- /*gm
- \function stateSetOnThread
- \brief stateSetOnThread will collapse the stack of the given thread id to nothing, and push the passed functions.
- \param int thread id
- \param function new state function to execute
- \param ... params for new state function
- */
- {"stateSetOnThread", gmSetStateOnThread},
- /*gm
- \function stateGet
- \brief stateGet will return the function on the bottom of this threads execution stack iff it was pushed using stateSet
- \param a_threadId Optional Id of thread to get state on.
- \reutrn function \ null
- */
- {"stateGet", gmGetState},
- /*gm
- \function stateGetLast
- \brief stateGetLast will return the last state function of this thread
- \param a_threadId Optional Id of thread to get last state on.
- \reutrn function \ null
- */
- {"stateGetLast", gmGetLastState},
- /*gm
- \function stateSetExitFunction
- \brief stateSetExitFunction will set an exit function for this state, that will be called with no parameters if this thread
- switches state
- \param function
- */
- {"stateSetExitFunction", gmSetExitState},
- /*gm
- \function tableCount
- \brief tableCount will return the number of elements in a table object
- \param table
- \return int
- */
- {"tableCount", gmTableCount},
- /*gm
- \function tableDuplicate
- \brief tableDuplicate will duplicate the passed table object
- \param table
- \return table
- */
- {"tableDuplicate", gmTableDuplicate},
-
- /*gm
- \function print
- \brief print will print the given vars to the print handler. passed strings are concatinated together with a seperating space.
- \param ... strings
- */
- {"print", gmPrint},
- /*gm
- \function format
- \brief format (like sprintf, but returns a string) %d, %s, %f, %c, %b, %x, %e
- \param string
- */
- {"format", gmfFormat},
- };
- void gmMachineLib(gmMachine * a_machine)
- {
- // create the state type
- s_gmStateUserType = a_machine->CreateUserType("gmState");
- #if GM_USE_INCGC
- a_machine->RegisterUserCallbacks(s_gmStateUserType, gmGCTraceStateUserType, gmGCDestructStateUserType);
- #else //GM_USE_INCGC
- a_machine->RegisterUserCallbacks(s_gmStateUserType, gmMarkStateUserType, gmGCStateUserType);
- #endif //GM_USE_INCGC
- // default lib
- a_machine->RegisterLibrary(s_binding, sizeof(s_binding) / sizeof(gmFunctionEntry), NULL);
- }
|