aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFraser Adams <fraser.adams@blueyonder.co.uk>2014-06-06 14:54:17 +0100
committerFraser Adams <fraser.adams@blueyonder.co.uk>2014-06-06 14:54:17 +0100
commit4e691c70549f3deead0fe4d6ba6fa4b95f31655d (patch)
tree061a090cc4c7925d202c1e4df24b65991e74a048
parent4d2d64b63d79ec8bc2ef96c58976c6b2a758407d (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.js32
-rw-r--r--system/include/emscripten/emscripten.h51
-rw-r--r--tests/sockets/test_sockets_echo_client.c14
-rw-r--r--tests/sockets/test_sockets_echo_server.c11
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