[Debugger] Fix nested JS callback registration (fix #2147) (#2157)

* [Debugger] Fix nested JS callback registration (fix #2147)

* [Debugger] Handle JS socket ungraceful disconnect
This commit is contained in:
shyguyhex 2021-12-28 01:31:19 -06:00 committed by GitHub
parent 3fe7fb7005
commit 9b5f45ea23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 97 deletions

View File

@ -100,7 +100,7 @@ void CJSSocketWorker::WorkerProc()
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = TIMEOUT_MS * 1000; timeout.tv_usec = TIMEOUT_MS * 1000;
bool bWritable = false; bool bHaveConnection = false;
bool bConnectPending = false; bool bConnectPending = false;
bool bWritesPending = false; bool bWritesPending = false;
bool bRecvClosed = false; bool bRecvClosed = false;
@ -112,8 +112,8 @@ void CJSSocketWorker::WorkerProc()
if (!bConnectPending) if (!bConnectPending)
{ {
// assume it's already writable // assume it's already connected
bWritable = true; bHaveConnection = true;
} }
if (bConnectPending && ProcConnect()) if (bConnectPending && ProcConnect())
@ -158,7 +158,7 @@ void CJSSocketWorker::WorkerProc()
FD_SET(m_Socket, pReadFds); FD_SET(m_Socket, pReadFds);
} }
if (bWritesPending || !bWritable) if (bWritesPending || !bHaveConnection)
{ {
pWriteFds = &writeFds; pWriteFds = &writeFds;
FD_ZERO(pWriteFds); FD_ZERO(pWriteFds);
@ -169,6 +169,7 @@ void CJSSocketWorker::WorkerProc()
if (numFds == SOCKET_ERROR) if (numFds == SOCKET_ERROR)
{ {
JSEmitError("select() error"); JSEmitError("select() error");
break;
} }
if (numFds == 0) if (numFds == 0)
@ -176,11 +177,14 @@ void CJSSocketWorker::WorkerProc()
continue; continue;
} }
if (pWriteFds && FD_ISSET(m_Socket, pWriteFds)) bool bWritable = pWriteFds && FD_ISSET(m_Socket, pWriteFds);
bool bReadable = pReadFds && FD_ISSET(m_Socket, pReadFds);
if (bWritable && !m_Queue.bSendClosed)
{ {
if (!bWritable) if (!bHaveConnection)
{ {
bWritable = true; bHaveConnection = true;
JSEmitConnect(); JSEmitConnect();
} }
@ -189,8 +193,13 @@ void CJSSocketWorker::WorkerProc()
ProcSendData(); ProcSendData();
} }
} }
else if (bHaveConnection)
{
JSEmitError("connection reset");
break;
}
if (pReadFds && FD_ISSET(m_Socket, pReadFds)) if (bReadable)
{ {
ProcRecvData(); ProcRecvData();
} }

View File

@ -375,7 +375,7 @@ JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, duk_idx_t callbackId
JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, JSAppHookID hookId, JSAppCallback& callback) JSAppCallbackID ScriptAPI::AddAppCallback(duk_context* ctx, JSAppHookID hookId, JSAppCallback& callback)
{ {
CScriptInstance* inst = GetInstance(ctx); CScriptInstance* inst = GetInstance(ctx);
JSAppCallbackID callbackId = inst->System()->QueueAddAppCallback(hookId, callback); JSAppCallbackID callbackId = inst->System()->RawAddAppCallback(hookId, callback);
if(callbackId == JS_INVALID_CALLBACK) if(callbackId == JS_INVALID_CALLBACK)
{ {
@ -429,7 +429,7 @@ duk_ret_t ScriptAPI::js__AppCallbackFinalizer(duk_context* ctx)
JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, -1); JSAppCallbackID callbackId = (JSAppCallbackID)duk_get_uint(ctx, -1);
duk_pop_n(ctx, 2); duk_pop_n(ctx, 2);
inst->System()->QueueRemoveAppCallback(hookId, callbackId); inst->System()->RawRemoveAppCallback(hookId, callbackId);
return 0; return 0;
} }
@ -531,6 +531,8 @@ duk_ret_t ScriptAPI::js__Emitter_emit(duk_context* ctx)
duk_get_prop_string(ctx, -1, eventName); duk_get_prop_string(ctx, -1, eventName);
duk_enum(ctx, -1, 0); duk_enum(ctx, -1, 0);
int count = 0;
while (duk_next(ctx, -1, (duk_bool_t)true)) while (duk_next(ctx, -1, (duk_bool_t)true))
{ {
duk_push_this(ctx); duk_push_this(ctx);
@ -546,6 +548,14 @@ duk_ret_t ScriptAPI::js__Emitter_emit(duk_context* ctx)
} }
duk_pop_n(ctx, 2); duk_pop_n(ctx, 2);
count++;
}
// throw if there are no listeners for error event
if (count == 0 && strcmp("error", eventName) == 0)
{
duk_dup(ctx, 1);
duk_throw(ctx);
} }
return 0; return 0;

