forked from enlightenment/efl
Summary: eina: Implement Eina_Thread for native windows The implementation design respects the fact that Eina_Thread is an uintptr_t. Thus we allocate the thread struct in the heap and return a pointer to it. As such, we store the created thread structure in the target thread TLS slot. For threads that were not created through eina API, in eina_thread_self we allocate a new structure, push it to the TLS slot and mark it to be freed on thread exit. Reviewers: jptiz, vtorri, cedric, walac Reviewed By: jptiz, cedric Subscribers: raster, cedric, #reviewers, #committers, lucas Tags: #efl Differential Revision: https://phab.enlightenment.org/D12037v-1.26.0
parent
287834b0da
commit
65d528a379
14 changed files with 956 additions and 432 deletions
@ -1,84 +0,0 @@ |
||||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2010 ProFUSION embedded systems |
||||
* |
||||
* 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 <pthread.h> |
||||
#ifdef __linux__ |
||||
# include <sched.h> |
||||
# include <sys/time.h> |
||||
# include <sys/resource.h> |
||||
# include <errno.h> |
||||
#endif |
||||
|
||||
#include "eina_sched.h" |
||||
#include "eina_log.h" |
||||
|
||||
#define RTNICENESS 1 |
||||
#define NICENESS 5 |
||||
|
||||
EINA_API void |
||||
eina_sched_prio_drop(void) |
||||
{ |
||||
struct sched_param param; |
||||
int pol, ret; |
||||
pthread_t pthread_id; |
||||
|
||||
pthread_id = pthread_self(); |
||||
ret = pthread_getschedparam(pthread_id, &pol, ¶m); |
||||
if (ret) |
||||
{ |
||||
EINA_LOG_ERR("Unable to query sched parameters"); |
||||
return; |
||||
} |
||||
|
||||
if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO)) |
||||
{ |
||||
param.sched_priority -= RTNICENESS; |
||||
|
||||
/* We don't change the policy */ |
||||
if (param.sched_priority < 1) |
||||
{ |
||||
EINA_LOG_INFO("RT prio < 1, setting to 1 instead"); |
||||
param.sched_priority = 1; |
||||
} |
||||
|
||||
pthread_setschedparam(pthread_id, pol, ¶m); |
||||
} |
||||
# ifdef __linux__ |
||||
else |
||||
{ |
||||
int prio; |
||||
errno = 0; |
||||
prio = getpriority(PRIO_PROCESS, 0); |
||||
if (errno == 0) |
||||
{ |
||||
prio += NICENESS; |
||||
if (prio > 19) |
||||
{ |
||||
EINA_LOG_INFO("Max niceness reached; keeping max (19)"); |
||||
prio = 19; |
||||
} |
||||
|
||||
setpriority(PRIO_PROCESS, 0, prio); |
||||
} |
||||
} |
||||
# endif |
||||
} |
@ -1,53 +0,0 @@ |
||||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2010 ProFUSION embedded systems |
||||
* |
||||
* 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/>.
|
||||
*/ |
||||
|
||||
/**
|
||||
* @defgroup Schedule Schedule |
||||
* @ingroup Eina_Tools_Group |
||||
* |
||||
* @{ |
||||
* |
||||
* TODO: description |
||||
* |
||||
*/ |
||||
|
||||
#ifndef EINA_SCHED_H_ |
||||
#define EINA_SCHED_H_ |
||||
|
||||
#include "eina_types.h" |
||||
|
||||
|
||||
/**
|
||||
* @brief Lowers the priority of the current thread. |
||||
* |
||||
* @details It's used by worker threads so that they use up the background CPU and do not stall |
||||
* the main thread. If the current thread is running with real-time priority, we |
||||
* decrease our priority by @c RTNICENESS. This is done in a portable way. |
||||
* |
||||
* Otherwise, (we are running with the SCHED_OTHER policy) there's no portable way to |
||||
* set the nice level on the current thread. In Linux, it does work and it's the |
||||
* only one that is implemented as of now. In this case, the nice level is |
||||
* incremented on this thread by @c NICENESS. |
||||
*/ |
||||
EINA_API void eina_sched_prio_drop(void); |
||||
|
||||
/**
|
||||
* @} |
||||
*/ |
||||
|
||||
#endif /* EINA_SCHED_H_ */ |
@ -0,0 +1,368 @@ |
||||
/* 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" |
||||
#include "eina_lock.h" /* it will include pthread.h with proper flags */ |
||||
#include "eina_thread.h" |
||||
#include "eina_cpu.h" |
||||
|
||||
/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ |
||||
#include "eina_safety_checks.h" |
||||
|
||||
#include "eina_debug_private.h" |
||||
|
||||
#include <pthread.h> |
||||
#include <errno.h> |
||||
#ifndef _WIN32 |
||||
# include <signal.h> |
||||
#endif |
||||
# include <string.h> |
||||
|
||||
#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME) |
||||
#ifndef __linux__ |
||||
#include <pthread_np.h> |
||||
#define cpu_set_t cpuset_t |
||||
#endif |
||||
#endif |
||||
|
||||
#ifdef __linux__ |
||||
# include <sched.h> |
||||
# include <sys/time.h> |
||||
# include <sys/resource.h> |
||||
#endif |
||||
|
||||
#include "eina_log.h" |
||||
|
||||
#define RTNICENESS 1 |
||||
#define NICENESS 5 |
||||
|
||||
|
||||
static inline void * |
||||
_eina_thread_join(Eina_Thread t) |
||||
{ |
||||
void *ret = NULL; |
||||
int err = pthread_join((pthread_t)t, &ret); |
||||
|
||||
if (err == 0) return ret; |
||||
return NULL; |
||||
} |
||||
|
||||
static inline Eina_Bool |
||||
_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data) |
||||
{ |
||||
int err; |
||||
pthread_attr_t attr; |
||||
sigset_t oldset, newset; |
||||
|
||||
if (pthread_attr_init(&attr) != 0) |
||||
{ |
||||
return EINA_FALSE; |
||||
} |
||||
if (affinity >= 0) |
||||
{ |
||||
#ifdef EINA_HAVE_PTHREAD_AFFINITY |
||||
cpu_set_t cpu; |
||||
|
||||
CPU_ZERO(&cpu); |
||||
CPU_SET(affinity, &cpu); |
||||
pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu); |
||||
#endif |
||||
} |
||||
|
||||
/* setup initial locks */ |
||||
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); |
||||
# ifdef SIGPWR |
||||
sigaddset(&newset, SIGPWR); |
||||
# endif |
||||
pthread_sigmask(SIG_BLOCK, &newset, &oldset); |
||||
err = pthread_create((pthread_t *)t, &attr, func, data); |
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL); |
||||
pthread_attr_destroy(&attr); |
||||
|
||||
if (err == 0) return EINA_TRUE; |
||||
|
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
static inline Eina_Bool |
||||
_eina_thread_equal(Eina_Thread t1, Eina_Thread t2) |
||||
{ |
||||
return pthread_equal((pthread_t)t1, (pthread_t)t2); |
||||
} |
||||
|
||||
static inline Eina_Thread |
||||
_eina_thread_self(void) |
||||
{ |
||||
return (Eina_Thread)pthread_self(); |
||||
} |
||||
|
||||
|
||||
typedef struct _Eina_Thread_Call Eina_Thread_Call; |
||||
struct _Eina_Thread_Call |
||||
{ |
||||
Eina_Thread_Cb func; |
||||
const void *data; |
||||
|
||||
Eina_Thread_Priority prio; |
||||
int affinity; |
||||
}; |
||||
|
||||
static void * |
||||
_eina_internal_call(void *context) |
||||
{ |
||||
Eina_Thread_Call *c = context; |
||||
void *r; |
||||
pthread_t self; |
||||
|
||||
// Default this thread to not cancellable as per Eina documentation
|
||||
eina_thread_cancellable_set(EINA_FALSE, NULL); |
||||
|
||||
EINA_THREAD_CLEANUP_PUSH(free, c); |
||||
|
||||
self = pthread_self(); |
||||
|
||||
if (c->prio == EINA_THREAD_IDLE) |
||||
{ |
||||
struct sched_param params; |
||||
int min; |
||||
#ifdef SCHED_IDLE |
||||
int pol = SCHED_IDLE; |
||||
#else |
||||
int pol; |
||||
pthread_getschedparam(self, &pol, ¶ms); |
||||
#endif |
||||
min = sched_get_priority_min(pol); |
||||
params.sched_priority = min; |
||||
pthread_setschedparam(self, pol, ¶ms); |
||||
} |
||||
else if (c->prio == EINA_THREAD_BACKGROUND) |
||||
{ |
||||
struct sched_param params; |
||||
int min, max; |
||||
#ifdef SCHED_BATCH |
||||
int pol = SCHED_BATCH; |
||||
#else |
||||
int pol; |
||||
pthread_getschedparam(self, &pol, ¶ms); |
||||
#endif |
||||
min = sched_get_priority_min(pol); |
||||
max = sched_get_priority_max(pol); |
||||
params.sched_priority = (max - min) / 2; |
||||
pthread_setschedparam(self, pol, ¶ms); |
||||
} |
||||
// do nothing for normal
|
||||
// else if (c->prio == EINA_THREAD_NORMAL)
|
||||
// {
|
||||
// }
|
||||
else if (c->prio == EINA_THREAD_URGENT) |
||||
{ |
||||
struct sched_param params; |
||||
int max, pol; |
||||
|
||||
pthread_getschedparam(self, &pol, ¶ms); |
||||
max = sched_get_priority_max(pol); |
||||
params.sched_priority += 5; |
||||
if (params.sched_priority > max) params.sched_priority = max; |
||||
pthread_setschedparam(self, pol, ¶ms); |
||||
} |
||||
|
||||
_eina_debug_thread_add(&self); |
||||
EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self); |
||||
r = c->func((void*) c->data, eina_thread_self()); |
||||
EINA_THREAD_CLEANUP_POP(EINA_TRUE); |
||||
EINA_THREAD_CLEANUP_POP(EINA_TRUE); |
||||
|
||||
return r; |
||||
} |
||||
|
||||
EINA_API Eina_Thread |
||||
eina_thread_self(void) |
||||
{ |
||||
return _eina_thread_self(); |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_equal(Eina_Thread t1, Eina_Thread t2) |
||||
{ |
||||
return !!_eina_thread_equal(t1, t2); |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_create(Eina_Thread *t, |
||||
Eina_Thread_Priority prio, int affinity, |
||||
Eina_Thread_Cb func, const void *data) |
||||
{ |
||||
Eina_Thread_Call *c; |
||||
|
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE); |
||||
EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE); |
||||
|
||||
c = malloc(sizeof (Eina_Thread_Call)); |
||||
if (!c) return EINA_FALSE; |
||||
|
||||
c->func = func; |
||||
c->data = data; |
||||
c->prio = prio; |
||||
c->affinity = affinity; |
||||
|
||||
// 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().
|
||||
if (_eina_thread_create(t, affinity, _eina_internal_call, c)) |
||||
return EINA_TRUE; |
||||
|
||||
free(c); |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
EINA_API void * |
||||
eina_thread_join(Eina_Thread t) |
||||
{ |
||||
return _eina_thread_join(t); |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_name_set(Eina_Thread t, const char *name) |
||||
{ |
||||
#ifdef EINA_HAVE_PTHREAD_SETNAME |
||||
char buf[16]; |
||||
if (name) |
||||
{ |
||||
strncpy(buf, name, 15); |
||||
buf[15] = 0; |
||||
} |
||||
else buf[0] = 0; |
||||
#ifndef __linux__ |
||||
pthread_set_name_np((pthread_t)t, buf); |
||||
return EINA_TRUE; |
||||
#else |
||||
if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE; |
||||
#endif |
||||
#else |
||||
(void)t; |
||||
(void)name; |
||||
#endif |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_cancel(Eina_Thread t) |
||||
{ |
||||
if (!t) return EINA_FALSE; |
||||
return pthread_cancel((pthread_t)t) == 0; |
||||
} |
||||
|
||||
EINA_API 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; |
||||
} |
||||
|
||||
EINA_API void |
||||
eina_thread_cancel_checkpoint(void) |
||||
{ |
||||
pthread_testcancel(); |
||||
} |
||||
|
||||
EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED; |
||||
|
||||
EINA_API void |
||||
eina_sched_prio_drop(void) |
||||
{ |
||||
struct sched_param param; |
||||
int pol, ret; |
||||
pthread_t pthread_id; |
||||
|
||||
pthread_id = pthread_self(); |
||||
ret = pthread_getschedparam(pthread_id, &pol, ¶m); |
||||
if (ret) |
||||
{ |
||||
EINA_LOG_ERR("Unable to query sched parameters"); |
||||
return; |
||||
} |
||||
|
||||
if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO)) |
||||
{ |
||||
param.sched_priority -= RTNICENESS; |
||||
|
||||
/* We don't change the policy */ |
||||
if (param.sched_priority < 1) |
||||
{ |
||||
EINA_LOG_INFO("RT prio < 1, setting to 1 instead"); |
||||
param.sched_priority = 1; |
||||
} |
||||
|
||||
pthread_setschedparam(pthread_id, pol, ¶m); |
||||
} |
||||
# ifdef __linux__ |
||||
else |
||||
{ |
||||
int prio; |
||||
errno = 0; |
||||
prio = getpriority(PRIO_PROCESS, 0); |
||||
if (errno == 0) |
||||
{ |
||||
prio += NICENESS; |
||||
if (prio > 19) |
||||
{ |
||||
EINA_LOG_INFO("Max niceness reached; keeping max (19)"); |
||||
prio = 19; |
||||
} |
||||
|
||||
setpriority(PRIO_PROCESS, 0, prio); |
||||
} |
||||
} |
||||
# endif |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_init(void) |
||||
{ |
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_shutdown(void) |
||||
{ |
||||
return EINA_TRUE; |
||||
} |
@ -0,0 +1,397 @@ |
||||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2020 Expertise Solutions Cons em Inf |
||||
* |
||||
* 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 "eina_types.h" |
||||
#include "eina_config.h" |
||||
#include "eina_array.h" |
||||
#include "eina_thread.h" |
||||
#include "eina_main.h" |
||||
#include "eina_debug_private.h" |
||||
#include "eina_log.h" |
||||
|
||||
#include <assert.h> |
||||
#include <evil_private.h> |
||||
#include <process.h> |
||||
|
||||
#define RTNICENESS 1 |
||||
#define NICENESS 5 |
||||
|
||||
/*
|
||||
* The underlying type of Eina_Thread |
||||
*/ |
||||
struct Thread |
||||
{ |
||||
CRITICAL_SECTION cancel_lock; /* mutex to protect the cancel handle */ |
||||
char name[16]; /* the thread name */ |
||||
HANDLE handle; /* thread handle */ |
||||
void *data; /* on entry, the thread function argument, on exit, the return value */ |
||||
Eina_Thread_Cb fn; /* the thread function */ |
||||
Eina_Array *cleanup_fn; |
||||
Eina_Array *cleanup_arg; |
||||
unsigned id; /* thread id */ |
||||
Eina_Bool free_on_exit; /* free the structure when thread exit */ |
||||
volatile Eina_Bool cancel; /* the cancel event handle */ |
||||
volatile Eina_Bool cancellable; /* is cancel enabled? */ |
||||
}; |
||||
|
||||
typedef struct Thread Thread_t; |
||||
|
||||
/*
|
||||
* This TLS stores the Eina_Thread for the current thread |
||||
*/ |
||||
static DWORD tls_thread_self = 0; |
||||
|
||||
static Thread_t main_thread = { 0 }; |
||||
|
||||
/*
|
||||
* If we alloc'ed the Thread_t in eina_thread_self, we set |
||||
* free_on_exit flag to true, we then free it here |
||||
*/ |
||||
void |
||||
free_thread(void) |
||||
{ |
||||
Thread_t *t = TlsGetValue(tls_thread_self); |
||||
if (t && t->free_on_exit) |
||||
{ |
||||
if (t) eina_array_free(t->cleanup_fn); |
||||
if (t) eina_array_free(t->cleanup_arg); |
||||
free(t); |
||||
} |
||||
} |
||||
|
||||
static unsigned |
||||
thread_fn(void *arg) |
||||
{ |
||||
Thread_t *thr = arg; |
||||
TlsSetValue(tls_thread_self, thr); |
||||
_eina_debug_thread_add(&thr); |
||||
EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &thr); |
||||
thr->data = thr->fn(thr->data, (Eina_Thread) thr); |
||||
EINA_THREAD_CLEANUP_POP(EINA_TRUE); |
||||
return 0; |
||||
} |
||||
|
||||
EINA_API Eina_Thread |
||||
eina_thread_self(void) |
||||
{ |
||||
Thread_t *self = TlsGetValue(tls_thread_self); |
||||
/*
|
||||
* If self is NULL this means |
||||
* 1) This function was called before eina_thread_init |
||||
* 2) This thread wasn't created by eina_thread_create |
||||
* |
||||
* In either case we alloc a new Thread struct and return |
||||
* it. |
||||
*/ |
||||
if (!self) |
||||
{ |
||||
self = calloc(1, sizeof(*self)); |
||||
self->handle = GetCurrentThread(); |
||||
self->id = GetCurrentThreadId(); |
||||
self->free_on_exit = EINA_TRUE; |
||||
self->cleanup_fn = eina_array_new(4); |
||||
self->cleanup_arg = eina_array_new(4); |
||||
if (tls_thread_self) |
||||
TlsSetValue(tls_thread_self, self); |
||||
} |
||||
return (Eina_Thread) self; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_equal(Eina_Thread t1, Eina_Thread t2) |
||||
{ |
||||
return ((Thread_t *) t1)->id == ((Thread_t *) t2)->id; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_create(Eina_Thread *t, Eina_Thread_Priority prio, |
||||
int affinity, Eina_Thread_Cb func, const void *data) |
||||
{ |
||||
Thread_t *thr = calloc(1, sizeof(Thread_t)); |
||||
if (!thr) |
||||
return EINA_FALSE; |
||||
|
||||
thr->data = (void *) data; |
||||
thr->fn = func; |
||||
|
||||
thr->handle = (HANDLE) _beginthreadex(NULL, 0, thread_fn, thr, CREATE_SUSPENDED, &thr->id); |
||||
if (!thr->handle) |
||||
goto fail; |
||||
|
||||
int priority; |
||||
switch (prio) |
||||
{ |
||||
case EINA_THREAD_URGENT: |
||||
priority = THREAD_PRIORITY_HIGHEST; |
||||
break; |
||||
case EINA_THREAD_BACKGROUND: |
||||
priority = THREAD_PRIORITY_BELOW_NORMAL; |
||||
break; |
||||
case EINA_THREAD_IDLE: |
||||
priority = THREAD_PRIORITY_IDLE; |
||||
break; |
||||
default: |
||||
priority = THREAD_PRIORITY_NORMAL; |
||||
} |
||||
|
||||
if (!SetThreadPriority(thr->handle, priority)) |
||||
goto fail; |
||||
|
||||
if ((affinity >= 0) && (!SetThreadAffinityMask(thr->handle, 1 << affinity))) |
||||
goto fail; |
||||
|
||||
thr->id = GetThreadId(thr->handle); |
||||
if (!thr->id) |
||||
goto fail; |
||||
|
||||
thr->cleanup_fn = eina_array_new(4); |
||||
thr->cleanup_arg = eina_array_new(4); |
||||
if ((!thr->cleanup_fn) || (!thr->cleanup_arg)) |
||||
goto fail; |
||||
|
||||
InitializeCriticalSection(&thr->cancel_lock); |
||||
|
||||
if (!ResumeThread(thr->handle)) |
||||
goto cs_fail; |
||||
|
||||
GetModuleFileNameA(NULL, thr->name, sizeof(thr->name)); |
||||
*t = (Eina_Thread) thr; |
||||
return EINA_TRUE; |
||||
|
||||
cs_fail: |
||||
DeleteCriticalSection(&thr->cancel_lock); |
||||
fail: |
||||
if (thr) |
||||
{ |
||||
if (thr->handle) CloseHandle(thr->handle); |
||||
if (thr->cleanup_fn) eina_array_free(thr->cleanup_fn); |
||||
if (thr->cleanup_arg) eina_array_free(thr->cleanup_arg); |
||||
free(thr); |
||||
} |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
EINA_API void * |
||||
eina_thread_join(Eina_Thread t) |
||||
{ |
||||
void *data; |
||||
Thread_t *thr = (Thread_t *) t; |
||||
|
||||
if (WAIT_OBJECT_0 == WaitForSingleObject(thr->handle, INFINITE)) |
||||
data = thr->data; |
||||
else |
||||
data = NULL; |
||||
|
||||
DeleteCriticalSection(&thr->cancel_lock); |
||||
CloseHandle(thr->handle); |
||||
eina_array_free(thr->cleanup_fn); |
||||
eina_array_free(thr->cleanup_arg); |
||||
free(thr); |
||||
|
||||
return data; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_name_set(Eina_Thread t, const char *name) |
||||
{ |
||||
Thread_t *thr = (Thread_t *) t; |
||||
strncpy(thr->name, name, sizeof(thr->name)); |
||||
thr->name[sizeof(thr->name)-1] = '\0'; |
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_cancel(Eina_Thread t) |
||||
{ |
||||
Eina_Bool ret = EINA_FALSE; |
||||
Thread_t *thr = (Thread_t *) t; |
||||
|
||||
if (thr) |
||||
{ |
||||
EnterCriticalSection(&thr->cancel_lock); |
||||
if (thr->cancellable) |
||||
{ |
||||
thr->cancel = EINA_TRUE; |
||||
ret = EINA_TRUE; |
||||
} |
||||
LeaveCriticalSection(&thr->cancel_lock); |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable) |
||||
{ |
||||
Thread_t *t = (Thread_t *) eina_thread_self(); |
||||
|
||||
EnterCriticalSection(&t->cancel_lock); |
||||
if (was_cancellable) *was_cancellable = t->cancellable; |
||||
t->cancellable = cancellable; |
||||
LeaveCriticalSection(&t->cancel_lock); |
||||
|
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
EINA_API void |
||||
eina_thread_cancel_checkpoint(void) |
||||
{ |
||||
Eina_Bool cancel; |
||||
Thread_t *t = (Thread_t *) eina_thread_self(); |
||||
|
||||
EnterCriticalSection(&t->cancel_lock); |
||||
cancel = t->cancellable && t->cancel; |
||||
LeaveCriticalSection(&t->cancel_lock); |
||||
|
||||
if (cancel) |
||||
{ |
||||
t->data = (void *) EINA_THREAD_JOIN_CANCELED; |
||||
while (eina_array_count(t->cleanup_fn)) |
||||
{ |
||||
Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn); |
||||
void *arg = eina_array_pop(t->cleanup_arg); |
||||
|
||||
if (fn) |
||||
fn(arg); |
||||
} |
||||
|
||||
ExitThread(0); |
||||
} |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data) |
||||
{ |
||||
Thread_t *t = TlsGetValue(tls_thread_self); |
||||
assert(t); |
||||
|
||||
if (!eina_array_push(t->cleanup_fn, fn)) |
||||
return EINA_FALSE; |
||||
|
||||
if (!eina_array_push(t->cleanup_arg, data)) |
||||
{ |
||||
eina_array_pop(t->cleanup_fn); |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
EINA_API void |
||||
eina_thread_cleanup_pop(int execute) |
||||
{ |
||||
Thread_t *t = TlsGetValue(tls_thread_self); |
||||
assert(t); |
||||
|
||||
if (eina_array_count(t->cleanup_fn)) |
||||
{ |
||||
Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn); |
||||
void *arg = eina_array_pop(t->cleanup_arg); |
||||
|
||||
if (execute && fn) |
||||
fn(arg); |
||||
} |
||||
} |
||||
|
||||
EINA_API const void *EINA_THREAD_JOIN_CANCELED = (void *) -1L; |
||||
|
||||
void |
||||
eina_sched_prio_drop(void) |
||||
{ |
||||
Thread_t *thread; |
||||
int sched_priority; |
||||
|
||||
thread = (Thread_t *) eina_thread_self(); |
||||
|
||||
sched_priority = GetThreadPriority(thread->handle); |
||||
|
||||
if (EINA_UNLIKELY(sched_priority == THREAD_PRIORITY_TIME_CRITICAL)) |
||||
{ |
||||
sched_priority -= RTNICENESS; |
||||
|
||||
/* We don't change the policy */ |
||||
if (sched_priority < 1) |
||||
{ |
||||
EINA_LOG_INFO("RT prio < 1, setting to 1 instead"); |
||||
sched_priority = 1; |
||||
} |
||||
if (!SetThreadPriority(thread->handle, sched_priority)) |
||||
{ |
||||
EINA_LOG_ERR("Unable to query sched parameters"); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
sched_priority += NICENESS; |
||||
|
||||
/* We don't change the policy */ |
||||
if (sched_priority > THREAD_PRIORITY_TIME_CRITICAL) |
||||
{ |
||||
EINA_LOG_INFO("Max niceness reached; keeping max (THREAD_PRIORITY_TIME_CRITICAL)"); |
||||
sched_priority = THREAD_PRIORITY_TIME_CRITICAL; |
||||
} |
||||
if (!SetThreadPriority(thread->handle, sched_priority)) |
||||
{ |
||||
EINA_LOG_ERR("Unable to query sched parameters"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_init(void) |
||||
{ |
||||
if (!eina_main_loop_is()) |
||||
return EINA_FALSE; |
||||
|
||||
tls_thread_self = TlsAlloc(); |
||||
if (TLS_OUT_OF_INDEXES == tls_thread_self) |
||||
return EINA_FALSE; |
||||
|
||||
if (!TlsSetValue(tls_thread_self, &main_thread)) |
||||
{ |
||||
assert(0); |
||||
TlsFree(tls_thread_self); |
||||
return EINA_FALSE; |
||||
} |
||||
|
||||
main_thread.cancellable = EINA_FALSE; |
||||
main_thread.cancel = EINA_FALSE; |
||||
main_thread.handle = GetCurrentThread(); |
||||
main_thread.id = GetCurrentThreadId(); |
||||
|
||||
InitializeCriticalSection(&main_thread.cancel_lock); |
||||
main_thread.cleanup_fn = eina_array_new(2); |
||||
main_thread.cleanup_arg = eina_array_new(2); |
||||
|
||||
GetModuleFileNameA(NULL, main_thread.name, sizeof(main_thread.name)/sizeof(main_thread.name[0])); |
||||
|
||||
return EINA_TRUE; |
||||
} |
||||
|
||||
EINA_API Eina_Bool |
||||
eina_thread_shutdown(void) |
||||
{ |
||||
DeleteCriticalSection(&main_thread.cancel_lock); |
||||
eina_array_free(main_thread.cleanup_fn); |
||||
eina_array_free(main_thread.cleanup_arg); |
||||
TlsFree(tls_thread_self); |
||||
return EINA_TRUE; |
||||
} |
@ -0,0 +1,18 @@ |
||||
#ifdef HAVE_CONFIG_H |
||||
# include "config.h" |
||||
#endif |
||||
|
||||
#include "eina_config.h" |
||||
#include "eina_types.h" |
||||
#include <evil_private.h> |
||||
|
||||
void free_thread(void); |
||||
|
||||
BOOL WINAPI |
||||
DllMain(HINSTANCE inst EINA_UNUSED, WORD reason, PVOID reserved EINA_UNUSED) |
||||
{ |
||||
if (DLL_THREAD_DETACH == reason) |
||||
free_thread(); |
||||
|
||||
return TRUE; |
||||
} |
@ -0,0 +1,124 @@ |
||||
/* EINA - EFL data type library
|
||||
* Copyright (C) 2020 Expertise Solutions Cons em Inf |
||||
* |
||||
* 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/>.
|
||||
*/ |
||||
|
||||
#include <check.h> |
||||
#ifdef HAVE_CONFIG_H |
||||
# include "config.h" |
||||
#endif |
||||
|
||||
#ifdef HAVE_UNISTD |
||||
# include <unistd.h> |
||||
#endif |
||||
|
||||
#ifdef _WIN32 |
||||
# include <evil_private.h> /* mkdir */ |
||||
#endif |
||||
|
||||
#include <Eina.h> |
||||
#include "eina_suite.h" |
||||
|
||||
static void |
||||
thread_cleanup_fn(void *arg) |
||||
{ |
||||
*(int *) arg = 1; |
||||
} |
||||
|
||||