2012-10-11 13:39:52 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
2019-06-06 05:28:23 +00:00
|
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2014-11-19 14:42:24 +00:00
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2014-11-19 14:42:24 +00:00
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2012-10-11 13:39:52 +00:00
|
|
|
*/
|
|
|
|
|
2016-04-28 14:33:10 +00:00
|
|
|
#include <qse/si/task.h>
|
2016-04-29 03:55:42 +00:00
|
|
|
#include "../cmn/mem-prv.h"
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#if defined(_WIN64)
|
2012-12-13 13:07:16 +00:00
|
|
|
# if !defined(_WIN32_WINNT)
|
|
|
|
# define _WIN32_WINNT 0x0400
|
|
|
|
# endif
|
2012-10-11 13:39:52 +00:00
|
|
|
# include <windows.h>
|
|
|
|
#else
|
2012-10-15 09:36:39 +00:00
|
|
|
# include <setjmp.h>
|
2012-10-11 13:39:52 +00:00
|
|
|
# if defined(HAVE_UCONTEXT_H)
|
2019-04-23 08:41:05 +00:00
|
|
|
# include <signal.h> /* for old DARWIN/MACOSX */
|
2012-10-11 13:39:52 +00:00
|
|
|
# include <ucontext.h>
|
|
|
|
# endif
|
2012-10-15 09:36:39 +00:00
|
|
|
# if defined(HAVE_MAKECONTEXT) && defined(HAVE_SWAPCONTEXT) && \
|
|
|
|
defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
|
|
|
|
# define USE_UCONTEXT
|
|
|
|
# endif
|
2012-10-11 13:39:52 +00:00
|
|
|
#endif
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
struct qse_task_t
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_mmgr_t* mmgr;
|
|
|
|
|
|
|
|
qse_task_slice_t* dead;
|
|
|
|
qse_task_slice_t* current;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_size_t count;
|
|
|
|
qse_task_slice_t* head;
|
|
|
|
qse_task_slice_t* tail;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#if defined(_WIN64)
|
|
|
|
void* fiber;
|
2012-10-15 09:36:39 +00:00
|
|
|
#elif defined(USE_UCONTEXT)
|
2012-10-11 13:39:52 +00:00
|
|
|
ucontext_t uctx;
|
|
|
|
#else
|
|
|
|
jmp_buf backjmp;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
struct qse_task_slice_t
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
|
|
|
#if defined(_WIN64)
|
|
|
|
void* fiber;
|
2012-10-15 09:36:39 +00:00
|
|
|
#elif defined(USE_UCONTEXT)
|
2012-10-11 13:39:52 +00:00
|
|
|
ucontext_t uctx;
|
|
|
|
#else
|
|
|
|
jmp_buf jmpbuf;
|
|
|
|
#endif
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_t* task;
|
|
|
|
|
|
|
|
int id;
|
|
|
|
qse_task_fnc_t fnc;
|
|
|
|
void* ctx;
|
|
|
|
qse_size_t stksize;
|
|
|
|
|
|
|
|
qse_task_slice_t* prev;
|
|
|
|
qse_task_slice_t* next;
|
2012-10-11 13:39:52 +00:00
|
|
|
};
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
int qse_task_init (qse_task_t* task, qse_mmgr_t* mmgr);
|
|
|
|
void qse_task_fini (qse_task_t* task);
|
|
|
|
|
|
|
|
static void purge_slice (qse_task_t* task, qse_task_slice_t* slice);
|
|
|
|
static void purge_dead_slices (qse_task_t* task);
|
|
|
|
static void purge_current_slice (qse_task_slice_t* slice, qse_task_slice_t* to);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_t* qse_task_open (qse_mmgr_t* mmgr, qse_size_t xtnsize)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
|
|
|
qse_task_t* task;
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
task = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(*task) + xtnsize);
|
2012-10-11 13:39:52 +00:00
|
|
|
if (task == QSE_NULL) return QSE_NULL;
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
if (qse_task_init (task, mmgr) <= -1)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MMGR_FREE (task->mmgr, task);
|
|
|
|
return QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MEMSET (task + 1, 0, xtnsize);
|
2012-10-11 13:39:52 +00:00
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
void qse_task_close (qse_task_t* task)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_fini (task);
|
|
|
|
QSE_MMGR_FREE (task->mmgr, task);
|
|
|
|
}
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
int qse_task_init (qse_task_t* task, qse_mmgr_t* mmgr)
|
|
|
|
{
|
|
|
|
QSE_MEMSET (task, 0, QSE_SIZEOF(*task));
|
|
|
|
task->mmgr = mmgr;
|
|
|
|
return 0;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
void qse_task_fini (qse_task_t* task)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_ASSERT (task->dead == QSE_NULL);
|
|
|
|
QSE_ASSERT (task->current == QSE_NULL);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
if (task->count > 0)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
/* am i closing after boot failure? */
|
|
|
|
qse_task_slice_t* slice, * next;
|
|
|
|
|
|
|
|
slice = task->head;
|
|
|
|
while (slice)
|
|
|
|
{
|
|
|
|
next = slice->next;
|
|
|
|
purge_slice (task, slice);
|
|
|
|
slice = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->count = 0;
|
|
|
|
task->head = QSE_NULL;
|
|
|
|
task->tail = QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_mmgr_t* qse_task_getmmgr (qse_task_t* task)
|
|
|
|
{
|
|
|
|
return task->mmgr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* qse_task_getxtn (qse_task_t* task)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
return (void*)(task + 1);
|
|
|
|
}
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
static qse_task_slice_t* alloc_slice (
|
|
|
|
qse_task_t* task, qse_task_fnc_t fnc,
|
|
|
|
void* ctx, qse_size_t stksize)
|
|
|
|
{
|
|
|
|
qse_task_slice_t* slice;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
slice = QSE_MMGR_ALLOC (task->mmgr, QSE_SIZEOF(*slice) + stksize);
|
|
|
|
if (slice == QSE_NULL) return QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MEMSET (slice, 0, QSE_SIZEOF(*slice));
|
|
|
|
slice->task = task;
|
|
|
|
slice->fnc = fnc;
|
|
|
|
slice->ctx = ctx;
|
|
|
|
slice->stksize = stksize;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
return slice;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
static void link_task (qse_task_t* task, qse_task_slice_t* slice)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
if (task->head)
|
|
|
|
{
|
|
|
|
slice->next = task->head;
|
|
|
|
task->head->prev = slice;
|
|
|
|
task->head = slice;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
task->head = slice;
|
|
|
|
task->tail = slice;
|
|
|
|
}
|
|
|
|
|
|
|
|
task->count++;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
#if defined(_WIN64)
|
|
|
|
# define __CALL_BACK__ __stdcall
|
2012-10-11 13:39:52 +00:00
|
|
|
#else
|
2012-10-15 09:36:39 +00:00
|
|
|
# define __CALL_BACK__
|
|
|
|
#endif
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2013-01-04 16:04:18 +00:00
|
|
|
#if defined(USE_UCONTEXT) && \
|
|
|
|
(QSE_SIZEOF_INT == QSE_SIZEOF_INT32_T) && \
|
|
|
|
(QSE_SIZEOF_VOID_P == (QSE_SIZEOF_INT32_T * 2))
|
|
|
|
|
|
|
|
static void __CALL_BACK__ execute_current_slice (qse_uint32_t ptr1, qse_uint32_t ptr2)
|
|
|
|
{
|
|
|
|
qse_task_slice_t* slice;
|
|
|
|
qse_task_slice_t* to;
|
|
|
|
|
|
|
|
slice = (qse_task_slice_t*)(((qse_uintptr_t)ptr1 << 32) | ptr2);
|
|
|
|
|
|
|
|
QSE_ASSERT (slice->task->current == slice);
|
|
|
|
to = slice->fnc (slice->task, slice, slice->ctx);
|
|
|
|
|
|
|
|
/* the task function is now terminated. we need to
|
|
|
|
* purge it from the slice list and switch to the next
|
|
|
|
* slice. */
|
|
|
|
purge_current_slice (slice, to);
|
|
|
|
QSE_ASSERT (!"must never reach here...");
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
static void __CALL_BACK__ execute_current_slice (qse_task_slice_t* slice)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_slice_t* to;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_ASSERT (slice->task->current == slice);
|
|
|
|
to = slice->fnc (slice->task, slice, slice->ctx);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
/* the task function is now terminated. we need to
|
2012-10-15 09:36:39 +00:00
|
|
|
* purge it from the slice list and switch to the next
|
|
|
|
* slice. */
|
|
|
|
purge_current_slice (slice, to);
|
|
|
|
QSE_ASSERT (!"must never reach here...");
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2013-01-04 16:04:18 +00:00
|
|
|
#endif
|
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
#if defined(__WATCOMC__)
|
|
|
|
/* for watcom, i support i386/32bit only */
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
extern void* prepare_sp (void*);
|
|
|
|
#pragma aux prepare_sp = \
|
|
|
|
"mov dword ptr[eax+4], esp" \
|
|
|
|
"mov esp, eax" \
|
|
|
|
"mov eax, dword ptr[esp+0]" \
|
2012-10-11 13:39:52 +00:00
|
|
|
parm [eax] value [eax] modify [esp]
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
extern void* restore_sp (void);
|
|
|
|
#pragma aux restore_sp = \
|
|
|
|
"mov esp, dword ptr[esp+4]" \
|
|
|
|
modify [esp]
|
|
|
|
|
|
|
|
extern void* get_slice (void);
|
|
|
|
#pragma aux get_slice = \
|
|
|
|
"mov eax, dword ptr[esp+0]" \
|
|
|
|
value [eax]
|
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
#endif
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_slice_t* qse_task_create (
|
|
|
|
qse_task_t* task, qse_task_fnc_t fnc,
|
|
|
|
void* ctx, qse_size_t stksize)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
qse_task_slice_t* slice;
|
|
|
|
register void* tmp;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
stksize = ((stksize + QSE_SIZEOF(void*) - 1) / QSE_SIZEOF(void*)) * QSE_SIZEOF(void*);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#if defined(_WIN64)
|
2012-10-15 09:36:39 +00:00
|
|
|
slice = alloc_slice (task, fnc, ctx, 0);
|
|
|
|
if (slice == QSE_NULL) return QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
slice->fiber = CreateFiberEx (
|
|
|
|
stksize, stksize, FIBER_FLAG_FLOAT_SWITCH,
|
|
|
|
execute_current_slice, slice);
|
|
|
|
if (slice->fiber == QSE_NULL)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MMGR_FREE (task->mmgr, slice);
|
2012-10-11 13:39:52 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
#elif defined(USE_UCONTEXT)
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
slice = alloc_slice (task, fnc, ctx, stksize);
|
|
|
|
if (slice == QSE_NULL) return QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
if (getcontext (&slice->uctx) <= -1)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MMGR_FREE (task->mmgr, slice);
|
2012-10-11 13:39:52 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
}
|
2012-10-15 09:36:39 +00:00
|
|
|
slice->uctx.uc_stack.ss_sp = slice + 1;
|
|
|
|
slice->uctx.uc_stack.ss_size = stksize;
|
|
|
|
slice->uctx.uc_link = QSE_NULL;
|
2013-01-04 16:04:18 +00:00
|
|
|
|
|
|
|
#if (QSE_SIZEOF_INT == QSE_SIZEOF_INT32_T) && \
|
|
|
|
(QSE_SIZEOF_VOID_P == (QSE_SIZEOF_INT32_T * 2))
|
|
|
|
|
|
|
|
/* limited work around for unclear makecontext parameters */
|
|
|
|
makecontext (&slice->uctx, execute_current_slice, 2,
|
|
|
|
(qse_uint32_t)(((qse_uintptr_t)slice) >> 32),
|
|
|
|
(qse_uint32_t)((qse_uintptr_t)slice & 0xFFFFFFFFu));
|
|
|
|
#else
|
2012-10-15 09:36:39 +00:00
|
|
|
makecontext (&slice->uctx, execute_current_slice, 1, slice);
|
2013-01-04 16:04:18 +00:00
|
|
|
#endif
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
if (stksize < QSE_SIZEOF(void*) * 3)
|
|
|
|
stksize = QSE_SIZEOF(void*) * 3; /* for t1 & t2 */
|
|
|
|
|
|
|
|
slice = alloc_slice (task, fnc, ctx, stksize);
|
|
|
|
if (slice == QSE_NULL) return QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
/* setjmp() doesn't allow different stacks for
|
|
|
|
* each execution content. let me use some assembly
|
|
|
|
* to change the stack pointer so that setjmp() remembers
|
|
|
|
* the new stack pointer for longjmp() later.
|
|
|
|
*
|
|
|
|
* this stack is used for the task function when
|
|
|
|
* longjmp() is made. */
|
2012-10-15 09:36:39 +00:00
|
|
|
tmp = ((qse_uint8_t*)(slice + 1)) + stksize - QSE_SIZEOF(void*);
|
|
|
|
|
|
|
|
tmp = (qse_uint8_t*)tmp - QSE_SIZEOF(void*);
|
2014-09-19 14:05:02 +00:00
|
|
|
*(void**)tmp = QSE_NULL; /* t1 */
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
tmp = (qse_uint8_t*)tmp - QSE_SIZEOF(void*);
|
|
|
|
*(void**)tmp = slice; /* t2 */
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#if defined(__WATCOMC__)
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
tmp = prepare_sp (tmp);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#elif defined(__GNUC__) && (defined(__x86_64) || defined(__amd64))
|
|
|
|
|
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"movq %%rsp, 8(%1)\n\t" /* t1 = %rsp */
|
|
|
|
"movq %1, %%rsp\n\t" /* %rsp = tmp */
|
|
|
|
"movq 0(%%rsp), %0\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
: "0"(tmp)
|
2012-10-11 13:39:52 +00:00
|
|
|
: "%rsp", "memory"
|
|
|
|
);
|
|
|
|
|
|
|
|
#elif defined(__GNUC__) && (defined(__i386) || defined(i386))
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"movl %%esp, 4(%1)\n\t" /* t1 = %esp */
|
|
|
|
"movl %1, %%esp\n\t" /* %esp = tmp */
|
|
|
|
"movl 0(%%esp), %0\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
: "0"(tmp)
|
|
|
|
: "%esp", "memory"
|
2012-10-11 13:39:52 +00:00
|
|
|
);
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
#elif defined(__GNUC__) && (defined(__mips) || defined(mips))
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"sw $sp, 4(%1)\n\t" /* t1 = $sp */
|
|
|
|
"move $sp, %1\n\t" /* %sp = tmp */
|
|
|
|
"lw %0, 0($sp)\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
: "0"(tmp)
|
2012-10-11 13:39:52 +00:00
|
|
|
: "$sp", "memory"
|
|
|
|
);
|
|
|
|
|
|
|
|
#elif defined(__GNUC__) && defined(__arm__)
|
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"str sp, [%1, #4]\n\t" /* t1 = sp */
|
|
|
|
"mov sp, %1\n" /* sp = tmp */
|
|
|
|
"ldr %0, [sp, #0]\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
: "0"(tmp)
|
2012-10-11 13:39:52 +00:00
|
|
|
: "sp", "memory"
|
|
|
|
);
|
|
|
|
|
|
|
|
#else
|
2012-10-15 09:36:39 +00:00
|
|
|
/* TODO: support more architecture */
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_MMGR_FREE (task->mmgr, task);
|
2012-10-11 13:39:52 +00:00
|
|
|
return QSE_NULL;
|
|
|
|
|
|
|
|
#endif /* __WATCOMC__ */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* automatic variables like 'task' and 'newsp' exist
|
|
|
|
* in the old stack. i can't access them.
|
|
|
|
* i access some key informaton via the global
|
|
|
|
* variables stored before stack pointer switching.
|
|
|
|
*
|
|
|
|
* this approach makes this function thread-unsafe.
|
|
|
|
*/
|
|
|
|
|
2014-10-21 17:58:18 +00:00
|
|
|
/* when qse_task_create() is called,
|
2012-10-11 13:39:52 +00:00
|
|
|
* setjmp() saves the context and return 0.
|
|
|
|
*
|
|
|
|
* subsequently, when longjmp() is made
|
|
|
|
* for this saved context, setjmp() returns
|
|
|
|
* a non-zero value.
|
|
|
|
*/
|
2012-10-15 09:36:39 +00:00
|
|
|
if (setjmp (((qse_task_slice_t*)tmp)->jmpbuf) != 0)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
|
|
|
/* longjmp() is made to here. */
|
2014-10-21 17:58:18 +00:00
|
|
|
#if defined(__WATCOMC__)
|
2012-10-15 09:36:39 +00:00
|
|
|
tmp = get_slice ();
|
|
|
|
|
2014-10-21 17:58:18 +00:00
|
|
|
#elif defined(__GNUC__) && (defined(__x86_64) || defined(__amd64))
|
2012-10-15 09:36:39 +00:00
|
|
|
__asm__ volatile (
|
|
|
|
"movq 0(%%rsp), %0\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
);
|
2014-10-21 17:58:18 +00:00
|
|
|
#elif defined(__GNUC__) && (defined(__i386) || defined(i386))
|
2012-10-15 09:36:39 +00:00
|
|
|
__asm__ volatile (
|
|
|
|
"movl 0(%%esp), %0\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
);
|
2014-10-21 17:58:18 +00:00
|
|
|
#elif defined(__GNUC__) && (defined(__mips) || defined(mips))
|
2012-10-15 09:36:39 +00:00
|
|
|
__asm__ volatile (
|
|
|
|
"lw %0, 0($sp)\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
);
|
2014-10-21 17:58:18 +00:00
|
|
|
#elif defined(__GNUC__) && defined(__arm__)
|
2012-10-15 09:36:39 +00:00
|
|
|
__asm__ volatile (
|
|
|
|
"ldr %0, [sp, #0]\n" /* tmp = t2 */
|
|
|
|
: "=r"(tmp)
|
|
|
|
);
|
2014-10-21 17:58:18 +00:00
|
|
|
#endif /* __WATCOMC__ */
|
2012-10-15 09:36:39 +00:00
|
|
|
|
|
|
|
execute_current_slice ((qse_task_slice_t*)tmp);
|
|
|
|
QSE_ASSERT (!"must never reach here....\n");
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* restore the stack pointer once i finish saving the longjmp() context.
|
2014-10-21 17:58:18 +00:00
|
|
|
* this part is reached only when qse_task_create() is invoked. */
|
2012-10-11 13:39:52 +00:00
|
|
|
#if defined(__WATCOMC__)
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
restore_sp ();
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#elif defined(__GNUC__) && (defined(__x86_64) || defined(__amd64))
|
2012-10-15 09:36:39 +00:00
|
|
|
/* i assume that %rsp didn't change after the call to setjmp() */
|
2012-10-11 13:39:52 +00:00
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"movq 8(%%rsp), %%rsp\n" /* %rsp = t1 */
|
|
|
|
:
|
|
|
|
:
|
|
|
|
: "%rsp"
|
2012-10-11 13:39:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
#elif defined(__GNUC__) && (defined(__i386) || defined(i386))
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"movl 4(%%esp), %%esp\n" /* %esp = t1 */
|
|
|
|
:
|
|
|
|
:
|
|
|
|
: "%esp"
|
2012-10-11 13:39:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
#elif defined(__GNUC__) && (defined(__mips) || defined(mips))
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"lw $sp, 4($sp)\n" /* $sp = t1 */
|
|
|
|
:
|
|
|
|
:
|
2012-10-11 13:39:52 +00:00
|
|
|
: "$sp"
|
|
|
|
);
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
#elif defined(__GNUC__) && defined(__arm__)
|
|
|
|
__asm__ volatile (
|
2012-10-15 09:36:39 +00:00
|
|
|
"ldr sp, [sp, #4]\n" /* sp = t1 */
|
|
|
|
:
|
|
|
|
:
|
2012-10-11 13:39:52 +00:00
|
|
|
: "sp"
|
|
|
|
);
|
2012-10-15 09:36:39 +00:00
|
|
|
|
2012-10-11 13:39:52 +00:00
|
|
|
#endif /* __WATCOMC__ */
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
link_task (task, slice);
|
|
|
|
return slice;
|
2012-10-11 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
int qse_task_boot (qse_task_t* task, qse_task_slice_t* to)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_ASSERT (task->current == QSE_NULL);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
if (to == QSE_NULL) to = task->head;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
#if defined(_WIN64)
|
2012-10-15 09:36:39 +00:00
|
|
|
task->fiber = ConvertThreadToFiberEx (QSE_NULL, FIBER_FLAG_FLOAT_SWITCH);
|
|
|
|
if (task->fiber == QSE_NULL) return -1;
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
task->current = to;
|
|
|
|
SwitchToFiber (task->current->fiber);
|
2012-10-11 13:39:52 +00:00
|
|
|
ConvertFiberToThread ();
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
#elif defined(USE_UCONTEXT)
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
task->current = to;
|
|
|
|
if (swapcontext (&task->uctx, &task->current->uctx) <= -1)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
2012-10-15 09:36:39 +00:00
|
|
|
task->current = QSE_NULL;
|
2012-10-11 13:39:52 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
2012-10-15 09:36:39 +00:00
|
|
|
if (setjmp (task->backjmp) != 0)
|
2012-10-11 13:39:52 +00:00
|
|
|
{
|
|
|
|
/* longjmp() back */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
task->current = to;
|
|
|
|
longjmp (task->current->jmpbuf, 1);
|
|
|
|
QSE_ASSERT (!"must never reach here");
|
2012-10-11 13:39:52 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
#endif
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
QSE_ASSERT (task->current == QSE_NULL);
|
|
|
|
QSE_ASSERT (task->count == 0);
|
2012-10-11 13:39:52 +00:00
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
if (task->dead) purge_dead_slices (task);
|
2012-10-11 13:39:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-15 09:36:39 +00:00
|
|
|
/* NOTE for __WATCOMC__.
|
2014-10-21 17:58:18 +00:00
|
|
|
when the number of parameters is more than 2 for qse_task_schedule(),
|
2012-10-15 09:36:39 +00:00
|
|
|
this setjmp()/longjmp() based tasking didn't work.
|
|
|
|
|
|
|
|
if i change this to
|
|
|
|
void qse_task_schedule (qse_task_slice_t* from, qse_task_slice_t* to)
|
|
|
|
{
|
|
|
|
qse_task_t* task = from->task
|
|
|
|
....
|
|
|
|
}
|
|
|
|
|
|
|
|
it worked. i stopped investigating this problem. so i don't know the
|
|
|
|
real cause of this problem. let me get back to this later when i have
|
|
|
|
time for this.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void qse_task_schedule (
|
|
|
|
qse_task_t* task, qse_task_slice_t* from, qse_task_slice_t* to)
|
|
|
|
{
|
|
|
|
QSE_ASSERT (from != QSE_NULL);
|
|
|
|
|
|
|
|
if (to == QSE_NULL)
|
|
|
|
{
|
|
|
|
/* round-robin if the target is not specified */
|
|
|
|
to = from->next? from->next: task->head;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_ASSERT (task == to->task);
|
|
|
|
QSE_ASSERT (task == from->task);
|
|
|
|
QSE_ASSERT (task->current == from);
|
|
|
|
|
|
|
|
if (to == from) return;
|
|
|
|
task->current = to;
|
|
|
|
|
|
|
|
#if defined(_WIN64)
|
|
|
|
/* current->fiber is handled by SwitchToFiber() implicitly */
|
|
|
|
SwitchToFiber (to->fiber);
|
|
|
|
if (task->dead) purge_dead_slices (task);
|
|
|
|
#elif defined(USE_UCONTEXT)
|
|
|
|
swapcontext (&from->uctx, &to->uctx);
|
|
|
|
if (task->dead) purge_dead_slices (task);
|
|
|
|
#else
|
|
|
|
if (setjmp (from->jmpbuf) != 0)
|
|
|
|
{
|
|
|
|
if (task->dead) purge_dead_slices (task);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
longjmp (to->jmpbuf, 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void purge_dead_slices (qse_task_t* task)
|
|
|
|
{
|
|
|
|
qse_task_slice_t* slice;
|
|
|
|
|
|
|
|
while (task->dead)
|
|
|
|
{
|
|
|
|
slice = task->dead;
|
|
|
|
task->dead = slice->next;
|
|
|
|
purge_slice (task, slice);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void purge_slice (qse_task_t* task, qse_task_slice_t* slice)
|
|
|
|
{
|
|
|
|
#if defined(_WIN64)
|
|
|
|
if (slice->fiber) DeleteFiber (slice->fiber);
|
|
|
|
#endif
|
|
|
|
QSE_MMGR_FREE (task->mmgr, slice);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void purge_current_slice (qse_task_slice_t* slice, qse_task_slice_t* to)
|
|
|
|
{
|
|
|
|
qse_task_t* task = slice->task;
|
|
|
|
|
|
|
|
QSE_ASSERT (task->current == slice);
|
|
|
|
|
|
|
|
if (task->count == 1)
|
|
|
|
{
|
|
|
|
/* to purge later */
|
|
|
|
slice->next = task->dead;
|
|
|
|
task->dead = slice;
|
|
|
|
|
|
|
|
task->current = QSE_NULL;
|
|
|
|
task->head = QSE_NULL;
|
|
|
|
task->tail = QSE_NULL;
|
|
|
|
task->count = 0;
|
|
|
|
|
|
|
|
#if defined(_WIN64)
|
|
|
|
SwitchToFiber (task->fiber);
|
|
|
|
#elif defined(USE_UCONTEXT)
|
|
|
|
setcontext (&task->uctx);
|
|
|
|
#else
|
|
|
|
longjmp (task->backjmp, 1);
|
|
|
|
#endif
|
|
|
|
QSE_ASSERT (!"must not reach here....");
|
|
|
|
}
|
|
|
|
|
|
|
|
task->current = (to && to != slice)? to: (slice->next? slice->next: task->head);
|
|
|
|
|
|
|
|
if (slice->prev) slice->prev->next = slice->next;
|
|
|
|
if (slice->next) slice->next->prev = slice->prev;
|
|
|
|
if (slice == task->head) task->head = slice->next;
|
|
|
|
if (slice == task->tail) task->tail = slice->prev;
|
|
|
|
task->count--;
|
|
|
|
|
|
|
|
/* to purge later */
|
|
|
|
slice->next = task->dead;
|
|
|
|
task->dead = slice;
|
|
|
|
|
|
|
|
#if defined(_WIN64)
|
|
|
|
SwitchToFiber (task->current->fiber);
|
|
|
|
#elif defined(USE_UCONTEXT)
|
|
|
|
setcontext (&task->current->uctx);
|
|
|
|
#else
|
|
|
|
longjmp (task->current->jmpbuf, 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|