From 4a23165fc59cbb38d9b70f2a7d2119a5a9b5cf4d Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Mon, 12 Dec 2016 04:40:11 +0000 Subject: [PATCH 1/3] Create XXX-uv_callback.md --- XXX-uv_callback.md | 283 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 XXX-uv_callback.md diff --git a/XXX-uv_callback.md b/XXX-uv_callback.md new file mode 100644 index 0000000..49329ac --- /dev/null +++ b/XXX-uv_callback.md @@ -0,0 +1,283 @@ +| Title | uv_callback | +|--------|----------------------| +| Author | @kroggen | +| Status | DRAFT | +| Date | 2016-12-12 02:00:00 | + + +# Proposal: Implementation of uv_callback and deprecation of uv_async + +This proposal inherit some ideas from LEP "request all the things" from @saghul but with some differences: + + * It does not require a request + * It supports call coalescing (and non coalescing) + * It supports synchronous and asynchronous calls + +It also deprecates the uv_async_t. + + +# Usage Examples + + +## Sending progress to the main thread + +In this case the calls can and must coalesce to avoid flooding the loop if the +work is running too fast. + +The call coalescing is enabled using the UV_COALESCE constant. + +### In the main thread + + uv_callback_t progress; + + void on_progress(uv_callback_t *handle, void *value) { + printf("progress: %d\n", (int)value); + } + + uv_callback_init(loop, &progress, on_progress, UV_COALESCE); + +### In the worker thread + + uv_callback_fire(&progress, (void*)value, NULL); + + + +## Sending allocated data that must be released + +In this case the calls cannot coalesce because it would cause data loss and memory leaks. + +So instead of UV_COALESCE it uses UV_DEFAULT. + +### In the main thread + + uv_callback_t send_data; + + void on_data(uv_callback_t *handle, void *data) { + do_something(data); + free(data); + } + + uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); + +### In the worker thread + + uv_callback_fire(&send_data, data, NULL); + + + +## Firing the callback synchronously + +In this case the thread firing the callback will wait until the function +called on the other loop returns. + +The main difference from the previous example is the use of UV_SYNCHRONOUS. + +This can be used when the worker thread does not have a loop. + +### In the main thread + + uv_callback_t send_data; + + void on_data(uv_callback_t *handle, void *data) { + do_something(data); + free(data); + } + + uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); + +### In the worker thread + + uv_callback_fire(&send_data, data, UV_SYNCHRONOUS); + + + +## Firing the callback and getting the result asynchronously + +In this case the thread firing the callback will receive the result in its +own callback when the function called on the other thread loop returns. + +Note that there are 2 callback definitions here, one for each thread. + +### In the main thread + + uv_callback_t send_data; + + void * on_data(uv_callback_t *handle, void *data) { + int result = do_something(data); + free(data); + return (void*)result; + } + + uv_callback_init(loop, &send_data, (uv_callback_cb)on_data, UV_DEFAULT); + +### In the worker thread + + uv_callback_t data_sent; + + void on_data_sent(uv_callback_t *handle, void *result) { + printf("The result is %d\n", (int)result); + } + + uv_callback_init(loop, &data_sent, on_data_sent, UV_DEFAULT); + + uv_callback_fire(&send_data, data, &data_sent); + + + +# Additions + +## Typedef + + uv_callback_t + +## Functions + + uv_callback_init + uv_callback_fire + +# Constants + +Used in uv_callback_init: + + UV_DEFAULT + UV_COALESCE + +Used in uv_callback_fire: + + UV_SYNCHRONOUS + +This last one is declared like this: + + #define UV_SYNCHRONOUS ((uv_callback_t*)-1); + +It is used in the 3rd argument of uv_callback_fire() function that expects a +uv_callback_t*. + +It is based on SQLITE_TRANSIENT constant, used in a function that expects a +pointer to a free function. If we call the function using this constant then +it will deal with it as an option. It can be used because there will never +be a function at address 0xFFFFFFFF (or its 64 bits counterpart). + + +# Implementation + +This first implementation idea is done on top of uv_async, with just small +changes inside the current uv_async code. + +## uv_callback_t + +The uv_callback_t will have the same fields of uv_async_t with some additions: + + struct uv_callback_s { + uv_async_t async; + int usequeue; /* async->data points to a queue (singly linked list) */ + uv_mutex_t mutex; /* used to access the queue */ + }; + +Optionally the queue can be in a private field instead of the public data field. + +## uv_callback_call_t + +This structure will store the information for the non coalescing calls: the data +argument and the notification callback (if supplied) that must be fired after +this fired callback returns. + +For each call there will be one of this in a linked list. The pointer to the first +will be in async->data or a private field. + + struct uv_callback_call_s { + uv_callback_call_t *next; /* pointer to the next call in the queue */ + void *data; /* data argument for this call */ + uv_callback_t *notify; /* callback to be fired with the result of this one */ + }; + +## uv_callback_cb + +The callback function must have the data as an argument because the data +will be retrieved from the call queue. + + typedef void (*uv_callback_cb)(uv_callback_t* handle, void *data); + +## uv_callback_init + +The uv_callback_init will just initialize the structure with the given values. +Something like this: + +``` +int uv_callback_init( + uv_loop_t* loop, + uv_callback_t* callback, + uv_callback_cb callback_cb, + int callback_type +){ + + callback->loop = loop; + callback->callback_cb = callback_cb; + + switch(callback_type) { + case UV_DEFAULT: + callback->usequeue = 1; + callback->data = NULL; + break; + case UV_COALESCE: + callback->usequeue = 0; + break; + default: + return xxx; + } + + return 0; +} +``` + +## uv_callback_fire + +The uv_callback_fire will store the call info in the callback_call queue/list and +then fire the "uv_async_send". Later it will replace the uv_async_send function, +when both functions will be merged. + +``` +int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) { + + if (!callback) return UV_ARGS; + + /* if there is a notification callback set, then the call must use a queue */ + if (notify!=NULL && notify!=UV_SYNCHRONOUS && callback->usequeue==0) return INVALID; // -- check if allowed for UV_SYNCHRONOUS + + if (callback->usequeue) { + /* allocate a new call info */ + uv_callback_call_t *call = malloc(sizeof(uv_callback_call_t)); + if (!call) return ENOBUFS; + /* save the call info */ + call->data = data; + call->notify = notify; + /* add the call to the queue */ + uv_mutex_enter(&callback->mutex); + llist_add(&callback->data, call); + uv_mutex_leave(&callback->mutex); + } else { + callback->data = data; + } + + return uv_async_send((uv_async_t*)callback); // <-- later this will be merged here +} +``` + +## Firing the callback in the destination loop + +The current uv_async code let the calls coalesce. This is not a problem and the +code can remain almost unchanged. + +We only need one call in the destination thread so the values can be retrieved +from the call queue. + +Here is a pseudo-code: + + while(data = dequeue(async->queue)): + result = fire_callback(async->cb, data); + if (call->notify) uv_callback_fire(call->notify, result); + + +# Note + +If for some reason this LEP is not approved, feel free to get code and ideas from it. From f9ebdda31204bcc8b72c1395fec0919eb8de79ee Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Mon, 12 Dec 2016 12:36:32 -0200 Subject: [PATCH 2/3] Removed some references for uv_async --- XXX-uv_callback.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/XXX-uv_callback.md b/XXX-uv_callback.md index 49329ac..6d55af6 100644 --- a/XXX-uv_callback.md +++ b/XXX-uv_callback.md @@ -168,14 +168,17 @@ changes inside the current uv_async code. The uv_callback_t will have the same fields of uv_async_t with some additions: - struct uv_callback_s { - uv_async_t async; - int usequeue; /* async->data points to a queue (singly linked list) */ - uv_mutex_t mutex; /* used to access the queue */ - }; + #define UV_CALLBACK_PRIVATE_FIELDS \ + \ + int usequeue; \ + uv_mutex_t mutex; \ + +If `usequeue==1` then `callback_t->data` points to a queue (singly linked list). Optionally the queue can be in a private field instead of the public data field. +The mutex is used to access the queue. + ## uv_callback_call_t This structure will store the information for the non coalescing calls: the data @@ -233,8 +236,7 @@ int uv_callback_init( ## uv_callback_fire The uv_callback_fire will store the call info in the callback_call queue/list and -then fire the "uv_async_send". Later it will replace the uv_async_send function, -when both functions will be merged. +then probably do the same work of the `uv_async_send`. ``` int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) { @@ -259,7 +261,7 @@ int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) callback->data = data; } - return uv_async_send((uv_async_t*)callback); // <-- later this will be merged here + /* here insert the code from uv_async_send, maybe modified */ } ``` @@ -276,6 +278,7 @@ Here is a pseudo-code: while(data = dequeue(async->queue)): result = fire_callback(async->cb, data); if (call->notify) uv_callback_fire(call->notify, result); + free(call); # Note From efd1a2e8b1a27a8b928679ec8637c3fdf9f9f7d6 Mon Sep 17 00:00:00 2001 From: Bernardo Ramos Date: Sat, 29 Apr 2017 03:19:46 +0000 Subject: [PATCH 3/3] added color on code --- XXX-uv_callback.md | 161 +++++++++++++++++++++++++-------------------- 1 file changed, 91 insertions(+), 70 deletions(-) diff --git a/XXX-uv_callback.md b/XXX-uv_callback.md index 6d55af6..513c430 100644 --- a/XXX-uv_callback.md +++ b/XXX-uv_callback.md @@ -26,20 +26,23 @@ work is running too fast. The call coalescing is enabled using the UV_COALESCE constant. -### In the main thread +### In the receiver thread - uv_callback_t progress; - - void on_progress(uv_callback_t *handle, void *value) { - printf("progress: %d\n", (int)value); - } - - uv_callback_init(loop, &progress, on_progress, UV_COALESCE); +```C +uv_callback_t progress; -### In the worker thread +void on_progress(uv_callback_t *handle, void *value) { + printf("progress: %d\n", (int)value); +} + +uv_callback_init(loop, &progress, on_progress, UV_COALESCE); +``` - uv_callback_fire(&progress, (void*)value, NULL); +### In the sender thread +```C +uv_callback_fire(&progress, (void*)value, NULL); +``` ## Sending allocated data that must be released @@ -48,21 +51,24 @@ In this case the calls cannot coalesce because it would cause data loss and memo So instead of UV_COALESCE it uses UV_DEFAULT. -### In the main thread +### In the receiver thread - uv_callback_t send_data; - - void on_data(uv_callback_t *handle, void *data) { - do_something(data); - free(data); - } - - uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); +```C +uv_callback_t send_data; -### In the worker thread +void on_data(uv_callback_t *handle, void *data) { + do_something(data); + free(data); +} + +uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); +``` - uv_callback_fire(&send_data, data, NULL); +### In the sender thread +```C +uv_callback_fire(&send_data, data, NULL); +``` ## Firing the callback synchronously @@ -74,21 +80,24 @@ The main difference from the previous example is the use of UV_SYNCHRONOUS. This can be used when the worker thread does not have a loop. -### In the main thread +### In the receiver thread - uv_callback_t send_data; - - void on_data(uv_callback_t *handle, void *data) { - do_something(data); - free(data); - } - - uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); +```C +uv_callback_t send_data; -### In the worker thread +void on_data(uv_callback_t *handle, void *data) { + do_something(data); + free(data); +} - uv_callback_fire(&send_data, data, UV_SYNCHRONOUS); +uv_callback_init(loop, &send_data, on_data, UV_DEFAULT); +``` +### In the sender thread + +```C +uv_callback_fire(&send_data, data, UV_SYNCHRONOUS); +``` ## Firing the callback and getting the result asynchronously @@ -98,30 +107,33 @@ own callback when the function called on the other thread loop returns. Note that there are 2 callback definitions here, one for each thread. -### In the main thread +### In the called thread - uv_callback_t send_data; - - void * on_data(uv_callback_t *handle, void *data) { - int result = do_something(data); - free(data); - return (void*)result; - } - - uv_callback_init(loop, &send_data, (uv_callback_cb)on_data, UV_DEFAULT); +```C +uv_callback_t send_data; -### In the worker thread +void * on_data(uv_callback_t *handle, void *data) { + int result = do_something(data); + free(data); + return (void*)result; +} - uv_callback_t data_sent; - - void on_data_sent(uv_callback_t *handle, void *result) { - printf("The result is %d\n", (int)result); - } - - uv_callback_init(loop, &data_sent, on_data_sent, UV_DEFAULT); +uv_callback_init(loop, &send_data, (uv_callback_cb)on_data, UV_DEFAULT); +``` - uv_callback_fire(&send_data, data, &data_sent); +### In the calling thread +```C +uv_callback_t data_sent; + +void on_data_sent(uv_callback_t *handle, void *result) { + printf("The result is %d\n", (int)result); +} + +uv_callback_init(loop, &data_sent, on_data_sent, UV_DEFAULT); + +uv_callback_fire(&send_data, data, &data_sent); +``` # Additions @@ -148,7 +160,9 @@ Used in uv_callback_fire: This last one is declared like this: - #define UV_SYNCHRONOUS ((uv_callback_t*)-1); +```C +#define UV_SYNCHRONOUS ((uv_callback_t*)-1); +``` It is used in the 3rd argument of uv_callback_fire() function that expects a uv_callback_t*. @@ -168,10 +182,12 @@ changes inside the current uv_async code. The uv_callback_t will have the same fields of uv_async_t with some additions: - #define UV_CALLBACK_PRIVATE_FIELDS \ - \ - int usequeue; \ - uv_mutex_t mutex; \ +```C +#define UV_CALLBACK_PRIVATE_FIELDS \ + \ + int usequeue; \ + uv_mutex_t mutex; \ +``` If `usequeue==1` then `callback_t->data` points to a queue (singly linked list). @@ -188,25 +204,29 @@ this fired callback returns. For each call there will be one of this in a linked list. The pointer to the first will be in async->data or a private field. - struct uv_callback_call_s { - uv_callback_call_t *next; /* pointer to the next call in the queue */ - void *data; /* data argument for this call */ - uv_callback_t *notify; /* callback to be fired with the result of this one */ - }; +```C +struct uv_callback_call_s { + uv_callback_call_t *next; /* pointer to the next call in the queue */ + void *data; /* data argument for this call */ + uv_callback_t *notify; /* callback to be fired with the result of this one */ +}; +``` ## uv_callback_cb The callback function must have the data as an argument because the data will be retrieved from the call queue. - typedef void (*uv_callback_cb)(uv_callback_t* handle, void *data); +```C +typedef void (*uv_callback_cb)(uv_callback_t* handle, void *data); +``` ## uv_callback_init The uv_callback_init will just initialize the structure with the given values. Something like this: -``` +```C int uv_callback_init( uv_loop_t* loop, uv_callback_t* callback, @@ -238,7 +258,7 @@ int uv_callback_init( The uv_callback_fire will store the call info in the callback_call queue/list and then probably do the same work of the `uv_async_send`. -``` +```C int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) { if (!callback) return UV_ARGS; @@ -258,7 +278,7 @@ int uv_callback_fire(uv_callback_t* callback, void *data, uv_callback_t* notify) llist_add(&callback->data, call); uv_mutex_leave(&callback->mutex); } else { - callback->data = data; + callback->data = data; } /* here insert the code from uv_async_send, maybe modified */ @@ -275,11 +295,12 @@ from the call queue. Here is a pseudo-code: - while(data = dequeue(async->queue)): - result = fire_callback(async->cb, data); - if (call->notify) uv_callback_fire(call->notify, result); - free(call); - +```javascript +while(data = dequeue(async->queue)): + result = fire_callback(async->cb, data); + if (call->notify) uv_callback_fire(call->notify, result); + free(call); +``` # Note