View File

@ -168,6 +168,7 @@ bool CScriptSystem::HaveAppCallbacks(JSAppHookID hookId)
void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env) void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env)
{ {
CGuard guard(m_InstancesCS); CGuard guard(m_InstancesCS);
RefreshCallbackMaps();
JSAppCallbackList& callbacks = m_AppCallbackHooks[hookId]; JSAppCallbackList& callbacks = m_AppCallbackHooks[hookId];
@ -175,7 +176,7 @@ void CScriptSystem::InvokeAppCallbacks(JSAppHookID hookId, void* env)
for (JSAppCallback& callback : callbacks) for (JSAppCallback& callback : callbacks)
{ {
if (!callback.m_ConditionFunc(&callback, env)) if (callback.m_bDisabled || !callback.m_ConditionFunc(&callback, env))
{ {
continue; continue;
} }
@ -257,68 +258,28 @@ void CScriptSystem::UpdateCpuCbListInfo(volatile JSCpuCbListInfo& info, JSAppCal
void CScriptSystem::RefreshCallbackMaps() void CScriptSystem::RefreshCallbackMaps()
{ {
for (JSQueuedCallbackRemove& cbRemove : m_CbRemoveQueue) for (JSAppCallbackList& callbacks : m_AppCallbackHooks)
{ {
RawRemoveAppCallback(cbRemove.hookId, cbRemove.callbackId); JSAppCallbackList::iterator it = callbacks.begin();
}
for (JSQueuedCallbackAdd& cbAdd : m_CbAddQueue) while (it != callbacks.end())
{ {
RawAddAppCallback(cbAdd.hookId, cbAdd.callback); if (it->m_bDisabled)
{
callbacks.erase(it);
}
else
{
it++;
}
}
} }
m_CbRemoveQueue.clear();
m_CbAddQueue.clear();
UpdateCpuCbListInfo(m_CpuExecCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_EXEC]); UpdateCpuCbListInfo(m_CpuExecCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_EXEC]);
UpdateCpuCbListInfo(m_CpuReadCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_READ]); UpdateCpuCbListInfo(m_CpuReadCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_READ]);
UpdateCpuCbListInfo(m_CpuWriteCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_WRITE]); UpdateCpuCbListInfo(m_CpuWriteCbInfo, m_AppCallbackHooks[JS_HOOK_CPU_WRITE]);
} }
JSAppCallbackID CScriptSystem::QueueAddAppCallback(JSAppHookID hookId, JSAppCallback callback)
{
if (hookId >= JS_NUM_APP_HOOKS)
{
return JS_INVALID_CALLBACK;
}
callback.m_CallbackId = m_NextAppCallbackId++;
m_CbAddQueue.push_back({ hookId, callback });
callback.m_Instance->IncRefCount();
return callback.m_CallbackId;
}
void CScriptSystem::QueueRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId)
{
// todo also remove from addqueue
for (size_t i = 0; i < m_CbRemoveQueue.size(); i++)
{
if (m_CbRemoveQueue[i].hookId == hookId &&
m_CbRemoveQueue[i].callbackId == callbackId)
{
return;
}
}
JSAppCallbackList::iterator it;
for (it = m_AppCallbackHooks[hookId].begin(); it != m_AppCallbackHooks[hookId].end(); it++)
{
if (it->m_CallbackId == callbackId)
{
break;
}
}
if (it == m_AppCallbackHooks[hookId].end())
{
return;
}
m_CbRemoveQueue.push_back({ hookId, callbackId });
it->m_Instance->DecRefCount();
}
void CScriptSystem::DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg) void CScriptSystem::DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg)
{ {
int button = -1; int button = -1;
@ -474,7 +435,7 @@ void CScriptSystem::OnSweep(bool bIfDone)
{ {
NotifyStatus(inst->Name().c_str(), JS_STATUS_STOPPED); NotifyStatus(inst->Name().c_str(), JS_STATUS_STOPPED);
delete inst; delete inst;
m_Instances.erase(it++); m_Instances.erase(it);
} }
else else
{ {
@ -509,28 +470,34 @@ bool CScriptSystem::RawRemoveInstance(const char *name)
return true; return true;
} }
void CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback) JSAppCallbackID CScriptSystem::RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback)
{ {
if(hookId >= JS_NUM_APP_HOOKS) if(hookId >= JS_NUM_APP_HOOKS)
{ {
return; return JS_INVALID_CALLBACK;
} }
callback.m_Instance->IncRefCount();
callback.m_CallbackId = m_NextAppCallbackId++;
m_AppCallbackHooks[hookId].push_back(callback); m_AppCallbackHooks[hookId].push_back(callback);
m_AppCallbackCount++; m_AppCallbackCount++;
return callback.m_CallbackId;
} }
void CScriptSystem::RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId) void CScriptSystem::RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId)
{ {
JSAppCallbackList::iterator it; JSAppCallbackList::iterator it = m_AppCallbackHooks[hookId].begin();
for (it = m_AppCallbackHooks[hookId].begin(); it != m_AppCallbackHooks[hookId].end(); it++) while (it != m_AppCallbackHooks[hookId].end())
{ {
if (it->m_CallbackId == callbackId) if (it->m_CallbackId == callbackId)
{ {
m_AppCallbackHooks[hookId].erase(it);
m_AppCallbackCount--; m_AppCallbackCount--;
it->m_Instance->DecRefCount();
it->m_bDisabled = true;
return; return;
} }
it++;
} }
} }

