forked from enlightenment/efl
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, walac, vtorri, woohyun, lucas 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
886723d47b
commit
6bc4748657
|
@ -53,7 +53,8 @@ header_checks = [
|
|||
'features.h',
|
||||
'langinfo.h',
|
||||
'locale.h',
|
||||
'crt_externs.h'
|
||||
'crt_externs.h',
|
||||
'pthread.h',
|
||||
]
|
||||
|
||||
#### The below is logically broken
|
||||
|
|
|
@ -241,7 +241,6 @@ extern "C" {
|
|||
#include <eina_benchmark.h>
|
||||
#include <eina_convert.h>
|
||||
#include <eina_cpu.h>
|
||||
#include <eina_sched.h>
|
||||
#include <eina_tiler.h>
|
||||
#include <eina_hamster.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"
|
||||
#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_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"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_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
|
||||
|
||||
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;
|
||||
#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();
|
||||
}
|
||||
#include "eina_types.h"
|
||||
#include "eina_config.h"
|
||||
#include "eina_thread.h"
|
||||
|
||||
EINA_API void *
|
||||
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);
|
||||
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 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
|
||||
* Type to enumerate different thread priorities
|
||||
|
@ -248,8 +254,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
|
|||
*
|
||||
* @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) \
|
||||
pthread_cleanup_push(cleanup, data)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def EINA_THREAD_CLEANUP_POP(exec_cleanup)
|
||||
|
@ -278,8 +292,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
|
|||
*
|
||||
* @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) \
|
||||
pthread_cleanup_pop(exec_cleanup)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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_cpu.h',
|
||||
'eina_inline_cpu.x',
|
||||
'eina_sched.h',
|
||||
'eina_tiler.h',
|
||||
'eina_hamster.h',
|
||||
'eina_matrixsparse.h',
|
||||
|
@ -171,7 +170,6 @@ eina_src = files([
|
|||
'eina_rbtree.c',
|
||||
'eina_rectangle.c',
|
||||
'eina_safety_checks.c',
|
||||
'eina_sched.c',
|
||||
'eina_share_common.c',
|
||||
'eina_simple_xml_parser.c',
|
||||
'eina_str.c',
|
||||
|
@ -203,10 +201,10 @@ eina_src = files([
|
|||
]) + eina_mp_sources
|
||||
|
||||
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')
|
||||
eina_src += 'eina_fnmatch.c'
|
||||
else
|
||||
eina_src += files('eina_file.c')
|
||||
eina_src += files('eina_file.c', 'eina_thread_posix.c')
|
||||
endif
|
||||
|
||||
eina_config = configuration_data()
|
||||
|
|
|
@ -91,6 +91,7 @@ static const Efl_Test_Case etc[] = {
|
|||
{ "Vpath", eina_test_vpath },
|
||||
{ "debug", eina_test_debug },
|
||||
{ "Abstract Content", eina_test_abstract_content },
|
||||
{ "thread", eina_test_thread },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -79,5 +79,6 @@ void eina_test_slstr(TCase *tc);
|
|||
void eina_test_vpath(TCase *tc);
|
||||
void eina_test_debug(TCase *tc);
|
||||
void eina_test_abstract_content(TCase *tc);
|
||||
void eina_test_thread(TCase *tc);
|
||||
|
||||
#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_vpath.c',
|
||||
'eina_test_abstract_content.c',
|
||||
'eina_test_thread.c',
|
||||
)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue