diff options
author | Fraser Adams <fraser.adams@blueyonder.co.uk> | 2014-06-06 14:54:17 +0100 |
---|---|---|
committer | Fraser Adams <fraser.adams@blueyonder.co.uk> | 2014-06-06 14:54:17 +0100 |
commit | 4e691c70549f3deead0fe4d6ba6fa4b95f31655d (patch) | |
tree | 061a090cc4c7925d202c1e4df24b65991e74a048 | |
parent | 4d2d64b63d79ec8bc2ef96c58976c6b2a758407d (diff) |
Improve callback mechanism to allow an arbitrary userData pointer to be passed during callback registration that will subsequently be passed to the callback
-rw-r--r-- | src/library_sockfs.js | 32 | ||||
-rw-r--r-- | system/include/emscripten/emscripten.h | 51 | ||||
-rw-r--r-- | tests/sockets/test_sockets_echo_client.c | 14 | ||||
-rw-r--r-- | tests/sockets/test_sockets_echo_server.c | 11 |
4 files changed, 70 insertions, 38 deletions
diff --git a/src/library_sockfs.js b/src/library_sockfs.js index f1c518d9..de51a599 100644 --- a/src/library_sockfs.js +++ b/src/library_sockfs.js @@ -10,6 +10,7 @@ mergeInto(LibraryManager.library, { // Add the Event registration mechanism to the exported websocket configuration // object so we can register network callbacks from native JavaScript too. + // For more documentation see system/include/emscripten/emscripten.h Module['websocket']._callbacks = {}; Module['websocket']['on'] = function(event, callback) { if ('function' === typeof callback) { @@ -686,16 +687,16 @@ mergeInto(LibraryManager.library, { * Passing a NULL callback function to a emscripten_set_socket_*_callback call * will deregister the callback registered for that Event. */ - __set_network_callback: function(event, callback) { + __set_network_callback: function(event, userData, callback) { function _callback(data) { try { if (event === 'error') { var sp = Runtime.stackSave(); var msg = allocate(intArrayFromString(data[2]), 'i8', ALLOC_STACK); - Runtime.dynCall('viii', callback, [data[0], data[1], msg]); + Runtime.dynCall('viiii', callback, [data[0], data[1], msg, userData]); Runtime.stackRestore(sp); } else { - Runtime.dynCall('vi', callback, [data]); + Runtime.dynCall('vii', callback, [data, userData]); } } catch (e) { if (e instanceof ExitStatus) { @@ -711,28 +712,27 @@ mergeInto(LibraryManager.library, { Module['websocket']['on'](event, callback ? _callback : null); }, emscripten_set_socket_error_callback__deps: ['__set_network_callback'], - emscripten_set_socket_error_callback: function(callback) { - ___set_network_callback('error', callback); + emscripten_set_socket_error_callback: function(userData, callback) { + ___set_network_callback('error', userData, callback); }, emscripten_set_socket_open_callback__deps: ['__set_network_callback'], - emscripten_set_socket_open_callback: function(callback) { - ___set_network_callback('open', callback); + emscripten_set_socket_open_callback: function(userData, callback) { + ___set_network_callback('open', userData, callback); }, emscripten_set_socket_listen_callback__deps: ['__set_network_callback'], - emscripten_set_socket_listen_callback: function(callback) { - ___set_network_callback('listen', callback); - + emscripten_set_socket_listen_callback: function(userData, callback) { + ___set_network_callback('listen', userData, callback); }, emscripten_set_socket_connection_callback__deps: ['__set_network_callback'], - emscripten_set_socket_connection_callback: function(callback) { - ___set_network_callback('connection', callback); + emscripten_set_socket_connection_callback: function(userData, callback) { + ___set_network_callback('connection', userData, callback); }, emscripten_set_socket_message_callback__deps: ['__set_network_callback'], - emscripten_set_socket_message_callback: function(callback) { - ___set_network_callback('message', callback); + emscripten_set_socket_message_callback: function(userData, callback) { + ___set_network_callback('message', userData, callback); }, emscripten_set_socket_close_callback__deps: ['__set_network_callback'], - emscripten_set_socket_close_callback: function(callback) { - ___set_network_callback('close', callback); + emscripten_set_socket_close_callback: function(userData, callback) { + ___set_network_callback('close', userData, callback); } }); diff --git a/system/include/emscripten/emscripten.h b/system/include/emscripten/emscripten.h index d3a3a257..994a75a7 100644 --- a/system/include/emscripten/emscripten.h +++ b/system/include/emscripten/emscripten.h @@ -159,28 +159,53 @@ extern void emscripten_cancel_main_loop(void); * These events are analogous to WebSocket events but are emitted * *after* the internal emscripten socket processing has occurred * so, for example, the message callback will be triggered after - * the data has been added to the recv_queue this means that an + * the data has been added to the recv_queue. This means that an * application receiving this callback can simply read/recv the data * using the file descriptor passed as a parameter to the callback. - * All of the callbacks except the error callback are passed a file - * descriptor representing the fd that the notified activity took - * place on. The error callback takes an int representing errno and - * a char* representing the error message. Passing a NULL callback - * function to a emscripten_set_socket_*_callback call will deregister - * the callback registered for that Event. + * All of the callbacks are passed a file descriptor representing + * the fd that the notified activity took place on. The error + * callback also takes an int representing errno and a char* that + * represents the error message. + * + * Only a single callback function may be registered to handle any + * given Event, so calling a given registration function more than + * once will cause the first callback to be replaced by the second. + * Similarly passing a NULL callback function to any + * emscripten_set_socket_*_callback call will deregister the callback + * registered for that Event. + * + * The userData pointer allows arbitrary data specified during Event + * registration to be passed to the callback, this is particularly + * useful for passing "this" pointers around in Object Orienter code. + * + * In addition to being able to register network callbacks from C + * it is also possible for native JavaScript code to directly use the + * underlying mechanism used to implement the callback registration: + * Module['websocket']['on']('error', function(error) {console.log('Socket error ' + error);}); + * Module['websocket']['on']('open', function(fd) {console.log('Socket open fd = ' + fd);}); + * Module['websocket']['on']('listen', function(fd) {console.log('Socket listen fd = ' + fd);}); + * Module['websocket']['on']('connection', function(fd) {console.log('Socket connection fd = ' + fd);}); + * Module['websocket']['on']('message', function(fd) {console.log('Socket message fd = ' + fd);}); + * Module['websocket']['on']('close', function(fd) {console.log('Socket close fd = ' + fd);}); + * + * Note that the underlying JavaScript implementation doesn't pass + * userData, this is actually mostly of use to C/C++ code and the + * emscripten_set_socket_*_callback calls actually create a closure + * containing the userData and pass that as the callback to the + * underlying JavaScript Event registration mechanism. */ // Triggered by a WebSocket error. -extern void emscripten_set_socket_error_callback(void (*func)(int fd, int err, const char* msg)); +extern void emscripten_set_socket_error_callback(void *userData, void (*func)(int fd, int err, const char* msg, void *userData)); // Triggered when the WebSocket has actually opened. -extern void emscripten_set_socket_open_callback(void (*func)(int fd)); +extern void emscripten_set_socket_open_callback(void *userData, void (*func)(int fd, void *userData)); // Triggered when listen has been called (synthetic event). -extern void emscripten_set_socket_listen_callback(void (*func)(int fd)); +extern void emscripten_set_socket_listen_callback(void *userData, void (*func)(int fd, void *userData)); // Triggered when the connection has actually been established. -extern void emscripten_set_socket_connection_callback(void (*func)(int fd)); +extern void emscripten_set_socket_connection_callback(void *userData, void (*func)(int fd, void *userData)); // Triggered when data is available to be read from the socket. -extern void emscripten_set_socket_message_callback(void (*func)(int fd)); +extern void emscripten_set_socket_message_callback(void *userData, void (*func)(int fd, void *userData)); // Triggered when the WebSocket has actually closed. -extern void emscripten_set_socket_close_callback(void (*func)(int fd)); +extern void emscripten_set_socket_close_callback(void *userData, void (*func)(int fd, void *userData)); /* * Add a function to a queue of events that will execute diff --git a/tests/sockets/test_sockets_echo_client.c b/tests/sockets/test_sockets_echo_client.c index 6f1c01c4..48c031a4 100644 --- a/tests/sockets/test_sockets_echo_client.c +++ b/tests/sockets/test_sockets_echo_client.c @@ -123,15 +123,17 @@ void main_loop() { // emscripten_set_main_loop (they get passed the fd of the socket triggering the event). // In this test application we want to try and keep as much in common as the timed loop // version but in a real application the fd can be used instead of needing to select(). -void async_main_loop(int fd) { +void async_main_loop(int fd, void* userData) { + printf("%s callback\n", userData); main_loop(); } -void error_callback(int fd, int err, const char* msg) { +void error_callback(int fd, int err, const char* msg, void* userData) { int error; socklen_t len = sizeof(error); int ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len); + printf("%s callback\n", userData); printf("error message: %s\n", msg); if (err == error) { @@ -186,9 +188,11 @@ int main() { #ifdef __EMSCRIPTEN__ #if TEST_ASYNC - emscripten_set_socket_error_callback(error_callback); - emscripten_set_socket_open_callback(async_main_loop); - emscripten_set_socket_message_callback(async_main_loop); + // The first parameter being passed is actually an arbitrary userData pointer + // for simplicity this test just passes a basic char* + emscripten_set_socket_error_callback("error", error_callback); + emscripten_set_socket_open_callback("open", async_main_loop); + emscripten_set_socket_message_callback("message", async_main_loop); #else emscripten_set_main_loop(main_loop, 60, 0); #endif diff --git a/tests/sockets/test_sockets_echo_server.c b/tests/sockets/test_sockets_echo_server.c index 492fc6bb..4b5b75c6 100644 --- a/tests/sockets/test_sockets_echo_server.c +++ b/tests/sockets/test_sockets_echo_server.c @@ -148,7 +148,8 @@ void main_loop() { // emscripten_set_main_loop (they get passed the fd of the socket triggering the event). // In this test application we want to try and keep as much in common as the timed loop // version but in a real application the fd can be used instead of needing to select(). -void async_main_loop(int fd) { +void async_main_loop(int fd, void* userData) { + printf("%s callback\n", userData); main_loop(); } @@ -198,9 +199,11 @@ int main() { #ifdef __EMSCRIPTEN__ #if TEST_ASYNC - emscripten_set_socket_connection_callback(async_main_loop); - emscripten_set_socket_message_callback(async_main_loop); - emscripten_set_socket_close_callback(async_main_loop); + // The first parameter being passed is actually an arbitrary userData pointer + // for simplicity this test just passes a basic char* + emscripten_set_socket_connection_callback("connection", async_main_loop); + emscripten_set_socket_message_callback("message", async_main_loop); + emscripten_set_socket_close_callback("close", async_main_loop); #else emscripten_set_main_loop(main_loop, 60, 0); #endif |