View File

@ -17,18 +17,6 @@ class CScriptSystem
typedef std::map<JSInstanceName, JSInstanceStatus> JSInstanceStatusMap; typedef std::map<JSInstanceName, JSInstanceStatus> JSInstanceStatusMap;
typedef std::vector<JSSysCommand> JSSysCommandQueue; typedef std::vector<JSSysCommand> JSSysCommandQueue;
struct JSQueuedCallbackAdd
{
JSAppHookID hookId;
JSAppCallback callback;
};
struct JSQueuedCallbackRemove
{
JSAppHookID hookId;
JSAppCallbackID callbackId;
};
enum { JS_CPU_CB_RANGE_CACHE_SIZE = 256 }; enum { JS_CPU_CB_RANGE_CACHE_SIZE = 256 };
struct JSCpuCbListInfo struct JSCpuCbListInfo
@ -55,9 +43,6 @@ class CScriptSystem
JSAppCallbackList m_AppCallbackHooks[JS_NUM_APP_HOOKS]; JSAppCallbackList m_AppCallbackHooks[JS_NUM_APP_HOOKS];
JSAppCallbackID m_NextAppCallbackId; JSAppCallbackID m_NextAppCallbackId;
std::vector<JSQueuedCallbackRemove> m_CbRemoveQueue;
std::vector<JSQueuedCallbackAdd> m_CbAddQueue;
volatile size_t m_AppCallbackCount; volatile size_t m_AppCallbackCount;
volatile JSCpuCbListInfo m_CpuExecCbInfo; volatile JSCpuCbListInfo m_CpuExecCbInfo;
@ -125,9 +110,6 @@ public:
static void UpdateCpuCbListInfo(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks); static void UpdateCpuCbListInfo(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks);
void DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg = (DWORD)-1); void DoMouseEvent(JSAppHookID hookId, int x, int y, DWORD uMsg = (DWORD)-1);
JSAppCallbackID QueueAddAppCallback(JSAppHookID hookId, JSAppCallback callback);
void QueueRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId);
void InvokeAppCallbacks(JSAppHookID hookId, void* env = nullptr); void InvokeAppCallbacks(JSAppHookID hookId, void* env = nullptr);
void ExecAutorunList(); void ExecAutorunList();
@ -135,6 +117,9 @@ public:
void LoadAutorunList(); void LoadAutorunList();
void SaveAutorunList(); void SaveAutorunList();
JSAppCallbackID RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback);
void RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId);
private: private:
inline bool HaveCpuCallbacks(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks, uint32_t address) inline bool HaveCpuCallbacks(volatile JSCpuCbListInfo& info, JSAppCallbackList& callbacks, uint32_t address)
{ {
@ -189,9 +174,6 @@ private:
void OnSweep(bool bIfDone); void OnSweep(bool bIfDone);
bool RawRemoveInstance(const char* key); bool RawRemoveInstance(const char* key);
void RawAddAppCallback(JSAppHookID hookId, JSAppCallback& callback);
void RawRemoveAppCallback(JSAppHookID hookId, JSAppCallbackID callbackId);
void RefreshCallbackMaps(); void RefreshCallbackMaps();
static stdstr FixStringReturns(const char* str); static stdstr FixStringReturns(const char* str);

View File

@ -77,6 +77,7 @@ struct JSAppCallback
{ {
// assigned by scriptsys when this is added to a callback map // assigned by scriptsys when this is added to a callback map
JSAppCallbackID m_CallbackId; JSAppCallbackID m_CallbackId;
bool m_bDisabled;
CScriptInstance *m_Instance; CScriptInstance *m_Instance;
void *m_DukFuncHeapPtr; void *m_DukFuncHeapPtr;
@ -98,12 +99,13 @@ struct JSAppCallback
JSAppCallbackCondFunc condFunc = nullptr, JSAppCallbackCondFunc condFunc = nullptr,
JSDukArgSetupFunc argSetupFunc = nullptr, JSDukArgSetupFunc argSetupFunc = nullptr,
JSAppCallbackCleanupFunc cleanupFunc = nullptr) : JSAppCallbackCleanupFunc cleanupFunc = nullptr) :
m_CallbackId(JS_INVALID_CALLBACK),
m_bDisabled(false),
m_Instance(instance), m_Instance(instance),
m_DukFuncHeapPtr(dukFuncHeapPtr), m_DukFuncHeapPtr(dukFuncHeapPtr),
m_ConditionFunc(condFunc), m_ConditionFunc(condFunc),
m_DukArgSetupFunc(argSetupFunc), m_DukArgSetupFunc(argSetupFunc),
m_CleanupFunc(cleanupFunc), m_CleanupFunc(cleanupFunc)
m_CallbackId(JS_INVALID_CALLBACK)
{ {
if (m_ConditionFunc == nullptr) if (m_ConditionFunc == nullptr)
{ {
@ -114,12 +116,13 @@ struct JSAppCallback
} }
JSAppCallback() : JSAppCallback() :
m_CallbackId(JS_INVALID_CALLBACK),
m_bDisabled(false),
m_Instance(nullptr), m_Instance(nullptr),
m_DukFuncHeapPtr(nullptr), m_DukFuncHeapPtr(nullptr),
m_ConditionFunc(nullptr), m_ConditionFunc(nullptr),
m_DukArgSetupFunc(nullptr), m_DukArgSetupFunc(nullptr),
m_CleanupFunc(nullptr), m_CleanupFunc(nullptr)
m_CallbackId(JS_INVALID_CALLBACK)
{ {
if (m_ConditionFunc == nullptr) if (m_ConditionFunc == nullptr)
{ {
@ -163,7 +166,8 @@ struct JSHookSpTaskEnv
uint32_t yieldDataSize; uint32_t yieldDataSize;
}; };
struct JSHookPiDmaEnv { struct JSHookPiDmaEnv
{
int direction; int direction;
uint32_t dramAddress; uint32_t dramAddress;
uint32_t cartAddress; uint32_t cartAddress;
@ -208,6 +212,9 @@ struct JSSysCMethodCall
~JSSysCMethodCall() ~JSSysCMethodCall()
{ {
delete[] (char*)m_ArgSetupParam; if (m_ArgSetupParam != nullptr)
{
delete[](char*)m_ArgSetupParam;
}
} }
}; };