forked from enlightenment/efl
Implement eina_thread for native windows
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/D12037
This commit is contained in:
parent
287834b0da
commit
65d528a379
|
@ -53,7 +53,8 @@ header_checks = [
|
||||||
'features.h',
|
'features.h',
|
||||||
'langinfo.h',
|
'langinfo.h',
|
||||||
'locale.h',
|
'locale.h',
|
||||||
'crt_externs.h'
|
'crt_externs.h',
|
||||||
|
'pthread.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
#### The below is logically broken
|
#### The below is logically broken
|
||||||
|
|
|
@ -241,7 +241,6 @@ extern "C" {
|
||||||
#include <eina_benchmark.h>
|
#include <eina_benchmark.h>
|
||||||
#include <eina_convert.h>
|
#include <eina_convert.h>
|
||||||
#include <eina_cpu.h>
|
#include <eina_cpu.h>
|
||||||
#include <eina_sched.h>
|
|
||||||
#include <eina_tiler.h>
|
#include <eina_tiler.h>
|
||||||
#include <eina_hamster.h>
|
#include <eina_hamster.h>
|
||||||
#include <eina_matrixsparse.h>
|
#include <eina_matrixsparse.h>
|
||||||
|
|
|
@ -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_ */
|
|
|
@ -20,284 +20,15 @@
|
||||||
# include "config.h"
|
# include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stddef.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"
|
|
||||||
|
|
||||||
/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
|
|
||||||
#include "eina_safety_checks.h"
|
|
||||||
|
|
||||||
#include "eina_debug_private.h"
|
|
||||||
|
|
||||||
|
#ifdef HAVE_PTHREAD_H
|
||||||
# include <pthread.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
|
#endif
|
||||||
|
|
||||||
static inline void *
|
#include "eina_types.h"
|
||||||
_eina_thread_join(Eina_Thread t)
|
#include "eina_config.h"
|
||||||
{
|
#include "eina_thread.h"
|
||||||
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;
|
|
||||||
#ifndef _WIN32
|
|
||||||
sigset_t oldset, newset;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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 */
|
|
||||||
#ifndef _WIN32
|
|
||||||
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);
|
|
||||||
#endif
|
|
||||||
err = pthread_create((pthread_t *)t, &attr, func, data);
|
|
||||||
#ifndef _WIN32
|
|
||||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
|
||||||
#endif
|
|
||||||
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 void *
|
EINA_API void *
|
||||||
eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data)
|
eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data)
|
||||||
|
@ -312,17 +43,3 @@ eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb clea
|
||||||
eina_thread_cancellable_set(old, NULL);
|
eina_thread_cancellable_set(old, NULL);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
|
|
||||||
|
|
||||||
Eina_Bool
|
|
||||||
eina_thread_init(void)
|
|
||||||
{
|
|
||||||
return EINA_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Eina_Bool
|
|
||||||
eina_thread_shutdown(void)
|
|
||||||
{
|
|
||||||
return EINA_TRUE;
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,6 +51,12 @@ typedef uintptr_t Eina_Thread;
|
||||||
*/
|
*/
|
||||||
typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
|
typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Eina_Thread_Cleanup_Cb
|
||||||
|
* Type for the definition of a thread cleanup function
|
||||||
|
*/
|
||||||
|
typedef void (*Eina_Thread_Cleanup_Cb) (void *data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Eina_Thread_Priority
|
* @typedef Eina_Thread_Priority
|
||||||
* Type to enumerate different thread priorities
|
* Type to enumerate different thread priorities
|
||||||
|
@ -248,8 +254,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
|
||||||
*
|
*
|
||||||
* @since 1.19
|
* @since 1.19
|
||||||
*/
|
*/
|
||||||
|
#ifdef _WIN32
|
||||||
|
EINA_API Eina_Bool
|
||||||
|
eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data);
|
||||||
|
|
||||||
|
#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
|
||||||
|
eina_thread_cleanup_push(cleanup, data)
|
||||||
|
#else
|
||||||
#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
|
#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
|
||||||
pthread_cleanup_push(cleanup, data)
|
pthread_cleanup_push(cleanup, data)
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def EINA_THREAD_CLEANUP_POP(exec_cleanup)
|
* @def EINA_THREAD_CLEANUP_POP(exec_cleanup)
|
||||||
|
@ -278,8 +292,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
|
||||||
*
|
*
|
||||||
* @since 1.19
|
* @since 1.19
|
||||||
*/
|
*/
|
||||||
|
#ifdef _WIN32
|
||||||
|
EINA_API void
|
||||||
|
eina_thread_cleanup_pop(int execute);
|
||||||
|
|
||||||
|
#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
|
||||||
|
eina_thread_cleanup_pop(exec_cleanup)
|
||||||
|
#else
|
||||||
#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
|
#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
|
||||||
pthread_cleanup_pop(exec_cleanup)
|
pthread_cleanup_pop(exec_cleanup)
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Eina_Thread_Cancellable_Run_Cb
|
* @typedef Eina_Thread_Cancellable_Run_Cb
|
||||||
|
@ -333,6 +355,20 @@ typedef void *(*Eina_Thread_Cancellable_Run_Cb)(void *data);
|
||||||
*/
|
*/
|
||||||
EINA_API void *eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data);
|
EINA_API void *eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -66,7 +66,6 @@ public_sub_headers = [
|
||||||
'eina_main.h',
|
'eina_main.h',
|
||||||
'eina_cpu.h',
|
'eina_cpu.h',
|
||||||
'eina_inline_cpu.x',
|
'eina_inline_cpu.x',
|
||||||
'eina_sched.h',
|
|
||||||
'eina_tiler.h',
|
'eina_tiler.h',
|
||||||
'eina_hamster.h',
|
'eina_hamster.h',
|
||||||
'eina_matrixsparse.h',
|
'eina_matrixsparse.h',
|
||||||
|
@ -168,7 +167,6 @@ eina_src = files([
|
||||||
'eina_rbtree.c',
|
'eina_rbtree.c',
|
||||||
'eina_rectangle.c',
|
'eina_rectangle.c',
|
||||||
'eina_safety_checks.c',
|
'eina_safety_checks.c',
|
||||||
'eina_sched.c',
|
|
||||||
'eina_share_common.c',
|
'eina_share_common.c',
|
||||||
'eina_simple_xml_parser.c',
|
'eina_simple_xml_parser.c',
|
||||||
'eina_str.c',
|
'eina_str.c',
|
||||||
|
@ -200,9 +198,9 @@ eina_src = files([
|
||||||
]) + eina_mp_sources
|
]) + eina_mp_sources
|
||||||
|
|
||||||
if sys_windows == true
|
if sys_windows == true
|
||||||
eina_src += files('eina_file_win32.c')
|
eina_src += files('eina_file_win32.c', 'eina_win32_dllmain.c', 'eina_thread_win32.c')
|
||||||
else
|
else
|
||||||
eina_src += files('eina_file_posix.c')
|
eina_src += files('eina_file_posix.c', 'eina_thread_posix.c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
eina_config = configuration_data()
|
eina_config = configuration_data()
|
||||||
|
|
|
@ -91,6 +91,7 @@ static const Efl_Test_Case etc[] = {
|
||||||
{ "Vpath", eina_test_vpath },
|
{ "Vpath", eina_test_vpath },
|
||||||
{ "debug", eina_test_debug },
|
{ "debug", eina_test_debug },
|
||||||
{ "Abstract Content", eina_test_abstract_content },
|
{ "Abstract Content", eina_test_abstract_content },
|
||||||
|
{ "thread", eina_test_thread },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -79,5 +79,6 @@ void eina_test_slstr(TCase *tc);
|
||||||
void eina_test_vpath(TCase *tc);
|
void eina_test_vpath(TCase *tc);
|
||||||
void eina_test_debug(TCase *tc);
|
void eina_test_debug(TCase *tc);
|
||||||
void eina_test_abstract_content(TCase *tc);
|
void eina_test_abstract_content(TCase *tc);
|
||||||
|
void eina_test_thread(TCase *tc);
|
||||||
|
|
||||||
#endif /* EINA_SUITE_H_ */
|
#endif /* EINA_SUITE_H_ */
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
thread_fn_execute(void *arg, Eina_Thread t EINA_UNUSED)
|
||||||
|
{
|
||||||
|
EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
|
||||||
|
EINA_THREAD_CLEANUP_POP(1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
thread_fn_skip(void *arg, Eina_Thread t EINA_UNUSED)
|
||||||
|
{
|
||||||
|
EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
|
||||||
|
EINA_THREAD_CLEANUP_POP(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
thread_fn_cancel(void *arg, Eina_Thread t EINA_UNUSED)
|
||||||
|
{
|
||||||
|
Eina_Condition *cond = arg;
|
||||||
|
|
||||||
|
ck_assert(eina_thread_cancellable_set(EINA_TRUE, NULL));
|
||||||
|
ck_assert(eina_condition_signal(cond));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
eina_thread_cancel_checkpoint();
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(100);
|
||||||
|
#else
|
||||||
|
usleep(100 * 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EFL_START_TEST(eina_thread_test_cleanup_execute)
|
||||||
|
{
|
||||||
|
Eina_Thread t;
|
||||||
|
int flag = 0;
|
||||||
|
ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_execute, &flag));
|
||||||
|
eina_thread_join(t);
|
||||||
|
ck_assert_uint_eq(flag, 1);
|
||||||
|
}
|
||||||
|
EFL_END_TEST
|
||||||
|
|
||||||
|
EFL_START_TEST(eina_thread_test_cleanup_skip)
|
||||||
|
{
|
||||||
|
Eina_Thread t;
|
||||||
|
int flag = 2;
|
||||||
|
ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_skip, &flag));
|
||||||
|
eina_thread_join(t);
|
||||||
|
ck_assert_uint_eq(flag, 2);
|
||||||
|
}
|
||||||
|
EFL_END_TEST
|
||||||
|
|
||||||
|
EFL_START_TEST(eina_thread_test_cancel)
|
||||||
|
{
|
||||||
|
Eina_Thread t;
|
||||||
|
Eina_Lock mutex;
|
||||||
|
Eina_Condition cond;
|
||||||
|
|
||||||
|
ck_assert(eina_lock_new(&mutex));
|
||||||
|
ck_assert(eina_condition_new(&cond, &mutex));
|
||||||
|
|
||||||
|
ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_cancel, &cond));
|
||||||
|
ck_assert(eina_lock_take(&mutex));
|
||||||
|
ck_assert(eina_condition_wait(&cond));
|
||||||
|
ck_assert(eina_thread_cancel(t));
|
||||||
|
ck_assert_ptr_eq(eina_thread_join(t), EINA_THREAD_JOIN_CANCELED);
|
||||||
|
|
||||||
|
eina_condition_free(&cond);
|
||||||
|
eina_lock_free(&mutex);
|
||||||
|
}
|
||||||
|
EFL_END_TEST
|
||||||
|
|
||||||
|
void
|
||||||
|
eina_test_thread(TCase *tc)
|
||||||
|
{
|
||||||
|
tcase_add_test(tc, eina_thread_test_cleanup_skip);
|
||||||
|
tcase_add_test(tc, eina_thread_test_cleanup_execute);
|
||||||
|
tcase_add_test(tc, eina_thread_test_cancel);
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ eina_test_src = files(
|
||||||
'eina_test_slstr.c',
|
'eina_test_slstr.c',
|
||||||
'eina_test_vpath.c',
|
'eina_test_vpath.c',
|
||||||
'eina_test_abstract_content.c',
|
'eina_test_abstract_content.c',
|
||||||
|
'eina_test_thread.c',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue