├── .travis.yml ├── LICENSE ├── README.md ├── test └── test.c ├── uv_callback.c └── uv_callback.h /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | install: 4 | - git clone https://github.com/libuv/libuv 5 | - cd libuv 6 | - sh autogen.sh 7 | - ./configure 8 | - make 9 | - sudo make install 10 | - sudo ldconfig 11 | - cd .. 12 | 13 | script: 14 | - cd test 15 | - gcc test.c -o test -luv 16 | - ./test 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 litesync 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uv_callback 2 | 3 | [![Build Status](https://travis-ci.org/litesync/uv_callback.svg?branch=master)](https://travis-ci.org/litesync/uv_callback) 4 | 5 | Module to call functions on other libuv threads. 6 | 7 | It is an alternative to uv_async with some differences: 8 | 9 | * It supports coalescing and non coalescing calls 10 | * It supports synchronous and asynchronous calls 11 | * It supports the transfer of an argument to the called function 12 | * It supports result notification callback 13 | 14 | 15 | # Usage Examples 16 | 17 | 18 | ## Sending progress to the main thread 19 | 20 | In this case the calls can and must coalesce to avoid flooding the event loop if the 21 | work is running too fast. 22 | 23 | The call coalescing is enabled using the UV_COALESCE constant. 24 | 25 | ### In the receiver thread 26 | 27 | ```C 28 | uv_callback_t progress; 29 | 30 | void * on_progress(uv_callback_t *handle, void *value, int size) { 31 | printf("progress: %d\n", (int)value); 32 | } 33 | 34 | uv_callback_init(loop, &progress, on_progress, UV_COALESCE); 35 | ``` 36 | 37 | ### In the sender thread 38 | 39 | ```C 40 | uv_callback_fire(&progress, (void*)value, NULL); 41 | ``` 42 | 43 | ⚠️ Do **NOT** send pointers with UV_COALESCE, only scalar values 44 | 45 | 46 | ## Sending allocated data that must be released 47 | 48 | In this case the calls cannot coalesce because it would cause data loss and memory leaks. 49 | 50 | So instead of UV_COALESCE it uses UV_DEFAULT. 51 | 52 | ### In the receiver thread 53 | 54 | ```C 55 | uv_callback_t send_data; 56 | 57 | void * on_data(uv_callback_t *handle, void *data, int size) { 58 | do_something(data); 59 | free(data); 60 | } 61 | 62 | uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); 63 | ``` 64 | 65 | ### In the sender thread 66 | 67 | ```C 68 | uv_callback_fire(&send_data, data, NULL); 69 | ``` 70 | 71 | 72 | ## Firing the callback synchronously 73 | 74 | In this case the thread firing the callback will wait until the function 75 | called on the other thread returns. 76 | 77 | It means it will not use the current thread event loop and it does not 78 | even need to have one (it can be used in caller threads with no event loop 79 | but the called thread needs to have one). 80 | 81 | The last argument is the timeout in milliseconds. 82 | 83 | If the called thread does not respond within the specified timeout time 84 | the function returns `UV_ETIMEDOUT`. 85 | 86 | 87 | ### In the called thread 88 | 89 | ```C 90 | uv_callback_t send_data; 91 | 92 | void * on_data(uv_callback_t *handle, void *args, int size) { 93 | int result = do_something(args); 94 | free(args); 95 | return (void*)result; 96 | } 97 | 98 | uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); 99 | ``` 100 | 101 | ### In the caller thread 102 | 103 | ```C 104 | int result; 105 | uv_callback_fire_sync(&send_data, args, (void**)&result, 1000); 106 | ``` 107 | 108 | 109 | ## Firing the callback and getting the result asynchronously 110 | 111 | In this case the thread firing the callback will receive the result in its 112 | own callback when the function called on the other thread loop returns. 113 | 114 | Note that there are 2 callback definitions here, one for each thread. 115 | 116 | ### In the called thread 117 | 118 | ```C 119 | uv_callback_t send_data; 120 | 121 | void * on_data(uv_callback_t *handle, void *data, int size) { 122 | int result = do_something(data); 123 | free(data); 124 | return (void*)result; 125 | } 126 | 127 | uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); 128 | ``` 129 | 130 | ### In the calling thread 131 | 132 | ```C 133 | uv_callback_t result_cb; 134 | 135 | void * on_result(uv_callback_t *handle, void *result, int size) { 136 | printf("The result is %d\n", (int)result); 137 | } 138 | 139 | uv_callback_init(loop, &result_cb, on_result, UV_DEFAULT); 140 | 141 | uv_callback_fire(&send_data, data, &result_cb); 142 | ``` 143 | 144 | 145 | # Non-static objects 146 | 147 | If the `uv_callback_t` object is allocated on memory then you can inform which function should be used to release it using the `uv_callback_init_ex` function: 148 | 149 | ```C 150 | uv_callback_t *cb = malloc(sizeof(uv_callback_t)); 151 | if (!cb) ... 152 | uv_callback_init_ex(loop, &cb, get_values, UV_DEFAULT, free, NULL); 153 | ``` 154 | 155 | You can discard it on the same thread it was created using the `uv_callback_stop` function just before closing the loop handles and then `uv_callback_release` on the callback of the `uv_close`. The object will be released when the reference counter reaches 0. 156 | 157 | ```C 158 | void on_close(uv_handle_t *handle) { 159 | if (uv_is_callback(handle)) { 160 | uv_callback_release((uv_callback_t*) handle); 161 | } 162 | } 163 | 164 | void on_walk(uv_handle_t *handle, void *arg) { 165 | uv_close(handle, on_close); 166 | } 167 | 168 | ... 169 | 170 | /* run the event loop */ 171 | uv_run(&loop, UV_RUN_DEFAULT); 172 | 173 | /* the event loop stopped */ 174 | uv_callback_stop_all(&loop); 175 | uv_walk(&loop, on_walk, NULL); 176 | uv_run(&loop, UV_RUN_DEFAULT); 177 | uv_loop_close(&loop); 178 | ``` 179 | 180 | You can also inform in the last argument which function should be used to release the result from the callback, if it is not used. 181 | 182 | ```C 183 | void * get_data(uv_callback_t *handle, void *arg, int size) { 184 | struct my_data *result = malloc(sizeof(struct my_data)) 185 | result->data1 = calc1(arg); 186 | result->data2 = calc2(arg); 187 | free(arg); 188 | return result; 189 | } 190 | 191 | void thread_init() { 192 | uv_callback_t *cb = malloc(sizeof(uv_callback_t)); 193 | if (!cb) ... 194 | uv_callback_init_ex(loop, &cb, get_data, UV_DEFAULT, free, free); 195 | ... 196 | } 197 | ``` 198 | 199 | Check the [test](test/test.c) for more usage examples. 200 | 201 | 202 | # Requirement 203 | 204 | ⚠️ For `uv_callback` to work you must **NOT** use `uv_async` handles in the same loops that use `uv_callback`! 205 | 206 | This requirement applies to the [this commit](https://github.com/litesync/uv_callback/commit/f9e54ca561e40cb61398534c3b069c800c537a41) that ensures that the order of calls will be preserved. 207 | 208 | If you want to use uv_async handles with uv_callback, use the [previous commit](https://github.com/litesync/uv_callback/commit/f19aba8b9c21f860f9e00fd5654baa6aeff81a76). It preserves the 209 | order of calls only within a single callback handle, not globally. 210 | 211 | 212 | # License 213 | 214 | MIT 215 | 216 | # Contact 217 | 218 | contact AT litereplica DOT io 219 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../uv_callback.c" 5 | #include 6 | #include 7 | #include 8 | 9 | uv_thread_t worker_thread; 10 | uv_barrier_t barrier; 11 | uv_callback_t stop_worker; 12 | 13 | int progress_called = 0; 14 | int static_call_counter = 0; 15 | int dynamic_call_counter = 0; 16 | 17 | char *msg1 = "Hello World!"; 18 | char *msg2 = "How you doing?"; 19 | char *msg3 = "I'm programming with libuv! :P"; 20 | char *msg4 = "The fourth message"; 21 | char *msg5 = "The fifth message"; 22 | char *msg6 = "The sixth message"; 23 | 24 | /* Common ********************************************************************/ 25 | 26 | void on_close(uv_handle_t *handle) { 27 | if (uv_is_callback(handle)) { 28 | uv_callback_release((uv_callback_t*) handle); 29 | } 30 | } 31 | 32 | void on_walk(uv_handle_t *handle, void *arg) { 33 | uv_close(handle, on_close); 34 | } 35 | 36 | /* Worker Thread *************************************************************/ 37 | 38 | struct numbers { 39 | int number1; 40 | int number2; 41 | int result; 42 | }; 43 | 44 | uv_callback_t cb_progress; 45 | uv_callback_t cb_static_pointer; 46 | uv_callback_t cb_dynamic_pointer; 47 | uv_callback_t cb_sum; 48 | uv_callback_t cb_sum2; 49 | uv_callback_t cb_slow; 50 | 51 | void * on_progress(uv_callback_t *callback, void *data, int size) { 52 | printf("progress: %" PRIxPTR " %%\n", (intptr_t)data); 53 | progress_called++; 54 | } 55 | 56 | void * on_static_pointer(uv_callback_t *callback, void *data, int size) { 57 | printf("static pointer: (%p) %s\n", data, (char*)data); 58 | //assert(strcmp((char*)data, msg1) == 0); 59 | static_call_counter++; 60 | } 61 | 62 | void * on_dynamic_pointer(uv_callback_t *callback, void *data, int size) { 63 | printf("dynamic pointer: (%p) %s\n", data, (char*)data); 64 | free(data); 65 | dynamic_call_counter++; 66 | } 67 | 68 | void * on_sum(uv_callback_t *callback, void *data, int size) { 69 | struct numbers *request = (struct numbers *)data; 70 | struct numbers *response = malloc(sizeof(struct numbers)); 71 | assert(response != 0); 72 | response->number1 = request->number1; 73 | response->number2 = request->number2; 74 | response->result = request->number1 + request->number2; 75 | printf("sum1 (%p) number1: %d number2: %d result: %d\n", data, request->number1, request->number2, response->result); 76 | free(request); 77 | return response; 78 | } 79 | 80 | void * on_sum2(uv_callback_t *callback, void *data, int size) { 81 | struct numbers *request = (struct numbers *)data; 82 | intptr_t result = request->number1 + request->number2; 83 | printf("sum2 (%p) number1: %d number2: %d result: %" PRIxPTR "\n", data, request->number1, request->number2, result); 84 | free(request); 85 | return (void*)result; 86 | } 87 | 88 | void * on_slow(uv_callback_t *callback, void *data, int size) { 89 | struct numbers *request = (struct numbers *)data; 90 | intptr_t result = request->number1 + request->number2; 91 | printf("slow function (%p) number1: %d number2: %d result: %" PRIxPTR "\n", data, request->number1, request->number2, result); 92 | free(request); 93 | sleep(2); 94 | return (void*)result; 95 | } 96 | 97 | void * stop_worker_cb(uv_callback_t *handle, void *data, int size) { 98 | puts("signal received to stop worker thread"); 99 | uv_stop(((uv_handle_t*)handle)->loop); 100 | return NULL; 101 | } 102 | 103 | void worker_start(void *arg) { 104 | uv_loop_t loop; 105 | int rc; 106 | 107 | uv_loop_init(&loop); 108 | 109 | rc = uv_callback_init(&loop, &cb_progress, on_progress, UV_COALESCE); 110 | printf("uv_callback_init rc=%d\n", rc); 111 | assert(rc == 0); 112 | 113 | rc = uv_callback_init(&loop, &cb_static_pointer, on_static_pointer, UV_DEFAULT); 114 | printf("uv_callback_init rc=%d\n", rc); 115 | assert(rc == 0); 116 | 117 | rc = uv_callback_init(&loop, &cb_dynamic_pointer, on_dynamic_pointer, UV_DEFAULT); 118 | printf("uv_callback_init rc=%d\n", rc); 119 | assert(rc == 0); 120 | 121 | rc = uv_callback_init(&loop, &cb_sum, on_sum, UV_DEFAULT); 122 | printf("uv_callback_init rc=%d\n", rc); 123 | assert(rc == 0); 124 | 125 | rc = uv_callback_init(&loop, &cb_sum2, on_sum2, UV_DEFAULT); 126 | printf("uv_callback_init rc=%d\n", rc); 127 | assert(rc == 0); 128 | 129 | rc = uv_callback_init(&loop, &cb_slow, on_slow, UV_DEFAULT); 130 | printf("uv_callback_init rc=%d\n", rc); 131 | assert(rc == 0); 132 | 133 | rc = uv_callback_init(&loop, &stop_worker, stop_worker_cb, UV_COALESCE); 134 | printf("uv_callback_init rc=%d\n", rc); 135 | assert(rc == 0); 136 | 137 | /* signal to the main thread the the listening socket is ready */ 138 | uv_barrier_wait(&barrier); 139 | 140 | /* run the event loop */ 141 | uv_run(&loop, UV_RUN_DEFAULT); 142 | 143 | /* cleanup */ 144 | puts("cleaning up worker thread"); 145 | uv_callback_stop_all(&loop); 146 | uv_walk(&loop, on_walk, NULL); 147 | uv_run(&loop, UV_RUN_DEFAULT); 148 | uv_loop_close(&loop); 149 | 150 | } 151 | 152 | /* Main Thread ***************************************************************/ 153 | 154 | uv_callback_t cb_result; 155 | 156 | void * on_result(uv_callback_t *callback, void *data, int size) { 157 | struct numbers *response = (struct numbers *)data; 158 | printf("on sum result (%p) number1: %d number2: %d result=%d\n", data, response->number1, response->number2, response->result); 159 | assert(response->number1 == 123); 160 | assert(response->number2 == 456); 161 | assert(response->result == 579); 162 | free(response); 163 | uv_stop(((uv_handle_t*)callback)->loop); 164 | return NULL; 165 | } 166 | 167 | void wait_it(){ 168 | char temp[64]; 169 | int a, b, c; 170 | 171 | strcpy(temp, "testing this thing"); 172 | a = 1; 173 | b = 2; 174 | c = 3; 175 | 176 | sleep(2); 177 | 178 | } 179 | 180 | int main() { 181 | uv_loop_t *loop = uv_default_loop(); 182 | struct numbers *req, *resp; 183 | int rc, result; 184 | 185 | uv_barrier_init(&barrier, 2); 186 | 187 | uv_thread_create(&worker_thread, worker_start, NULL); 188 | 189 | /* wait until the worker thread is ready */ 190 | uv_barrier_wait(&barrier); 191 | 192 | /* fire the callbacks */ 193 | 194 | /* this calls can coalesce */ 195 | uv_callback_fire(&cb_progress, (void*)10, NULL); 196 | uv_callback_fire(&cb_progress, (void*)20, NULL); 197 | uv_callback_fire(&cb_progress, (void*)30, NULL); 198 | uv_callback_fire(&cb_progress, (void*)40, NULL); 199 | uv_callback_fire(&cb_progress, (void*)50, NULL); 200 | usleep(50000); 201 | uv_callback_fire(&cb_progress, (void*)60, NULL); 202 | uv_callback_fire(&cb_progress, (void*)70, NULL); 203 | uv_callback_fire(&cb_progress, (void*)80, NULL); 204 | uv_callback_fire(&cb_progress, (void*)90, NULL); 205 | uv_callback_fire(&cb_progress, (void*)99, NULL); 206 | 207 | /* this calls should not coalesce, and the memory should not be released */ 208 | uv_callback_fire(&cb_static_pointer, msg1, NULL); 209 | uv_callback_fire(&cb_static_pointer, msg2, NULL); 210 | uv_callback_fire(&cb_static_pointer, msg3, NULL); 211 | 212 | /* this calls should not coalesce, and the memory must be released */ 213 | uv_callback_fire(&cb_dynamic_pointer, strdup(msg4), NULL); 214 | uv_callback_fire(&cb_dynamic_pointer, strdup(msg5), NULL); 215 | uv_callback_fire(&cb_dynamic_pointer, strdup(msg6), NULL); 216 | 217 | /* make a call and receive the response asynchronously */ 218 | 219 | /* set the result callback */ 220 | rc = uv_callback_init(loop, &cb_result, on_result, UV_DEFAULT); 221 | printf("uv_callback_init rc=%d\n", rc); 222 | assert(rc == 0); 223 | 224 | /* allocate memory fo the arguments */ 225 | req = malloc(sizeof(struct numbers)); 226 | assert(req != 0); 227 | req->number1 = 123; 228 | req->number2 = 456; 229 | req->result = 0; 230 | 231 | /* call the function in the other thread */ 232 | uv_callback_fire(&cb_sum, req, &cb_result); 233 | 234 | /* run the event loop */ 235 | puts("running the event loop in the main thread"); 236 | uv_run(loop, UV_RUN_DEFAULT); 237 | 238 | /* close the handles from this loop */ 239 | puts("cleaning up the main thread"); 240 | uv_walk(loop, on_walk, NULL); 241 | uv_run(loop, UV_RUN_DEFAULT); 242 | uv_loop_close(loop); 243 | 244 | /* check the values */ 245 | assert(progress_called > 0); 246 | assert(static_call_counter == 3); 247 | assert(dynamic_call_counter == 3); 248 | 249 | 250 | /* test synchronous calls */ 251 | 252 | /* allocate memory fo the arguments */ 253 | req = malloc(sizeof(struct numbers)); 254 | assert(req != 0); 255 | req->number1 = 111; 256 | req->number2 = 222; 257 | req->result = 0; 258 | 259 | /* call the function in the other thread */ 260 | rc = uv_callback_fire_sync(&cb_sum, req, (void**)&resp, 1000); 261 | printf("uv_callback_fire_sync rc=%d\n", rc); 262 | assert(rc == 0); 263 | printf("response=%p\n", resp); 264 | assert(resp != 0); 265 | assert(resp->result == 333); 266 | free(resp); 267 | 268 | 269 | /* allocate memory fo the arguments */ 270 | req = malloc(sizeof(struct numbers)); 271 | assert(req != 0); 272 | req->number1 = 111; 273 | req->number2 = 222; 274 | req->result = 0; 275 | 276 | /* call the function in the other thread - this one returns an integer */ 277 | rc = uv_callback_fire_sync(&cb_sum2, req, (void**)&result, 1000); 278 | printf("uv_callback_fire_sync rc=%d\n", rc); 279 | assert(rc == 0); 280 | printf("result=%d\n", result); 281 | assert(result == 333); 282 | 283 | 284 | /* allocate memory fo the arguments */ 285 | req = malloc(sizeof(struct numbers)); 286 | assert(req != 0); 287 | req->number1 = 111; 288 | req->number2 = 222; 289 | req->result = 0; 290 | 291 | /* call the function in the other thread */ 292 | rc = uv_callback_fire_sync(&cb_slow, req, (void**)&resp, 1000); 293 | printf("uv_callback_fire_sync rc=%d\n", rc); 294 | assert(rc != 0); 295 | printf("response=%p\n", resp); 296 | assert(resp == NULL); 297 | 298 | puts("waiting the worker thread to answer..."); 299 | wait_it(); 300 | 301 | 302 | /* end of the tests */ 303 | 304 | /* send a signal to the worker thread to exit */ 305 | uv_callback_fire(&stop_worker, NULL, NULL); 306 | 307 | /* wait the worker thread to exit */ 308 | uv_thread_join(&worker_thread); 309 | puts("worker thread closed"); 310 | 311 | /* test calls with the worker thread closed */ 312 | uv_callback_fire(&cb_progress, (void*)99, NULL); 313 | uv_callback_fire(&cb_static_pointer, msg1, NULL); 314 | 315 | 316 | puts("All tests pass!"); 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /uv_callback.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "uv_callback.h" 3 | 4 | // not covered now: closing a uv_callback handle does not release all the resources 5 | // automatically. 6 | // for this libuv should support calling a callback when our handle is being closed. 7 | // for now we must use the uv_callback_stop or .._stop_all before closing the event 8 | // loop and then call uv_callback_release on the callback from uv_close. 9 | 10 | /*****************************************************************************/ 11 | /* RECEIVER / CALLED THREAD **************************************************/ 12 | /*****************************************************************************/ 13 | 14 | #ifndef container_of 15 | #define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member))) 16 | #endif 17 | 18 | void uv_callback_idle_cb(uv_idle_t* handle); 19 | 20 | /* Master Callback ***********************************************************/ 21 | 22 | int uv_is_callback(uv_handle_t *handle) { 23 | return (handle->type == UV_ASYNC && handle->data == handle); 24 | } 25 | 26 | void master_on_walk(uv_handle_t *handle, void *arg) { 27 | if (handle->type == UV_ASYNC && ((uv_callback_t*)handle)->usequeue) { 28 | *(uv_callback_t**)arg = (uv_callback_t *) handle; 29 | } 30 | } 31 | 32 | uv_callback_t * get_master_callback(uv_loop_t *loop) { 33 | uv_callback_t *callback=0; 34 | uv_walk(loop, master_on_walk, &callback); 35 | return callback; 36 | } 37 | 38 | /* Callback Release **********************************************************/ 39 | 40 | void uv_callback_release(uv_callback_t *callback) { 41 | if (callback) { 42 | callback->refcount--; 43 | if (callback->refcount == 0 && callback->free_cb) { 44 | /* remove the object from the list */ 45 | uv_callback_t *cb = callback->master; 46 | while (cb) { 47 | if (cb->next == callback) { 48 | cb->next = callback->next; 49 | break; 50 | } 51 | cb = cb->next; 52 | } 53 | /* stop the idle handle */ 54 | if (callback->idle_active) { 55 | uv_idle_stop(&callback->idle); 56 | } 57 | /* release the object */ 58 | callback->free_cb(callback); 59 | } 60 | } 61 | } 62 | 63 | /* Dequeue *******************************************************************/ 64 | 65 | void * dequeue_call(uv_callback_t* callback) { 66 | uv_call_t *current, *prev = NULL; 67 | 68 | uv_mutex_lock(&callback->mutex); 69 | 70 | current = callback->queue; 71 | while (current && current->next) { 72 | prev = current; 73 | current = current->next; 74 | } 75 | 76 | if (prev) 77 | prev->next = NULL; 78 | else 79 | callback->queue = NULL; 80 | 81 | uv_mutex_unlock(&callback->mutex); 82 | 83 | return current; 84 | } 85 | 86 | void dequeue_all_from_callback(uv_callback_t* master, uv_callback_t* callback) { 87 | uv_call_t *call, *prev = NULL; 88 | 89 | if (!master) master = callback; 90 | 91 | uv_mutex_lock(&master->mutex); 92 | 93 | call = master->queue; 94 | while (call) { 95 | uv_call_t *next = call->next; 96 | if (call->callback == callback) { 97 | /* remove it from the queue */ 98 | if (prev) 99 | prev->next = next; 100 | else 101 | master->queue = next; 102 | /* discard this call */ 103 | if (call->data && call->free_data) { 104 | call->free_data(call->data); 105 | } 106 | free(call); 107 | } else { 108 | prev = call; 109 | } 110 | /* move to the next call */ 111 | call = next; 112 | } 113 | 114 | callback->queue = NULL; 115 | 116 | uv_mutex_unlock(&master->mutex); 117 | 118 | } 119 | 120 | /* Callback Function Call ****************************************************/ 121 | 122 | void uv_callback_async_cb(uv_async_t* handle) { 123 | uv_callback_t* callback = (uv_callback_t*) handle; 124 | 125 | if (callback->usequeue) { 126 | uv_call_t *call = dequeue_call(callback); 127 | if (call) { 128 | void *result = call->callback->function(call->callback, call->data, call->size); 129 | /* check if the result notification callback is still active */ 130 | if (call->notify && !call->notify->inactive) { 131 | uv_callback_fire(call->notify, result, NULL); 132 | } else if (result && call->callback->free_result) { 133 | call->callback->free_result(result); 134 | } 135 | if (call->notify) { 136 | uv_callback_release(call->notify); 137 | } 138 | if (call->data && call->free_data) { 139 | call->free_data(call->data); 140 | } 141 | free(call); 142 | /* don't check for new calls now to prevent the loop from blocking 143 | for i/o events. start an idle handle to call this function again */ 144 | if (!callback->idle_active) { 145 | uv_idle_start(&callback->idle, uv_callback_idle_cb); 146 | callback->idle_active = 1; 147 | } 148 | } else { 149 | /* no more calls in the queue. stop the idle handle */ 150 | uv_idle_stop(&callback->idle); 151 | callback->idle_active = 0; 152 | } 153 | } else { 154 | callback->function(callback, callback->arg, 0); 155 | } 156 | 157 | } 158 | 159 | void uv_callback_idle_cb(uv_idle_t* handle) { 160 | uv_callback_t* callback = container_of(handle, uv_callback_t, idle); 161 | uv_callback_async_cb((uv_async_t*)callback); 162 | } 163 | 164 | /* Initialization ************************************************************/ 165 | 166 | int uv_callback_init_ex( 167 | uv_loop_t* loop, 168 | uv_callback_t* callback, 169 | uv_callback_func function, 170 | int callback_type, 171 | void (*free_cb)(void*), 172 | void (*free_result)(void*) 173 | ){ 174 | int rc; 175 | 176 | if (!loop || !callback || !function) return UV_EINVAL; 177 | 178 | memset(callback, 0, sizeof(uv_callback_t)); 179 | callback->async.data = callback; /* mark as a uv_callback handle */ 180 | 181 | callback->function = function; 182 | 183 | callback->refcount = 1; 184 | callback->free_cb = free_cb; 185 | 186 | switch(callback_type) { 187 | case UV_DEFAULT: 188 | callback->usequeue = 1; 189 | callback->free_result = free_result; 190 | callback->master = get_master_callback(loop); 191 | if (callback->master) { 192 | /* add this callback to the list */ 193 | uv_callback_t *base = callback->master; 194 | while (base->next) { base = base->next; } 195 | base->next = callback; 196 | return 0; /* the uv_async handle is already initialized */ 197 | } else { 198 | uv_mutex_init(&callback->mutex); 199 | rc = uv_idle_init(loop, &callback->idle); 200 | if (rc) return rc; 201 | } 202 | /* fallthrough */ 203 | case UV_COALESCE: 204 | break; 205 | default: 206 | return UV_EINVAL; 207 | } 208 | 209 | return uv_async_init(loop, (uv_async_t*) callback, uv_callback_async_cb); 210 | } 211 | 212 | int uv_callback_init(uv_loop_t* loop, uv_callback_t* callback, uv_callback_func function, int callback_type) { 213 | return uv_callback_init_ex(loop, callback, function, callback_type, NULL, NULL); 214 | } 215 | 216 | void uv_callback_stop(uv_callback_t* callback) { 217 | 218 | if (!callback) return; 219 | 220 | callback->inactive = 1; 221 | 222 | if (callback->usequeue) { 223 | dequeue_all_from_callback(callback->master, callback); 224 | } 225 | 226 | } 227 | 228 | void stop_all_on_walk(uv_handle_t *handle, void *arg) { 229 | if (uv_is_callback(handle)) { 230 | uv_callback_t *callback = (uv_callback_t *) handle; 231 | while (callback) { 232 | uv_callback_t *next = callback->next; 233 | uv_callback_stop(callback); 234 | callback = next; 235 | } 236 | } 237 | } 238 | 239 | void uv_callback_stop_all(uv_loop_t* loop) { 240 | uv_walk(loop, stop_all_on_walk, NULL); 241 | } 242 | 243 | /*****************************************************************************/ 244 | /* SENDER / CALLER THREAD ****************************************************/ 245 | /*****************************************************************************/ 246 | 247 | /* Asynchronous Callback Firing **********************************************/ 248 | 249 | int uv_callback_fire_ex(uv_callback_t* callback, void *data, int size, void (*free_data)(void*), uv_callback_t* notify) { 250 | 251 | if (!callback) return UV_EINVAL; 252 | if (callback->inactive) return UV_EPERM; 253 | 254 | /* if there is a notification callback set, then the call must use a queue */ 255 | if (notify && !callback->usequeue) return UV_EINVAL; 256 | 257 | if (callback->usequeue) { 258 | /* allocate a new call info */ 259 | uv_call_t *call = malloc(sizeof(uv_call_t)); 260 | if (!call) return UV_ENOMEM; 261 | /* save the call info */ 262 | call->data = data; 263 | call->size = size; 264 | call->notify = notify; 265 | call->callback = callback; 266 | call->free_data = free_data; 267 | /* if there is a master callback, use it */ 268 | if (callback->master) callback = callback->master; 269 | /* add the call to the queue */ 270 | uv_mutex_lock(&callback->mutex); 271 | call->next = callback->queue; 272 | callback->queue = call; 273 | uv_mutex_unlock(&callback->mutex); 274 | /* increase the reference counter */ 275 | if (notify) notify->refcount++; 276 | } else { 277 | callback->arg = data; 278 | } 279 | 280 | /* call uv_async_send */ 281 | return uv_async_send((uv_async_t*)callback); 282 | } 283 | 284 | int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) { 285 | return uv_callback_fire_ex(callback, data, 0, NULL, notify); 286 | } 287 | 288 | /* Synchronous Callback Firing ***********************************************/ 289 | 290 | struct call_result { 291 | int timed_out; 292 | int called; 293 | void *data; 294 | int size; 295 | }; 296 | 297 | void callback_on_close(uv_handle_t *handle) { 298 | if (uv_is_callback(handle)) { 299 | uv_callback_release((uv_callback_t*) handle); 300 | } 301 | } 302 | 303 | void callback_on_walk(uv_handle_t *handle, void *arg) { 304 | uv_close(handle, callback_on_close); 305 | } 306 | 307 | void * on_call_result(uv_callback_t *callback, void *data, int size) { 308 | uv_loop_t *loop = ((uv_handle_t*)callback)->loop; 309 | struct call_result *result = loop->data; 310 | result->called = 1; 311 | result->data = data; 312 | result->size = size; 313 | uv_stop(loop); 314 | return NULL; 315 | } 316 | 317 | void on_timer(uv_timer_t *timer) { 318 | uv_loop_t *loop = timer->loop; 319 | struct call_result *result = loop->data; 320 | result->timed_out = 1; 321 | uv_stop(loop); 322 | } 323 | 324 | int uv_callback_fire_sync(uv_callback_t* callback, void *data, void** presult, int timeout) { 325 | struct call_result result = {0}; 326 | uv_loop_t loop; 327 | uv_timer_t timer; 328 | uv_callback_t *notify; /* must be allocated because it is shared with the called thread */ 329 | int rc=0; 330 | 331 | if (!callback || callback->usequeue==0) return UV_EINVAL; 332 | 333 | notify = malloc(sizeof(uv_callback_t)); 334 | if (!notify) return UV_ENOMEM; 335 | 336 | /* set the call result */ 337 | uv_loop_init(&loop); 338 | uv_callback_init_ex(&loop, notify, on_call_result, UV_DEFAULT, free, NULL); 339 | loop.data = &result; 340 | 341 | /* fire the callback on the other thread */ 342 | rc = uv_callback_fire(callback, data, notify); 343 | if (rc) { 344 | uv_close((uv_handle_t*) notify, callback_on_close); 345 | goto loc_exit; 346 | } 347 | 348 | /* if a timeout is supplied, set a timer */ 349 | if (timeout > 0) { 350 | uv_timer_init(&loop, &timer); 351 | uv_timer_start(&timer, on_timer, timeout, 0); 352 | } 353 | 354 | /* run the event loop */ 355 | uv_run(&loop, UV_RUN_DEFAULT); 356 | 357 | /* exited the event loop */ 358 | /* before closing the loop handles */ 359 | //uv_callback_stop(notify); 360 | uv_callback_stop_all(&loop); 361 | uv_walk(&loop, callback_on_walk, NULL); 362 | uv_run(&loop, UV_RUN_DEFAULT); 363 | loc_exit: 364 | uv_loop_close(&loop); 365 | 366 | /* store the result */ 367 | if (presult) *presult = result.data; 368 | if (rc==0 && result.timed_out) rc = UV_ETIMEDOUT; 369 | if (rc==0 && result.called==0) rc = UV_UNKNOWN; 370 | return rc; 371 | 372 | } 373 | -------------------------------------------------------------------------------- /uv_callback.h: -------------------------------------------------------------------------------- 1 | #ifndef UV_CALLBACK_H 2 | #define UV_CALLBACK_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | 9 | 10 | /* Typedefs */ 11 | 12 | typedef struct uv_callback_s uv_callback_t; 13 | typedef struct uv_call_s uv_call_t; 14 | 15 | 16 | /* Callback Functions */ 17 | 18 | typedef void* (*uv_callback_func)(uv_callback_t* handle, void *data, int size); 19 | 20 | 21 | /* Functions */ 22 | 23 | int uv_callback_init(uv_loop_t* loop, uv_callback_t* callback, uv_callback_func function, int callback_type); 24 | 25 | int uv_callback_init_ex( 26 | uv_loop_t* loop, 27 | uv_callback_t* callback, 28 | uv_callback_func function, 29 | int callback_type, 30 | void (*free_cb)(void*), 31 | void (*free_result)(void*) 32 | ); 33 | 34 | int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify); 35 | 36 | int uv_callback_fire_ex(uv_callback_t* callback, void *data, int size, void (*free_data)(void*), uv_callback_t* notify); 37 | 38 | int uv_callback_fire_sync(uv_callback_t* callback, void *data, void** presult, int timeout); 39 | 40 | void uv_callback_stop(uv_callback_t* callback); 41 | void uv_callback_stop_all(uv_loop_t* loop); 42 | 43 | int uv_is_callback(uv_handle_t *handle); 44 | void uv_callback_release(uv_callback_t *callback); 45 | 46 | 47 | /* Constants */ 48 | 49 | #define UV_DEFAULT 0 50 | #define UV_COALESCE 1 51 | 52 | 53 | /* Structures */ 54 | 55 | struct uv_callback_s { 56 | uv_async_t async; /* base async handle used for thread signal */ 57 | void *data; /* additional data pointer. not the same from the handle */ 58 | int usequeue; /* if this callback uses a queue of calls */ 59 | uv_call_t *queue; /* queue of calls to this callback */ 60 | uv_mutex_t mutex; /* mutex used to access the queue */ 61 | uv_callback_func function; /* the function to be called */ 62 | void *arg; /* data argument for coalescing calls (when not using queue) */ 63 | uv_idle_t idle; /* idle handle used to drain the queue if new async request was sent while an old one was being processed */ 64 | int idle_active; /* flags if the idle handle is active */ 65 | uv_callback_t *master; /* master callback handle, the one with the valid uv_async handle */ 66 | uv_callback_t *next; /* the next callback from this uv_async handle */ 67 | int inactive; /* this callback is no more valid. the called thread should not fire the response callback */ 68 | int refcount; /* reference counter */ 69 | void (*free_cb)(void*); /* function to release this object */ 70 | void (*free_result)(void*);/* function to release the result of the call if not used */ 71 | }; 72 | 73 | struct uv_call_s { 74 | uv_call_t *next; /* pointer to the next call in the queue */ 75 | uv_callback_t *callback; /* callback linked to this call */ 76 | void *data; /* data argument for this call */ 77 | int size; /* size argument for this call */ 78 | void (*free_data)(void*); /* function to release the data if the call is not fired */ 79 | uv_callback_t *notify; /* callback to be fired with the result of this one */ 80 | }; 81 | 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | #endif // UV_CALLBACK_H 87 | --------------------------------------------------------------------------------