efl/src/lib/evas/common/evas_thread_render.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;
}