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:
Wander Lairson Costa 2021-04-17 15:31:09 -03:00 committed by Felipe Magno de Almeida
parent 287834b0da
commit 65d528a379
14 changed files with 956 additions and 432 deletions

View File

@ -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

View File

@ -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>

View File

@ -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, &param);
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, &param);
}
# 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
}

View File

@ -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_ */

View File

@ -20,284 +20,15 @@
# include "config.h"
#endif
#include <stdlib.h>
#include <stddef.h>
#ifdef HAVE_PTHREAD_H
# include <pthread.h>
#endif
#include "eina_types.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 <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, &params);
#endif
min = sched_get_priority_min(pol);
params.sched_priority = min;
pthread_setschedparam(self, pol, &params);
}
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, &params);
#endif
min = sched_get_priority_min(pol);
max = sched_get_priority_max(pol);
params.sched_priority = (max - min) / 2;
pthread_setschedparam(self, pol, &params);
}
// 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, &params);
max = sched_get_priority_max(pol);
params.sched_priority += 5;
if (params.sched_priority > max) params.sched_priority = max;
pthread_setschedparam(self, pol, &params);
}
_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_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;
}

View File

@ -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);
/**
* @}
*/

View File

@ -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, &params);
#endif
min = sched_get_priority_min(pol);
params.sched_priority = min;
pthread_setschedparam(self, pol, &params);
}
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, &params);
#endif
min = sched_get_priority_min(pol);
max = sched_get_priority_max(pol);
params.sched_priority = (max - min) / 2;
pthread_setschedparam(self, pol, &params);
}
// 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, &params);
max = sched_get_priority_max(pol);
params.sched_priority += 5;
if (params.sched_priority > max) params.sched_priority = max;
pthread_setschedparam(self, pol, &params);
}
_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, &param);
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, &param);
}
# 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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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',
@ -168,7 +167,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',
@ -200,9 +198,9 @@ 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')
else
eina_src += files('eina_file_posix.c')
eina_src += files('eina_file_posix.c', 'eina_thread_posix.c')
endif
eina_config = configuration_data()

View File

@ -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 }
};

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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',
)