You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Nov 10, 2017. It is now read-only.
Windows XP crashes when ADB starts up. The following is an explanation and suggested solution to the problem.
The Problem
Apparently, Windows XP doesn't like it when you use thread local storage. Unfortunately, ADB uses thread local storage to ensure that JavaScript callbacks are called on the same thread that they were declared on (a requirement for js-ctypes).
The Current State of the C->JS Code
Right now, most events are triggered from C to JS with the MSG macro. The MSG macro relies on a thread-local function pointer, js_msg installed at startup for each web worker (look in the init channel of any adb-*-thread.js). MSG is defined in js_message.h, and right now only casts the data argument for you before calling the function pointer installed in each thread. MSG returns data packed in a void * which should be casted to whatever type of result you expect for that message.
On the JavaScript side, the callback installed into the function pointer is jsMsgFn which is declared just above the init listener in each of the workers. jsMsgFn is defined to be the result of the higher-order function CommonMessageHandler that first handles messages common to many listeners, and then handles the thread-specific messages that could occur (the 3rd argument). The 3rd argument to CommonMessageHandler is a (String, CData[void *]) -> CData. The first parameter is the channel for the event, the second parameter is pointer to the structure that was passed from C, and it returns a packed structure back to native code.
To extract the data from the packed structure use JsMessage.unpack : (CData[void *], CType...) -> Array[CData]. To pack a value for return back to native code use JsMessage.pack : (Number|String, (Number|String) -> (Number|String)) -> CData. The second argument for JsMessage.pack is specifically one of either global values Number or String. For example, JsMessage.pack(1, Number) or JsMessage.pack("hello", String).
There is also a dirty hack for bridging DLL calls through JavaScript used in the device IO threads and the device polling thread. I don't think this will cause any issues though, since these don't use the THREAD_LOCAL modifier.
The Fix
I think the following solution is the best in a world without thread local storage.
The MSG macro should first send data to one specific "communication" thread and then callback to JavaScript from only one thread. When the JavaScript returns a value back, we send the data back across and return it back to the thread that called MSG.
How to implement this?
The best way would probably be to reuse the fdevent infrastructure already in place in ADB.
On start-up, rather than passing the jsMsgFn callback and installing it in the native code for each thread. We can do something like the following once in the beginning of the program:
// create a pipeint[2] sv;
adb_socketpair(sv);
// global JsMessage fdjs_message_fd=sv[0];
// create an fdeventfdeventfde;
// install itfdevent_install(&fde,
sv[1],
jsmsg_handler,
NULL);
// callback when there is data ready to be readfdevent_set(&fde, FDE_READ);
voidjsmsg_handler(intfd, unsignedev, void*unused) {
if(ev&FDE_READ) {
charchannel[128];
// grab the channel (and possibly include info about which thread?)if(readx(fd, channel, sizeof(channel))) {
// error handle
}
void*data;
// grab a pointer to the dataif (readx(fd, &data, sizeof(void*))) {
// error handle
}
// send the messagevoid*result=js_msg(channel, data);
// write the result back to the callerif (writex(fd, &result, sizeof(void*))) {
// error handle
}
}
}
That way MSG acts in exactly the same way (i.e. blocking until it gets a response from JS) and we can easily communicate between threads re-using the event loop architecture that ADB already provides. This is assuming that the overhead of communicating over a socket pipe created with adb_socketpair is not high. Also, you can use blocking I/O on file descriptors that are shared cross-thread without having to have explicit mutexes.
The JavaScript side would also have to be refactored so that information still gets to the right places. Since now all the messages will come in on one web worker. See EventedChromeWorker for sending messages (optionally with responses) between ChromeWorkers.
Caveats
The fdevent_loop runs on the adb-server-thread. So if any part of adb-server-thread needs to use MSG (which it does), we will have to start a second fdevent_loop on a separate worker (and this event loop should be specifically for MSG handling for speed and thread-safety for the rest of the code). Keep in mind, that threads need to return from native code only millis later (and on Windows completely before) the web worker is terminated.
A problem that might come up is that we won't be able to pass information from MSGs into web workers that are currently in blocking native code (like the ones that called MSG for example) due to web workers' inability to block on recieving messages. The only way to get that information would be to completely return from native code back to JavaScript. In some places, this is going to be a deal-breaker and we'll have to revert back to passing function pointers around manually.
Acknowledgements
Thanks @nickdesaulniers for suggesting using one thread as the communication medium.
The text was updated successfully, but these errors were encountered:
Windows XP crashes when ADB starts up. The following is an explanation and suggested solution to the problem.
The Problem
Apparently, Windows XP doesn't like it when you use thread local storage. Unfortunately, ADB uses thread local storage to ensure that JavaScript callbacks are called on the same thread that they were declared on (a requirement for js-ctypes).
The Current State of the C->JS Code
Right now, most events are triggered from C to JS with the
MSG
macro. TheMSG
macro relies on a thread-local function pointer, js_msg installed at startup for each web worker (look in the init channel of any adb-*-thread.js).MSG
is defined in js_message.h, and right now only casts the data argument for you before calling the function pointer installed in each thread.MSG
returns data packed in avoid *
which should be casted to whatever type of result you expect for that message.On the JavaScript side, the callback installed into the function pointer is
jsMsgFn
which is declared just above the init listener in each of the workers.jsMsgFn
is defined to be the result of the higher-order functionCommonMessageHandler
that first handles messages common to many listeners, and then handles the thread-specific messages that could occur (the 3rd argument). The 3rd argument toCommonMessageHandler
is a(String, CData[void *]) -> CData
. The first parameter is the channel for the event, the second parameter is pointer to the structure that was passed from C, and it returns a packed structure back to native code.To extract the data from the packed structure use
JsMessage.unpack : (CData[void *], CType...) -> Array[CData]
. To pack a value for return back to native code useJsMessage.pack : (Number|String, (Number|String) -> (Number|String)) -> CData
. The second argument forJsMessage.pack
is specifically one of either global valuesNumber
orString
. For example,JsMessage.pack(1, Number)
orJsMessage.pack("hello", String)
.There is also a dirty hack for bridging DLL calls through JavaScript used in the device IO threads and the device polling thread. I don't think this will cause any issues though, since these don't use the THREAD_LOCAL modifier.
The Fix
I think the following solution is the best in a world without thread local storage.
The
MSG
macro should first send data to one specific "communication" thread and then callback to JavaScript from only one thread. When the JavaScript returns a value back, we send the data back across and return it back to the thread that calledMSG
.How to implement this?
The best way would probably be to reuse the fdevent infrastructure already in place in ADB.
On start-up, rather than passing the
jsMsgFn
callback and installing it in the native code for each thread. We can do something like the following once in the beginning of the program:Then
MSG
is something like:That way
MSG
acts in exactly the same way (i.e. blocking until it gets a response from JS) and we can easily communicate between threads re-using the event loop architecture that ADB already provides. This is assuming that the overhead of communicating over a socket pipe created withadb_socketpair
is not high. Also, you can use blocking I/O on file descriptors that are shared cross-thread without having to have explicit mutexes.The JavaScript side would also have to be refactored so that information still gets to the right places. Since now all the messages will come in on one web worker. See EventedChromeWorker for sending messages (optionally with responses) between ChromeWorkers.
Caveats
The
fdevent_loop
runs on the adb-server-thread. So if any part of adb-server-thread needs to useMSG
(which it does), we will have to start a secondfdevent_loop
on a separate worker (and this event loop should be specifically forMSG
handling for speed and thread-safety for the rest of the code). Keep in mind, that threads need to return from native code only millis later (and on Windows completely before) the web worker is terminated.A problem that might come up is that we won't be able to pass information from
MSG
s into web workers that are currently in blocking native code (like the ones that calledMSG
for example) due to web workers' inability to block on recieving messages. The only way to get that information would be to completely return from native code back to JavaScript. In some places, this is going to be a deal-breaker and we'll have to revert back to passing function pointers around manually.Acknowledgements
Thanks @nickdesaulniers for suggesting using one thread as the communication medium.
The text was updated successfully, but these errors were encountered: