Hi Gustavo, On 14 September 2016 at 13:55, Gustavo Sverzut Barbieri <barbi...@gmail.com> wrote:
> HEADS UP -- BINDINGS: > > If you wish to expose the new "cancellable" property, or cope with > naughty users or 3rd party deps that may call > pthread_setcancelstate(PTHREAD_CANCEL_ENABLE), please register your > cleanup functions with: > > EINA_THREAD_CLEANUP_PUSH(cb, ctx); // will call cb() on > pthread_cancel()/ecore_thread_cancel()/eina_thread_cancel() > call_user(); > EINA_THREAD_CLEANUP_POP(EINA_TRUE); // will call cb() on clean exit > > If different functions are desired: > > EINA_THREAD_CLEANUP_PUSH(cb, ctx); // will call cb() on > pthread_cancel()/ecore_thread_cancel()/eina_thread_cancel() > call_user(); > EINA_THREAD_CLEANUP_POP(EINA_FALSE); // will NOT call cb() on clean > exit > another_cb(ctx); > > This is recommended if you expose ecore_thread to your users. If you > don't, then you do not need to do anything. pthread_cancel did (does?) not exist on Android, by design choice. bionic isn't posix in that regard. One way or another, I remember that using cancel is actually quite hard, because you need to be very careful about the cleanup. So I wonder if adding cancel like pthread here is the best choice? Note that I understand the need and don't have any good alternative :) > On Wed, Sep 14, 2016 at 1:47 AM, Gustavo Sverzut Barbieri > <barbi...@gmail.com> wrote: > > barbieri pushed a commit to branch master. > > > > http://git.enlightenment.org/core/efl.git/commit/?id= > 960e1a1d168ba0044544ca22b05cbf505941f150 > > > > commit 960e1a1d168ba0044544ca22b05cbf505941f150 > > Author: Gustavo Sverzut Barbieri <barbi...@profusion.mobi> > > Date: Wed Sep 14 01:38:58 2016 -0300 > > > > eina/ecore: allow threads to be canceled, use in ecore_con. > > > > As discussed in the mailing list, many people will use worker threads > > to execute blocking syscalls and mandating ecore_thread_check() for > > voluntary preemption reduces the ecore_thread usefulness a lot. > > > > A clear example is ecore_con usage of connect() and getaddrinfo() in > > threads. If the connect timeout expires, the thread will be > cancelled, > > but it was blocked on syscalls and they will hang around for long > > time. If the application exits, ecore will print an error saying it > > can SEGV. > > > > Then enable access to pthread_setcancelstate(PTHREAD_CANCEL_ENABLE) > > via eina_thread_cancellable_set(EINA_TRUE), to pthread_cancel() via > > eina_thread_cancel(), to pthread_cleanup_push()/ > pthread_cleanup_pop() > > via EINA_THREAD_CLEANUP_PUSH()/EINA_THREAD_CLEANUP_POP() and so on. > > > > Ecore threads will enforce non-cancellable threads on its own code, > > but the user may decide to enable that and allow cancellation, that's > > not an issue since ecore_thread now plays well and use cleanup > > functions. > > > > Ecore con connect/resolve make use of that and enable cancellable > > state, efl_net_dialer_tcp benefits a lot from that. > > > > A good comparison of the benefit is to run: > > > > ./src/examples/ecore/efl_io_copier_example tcp://google.com:1234 > :stdout: > > > > before and after. It will timeout after 30s and with this patch the > > thread is gone, no ecore error is printed about possible SEGV. > > --- > > src/lib/ecore/Ecore_Common.h | 12 +++ > > src/lib/ecore/ecore_thread.c | 108 +++++++++++++++------ > > src/lib/ecore_con/ecore_con.c | 41 +++++--- > > src/lib/eina/Eina.h | 2 +- > > src/lib/eina/eina_thread.c | 32 ++++++ > > src/lib/eina/eina_thread.h | 219 ++++++++++++++++++++++++++++++ > ++++++++++++ > > 6 files changed, 373 insertions(+), 41 deletions(-) > > > > diff --git a/src/lib/ecore/Ecore_Common.h b/src/lib/ecore/Ecore_Common.h > > index 5404438..22da9ab 100644 > > --- a/src/lib/ecore/Ecore_Common.h > > +++ b/src/lib/ecore/Ecore_Common.h > > @@ -1794,6 +1794,17 @@ EAPI Ecore_Thread > > *ecore_thread_feedback_run(Ecore_Thread_Cb > func_heavy, Ecore_T > > * ecore_thread_reschedule(). > > * @li The function is prepared to leave early by checking if > > * ecore_thread_check() returns @c EINA_TRUE. > > + > > + * @li The function marks the thread as cancellable using > > + * eina_thread_cancellable_set(), allowing the thread to be terminated > > + * at explicit cancellation points defined with > > + * eina_thread_cancel_checkpoint() or with syscalls mentioned at > > + * man:pthreads(7). This allows blocking operations such as network or > > + * disk access to be stopped without polling > > + * ecore_thread_check(). Note that a cancelled thread may leak > > + * resources if no cleanup function is registered with > > + * EINA_THREAD_CLEANUP_PUSH(). Consider running such code using > > + * eina_thread_cancellable_run(). > > * > > * The user function can cancel itself by calling > ecore_thread_cancel(), but > > * it should always use the ::Ecore_Thread handle passed to it and never > > @@ -1804,6 +1815,7 @@ EAPI Ecore_Thread > > *ecore_thread_feedback_run(Ecore_Thread_Cb > func_heavy, Ecore_T > > * returns @c EINA_TRUE or after the @c func_cancel callback returns. > > * > > * @see ecore_thread_check() > > + * @see eina_thread_cancellable_run() > > */ > > EAPI Eina_Bool ecore_thread_cancel(Ecore_Thread *thread); > > > > diff --git a/src/lib/ecore/ecore_thread.c b/src/lib/ecore/ecore_thread.c > > index 6a68d6b..6a10164 100644 > > --- a/src/lib/ecore/ecore_thread.c > > +++ b/src/lib/ecore/ecore_thread.c > > @@ -202,6 +202,7 @@ _ecore_thread_data_free(void *data) > > static void > > _ecore_thread_join(PH(thread)) > > { > > + DBG("joining thread=%" PRIu64, (uint64_t)thread); > > PHJ(thread); > > } > > > > @@ -320,6 +321,31 @@ _ecore_message_notify_handler(void *data) > > } > > > > static void > > +_ecore_short_job_cleanup(void *data) > > +{ > > + Ecore_Pthread_Worker *work = data; > > + > > + DBG("cleanup work=%p, thread=%" PRIu64, work, (uint64_t)work->self); > > + > > + SLKL(_ecore_running_job_mutex); > > + _ecore_running_job = eina_list_remove(_ecore_running_job, work); > > + SLKU(_ecore_running_job_mutex); > > + > > + if (work->reschedule) > > + { > > + work->reschedule = EINA_FALSE; > > + > > + SLKL(_ecore_pending_job_threads_mutex); > > + _ecore_pending_job_threads = > > eina_list_append(_ecore_pending_job_threads, > work); > > + SLKU(_ecore_pending_job_threads_mutex); > > + } > > + else > > + { > > + ecore_main_loop_thread_safe_call_async(_ecore_thread_handler, > work); > > + } > > +} > > + > > +static void > > _ecore_short_job(PH(thread)) > > { > > Ecore_Pthread_Worker *work; > > @@ -346,19 +372,31 @@ _ecore_short_job(PH(thread)) > > cancel = work->cancel; > > SLKU(work->cancel_mutex); > > work->self = thread; > > + > > + EINA_THREAD_CLEANUP_PUSH(_ecore_short_job_cleanup, work); > > if (!cancel) > > work->u.short_run.func_blocking((void *) work->data, > (Ecore_Thread*) work); > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > + EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > +} > > + > > +static void > > +_ecore_feedback_job_cleanup(void *data) > > +{ > > + Ecore_Pthread_Worker *work = data; > > + > > + DBG("cleanup work=%p, thread=%" PRIu64, work, (uint64_t)work->self); > > > > SLKL(_ecore_running_job_mutex); > > _ecore_running_job = eina_list_remove(_ecore_running_job, work); > > SLKU(_ecore_running_job_mutex); > > - > > + > > if (work->reschedule) > > { > > work->reschedule = EINA_FALSE; > > - > > + > > SLKL(_ecore_pending_job_threads_mutex); > > - _ecore_pending_job_threads = > > eina_list_append(_ecore_pending_job_threads, > work); > > + _ecore_pending_job_threads_feedback = > > eina_list_append(_ecore_pending_job_threads_feedback, > work); > > SLKU(_ecore_pending_job_threads_mutex); > > } > > else > > @@ -393,52 +431,69 @@ _ecore_feedback_job(PH(thread)) > > cancel = work->cancel; > > SLKU(work->cancel_mutex); > > work->self = thread; > > + > > + EINA_THREAD_CLEANUP_PUSH(_ecore_feedback_job_cleanup, work); > > if (!cancel) > > work->u.feedback_run.func_heavy((void *) work->data, > (Ecore_Thread *) work); > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > + EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > +} > > > > - SLKL(_ecore_running_job_mutex); > > - _ecore_running_job = eina_list_remove(_ecore_running_job, work); > > - SLKU(_ecore_running_job_mutex); > > +static void > > +_ecore_direct_worker_cleanup(void *data) > > +{ > > + Ecore_Pthread_Worker *work = data; > > > > - if (work->reschedule) > > - { > > - work->reschedule = EINA_FALSE; > > - > > - SLKL(_ecore_pending_job_threads_mutex); > > - _ecore_pending_job_threads_feedback = > > eina_list_append(_ecore_pending_job_threads_feedback, > work); > > - SLKU(_ecore_pending_job_threads_mutex); > > - } > > - else > > - { > > - ecore_main_loop_thread_safe_call_async(_ecore_thread_handler, > work); > > - } > > + DBG("cleanup work=%p, thread=%" PRIu64 " (should join)", work, > (uint64_t)work->self); > > + > > + ecore_main_loop_thread_safe_call_async(_ecore_thread_handler, work); > > + > > + ecore_main_loop_thread_safe_call_async((Ecore_Cb) > _ecore_thread_join, > > + (void*)(intptr_t)PHS()); > > } > > > > static void * > > _ecore_direct_worker(Ecore_Pthread_Worker *work) > > { > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > eina_thread_name_set(eina_thread_self(), "Ethread-feedback"); > > work->self = PHS(); > > + > > + EINA_THREAD_CLEANUP_PUSH(_ecore_direct_worker_cleanup, work); > > if (work->message_run) > > work->u.message_run.func_main((void *) work->data, (Ecore_Thread > *) work); > > else > > work->u.feedback_run.func_heavy((void *) work->data, > (Ecore_Thread *) work); > > - > > - ecore_main_loop_thread_safe_call_async(_ecore_thread_handler, work); > > - > > - ecore_main_loop_thread_safe_call_async((Ecore_Cb) > _ecore_thread_join, > > - (void*)(intptr_t)PHS()); > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > + EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > > > return NULL; > > } > > > > +static void > > +_ecore_thread_worker_cleanup(void *data EINA_UNUSED) > > +{ > > + DBG("cleanup thread=%" PRIu64 " (should join)", PHS()); > > + SLKL(_ecore_pending_job_threads_mutex); > > + _ecore_thread_count--; > > + SLKU(_ecore_pending_job_threads_mutex); > > + ecore_main_loop_thread_safe_call_async((Ecore_Cb) > _ecore_thread_join, > > + (void*)(intptr_t)PHS()); > > +} > > + > > static void * > > _ecore_thread_worker(void *data EINA_UNUSED) > > { > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > + EINA_THREAD_CLEANUP_PUSH(_ecore_thread_worker_cleanup, NULL); > > restart: > > + > > + /* these 2 are cancellation points as user cb may enable */ > > _ecore_short_job(PHS()); > > _ecore_feedback_job(PHS()); > > > > + /* from here on, cancellations are guaranteed to be disabled */ > > + > > /* FIXME: Check if there is feedback running task todo, and switch > to feedback run handler. */ > > eina_thread_name_set(eina_thread_self(), "Ethread-worker"); > > > > @@ -463,12 +518,10 @@ restart: > > SLKU(_ecore_pending_job_threads_mutex); > > goto restart; > > } > > - _ecore_thread_count--; > > - > > - ecore_main_loop_thread_safe_call_async((Ecore_Cb) > _ecore_thread_join, > > - (void*)(intptr_t)PHS()); > > SLKU(_ecore_pending_job_threads_mutex); > > > > + EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > + > > return NULL; > > } > > > > @@ -728,6 +781,7 @@ ecore_thread_cancel(Ecore_Thread *thread) > > > > /* Delay the destruction */ > > on_exit: > > + eina_thread_cancel(work->self); /* noop unless > eina_thread_cancellable_set() was used by user */ > > SLKL(work->cancel_mutex); > > work->cancel = EINA_TRUE; > > SLKU(work->cancel_mutex); > > diff --git a/src/lib/ecore_con/ecore_con.c > b/src/lib/ecore_con/ecore_con.c > > index 63f2c8e..4efcb2a 100644 > > --- a/src/lib/ecore_con/ecore_con.c > > +++ b/src/lib/ecore_con/ecore_con.c > > @@ -3126,11 +3126,19 @@ typedef struct _Efl_Net_Resolve_Async_Data > > } Efl_Net_Resolve_Async_Data; > > > > static void > > -_efl_net_resolve_async_run(void *data, Ecore_Thread *thread) > > +_efl_net_resolve_async_run(void *data, Ecore_Thread *thread > EINA_UNUSED) > > { > > Efl_Net_Resolve_Async_Data *d = data; > > > > - while (!ecore_thread_check(thread)) > > + /* allows ecore_thread_cancel() to cancel at some points, see > > + * man:pthreads(7). > > + * > > + * no need to set cleanup functions since the main thread will > > + * handle that with _efl_net_resolve_async_cancel(). > > + */ > > + eina_thread_cancellable_set(EINA_TRUE, NULL); > > + > > + while (EINA_TRUE) > > { > > DBG("resolving host='%s' port='%s'", d->host, d->port); > > d->gai_error = getaddrinfo(d->host, d->port, d->hints, > &d->result); > > @@ -3141,6 +3149,8 @@ _efl_net_resolve_async_run(void *data, > Ecore_Thread *thread) > > break; > > } > > > > + eina_thread_cancellable_set(EINA_FALSE, NULL); > > + > > if (eina_log_domain_level_check(_ecore_con_log_dom, > EINA_LOG_LEVEL_DBG)) > > { > > char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = ""; > > @@ -3235,12 +3245,20 @@ typedef struct _Efl_Net_Connect_Async_Data > > } Efl_Net_Connect_Async_Data; > > > > static void > > -_efl_net_connect_async_run(void *data, Ecore_Thread *thread) > > +_efl_net_connect_async_run(void *data, Ecore_Thread *thread > EINA_UNUSED) > > { > > Efl_Net_Connect_Async_Data *d = data; > > char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = ""; > > int r; > > > > + /* allows ecore_thread_cancel() to cancel at some points, see > > + * man:pthreads(7). > > + * > > + * no need to set cleanup functions since the main thread will > > + * handle that with _efl_net_connect_async_cancel(). > > + */ > > + eina_thread_cancellable_set(EINA_TRUE, NULL); > > + > > d->error = 0; > > > > d->sockfd = efl_net_socket4(d->addr->sa_family, d->type, > d->protocol, d->close_on_exec); > > @@ -3251,14 +3269,6 @@ _efl_net_connect_async_run(void *data, > Ecore_Thread *thread) > > return; > > } > > > > - if (ecore_thread_check(thread)) > > - { > > - d->error = ECANCELED; > > - close(d->sockfd); > > - d->sockfd = -1; > > - return; > > - } > > - > > if (eina_log_domain_level_check(_ecore_con_log_dom, > EINA_LOG_LEVEL_DBG)) > > efl_net_ip_port_fmt(buf, sizeof(buf), d->addr); > > > > @@ -3267,10 +3277,15 @@ _efl_net_connect_async_run(void *data, > Ecore_Thread *thread) > > r = connect(d->sockfd, d->addr, d->addrlen); > > if (r < 0) > > { > > + int fd = d->sockfd; > > d->error = errno; > > - close(d->sockfd); > > d->sockfd = -1; > > - DBG("connect(%d, %s) failed: %s", d->sockfd, buf, > strerror(errno)); > > + /* close() is a cancellation point, thus unset sockfd before > > + * closing, so the main thread _efl_net_connect_async_cancel() > > + * won't close it again. > > + */ > > + close(fd); > > + DBG("connect(%d, %s) failed: %s", fd, buf, strerror(errno)); > > return; > > } > > > > diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h > > index f57cc62..a7a364f 100644 > > --- a/src/lib/eina/Eina.h > > +++ b/src/lib/eina/Eina.h > > @@ -243,7 +243,6 @@ extern "C" { > > #include <eina_cpu.h> > > #include <eina_sched.h> > > #include <eina_tiler.h> > > -#include <eina_thread.h> > > #include <eina_hamster.h> > > #include <eina_matrixsparse.h> > > #include <eina_str.h> > > @@ -254,6 +253,7 @@ extern "C" { > > #include <eina_quadtree.h> > > #include <eina_simple_xml_parser.h> > > #include <eina_lock.h> > > +#include <eina_thread.h> /* after eina_lock.h since it will include > pthread.h with proper flags */ > > #include <eina_prefix.h> > > #include <eina_refcount.h> > > #include <eina_mmap.h> > > diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c > > index 52cbc39..d40073a 100644 > > --- a/src/lib/eina/eina_thread.c > > +++ b/src/lib/eina/eina_thread.c > > @@ -23,6 +23,7 @@ > > #include <stdlib.h> > > > > #include "eina_config.h" > > +#include "eina_lock.h" /* it will include pthread.h with proper flags */ > > #include "eina_thread.h" > > #include "eina_sched.h" > > #include "eina_cpu.h" > > @@ -226,6 +227,37 @@ eina_thread_name_set(Eina_Thread t, const char > *name) > > return EINA_FALSE; > > } > > > > +EAPI Eina_Bool > > +eina_thread_cancel(Eina_Thread t) > > +{ > > + return pthread_cancel((pthread_t)t) == 0; > > +} > > + > > +EAPI Eina_Bool > > +eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool > *was_cancellable) > > +{ > > + int state = cancellable ? PTHREAD_CANCEL_ENABLE : > PTHREAD_CANCEL_DISABLE; > > + int old = 0; > > + int r; > > + > > + /* enforce deferred in case users changed to asynchronous themselves > */ > > + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old); > > + > > + r = pthread_setcancelstate(state, &old); > > + if (was_cancellable && r == 0) > > + *was_cancellable = (old == PTHREAD_CANCEL_ENABLE); > > + > > + return r == 0; > > +} > > + > > +EAPI void > > +eina_thread_cancel_checkpoint(void) > > +{ > > + pthread_testcancel(); > > +} > > + > > +EAPI const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED; > > + > > Eina_Bool > > eina_thread_init(void) > > { > > diff --git a/src/lib/eina/eina_thread.h b/src/lib/eina/eina_thread.h > > index 6a2c26d..07ecec3 100644 > > --- a/src/lib/eina/eina_thread.h > > +++ b/src/lib/eina/eina_thread.h > > @@ -100,6 +100,20 @@ EAPI Eina_Bool eina_thread_create(Eina_Thread *t, > > Eina_Thread_Cb func, const void > *data) EINA_ARG_NONNULL(1, 4) EINA_WARN_UNUSED_RESULT; > > > > /** > > + * The return value of eina_thread_join() if it was canceled with > > + * eina_thread_cancel(). > > + * > > + * A thread must be explicitly flagged as cancellable with > > + * eina_thread_cancellable_set(), by default it's not and this value > > + * shouldn't be returned. > > + * > > + * @see eina_thread_join() > > + * > > + * @since 1.19 > > + */ > > +EAPI extern const void *EINA_THREAD_JOIN_CANCELED; > > + > > +/** > > * Join a currently running thread, waiting until it finishes. > > * > > * This function will block the current thread until @a t > > @@ -110,6 +124,8 @@ EAPI Eina_Bool eina_thread_create(Eina_Thread *t, > > * @param t thread identifier to wait. > > * @return value returned by @a t creation function @c func() or > > * @c NULL on errors. Check error with @ref Eina_Error_Group. > > + * If the thread was canceled, it will return > > + * EINA_THREAD_JOIN_CANCELED. > > * @since 1.8 > > */ > > EAPI void *eina_thread_join(Eina_Thread t); > > @@ -132,6 +148,209 @@ EAPI void *eina_thread_join(Eina_Thread t); > > EAPI Eina_Bool eina_thread_name_set(Eina_Thread t, const char *name); > > > > /** > > + * Attempt to cancel a running thread. > > + * > > + * This function sends a cancellation request to the thread, however > > + * that request is only fulfilled if the thread is cancellable > > + * (eina_thread_cancellable_set() with EINA_TRUE as first paramter) > > + * and it will wait for a cancellation point, be > > + * eina_thread_cancel_checkpoint() or some syscall as defined in > > + * man:pthreads(7). > > + * > > + * A thread that was canceled will return EINA_THREAD_JOIN_CANCELED > > + * when eina_thread_join() is called. > > + * > > + * @param t thread to cancel. > > + * > > + * @return EINA_FALSE if thread was not running, EINA_TRUE > > + * otherwise. Note that if a thread is not cancellable and it > > + * is running, this function will return EINA_TRUE! > > + * > > + * @since 1.19 > > + */ > > +EAPI Eina_Bool eina_thread_cancel(Eina_Thread t); > > + > > +/** > > + * Enable or disable if the current thread can be canceled. > > + * > > + * By default eina_thread_create() will return a thread with > > + * cancellation disabled. One can enable the cancellation by using > > + * EINA_TRUE in @a cancellable. > > + * > > + * Eina threads follow pthread_setcanceltype() > > + * PTHREAD_CANCEL_DEFERRED, that is, the actual termination will wait > > + * for a cancellation point, usually a syscall defined in > > + * man:pthreads(7) or an explicit cancellation point defined with > > + * eina_thread_cancel_checkpoint(). > > + * > > + * In order to provide cleanup around critical blocks use > > + * EINA_THREAD_CLEANUP_PUSH() and EINA_THREAD_CLEANUP_POP() macros > > + * (which maps to pthread_cleanup_push() and pthread_cleanup_pop()), > > + * or the helper function eina_thread_cancellable_run() which does the > > + * pair for you. > > + * > > + * @param cancellable if EINA_TRUE, this thread will be accept > > + * cancellation requests. If EINA_FALSE -- the default, it will > > + * ignore cancellation requests. > > + * @param was_cancellable if non-NULL, will return the previous state, > > + * shall you want to restore. > > + * > > + * @return EINA_TRUE if it succeeds in setting the cancellable state > > + * or EINA_FALSE otherwise. > > + * > > + * @see eina_thread_cancel_checkpoint() > > + * @see EINA_THREAD_CLEANUP_PUSH() > > + * @see EINA_THREAD_CLEANUP_POP() > > + * @see eina_thread_cancellable_run() > > + * @see eina_thread_cancel() > > + * > > + * @since 1.19 > > + */ > > +EAPI Eina_Bool eina_thread_cancellable_set(Eina_Bool cancellable, > Eina_Bool *was_cancellable); > > + > > +/** > > + * If the current thread is cancellable, this introduces a > > + * cancellation check point. Otherwise it's a no-operation. > > + * > > + * Eina threads follow pthread_setcanceltype() > > + * PTHREAD_CANCEL_DEFERRED, that is, the actual termination will wait > > + * for a cancellation point, usually a syscall defined in > > + * man:pthreads(7) or an explicit cancellation point defined with this > > + * function. > > + * > > + * @see eina_thread_cancel_checkpoint() > > + * > > + * @since 1.19 > > + */ > > +EAPI void eina_thread_cancel_checkpoint(void); > > + > > +/** > > + * @def EINA_THREAD_CLEANUP_PUSH(cleanup, data) > > + * > > + * @brief Push a cleanup function to be executed when the thread is > > + * canceled. > > + * > > + * This macro will schedule a function cleanup(data) to be executed if > > + * the thread is canceled with eina_thread_cancel() and the thread > > + * was previously marked as cancellable with > > + * eina_thread_cancellable_set(). > > + * > > + * It @b must be paired with EINA_THREAD_CLEANUP_POP() in the same > > + * code block as they will expand to do {} while ()! > > + * > > + * The cleanup function may also be executed if > > + * EINA_THREAD_CLEANUP_POP(EINA_TRUE) is used. > > + * > > + * @note If the block within EINA_THREAD_CLEANUP_PUSH() and > > + * EINA_THREAD_CLEANUP_POP() returns, the cleanup callback will > > + * @b not be executed! To avoid problems prefer to use > > + * eina_thread_cancellable_run()! > > + * > > + * @param cleanup the function to execute on cancellation. > > + * @param data the context to give to cleanup function. > > + * > > + * @see eina_thread_cancellable_run() > > + * > > + * @since 1.19 > > + */ > > +#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \ > > + pthread_cleanup_push(cleanup, data) > > + > > +/** > > + * @def EINA_THREAD_CLEANUP_POP(exec_cleanup) > > + * > > + * @brief Pop a cleanup function to be executed when the thread is > > + * canceled. > > + * > > + * This macro will remove a previously pushed cleanup function, thus > > + * if the thread is canceled with eina_thread_cancel() and the thread > > + * was previously marked as cancellable with > > + * eina_thread_cancellable_set(), that cleanup won't be executed > > + * anymore. > > + * > > + * It @b must be paired with EINA_THREAD_CLEANUP_PUSH() in the same > > + * code block as they will expand to do {} while ()! > > + * > > + * @note If the block within EINA_THREAD_CLEANUP_PUSH() and > > + * EINA_THREAD_CLEANUP_POP() returns, the cleanup callback will > > + * @b not be executed even if exec_cleanup is EINA_TRUE! To > > + * avoid problems prefer to use eina_thread_cancellable_run()! > > + * > > + * @param exec_cleanup if EINA_TRUE, the function registered with > > + * EINA_THREAD_CLEANUP_PUSH() will be executed. > > + * > > + * @see eina_thread_cancellable_run() > > + * > > + * @since 1.19 > > + */ > > +#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \ > > + pthread_cleanup_pop(exec_cleanup) > > + > > +/** > > + * @typedef Eina_Thread_Cancellable_Run_Cb > > + * Type for the definition of a cancellable callback to run. > > + * > > + * @since 1.19 > > + */ > > +typedef void *(*Eina_Thread_Cancellable_Run_Cb)(void *data); > > + > > +/** > > + * This function will setup cleanup callback, turn the thread > > + * cancellable, execute the given callback, reset the cancellable > > + * state to its old value, run the cleanup callback and then return > > + * the callback return value. > > + * > > + * @note cleanup_cb is configured @b before the thread is made > > + * cancellable, thus it @b will be executed while @a cb may not > > + * in the case the thread was already canceled and that was > > + * pending. > > + * > > + * This helper does exactly the following code. Shall you need a > > + * slightly different behavior, use the base calls yourself. > > + * > > + * @code > > + * Eina_Bool old = EINA_FALSE; > > + * void *ret; > > + * > > + * EINA_THREAD_CLEANUP_PUSH(cleanup_cb, data); > > + * eina_thread_cancellable_set(EINA_TRUE, &old); // is a > cancellation point > > + * ret = cb(data); // may not run if was previously canceled > > + * EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > + * eina_thread_cancellable_set(old, NULL); > > + * return ret; > > + * @endcode > > + * > > + * @param cb a cancellable callback to possibly run. The callback @b > > + * may not be executed if the thread had a pending cancellation > > + * request. During its execution the callback may be canceled > > + * at explicit cancellation points using > > + * eina_thread_cancel_checkpoint(), as well as some syscalls > > + * defined in man:pthreads(7). > > + * @param cleanup_cb a cleanup callback to be executed regardless of > > + * the thread being canceled or not. This function will be > > + * executed even if @a cb wasn't. > > + * @param data context to give to both @a cb and @a cleanup_cb. > > + * > > + * @return the return value of @a cb. If the thread was canceled, > > + * this function will not return. > > + * > > + * @since 1.19 > > + */ > > +static inline void * > > +eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, > Eina_Free_Cb cleanup_cb, void *data) > > +{ > > + Eina_Bool old = EINA_FALSE; > > + void *ret; > > + > > + EINA_THREAD_CLEANUP_PUSH(cleanup_cb, data); > > + eina_thread_cancellable_set(EINA_TRUE, &old); // is a cancellation > point > > + ret = cb(data); // may not run if was previously canceled > > + EINA_THREAD_CLEANUP_POP(EINA_TRUE); > > + eina_thread_cancellable_set(old, NULL); > > + return ret; > > +} > > + > > +/** > > * @} > > */ > > > > > > -- > > > > > > > > -- > Gustavo Sverzut Barbieri > -------------------------------------- > Mobile: +55 (16) 99354-9890 > > ------------------------------------------------------------ > ------------------ > _______________________________________________ > enlightenment-devel mailing list > enlightenment-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/enlightenment-devel > > -- Jean-Philippe André ------------------------------------------------------------------------------ _______________________________________________ enlightenment-devel mailing list enlightenment-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/enlightenment-devel