346 lines
8.5 KiB
C
346 lines
8.5 KiB
C
#include "evas_common_private.h"
|
|
|
|
#include "Ecore.h"
|
|
#include <assert.h>
|
|
|
|
static Eina_Thread evas_thread_worker;
|
|
static Eina_Condition evas_thread_queue_condition;
|
|
static Eina_Lock evas_thread_queue_lock;
|
|
static Eina_Bool evas_thread_queue_ready = EINA_FALSE;
|
|
static Eina_Inarray evas_thread_queue;
|
|
static Evas_Thread_Command *evas_thread_queue_cache = NULL;
|
|
static unsigned int evas_thread_queue_cache_max = 0;
|
|
|
|
static Eina_Lock evas_thread_exited_lock;
|
|
static int evas_thread_exited = 0;
|
|
static Eina_Bool exit_thread = EINA_FALSE;
|
|
static int init_count = 0;
|
|
|
|
#define SHUTDOWN_TIMEOUT_RESET (0)
|
|
#define SHUTDOWN_TIMEOUT_CHECK (1)
|
|
#define SHUTDOWN_TIMEOUT (3000)
|
|
|
|
struct fence_stuff {
|
|
Eina_Lock lock;
|
|
Eina_Condition cond;
|
|
};
|
|
|
|
static void
|
|
_evas_thread_queue_fence(void *data)
|
|
{
|
|
struct fence_stuff *f;
|
|
|
|
f = data;
|
|
|
|
eina_lock_take(&f->lock);
|
|
eina_condition_signal(&f->cond);
|
|
eina_lock_release(&f->lock);
|
|
}
|
|
|
|
EAPI void
|
|
evas_thread_queue_wait(void)
|
|
{
|
|
struct fence_stuff f;
|
|
|
|
/* No shortcuts here - if the thread queue looks empty it could just mean
|
|
* it's being processed. Need a round trip.
|
|
*/
|
|
if (!eina_lock_new(&f.lock)) return;
|
|
if (!eina_condition_new(&f.cond, &f.lock))
|
|
{
|
|
eina_lock_free(&f.lock);
|
|
return;
|
|
}
|
|
|
|
eina_lock_take(&f.lock);
|
|
evas_thread_queue_flush(_evas_thread_queue_fence, &f);
|
|
eina_condition_wait(&f.cond);
|
|
eina_lock_release(&f.lock);
|
|
|
|
eina_lock_free(&f.lock);
|
|
eina_condition_free(&f.cond);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_shutdown_timeout(double *time, int mode, int timeout_ms)
|
|
{
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
if ( mode == SHUTDOWN_TIMEOUT_RESET )
|
|
*time = (tv.tv_sec + tv.tv_usec / 1000000.0) * 1000.0;
|
|
return ((tv.tv_sec + tv.tv_usec / 1000000.0) * 1000.0 - (*time)) > timeout_ms ;
|
|
}
|
|
|
|
|
|
static void
|
|
evas_thread_queue_append(Evas_Thread_Command_Cb cb, void *data, Eina_Bool do_flush)
|
|
{
|
|
Evas_Thread_Command *cmd;
|
|
|
|
eina_lock_take(&evas_thread_queue_lock);
|
|
|
|
cmd = eina_inarray_grow(&evas_thread_queue, 1);
|
|
if (cmd)
|
|
{
|
|
cmd->cb = cb;
|
|
cmd->data = data;
|
|
}
|
|
else
|
|
{
|
|
ERR("Out of memory allocating thread command.");
|
|
goto out;
|
|
}
|
|
|
|
if (do_flush)
|
|
{
|
|
evas_thread_queue_ready = EINA_TRUE;
|
|
eina_condition_signal(&evas_thread_queue_condition);
|
|
}
|
|
|
|
out:
|
|
eina_lock_release(&evas_thread_queue_lock);
|
|
}
|
|
|
|
EAPI void
|
|
evas_thread_cmd_enqueue(Evas_Thread_Command_Cb cb, void *data)
|
|
{
|
|
evas_thread_queue_append(cb, data, EINA_FALSE);
|
|
}
|
|
|
|
EAPI void
|
|
evas_thread_queue_flush(Evas_Thread_Command_Cb cb, void *data)
|
|
{
|
|
evas_thread_queue_append(cb, data, EINA_TRUE);
|
|
}
|
|
|
|
static void*
|
|
evas_thread_worker_func(void *data EINA_UNUSED, Eina_Thread thread EINA_UNUSED)
|
|
{
|
|
eina_thread_name_set(eina_thread_self(), "Eevas-thread-wk");
|
|
while (1)
|
|
{
|
|
Evas_Thread_Command *cmd;
|
|
unsigned int len, max;
|
|
|
|
eina_lock_take(&evas_thread_queue_lock);
|
|
|
|
while (!evas_thread_queue_ready)
|
|
{
|
|
if (exit_thread)
|
|
{
|
|
eina_lock_release(&evas_thread_queue_lock);
|
|
goto out;
|
|
}
|
|
eina_condition_timedwait(&evas_thread_queue_condition, SHUTDOWN_TIMEOUT * 0.75);
|
|
}
|
|
|
|
if (!eina_inarray_count(&evas_thread_queue))
|
|
{
|
|
ERR("Signaled to find an empty queue. BUG!");
|
|
evas_thread_queue_ready = EINA_FALSE;
|
|
eina_lock_release(&evas_thread_queue_lock);
|
|
continue;
|
|
}
|
|
|
|
cmd = evas_thread_queue.members;
|
|
evas_thread_queue.members = evas_thread_queue_cache;
|
|
evas_thread_queue_cache = cmd;
|
|
|
|
max = evas_thread_queue.max;
|
|
evas_thread_queue.max = evas_thread_queue_cache_max;
|
|
evas_thread_queue_cache_max = max;
|
|
|
|
len = evas_thread_queue.len;
|
|
evas_thread_queue.len = 0;
|
|
|
|
evas_thread_queue_ready = EINA_FALSE;
|
|
|
|
eina_lock_release(&evas_thread_queue_lock);
|
|
|
|
DBG("Evas render thread command queue length: %u", len);
|
|
|
|
eina_evlog("+thread", NULL, 0.0, NULL);
|
|
while (len)
|
|
{
|
|
assert(cmd->cb);
|
|
|
|
eina_evlog("+thread_do", cmd->data, 0.0, NULL);
|
|
cmd->cb(cmd->data);
|
|
eina_evlog("-thread_do", cmd->data, 0.0, NULL);
|
|
|
|
cmd++;
|
|
len--;
|
|
}
|
|
eina_evlog("-thread", NULL, 0.0, NULL);
|
|
}
|
|
|
|
out:
|
|
eina_lock_take(&evas_thread_exited_lock);
|
|
evas_thread_exited = 1;
|
|
eina_lock_release(&evas_thread_exited_lock);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
evas_thread_fork_reset(void *data EINA_UNUSED)
|
|
{
|
|
if (!eina_lock_new(&evas_thread_exited_lock))
|
|
{
|
|
CRI("Could not create exit thread lock (%m)");
|
|
goto on_error;
|
|
}
|
|
if (!eina_lock_new(&evas_thread_queue_lock))
|
|
{
|
|
CRI("Could not create draw thread lock (%m)");
|
|
goto on_error;
|
|
}
|
|
if (!eina_condition_new(&evas_thread_queue_condition, &evas_thread_queue_lock))
|
|
{
|
|
CRI("Could not create draw thread condition (%m)");
|
|
goto on_error;
|
|
}
|
|
|
|
if (!eina_thread_create(&evas_thread_worker, EINA_THREAD_NORMAL, -1,
|
|
evas_thread_worker_func, NULL))
|
|
{
|
|
CRI("Could not recreate draw thread.");
|
|
goto on_error;
|
|
}
|
|
|
|
return ;
|
|
|
|
on_error:
|
|
eina_lock_free(&evas_thread_exited_lock);
|
|
eina_lock_free(&evas_thread_queue_lock);
|
|
eina_condition_free(&evas_thread_queue_condition);
|
|
|
|
evas_thread_worker = 0;
|
|
|
|
free(evas_thread_queue_cache);
|
|
evas_thread_queue_cache = NULL;
|
|
evas_thread_queue_cache_max = 0;
|
|
eina_inarray_flush(&evas_thread_queue);
|
|
|
|
eina_threads_shutdown();
|
|
}
|
|
|
|
int
|
|
evas_thread_init(void)
|
|
{
|
|
if (init_count++)
|
|
return init_count;
|
|
|
|
exit_thread = EINA_FALSE;
|
|
evas_thread_exited = 0;
|
|
|
|
if(!eina_threads_init())
|
|
{
|
|
CRI("Could not init eina threads");
|
|
goto fail_on_eina_thread_init;
|
|
}
|
|
|
|
eina_inarray_step_set(&evas_thread_queue, sizeof (Eina_Inarray), sizeof (Evas_Thread_Command), 128);
|
|
|
|
if (!eina_lock_new(&evas_thread_exited_lock))
|
|
{
|
|
CRI("Could not create exit thread lock (%m)");
|
|
goto fail_on_lock_creation;
|
|
}
|
|
|
|
if (!eina_lock_new(&evas_thread_queue_lock))
|
|
{
|
|
CRI("Could not create draw thread lock (%m)");
|
|
goto fail_on_lock_creation;
|
|
}
|
|
if (!eina_condition_new(&evas_thread_queue_condition, &evas_thread_queue_lock))
|
|
{
|
|
CRI("Could not create draw thread condition (%m)");
|
|
goto fail_on_cond_creation;
|
|
}
|
|
|
|
if (!eina_thread_create(&evas_thread_worker, EINA_THREAD_NORMAL, -1,
|
|
evas_thread_worker_func, NULL))
|
|
{
|
|
CRI("Could not create draw thread (%m)");
|
|
goto fail_on_thread_creation;
|
|
}
|
|
|
|
ecore_fork_reset_callback_add(evas_thread_fork_reset, NULL);
|
|
|
|
return init_count;
|
|
|
|
fail_on_thread_creation:
|
|
evas_thread_worker = 0;
|
|
eina_condition_free(&evas_thread_queue_condition);
|
|
fail_on_cond_creation:
|
|
eina_lock_free(&evas_thread_exited_lock);
|
|
eina_lock_free(&evas_thread_queue_lock);
|
|
fail_on_lock_creation:
|
|
eina_threads_shutdown();
|
|
fail_on_eina_thread_init:
|
|
exit_thread = EINA_TRUE;
|
|
evas_thread_exited = 1;
|
|
return --init_count;
|
|
}
|
|
|
|
int
|
|
evas_thread_shutdown(void)
|
|
{
|
|
double to = 0 ;
|
|
|
|
if (init_count <= 0)
|
|
{
|
|
ERR("Too many calls to shutdown, ignored.");
|
|
return 0;
|
|
}
|
|
|
|
if (--init_count)
|
|
return init_count;
|
|
|
|
if (!evas_thread_worker)
|
|
return init_count;
|
|
|
|
eina_lock_take(&evas_thread_queue_lock);
|
|
|
|
ecore_fork_reset_callback_del(evas_thread_fork_reset, NULL);
|
|
|
|
exit_thread = EINA_TRUE;
|
|
eina_condition_signal(&evas_thread_queue_condition);
|
|
|
|
eina_lock_release(&evas_thread_queue_lock);
|
|
|
|
_shutdown_timeout(&to, SHUTDOWN_TIMEOUT_RESET, SHUTDOWN_TIMEOUT);
|
|
while (1)
|
|
{
|
|
int exited;
|
|
eina_lock_take(&evas_thread_exited_lock);
|
|
exited = evas_thread_exited;
|
|
eina_lock_release(&evas_thread_exited_lock);
|
|
if (exited || (evas_async_events_process() == -1)) break;
|
|
if(_shutdown_timeout(&to, SHUTDOWN_TIMEOUT_CHECK, SHUTDOWN_TIMEOUT))
|
|
{
|
|
CRI("Timeout shutdown thread. Skipping thread_join. Some resources could be leaked");
|
|
goto timeout_shutdown;
|
|
}
|
|
}
|
|
|
|
eina_thread_join(evas_thread_worker);
|
|
timeout_shutdown:
|
|
eina_lock_free(&evas_thread_exited_lock);
|
|
eina_lock_free(&evas_thread_queue_lock);
|
|
eina_condition_free(&evas_thread_queue_condition);
|
|
|
|
evas_thread_worker = 0;
|
|
|
|
free(evas_thread_queue_cache);
|
|
evas_thread_queue_cache = NULL;
|
|
evas_thread_queue_cache_max = 0;
|
|
eina_inarray_flush(&evas_thread_queue);
|
|
|
|
eina_threads_shutdown();
|
|
|
|
return 0;
|
|
}
|