1
0
mirror of https://github.com/Ylianst/MeshAgent synced 2026-01-03 09:03:48 +00:00

Added Async Threading helpers

This commit is contained in:
Bryan Roe
2019-01-11 12:22:16 -08:00
parent a9b159ea7e
commit 70daead65e

View File

@@ -29,6 +29,10 @@ limitations under the License.
#include "microstack/ILibCrypto.h"
#include "microstack/ILibRemoteLogging.h"
#ifdef _POSIX
#include <pthread.h>
#endif
#if defined(_WIN64) || defined(__LP64__)
typedef uint_fast64_t PTRSIZE;
#else
@@ -48,6 +52,7 @@ limitations under the License.
#define ILibDuktape_GenericMarshal_Variable_AutoFree "\xFF_GenericMarshal_Variable_AutoFree"
#define ILibDuktape_GenericMarshal_Variable_Parms "\xFF_GenericMarshal_Variable_Parms"
#define ILibDuktape_GenericMarshal_StashTable "\xFF_GenericMarshal_StashTable"
#define ILibDuktape_GenericMarshal_GlobalCallback_ThreadID "\xFF_GenericMarshal_ThreadID"
#define ILibDuktape_GenericMarshal_Variable_EnableAutoFree(ctx, idx) duk_dup(ctx, idx);duk_push_true(ctx);duk_put_prop_string(ctx, -2, ILibDuktape_GenericMarshal_Variable_AutoFree);duk_pop(ctx)
#define ILibDuktape_GenericMarshal_Variable_DisableAutoFree(ctx, idx) duk_dup(ctx, idx);duk_push_false(ctx);duk_put_prop_string(ctx, -2, ILibDuktape_GenericMarshal_Variable_AutoFree);duk_pop(ctx)
@@ -88,6 +93,11 @@ typedef struct Duktape_GlobalGeneric_Data
void *retVal;
void *chain;
sem_t contextWaiter;
#ifdef WIN32
DWORD callingThread;
#else
pthread_t callingThread;
#endif
int numArgs;
PTRSIZE args[];
}Duktape_GlobalGeneric_Data;
@@ -796,6 +806,306 @@ void ILibDuktape_GenericMarshal_MethodInvoke_ThreadSink(void *args)
ILibChain_RunOnMicrostackThreadEx(chain, ILibDuktape_GenericMarshal_MethodInvoke_ThreadSink_Return, args);
}
#define ILibDuktape_FFI_AsyncDataPtr "\xFF_FFI_AsyncDataPtr"
typedef struct ILibDuktape_FFI_AsyncData
{
duk_context *ctx;
void *chain;
void *workerThread;
#ifdef WIN32
DWORD workerThreadId;
#else
pthread_t workerThreadId;
#endif
void *fptr;
void *fptr_redirection;
int abort;
int waitingForResult;
PTRSIZE *vars;
void *promise;
uint32_t lastError;
sem_t workAvailable;
sem_t workStarted;
sem_t workFinished;
}ILibDuktape_FFI_AsyncData;
void ILibDuktape_GenericMarshal_MethodInvokeAsync_ChainDispatch(void *chain, void *user)
{
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)user;
if (!ILibMemory_CanaryOK(data)) { return; }
duk_push_heapptr(data->ctx, data->promise); // [promise]
duk_get_prop_string(data->ctx, -1, "_RES"); // [promise][resolver]
duk_swap_top(data->ctx, -2); // [resolver][this]
ILibDuktape_GenericMarshal_Variable_PUSH(data->ctx, (void*)(PTRSIZE)data->vars, (int)sizeof(void*)); // [resolver][this][var]
duk_push_int(data->ctx, data->lastError); duk_put_prop_string(data->ctx, -2, "_LastError");
data->promise = NULL;
if (duk_pcall_method(data->ctx, 1) != 0) { ILibDuktape_Process_UncaughtExceptionEx(data->ctx, "Error Resolving Promise: "); }
duk_pop(data->ctx); // ...
}
void ILibDuktape_GenericMarshal_MethodInvokeAsync_WorkerRunLoop(void *arg)
{
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)arg;
PTRSIZE var[20];
int varCount, i;
#ifdef WIN32
data->workerThreadId = GetCurrentThreadId();
#else
data->workerThreadId = pthread_self();
#endif
while (data->abort == 0)
{
sem_wait(&(data->workAvailable));
if (data->abort != 0) { break; }
varCount = (int)(ILibMemory_Size(data->vars) / sizeof(PTRSIZE));
for (i = (data->fptr_redirection == NULL ? 0 : 1); i < varCount; ++i)
{
var[(data->fptr_redirection == NULL ? i : (i - 1))] = data->vars[i];
}
sem_post(&(data->workStarted));
if (data->fptr_redirection == NULL)
{
data->vars = (PTRSIZE*)ILibDuktape_GenericMarshal_MethodInvoke_Native(varCount, data->fptr, var);
}
else
{
data->vars = (PTRSIZE*)ILibDuktape_GenericMarshal_MethodInvoke_Native(varCount-1, data->fptr_redirection, var);
data->fptr_redirection = NULL;
}
#ifdef WIN32
data->lastError = (DWORD)GetLastError();
#endif
if (ILibMemory_CanaryOK(data))
{
if (data->waitingForResult == 0)
{
ILibChain_RunOnMicrostackThread(data->chain, ILibDuktape_GenericMarshal_MethodInvokeAsync_ChainDispatch, data);
}
else
{
data->waitingForResult = 0;
sem_post(&(data->workFinished));
}
}
}
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_promise(duk_context *ctx)
{
duk_push_this(ctx); // [promise]
duk_dup(ctx, 0); duk_put_prop_string(ctx, -2, "_RES");
duk_dup(ctx, 1); duk_put_prop_string(ctx, -2, "_REJ");
return(0);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_abort(duk_context *ctx)
{
duk_push_this(ctx);
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
if (data != NULL)
{
data->abort = 1;
if (data->promise == NULL)
{
// We can gracefully exit this thread
sem_post(&(data->workAvailable));
#ifdef WIN32
ILibThread_Join(data->workerThread);
#endif
}
else
{
if (data->waitingForResult == 0)
{
// We cannot gracefully exit the thread, so let's reject the promise, and let the app layer figure it out
duk_push_heapptr(data->ctx, data->promise); // [promise]
duk_get_prop_string(data->ctx, -1, "_REJ"); // [promise][rej]
duk_swap_top(data->ctx, -2); // [rej][this]
duk_push_string(data->ctx, "ABORT"); // [rej][this][abort]
duk_call_method(data->ctx, 1);
duk_pop(data->ctx); // ...
// We are purposefully not clearing the promise, becuase the hope is that the above layer
// will receive this rejection, and do a proper cleanup, which may need the promise to accomplish that
}
else
{
// Invalid scenario
return(ILibDuktape_Error(ctx, "Cannot abort operation that is marked as 'wait for result'"));
}
}
duk_push_this(ctx);
sem_destroy(&(data->workAvailable));
sem_destroy(&(data->workStarted));
sem_destroy(&(data->workFinished));
duk_del_prop_string(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
}
return(0);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_dataFinalizer(duk_context *ctx)
{
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, 0, ILibDuktape_FFI_AsyncDataPtr);
if (data != NULL)
{
data->abort = 1;
if (data->promise == NULL)
{
data->abort = 1;
sem_post(&(data->workAvailable));
#ifdef WIN32
ILibThread_Join(data->workerThread);
#endif
sem_destroy(&(data->workAvailable));
sem_destroy(&(data->workStarted));
sem_destroy(&(data->workFinished));
}
}
return(0);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync(duk_context *ctx)
{
void *redirectionPtr = NULL;
int i;
int parms = duk_get_top(ctx);
ILibDuktape_FFI_AsyncData *data = NULL;
if (parms > 20) { return(ILibDuktape_Error(ctx, "Too many parameters")); }
if (duk_is_function(ctx, 0))
{
data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, 0, ILibDuktape_FFI_AsyncDataPtr);
if (data != NULL)
{
redirectionPtr = Duktape_GetPointerProperty(ctx, 0, "_address");
}
}
if (redirectionPtr == NULL)
{
duk_push_current_function(ctx); // [func]
data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
if (data == NULL)
{
data = Duktape_PushBuffer(ctx, sizeof(ILibDuktape_FFI_AsyncData));
duk_push_current_function(ctx);
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_dataFinalizer, 1);
duk_set_finalizer(ctx, -2);
duk_pop(ctx);
duk_put_prop_string(ctx, -2, ILibDuktape_FFI_AsyncDataPtr);
data->ctx = ctx;
data->chain = Duktape_GetChain(ctx);
data->fptr = Duktape_GetPointerProperty(ctx, -1, "_address");
sem_init(&(data->workAvailable), 0, 0);
sem_init(&(data->workStarted), 0, 0);
sem_init(&(data->workFinished), 0, 0);
data->workerThread = ILibSpawnNormalThread(ILibDuktape_GenericMarshal_MethodInvokeAsync_WorkerRunLoop, data);
}
}
else
{
duk_push_current_function(ctx);
redirectionPtr = Duktape_GetPointerProperty(ctx, -1, "_address");
}
if (data->promise != NULL) { return(ILibDuktape_Error(ctx, "Async Operation already in progress")); }
if (data->waitingForResult == 0)
{
// Only need to create a promise, if it's fully async
duk_eval_string(ctx, "require('promise');"); // [func][promise]
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_promise, 2);
duk_new(ctx, 1);
data->promise = duk_get_heapptr(ctx, -1);
}
data->vars = (PTRSIZE*)ILibMemory_AllocateA(sizeof(PTRSIZE)*parms);
data->fptr_redirection = redirectionPtr;
duk_push_array(ctx);
for (i = 0; i < parms; ++i)
{
duk_dup(ctx, i);
duk_put_prop_index(ctx, -2, i); // Stash the input arguments in the promise, so they don't get GC'ed until we're done
}
duk_put_prop_string(ctx, -2, "_varArray");
for (i = 0; i < parms; ++i)
{
if (duk_is_object(ctx, i))
{
duk_get_prop_string(ctx, i, "_ptr");
data->vars[i] = (PTRSIZE)duk_to_pointer(ctx, -1);
}
else if (duk_is_number(ctx, i))
{
data->vars[i] = (PTRSIZE)duk_require_int(ctx, i);
}
else if (duk_is_pointer(ctx, i))
{
data->vars[i] = (PTRSIZE)duk_require_pointer(ctx, i);
}
else if(!(i==0 && duk_is_function(ctx, 0)))
{
return(ILibDuktape_Error(ctx, "INVALID Parameter"));
}
}
sem_post(&(data->workAvailable)); // Let worker know there is work available
sem_wait(&(data->workStarted)); // Wait for work to start before exiting, because VARS will be gone when we leave
duk_push_heapptr(ctx, data->promise); // [promise]
duk_push_current_function(ctx); // [promise][func]
duk_get_prop_string(ctx, -1, "_obj"); // [promise][func][obj]
duk_remove(ctx, -2); // [promise][obj]
duk_put_prop_string(ctx, -2, "nativeProxy");// [promise]
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_wait(duk_context *ctx)
{
int nargs = duk_get_top(ctx), i;
ILibDuktape_FFI_AsyncData *data;
duk_push_this(ctx); // [func]
data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
if (data == NULL)
{
data = Duktape_PushBuffer(ctx, sizeof(ILibDuktape_FFI_AsyncData)); // [func][buffer]
duk_push_this(ctx); // [func][buffer][func]
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_dataFinalizer, 1); // [func][buffer][func][finalizer]
duk_set_finalizer(ctx, -2); // [func][buffer][func]
duk_pop(ctx); // [func][buffer]
duk_put_prop_string(ctx, -2, ILibDuktape_FFI_AsyncDataPtr); // [func]
data->ctx = ctx;
data->chain = Duktape_GetChain(ctx);
data->fptr = Duktape_GetPointerProperty(ctx, -1, "_address");
sem_init(&(data->workAvailable), 0, 0);
sem_init(&(data->workStarted), 0, 0);
sem_init(&(data->workFinished), 0, 0);
data->workerThread = ILibSpawnNormalThread(ILibDuktape_GenericMarshal_MethodInvokeAsync_WorkerRunLoop, data);
}
// If we set this flag, a promise won't be created, instead we can just wait for the response
data->waitingForResult = 1; // [func]
duk_get_prop_string(ctx, -1, "apply"); // [func][apply]
duk_swap_top(ctx, -2); // [apply][this]
duk_dup(ctx, -1); // [apply][this][this]
duk_push_array(ctx); // [apply][this][this][args]
for (i = 0; i < nargs; ++i)
{
duk_dup(ctx, i); // [apply][this][this][args][arg]
duk_put_prop_index(ctx, -2, i); // [apply][this][this][args]
}
duk_call_method(ctx, 2);
sem_wait(&(data->workFinished));
ILibDuktape_GenericMarshal_Variable_PUSH(ctx, (void*)(PTRSIZE)data->vars, (int)sizeof(void*));
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvoke(duk_context *ctx)
{
void *fptr = NULL;
@@ -851,7 +1161,13 @@ duk_ret_t ILibDuktape_GenericMarshal_MethodInvoke(duk_context *ctx)
if (spawnThread == 0)
{
retVal = ILibDuktape_GenericMarshal_MethodInvoke_Native(parms, fptr, vars);
#ifdef WIN32
DWORD err = GetLastError();
#endif
ILibDuktape_GenericMarshal_Variable_PUSH(ctx, (void*)(PTRSIZE)retVal, (int)sizeof(void*));
#ifdef WIN32
duk_push_int(ctx, err); duk_put_prop_string(ctx, -2, "_LastError");
#endif
}
else
{
@@ -880,6 +1196,25 @@ duk_ret_t ILibDuktape_GenericMarshal_MethodInvoke(duk_context *ctx)
return 1;
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_thread(duk_context *ctx)
{
duk_push_this(ctx); // [async]
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
if (data == NULL) { return(ILibDuktape_Error(ctx, "No thread")); }
ILibDuktape_GenericMarshal_Variable_PUSH(ctx, data->workerThread, sizeof(void*));
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_MethodInvokeAsync_thread_id(duk_context *ctx)
{
duk_push_this(ctx); // [async]
ILibDuktape_FFI_AsyncData *data = (ILibDuktape_FFI_AsyncData*)Duktape_GetBufferProperty(ctx, -1, ILibDuktape_FFI_AsyncDataPtr);
char tmp[255];
sprintf_s(tmp, sizeof(tmp), "%ul", data->workerThreadId);
duk_push_string(ctx, tmp);
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_CreateMethod(duk_context *ctx)
{
void* module = NULL;
@@ -930,6 +1265,20 @@ duk_ret_t ILibDuktape_GenericMarshal_CreateMethod(duk_context *ctx)
duk_push_pointer(ctx, funcAddress); // [obj][func][addr]
duk_put_prop_string(ctx, -2, "_address"); // [obj][func]
if (threadDispatch != 0) { duk_push_true(ctx); duk_put_prop_string(ctx, -2, "_spawnThread"); } // [obj][func]
// Add an 'async' method, to use a dispatch thread to invoke the method
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync, DUK_VARARGS); // [obj][func][func]
duk_push_pointer(ctx, funcAddress); // [obj][func][func][addr]
duk_put_prop_string(ctx, -2, "_address"); // [obj][func][func]
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_abort, 0); // [obj][func][func][func]
duk_put_prop_string(ctx, -2, "abort"); // [obj][func][func]
duk_push_this(ctx); duk_put_prop_string(ctx, -2, "_obj");
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_thread, 0); duk_put_prop_string(ctx, -2, "thread");
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_wait, DUK_VARARGS); duk_put_prop_string(ctx, -2, "wait");
duk_push_c_function(ctx, ILibDuktape_GenericMarshal_MethodInvokeAsync_thread_id, 0); duk_put_prop_string(ctx, -2, "threadId");
duk_put_prop_string(ctx, -2, "async"); // [obj][func]
duk_put_prop_string(ctx, -2, exposedMethod); // [obj]
return 0;
@@ -1018,12 +1367,27 @@ duk_ret_t ILibDuktape_GenericMarshal_CreateVariableEx(duk_context *ctx)
}
return 1;
}
void ILibDuktape_GlobalGenericCallback_ProcessEx_Abort(void *chain, void *user)
{
Duktape_GlobalGeneric_Data *data = (Duktape_GlobalGeneric_Data*)user;
if (ILibMemory_CanaryOK(data))
{
data->retVal = NULL;
sem_post(&(data->contextWaiter));
}
}
void ILibDuktape_GlobalGenericCallback_ProcessEx(void *chain, void *user)
{
int i;
//void *retVal = NULL;
Duktape_GlobalGeneric_Data *data = (Duktape_GlobalGeneric_Data*)user;
char tmp[255];
sprintf_s(tmp, sizeof(tmp), "%ul", data->callingThread);
duk_push_heapptr(data->emitter->ctx, data->emitter->object); // [obj]
duk_push_string(data->emitter->ctx, tmp); // [obj][str]
duk_put_prop_string(data->emitter->ctx, -2, ILibDuktape_GenericMarshal_GlobalCallback_ThreadID); // [obj]
duk_pop(data->emitter->ctx); // ...
ILibDuktape_EventEmitter_SetupEmit(data->emitter->ctx, data->emitter->object, "GlobalCallback"); // [emit][this][GlobalCallback]
for (i = 0; i < data->numArgs; ++i)
{
@@ -1049,82 +1413,106 @@ void* ILibDuktape_GlobalGenericCallback_Process(int numParms, ...)
{
void *retVal = NULL;
PTRSIZE v;
Duktape_GlobalGeneric_Data *user;
if (GlobalCallbackList == NULL) { return(NULL); }
ILibLinkedList_Lock(GlobalCallbackList);
Duktape_GlobalGeneric_Data *data;
int i = 0, maxCount = ILibLinkedList_GetCount(GlobalCallbackList), count = 0;
int i = 0, maxCount = ILibLinkedList_GetCount(GlobalCallbackList), count = 0, j = 0;
void *node = ILibLinkedList_GetNode_Head(GlobalCallbackList);
Duktape_GlobalGeneric_Data **refList = (Duktape_GlobalGeneric_Data**)ILibMemory_AllocateA(maxCount * sizeof(Duktape_GlobalGeneric_Data*));
while (node != NULL)
{
data = (Duktape_GlobalGeneric_Data*)ILibLinkedList_GetDataFromNode(node);
if (!ILibIsRunningOnChainThread(data->chain))
{
// Need to Context Switch
Duktape_GlobalGeneric_Data *user = ILibMemory_Allocate(sizeof(Duktape_GlobalGeneric_Data) + (numParms * sizeof(PTRSIZE)), 0, NULL, NULL);
sem_init(&(user->contextWaiter), 0, 0);
user->chain = data->chain;
user->emitter = data->emitter;
user->numArgs = numParms;
if (numParms > 0)
{
va_list vlist;
va_start(vlist, numParms);
for (i = 0; i < numParms; ++i)
{
user->args[i] = va_arg(vlist, PTRSIZE);
}
va_end(vlist);
}
ILibChain_RunOnMicrostackThreadEx(user->chain, ILibDuktape_GlobalGenericCallback_ProcessEx, user);
sem_wait(&(user->contextWaiter));
if (user->retVal != NULL) { retVal = user->retVal; }
sem_destroy(&(user->contextWaiter));
free(user);
}
else
{
// No need to context switch, so just reference this
refList[count++] = data;
}
refList[count++] = data;
node = ILibLinkedList_GetNextNode(node);
}
ILibLinkedList_UnLock(GlobalCallbackList);
for (i = 0; i < count; ++i)
{
ILibDuktape_EventEmitter_SetupEmit(refList[i]->emitter->ctx, refList[i]->emitter->object, "GlobalCallback"); // [emit][this][GlobalCallback]
if (numParms > 0)
user = NULL;
ILibLinkedList_Lock(GlobalCallbackList);
if (ILibMemory_CanaryOK(refList[i]))
{
int z;
va_list vlist;
va_start(vlist, numParms);
for (z = 0; z < numParms; ++z)
if (!ILibIsRunningOnChainThread(refList[i]->chain))
{
v = va_arg(vlist, PTRSIZE);
ILibDuktape_GenericMarshal_Variable_PUSH(refList[i]->emitter->ctx, (void*)v, sizeof(void*));
ILibDuktape_GenericMarshal_Variable_DisableAutoFree(refList[i]->emitter->ctx, -1);
}
va_end(vlist);
}
if (duk_pcall_method(refList[i]->emitter->ctx, numParms + 1) != 0)
{
ILibDuktape_Process_UncaughtException(refList[i]->emitter->ctx);
}
else
{
if ((retVal = refList[i]->emitter->lastReturnValue) != NULL)
{
duk_push_heapptr(refList[i]->emitter->ctx, refList[i]->emitter->lastReturnValue); // [retVal]
if (duk_has_prop_string(refList[i]->emitter->ctx, -1, ILibDuktape_GenericMarshal_VariableType))
// Need to context switch
user = ILibMemory_SmartAllocate(sizeof(Duktape_GlobalGeneric_Data) + (numParms * sizeof(PTRSIZE)));
#ifdef WIN32
user->callingThread = GetCurrentThreadId();
#else
user->callingThread = pthread_self();
#endif
sem_init(&(user->contextWaiter), 0, 0);
user->chain = refList[i]->chain;
user->emitter = refList[i]->emitter;
user->numArgs = numParms;
if (numParms > 0)
{
retVal = Duktape_GetPointerProperty(refList[i]->emitter->ctx, -1, "_ptr");
va_list vlist;
va_start(vlist, numParms);
for (j = 0; j < numParms; ++j)
{
user->args[j] = va_arg(vlist, PTRSIZE);
}
va_end(vlist);
}
duk_pop(refList[i]->emitter->ctx); // ...
ILibChain_RunOnMicrostackThreadEx3(refList[i]->chain, ILibDuktape_GlobalGenericCallback_ProcessEx, ILibDuktape_GlobalGenericCallback_ProcessEx_Abort, user);
}
else
{
// No need to context switch
duk_push_heapptr(refList[i]->emitter->ctx, refList[i]->emitter->object); // [obj]
duk_del_prop_string(refList[i]->emitter->ctx, -1, ILibDuktape_GenericMarshal_GlobalCallback_ThreadID);
duk_pop(refList[i]->emitter->ctx); // ...
ILibDuktape_EventEmitter_SetupEmit(refList[i]->emitter->ctx, refList[i]->emitter->object, "GlobalCallback"); // [emit][this][GlobalCallback]
if (numParms > 0)
{
int z;
va_list vlist;
va_start(vlist, numParms);
for (z = 0; z < numParms; ++z)
{
v = va_arg(vlist, PTRSIZE);
ILibDuktape_GenericMarshal_Variable_PUSH(refList[i]->emitter->ctx, (void*)v, sizeof(void*));
ILibDuktape_GenericMarshal_Variable_DisableAutoFree(refList[i]->emitter->ctx, -1);
}
va_end(vlist);
}
if (duk_pcall_method(refList[i]->emitter->ctx, numParms + 1) != 0)
{
ILibDuktape_Process_UncaughtException(refList[i]->emitter->ctx);
}
else
{
if ((retVal = refList[i]->emitter->lastReturnValue) != NULL)
{
duk_push_heapptr(refList[i]->emitter->ctx, refList[i]->emitter->lastReturnValue); // [retVal]
if (duk_has_prop_string(refList[i]->emitter->ctx, -1, ILibDuktape_GenericMarshal_VariableType))
{
retVal = Duktape_GetPointerProperty(refList[i]->emitter->ctx, -1, "_ptr");
}
duk_pop(refList[i]->emitter->ctx); // ...
}
}
duk_pop(refList[i]->emitter->ctx);
}
}
duk_pop(refList[i]->emitter->ctx);
ILibLinkedList_UnLock(GlobalCallbackList);
if (user != NULL)
{
sem_wait(&(user->contextWaiter));
if (user->retVal != NULL) { retVal = user->retVal; }
sem_destroy(&(user->contextWaiter));
ILibMemory_Free(user);
}
}
return(retVal);
}
@@ -1172,6 +1560,14 @@ duk_ret_t ILibDuktape_GenericMarshal_GlobalGenericCallback_EventSink(duk_context
void *self = duk_get_heapptr(ctx, -1);
if (Duktape_GetIntPropertyValue(ctx, -1, ILibDuktape_GenericMarshal_Variable_Parms, -1) == nargs)
{
duk_dup(ctx, -1); // [var]
duk_eval_string(ctx, "require('_GenericMarshal');"); // [var][GM]
duk_get_prop_string(ctx, -1, "CallingThread"); // [var][GM][CallingThread]
duk_swap_top(ctx, -2); // [var][CallingThread][this]
duk_call_method(ctx, 0); // [var][ThreadId]
duk_put_prop_string(ctx, -2, ILibDuktape_GenericMarshal_GlobalCallback_ThreadID);
duk_pop(ctx);
ILibDuktape_EventEmitter_SetupEmit(ctx, duk_get_heapptr(ctx, -1), "GlobalCallback"); // [emit][this][GlobalCallback]
for (i = 0; i < nargs; ++i) { duk_dup(ctx, i); }
duk_pcall_method(ctx, nargs + 1);
@@ -1194,6 +1590,22 @@ duk_ret_t ILibDuktape_GenericMarshal_ObjectToPtr_Verify(duk_context *ctx)
duk_push_boolean(ctx, ptr == var);
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_GlobalCallback_CallingThread(duk_context *ctx)
{
duk_push_this(ctx);
if (duk_has_prop_string(ctx, -1, ILibDuktape_GenericMarshal_GlobalCallback_ThreadID))
{
duk_get_prop_string(ctx, -1, ILibDuktape_GenericMarshal_GlobalCallback_ThreadID);
}
else
{
duk_eval_string(ctx, "require('_GenericMarshal');"); // [GM]
duk_get_prop_string(ctx, -1, "GetCurrentThread"); // [GM][GCT]
duk_swap_top(ctx, -2); // [GCT][this]
duk_call_method(ctx, 0); // [ThreadId]
}
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_GetGlobalGenericCallback(duk_context *ctx)
{
int numParms = duk_require_int(ctx, 0);
@@ -1205,11 +1617,12 @@ duk_ret_t ILibDuktape_GenericMarshal_GetGlobalGenericCallback(duk_context *ctx)
{
GlobalCallbackList = ILibLinkedList_Create();
}
Duktape_GlobalGeneric_Data *data = ILibMemory_Allocate(sizeof(Duktape_GlobalGeneric_Data), 0, NULL, NULL);
Duktape_GlobalGeneric_Data *data = (Duktape_GlobalGeneric_Data*)ILibMemory_SmartAllocate(sizeof(Duktape_GlobalGeneric_Data));
data->emitter = ILibDuktape_EventEmitter_Create(ctx);
data->chain = Duktape_GetChain(ctx);
ILibDuktape_EventEmitter_CreateEventEx(data->emitter, "GlobalCallback");
ILibDuktape_CreateInstanceMethod(ctx, "CallingThread", ILibDuktape_GenericMarshal_GlobalCallback_CallingThread, 0);
ILibLinkedList_Lock(GlobalCallbackList);
ILibLinkedList_AddTail(GlobalCallbackList, data);
@@ -1255,6 +1668,7 @@ duk_ret_t ILibDuktape_GenericMarshal_GetGlobalGenericCallback(duk_context *ctx)
ILibDuktape_EventEmitter *varEmitter = ILibDuktape_EventEmitter_Create(ctx);
ILibDuktape_EventEmitter_CreateEventEx(varEmitter, "GlobalCallback");
duk_push_int(ctx, numParms); duk_put_prop_string(ctx, -2, ILibDuktape_GenericMarshal_Variable_Parms);
ILibDuktape_CreateInstanceMethod(ctx, "CallingThread", ILibDuktape_GenericMarshal_GlobalCallback_CallingThread, 0);
duk_get_prop_string(ctx, -2, "on"); // [GenericMarshal][Variable][on]
duk_dup(ctx, -3); // [GenericMarshal][Variable][on][this/GM]
@@ -1279,7 +1693,7 @@ duk_ret_t ILibDuktape_GenericMarshal_Finalizer(duk_context *ctx)
Duktape_GlobalGeneric_Data *data = (Duktape_GlobalGeneric_Data*)ILibLinkedList_GetDataFromNode(node);
if (data->chain == Duktape_GetChain(ctx))
{
free(data);
ILibMemory_Free(data);
void *next = ILibLinkedList_GetNextNode(node);
ILibLinkedList_Remove(node);
node = next;
@@ -1374,6 +1788,18 @@ duk_ret_t ILibDuktape_GenericMarshal_ObjectToPtr(duk_context *ctx)
return(1);
}
duk_ret_t ILibDuktape_GenericMarshal_GetCurrentThread(duk_context *ctx)
{
char tmp[255];
#if defined(WIN32)
sprintf_s(tmp, sizeof(tmp), "%ul", GetCurrentThreadId());
#else
sprintf_s(tmp, sizeof(tmp), "%ul", pthread_self());
#endif
duk_push_string(ctx, tmp);
return(1);
}
void ILibDuktape_GenericMarshal_Push(duk_context *ctx, void *chain)
{
duk_push_object(ctx); // [obj]
@@ -1389,6 +1815,7 @@ void ILibDuktape_GenericMarshal_Push(duk_context *ctx, void *chain)
ILibDuktape_CreateInstanceMethod(ctx, "StashObject", ILibDuktape_GenericMarshal_StashObject, 1);
ILibDuktape_CreateInstanceMethod(ctx, "UnstashObject", ILibDuktape_GenericMarshal_UnstashObject, DUK_VARARGS);
ILibDuktape_CreateInstanceMethod(ctx, "ObjectToPtr", ILibDuktape_GenericMarshal_ObjectToPtr, 1);
ILibDuktape_CreateInstanceMethod(ctx, "GetCurrentThread", ILibDuktape_GenericMarshal_GetCurrentThread, 0);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "_VarSize", 4, "CreateInteger", ILibDuktape_GenericMarshal_CreateVariableEx, 0);
ILibDuktape_CreateInstanceMethodWithIntProperty(ctx, "_VarSize", ((int)sizeof(void*)), "CreatePointer", ILibDuktape_GenericMarshal_CreateVariableEx, DUK_VARARGS);