2012-10-18 22:47:33 -07:00
|
|
|
/* EINA - EFL data type library
|
|
|
|
* Copyright (C) 2012 Cedric Bail
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library;
|
|
|
|
* if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include "eina_config.h"
|
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.
2016-09-13 21:38:58 -07:00
|
|
|
#include "eina_lock.h" /* it will include pthread.h with proper flags */
|
2012-10-18 22:47:33 -07:00
|
|
|
#include "eina_thread.h"
|
|
|
|
#include "eina_sched.h"
|
2015-09-08 20:45:47 -07:00
|
|
|
#include "eina_cpu.h"
|
2012-10-18 22:47:33 -07:00
|
|
|
|
2012-12-31 09:26:33 -08:00
|
|
|
/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
|
|
|
|
#include "eina_safety_checks.h"
|
2012-10-18 22:47:33 -07:00
|
|
|
|
2016-11-26 21:48:10 -08:00
|
|
|
#include "eina_debug_private.h"
|
2015-05-04 19:35:16 -07:00
|
|
|
|
2016-06-06 11:23:09 -07:00
|
|
|
#include <pthread.h>
|
|
|
|
#include <errno.h>
|
2016-07-08 05:09:17 -07:00
|
|
|
#ifndef _WIN32
|
|
|
|
# include <signal.h>
|
|
|
|
#endif
|
2016-11-26 21:48:10 -08:00
|
|
|
# include <string.h>
|
2012-10-18 22:47:33 -07:00
|
|
|
|
2016-06-06 11:23:09 -07:00
|
|
|
#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
|
2015-09-11 05:37:53 -07:00
|
|
|
#ifndef __linux__
|
|
|
|
#include <pthread_np.h>
|
|
|
|
#define cpu_set_t cpuset_t
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2012-12-31 09:26:33 -08:00
|
|
|
static inline void *
|
2012-10-18 22:47:33 -07:00
|
|
|
_eina_thread_join(Eina_Thread t)
|
|
|
|
{
|
|
|
|
void *ret = NULL;
|
2014-05-30 01:01:42 -07:00
|
|
|
int err = pthread_join((pthread_t)t, &ret);
|
2012-10-18 22:47:33 -07:00
|
|
|
|
2013-10-11 00:49:13 -07:00
|
|
|
if (err == 0) return ret;
|
2012-10-18 22:47:33 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-31 09:26:33 -08:00
|
|
|
static inline Eina_Bool
|
2012-10-19 00:13:08 -07:00
|
|
|
_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
|
|
|
|
{
|
2012-12-31 09:26:33 -08:00
|
|
|
int err;
|
2012-10-19 00:13:08 -07:00
|
|
|
pthread_attr_t attr;
|
2016-07-08 05:09:17 -07:00
|
|
|
#ifndef _WIN32
|
2016-07-04 07:47:25 -07:00
|
|
|
sigset_t oldset, newset;
|
2016-07-08 05:09:17 -07:00
|
|
|
#endif
|
|
|
|
|
2012-10-19 00:13:08 -07:00
|
|
|
pthread_attr_init(&attr);
|
|
|
|
if (affinity >= 0)
|
|
|
|
{
|
2012-12-31 09:26:33 -08:00
|
|
|
#ifdef EINA_HAVE_PTHREAD_AFFINITY
|
|
|
|
cpu_set_t cpu;
|
2012-10-19 00:13:08 -07:00
|
|
|
|
|
|
|
CPU_ZERO(&cpu);
|
2016-09-15 14:09:00 -07:00
|
|
|
CPU_SET(affinity, &cpu);
|
2012-10-19 00:13:08 -07:00
|
|
|
pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
|
|
|
|
#endif
|
2012-12-31 09:26:33 -08:00
|
|
|
}
|
|
|
|
|
2012-10-19 00:13:08 -07:00
|
|
|
/* setup initial locks */
|
2016-07-08 05:09:17 -07:00
|
|
|
#ifndef _WIN32
|
2016-07-04 07:47:25 -07:00
|
|
|
sigemptyset(&newset);
|
|
|
|
sigaddset(&newset, SIGPIPE);
|
|
|
|
sigaddset(&newset, SIGALRM);
|
|
|
|
sigaddset(&newset, SIGCHLD);
|
|
|
|
sigaddset(&newset, SIGUSR1);
|
|
|
|
sigaddset(&newset, SIGUSR2);
|
|
|
|
sigaddset(&newset, SIGHUP);
|
|
|
|
sigaddset(&newset, SIGQUIT);
|
|
|
|
sigaddset(&newset, SIGINT);
|
|
|
|
sigaddset(&newset, SIGTERM);
|
2016-07-08 05:09:17 -07:00
|
|
|
# ifdef SIGPWR
|
2016-07-04 07:47:25 -07:00
|
|
|
sigaddset(&newset, SIGPWR);
|
2016-07-08 05:09:17 -07:00
|
|
|
# endif
|
2016-07-13 16:32:16 -07:00
|
|
|
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
|
2016-07-08 05:09:17 -07:00
|
|
|
#endif
|
2014-05-30 01:01:42 -07:00
|
|
|
err = pthread_create((pthread_t *)t, &attr, func, data);
|
2016-07-08 05:09:17 -07:00
|
|
|
#ifndef _WIN32
|
2016-07-13 16:32:16 -07:00
|
|
|
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
2016-07-08 05:09:17 -07:00
|
|
|
#endif
|
2012-10-19 00:13:08 -07:00
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
|
2013-10-11 00:49:13 -07:00
|
|
|
if (err == 0) return EINA_TRUE;
|
2012-12-31 09:26:33 -08:00
|
|
|
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Eina_Bool
|
|
|
|
_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
|
|
|
{
|
2014-05-30 01:01:42 -07:00
|
|
|
return pthread_equal((pthread_t)t1, (pthread_t)t2);
|
2012-10-19 00:13:08 -07:00
|
|
|
}
|
|
|
|
|
2012-12-31 09:26:33 -08:00
|
|
|
static inline Eina_Thread
|
|
|
|
_eina_thread_self(void)
|
|
|
|
{
|
2014-05-30 01:01:42 -07:00
|
|
|
return (Eina_Thread)pthread_self();
|
2012-12-31 09:26:33 -08:00
|
|
|
}
|
2012-10-18 22:47:33 -07:00
|
|
|
|
|
|
|
|
|
|
|
typedef struct _Eina_Thread_Call Eina_Thread_Call;
|
|
|
|
struct _Eina_Thread_Call
|
|
|
|
{
|
|
|
|
Eina_Thread_Cb func;
|
|
|
|
const void *data;
|
|
|
|
|
|
|
|
Eina_Thread_Priority prio;
|
2012-10-19 00:13:08 -07:00
|
|
|
int affinity;
|
2012-10-18 22:47:33 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static void *
|
|
|
|
_eina_internal_call(void *context)
|
|
|
|
{
|
|
|
|
Eina_Thread_Call *c = context;
|
|
|
|
void *r;
|
2015-05-04 19:35:16 -07:00
|
|
|
pthread_t self;
|
2012-10-18 22:47:33 -07:00
|
|
|
|
2018-04-05 10:39:30 -07:00
|
|
|
// Default this thread to not cancellable as per Eina documentation
|
|
|
|
eina_thread_cancellable_set(EINA_FALSE, NULL);
|
|
|
|
|
2016-09-16 18:17:25 -07:00
|
|
|
EINA_THREAD_CLEANUP_PUSH(free, c);
|
|
|
|
|
2012-10-18 22:47:33 -07:00
|
|
|
if (c->prio == EINA_THREAD_BACKGROUND ||
|
|
|
|
c->prio == EINA_THREAD_IDLE)
|
|
|
|
eina_sched_prio_drop();
|
|
|
|
|
2015-05-14 00:19:53 -07:00
|
|
|
self = pthread_self();
|
2015-05-04 19:35:16 -07:00
|
|
|
_eina_debug_thread_add(&self);
|
2016-09-16 18:17:25 -07:00
|
|
|
EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
|
2012-10-18 22:47:33 -07:00
|
|
|
r = c->func((void*) c->data, eina_thread_self());
|
2016-09-16 18:17:25 -07:00
|
|
|
EINA_THREAD_CLEANUP_POP(EINA_TRUE);
|
|
|
|
EINA_THREAD_CLEANUP_POP(EINA_TRUE);
|
2012-10-18 22:47:33 -07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Thread
|
|
|
|
eina_thread_self(void)
|
|
|
|
{
|
2012-12-31 09:26:33 -08:00
|
|
|
return _eina_thread_self();
|
2012-10-18 22:47:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
|
|
|
{
|
2012-12-31 09:26:33 -08:00
|
|
|
return !!_eina_thread_equal(t1, t2);
|
2012-10-18 22:47:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_thread_create(Eina_Thread *t,
|
2012-10-19 00:13:08 -07:00
|
|
|
Eina_Thread_Priority prio, int affinity,
|
2012-10-18 22:47:33 -07:00
|
|
|
Eina_Thread_Cb func, const void *data)
|
|
|
|
{
|
|
|
|
Eina_Thread_Call *c;
|
|
|
|
|
2012-12-31 09:26:33 -08:00
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
|
|
|
|
|
2012-10-18 22:47:33 -07:00
|
|
|
c = malloc(sizeof (Eina_Thread_Call));
|
2013-10-11 00:49:13 -07:00
|
|
|
if (!c) return EINA_FALSE;
|
2012-10-18 22:47:33 -07:00
|
|
|
|
|
|
|
c->func = func;
|
|
|
|
c->data = data;
|
|
|
|
c->prio = prio;
|
|
|
|
c->affinity = affinity;
|
|
|
|
|
2013-11-03 03:51:41 -08:00
|
|
|
// valgrind complains c is lost - but it's not - it is handed to the
|
|
|
|
// child thread to be freed when c->func returns in _eina_internal_call().
|
2012-12-31 09:26:33 -08:00
|
|
|
if (_eina_thread_create(t, affinity, _eina_internal_call, c))
|
2012-10-18 22:47:33 -07:00
|
|
|
return EINA_TRUE;
|
|
|
|
|
|
|
|
free(c);
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
EAPI void *
|
|
|
|
eina_thread_join(Eina_Thread t)
|
|
|
|
{
|
2012-12-31 09:26:33 -08:00
|
|
|
return _eina_thread_join(t);
|
2012-10-18 22:47:33 -07:00
|
|
|
}
|
|
|
|
|
2015-09-08 20:45:47 -07:00
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_thread_name_set(Eina_Thread t, const char *name)
|
|
|
|
{
|
|
|
|
#ifdef EINA_HAVE_PTHREAD_SETNAME
|
2015-09-09 23:15:01 -07:00
|
|
|
char buf[16];
|
|
|
|
if (name)
|
|
|
|
{
|
|
|
|
strncpy(buf, name, 15);
|
|
|
|
buf[15] = 0;
|
|
|
|
}
|
|
|
|
else buf[0] = 0;
|
2015-09-11 05:37:53 -07:00
|
|
|
#ifndef __linux__
|
|
|
|
pthread_set_name_np((pthread_t)t, buf);
|
|
|
|
return EINA_TRUE;
|
|
|
|
#else
|
2015-09-09 23:15:01 -07:00
|
|
|
if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE;
|
2015-09-11 05:37:53 -07:00
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
(void)t;
|
|
|
|
(void)name;
|
2015-09-08 20:45:47 -07:00
|
|
|
#endif
|
|
|
|
return EINA_FALSE;
|
|
|
|
}
|
|
|
|
|
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.
2016-09-13 21:38:58 -07:00
|
|
|
EAPI Eina_Bool
|
|
|
|
eina_thread_cancel(Eina_Thread t)
|
|
|
|
{
|
2016-09-14 16:50:05 -07:00
|
|
|
if (!t) return EINA_FALSE;
|
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.
2016-09-13 21:38:58 -07:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-11-15 10:59:54 -08:00
|
|
|
EAPI 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;
|
|
|
|
}
|
|
|
|
|
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.
2016-09-13 21:38:58 -07:00
|
|
|
EAPI const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
|
|
|
|
|
2012-10-19 09:19:33 -07:00
|
|
|
Eina_Bool
|
2012-10-18 22:47:33 -07:00
|
|
|
eina_thread_init(void)
|
|
|
|
{
|
2012-10-19 09:19:33 -07:00
|
|
|
return EINA_TRUE;
|
2012-10-18 22:47:33 -07:00
|
|
|
}
|
|
|
|
|
2012-10-19 09:19:33 -07:00
|
|
|
Eina_Bool
|
2012-10-18 22:47:33 -07:00
|
|
|
eina_thread_shutdown(void)
|
|
|
|
{
|
2012-10-19 09:19:33 -07:00
|
|
|
return EINA_TRUE;
|
2012-10-18 22:47:33 -07:00
|
|
|
}
|