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

Reply via email to