Commit 9cc93657 authored by Pekka Pessi's avatar Pekka Pessi

su_port: improved virtualization. Moved public clone functions to su_port.c.

There are now two different preferred port implementations: one used with
su_root_create() and another with su_clone_start().

darcs-hash:20070202155042-65a35-90517d7a3f3dbbd7b0ce8c9169a40915d75e139b.gz
parent 8d90608d
......@@ -183,7 +183,7 @@ int su_base_port_getmsgs(su_port_t *self)
}
int su_base_port_getmsgs_from_port(su_port_t *self, su_port_t *from)
int su_base_port_getmsgs_from(su_port_t *self, su_port_t *from)
{
su_msg_t *msg, *selected;
su_msg_t **next = &self->sup_head, **tail= &selected;
......@@ -509,11 +509,11 @@ static void su_base_port_clone_break(su_root_magic_t *m,
su_msg_r msg,
su_msg_arg_t *arg);
int su_base_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
int su_base_port_start_shared(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
su_port_t *self = parent->sur_task->sut_port;
su_root_t *child;
......
......@@ -108,6 +108,7 @@ static int su_epoll_port_eventmask(su_port_t *self,
int events);
static int su_epoll_port_multishot(su_port_t *self, int multishot);
static int su_epoll_port_wait_events(su_port_t *self, su_duration_t tout);
static char const *su_epoll_port_name(su_port_t const *self);
su_port_vtable_t const su_epoll_port_vtable[1] =
{{
......@@ -135,12 +136,17 @@ su_port_vtable_t const su_epoll_port_vtable[1] =
su_base_port_yield,
su_epoll_port_wait_events,
su_base_port_getmsgs,
su_epoll_port_create,
su_pthread_port_start,
su_base_port_getmsgs_from,
su_epoll_port_name,
su_base_port_start_shared,
su_pthread_port_wait,
su_pthread_port_execute,
}};
static char const *su_epoll_port_name(su_port_t const *self)
{
return "epoll";
}
static void su_epoll_port_decref(su_port_t *self, int blocking, char const *who)
{
......@@ -546,6 +552,16 @@ su_port_t *su_epoll_port_create(void)
return self;
}
int su_epoll_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
return su_pthreaded_port_start(su_epoll_port_create,
parent, return_clone, magic, init, deinit);
}
#else
su_port_t *su_epoll_port_create(void)
......@@ -553,4 +569,15 @@ su_port_t *su_epoll_port_create(void)
return su_poll_port_create();
}
int su_epoll_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
return su_pthreaded_port_start(su_poll_port_create,
parent, return_clone, magic, init, deinit);
}
#endif /* HAVE_EPOLL */
......@@ -35,7 +35,7 @@
#include "config.h"
#if HAVE_POLL
#if HAVE_POLL || HAVE_WIN32
#include <stdlib.h>
#include <assert.h>
......@@ -114,6 +114,7 @@ static int su_poll_port_eventmask(su_port_t *self,
int events);
static int su_poll_port_multishot(su_port_t *self, int multishot);
static int su_poll_port_wait_events(su_port_t *self, su_duration_t tout);
static char const *su_poll_port_name(su_port_t const *self);
su_port_vtable_t const su_poll_port_vtable[1] =
{{
......@@ -141,12 +142,18 @@ su_port_vtable_t const su_poll_port_vtable[1] =
su_base_port_yield,
su_poll_port_wait_events,
su_base_port_getmsgs,
su_poll_port_create,
su_pthread_port_start,
su_base_port_getmsgs_from,
su_poll_port_name,
su_base_port_start_shared,
su_pthread_port_wait,
su_pthread_port_execute,
}};
static char const *su_poll_port_name(su_port_t const *self)
{
return "poll";
}
static void su_poll_port_deinit(void *arg)
{
su_port_t *self = arg;
......@@ -662,4 +669,14 @@ su_port_t *su_poll_port_create(void)
return self;
}
int su_poll_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
return su_pthreaded_port_start(su_poll_port_create,
parent, return_clone, magic, init, deinit);
}
#endif /* HAVE_POLL */
......@@ -48,60 +48,73 @@
#include <string.h>
#include <stdlib.h>
static su_port_t *(*preferred_su_port_create)(void);
static su_port_create_f *preferred_su_port_create;
static su_clone_start_f *preferred_su_clone_start;
/** Explicitly set the preferred su_port_t implementation.
*
* @sa su_epoll_port_create(), su_poll_port_create(), su_select_port_create()
*/
void su_port_prefer(su_port_t *(*implementation)(void))
void su_port_prefer(su_port_create_f *create,
su_clone_start_f *start)
{
preferred_su_port_create = implementation;
if (create) preferred_su_port_create = create;
if (start) preferred_su_clone_start = start;
}
/** Create the preferred su_port_t implementation.
*/
su_port_t *su_port_create(void)
void su_port_set_system_preferences(char const *name)
{
if (preferred_su_port_create == NULL) {
char const *SU_PORT = getenv("SU_PORT");
su_port_t *(*create)(void) = NULL;
su_port_create_f *create = preferred_su_port_create;
su_clone_start_f *start = preferred_su_clone_start;
if (SU_PORT == NULL)
if (name == NULL)
;
#if HAVE_POLL_PORT
#if HAVE_EPOLL
else if (strcmp(SU_PORT, "epoll") == 0)
create = su_epoll_port_create;
else if (strcmp(name, "epoll") == 0) {
create = su_epoll_port_create;
start = su_epoll_clone_start;
}
#endif
else if (strcmp(SU_PORT, "poll") == 0)
create = su_poll_port_create;
else if (strcmp(name, "poll") == 0) {
create = su_poll_port_create;
start = su_poll_clone_start;
}
#else
/* select port does not work yet */
#error no poll!
#endif
#if HAVE_SELECT
else if (strcmp(SU_PORT, "select") == 0)
create = su_select_port_create;
else if (strcmp(name, "select") == 0) {
create = su_select_port_create;
start = su_select_clone_start;
}
#endif
if (create == NULL) {
create = su_epoll_port_create;
#if HAVE_POLL_PORT
#if HAVE_EPOLL
create = su_epoll_port_create;
if (create == NULL) create = su_epoll_port_create;
if (start == NULL) start = su_epoll_clone_start;
#else
create = su_poll_port_create;
if (create == NULL) create = su_poll_port_create;
if (start == NULL) start = su_poll_clone_start;
#endif
#else
#if HAVE_SELECT
create = su_select_port_create;
if (create == NULL) create = su_select_port_create;
if (start == NULL) start = su_select_clone_start;
#endif
#endif
}
if (create)
preferred_su_port_create = create;
}
if (preferred_su_port_create == NULL) preferred_su_port_create = create;
if (preferred_su_clone_start == NULL) preferred_su_clone_start = start;
}
/** Create the preferred su_port_t implementation. */
su_port_t *su_port_create(void)
{
if (preferred_su_port_create == NULL)
su_port_set_system_preferences(getenv("SU_PORT"));
if (preferred_su_port_create)
return preferred_su_port_create();
......@@ -109,49 +122,276 @@ su_port_t *su_port_create(void)
return NULL;
}
/** Create a su-task (su_clone) using its own thread. */
int su_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
/* ========================================================================
* su_clone_t
*/
/**@ingroup su_wait
*
* @page su_clone_t Clone Objects
*
* The process may be divided into many tasks via cloning. Several tasks may
* run in context of one thread, or each task may be run by its own thread.
* However, only a single thread can execute code within a task. There can
* be a 1-to-N mapping from thread to tasks. Thus, software using tasks can
* be executed by multiple threads in a multithreaded environment and by a
* single thread in a singlethreaded environment.
*
* The clones are useful for handling tasks that can be executed by a
* separate threads, but which do not block excessively. When threads are
* not available or they are not needed, clones can also be run in a
* single-threaded mode. Running in single-threaded mode is especially
* useful while debugging.
*
* A clone task is created with function su_clone_start(). Each clone has
* its own root object (su_root_t), which holds a context pointer
* (su_root_magic_t *). The context object can be different from that of
* parent task.
*
* When a clone is started, the clone initialization function is called. The
* initialization function should do whatever initialization there is to be
* performed, register I/O events and timers, and then return. If the
* initialization is successful, the clone task reverts to run the event
* loop and invoking the event callbacks until its parent stops it by
* calling su_clone_wait() which invokes the deinit function. The clone task
* is destroyed when the deinit function returns.
*
* The public API consists of following functions:
* - su_clone_start()
* - su_clone_task()
* - su_clone_wait()
* - su_clone_forget()
*
* @note
* There is only one event loop for each thread which can be shared by
* multiple clone tasks. Therefore, the clone tasks can not explicitly run
* or step the event loop, but they are limited to event callbacks. A clone
* task may not call su_root_break(), su_root_run() or su_root_step().
*/
static int su_root_init_nothing(su_root_t *root, su_root_magic_t *magic)
{
return 0;
}
static void su_root_deinit_nothing(su_root_t *root, su_root_magic_t *magic)
{
}
/** Start a clone task.
*
* Allocate and initialize a sub-task. Depending on the su_root_threading()
* settings, a separate thread may be created to execute the sub-task. The
* sub-task is represented by clone handle to the rest of the application.
* The function su_clone_start() returns the clone handle in @a
* return_clone. The clone handle is used to communicate with the newly
* created clone task using messages.
*
* A new #su_root_t object is created for the sub-task with the @a magic as
* the root context pointer. Because the sub-task may or may not have its
* own thread, all its activity must be scheduled via this root object. In
* other words, the sub-task can be schedule
* -# I/O events with su_root_register()
* -# timers with su_timer_set(), su_timer_set_at() or su_timer_run()
* -# messages with su_msg_send().
*
* Messages can also be used to pass information between tasks or threads.
*
* In multi-threaded implementation, su_clone_start() launches a new thread,
* and the initialization routine is executed by this newly created thread.
* The calling thread blocks until the initialization routine completes. If
* the initialization routine returns #su_success (0), the sub-task is
* considered to be created successfully. After the successful
* initialization, the sub-task continues to execeute the function
* su_root_run().
*
* In single-threaded implementations, just a new root object is created.
* The initialization routine is called directly from su_clone_start().
*
* If the initalization function @a init fails, the sub-task (either the
* newly created thread or the current thread executing the su_clone_start()
* function) calls the deinitialization function, and su_clone_start()
* returns NULL.
*
* @param parent root to be cloned
* @param return_clone reference to a clone [OUT]
* @param magic pointer to user data
* @param init initialization function
* @param deinit deinitialization function
*
* @return 0 if successfull, -1 upon an error.
*
* @note Earlier documentation mentioned that @a parent could be NULL. That
* feature has never been implemented, however.
*
* @sa su_root_threading(), su_clone_task(), su_clone_stop(), su_clone_wait(),
* su_clone_forget().
*/
int su_clone_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
su_port_vtable_t const *svp;
if (init == NULL)
init = su_root_init_nothing;
if (deinit == NULL)
deinit = su_root_deinit_nothing;
if (parent == NULL)
return su_seterrno(EINVAL); /* For now */
if (parent == NULL || parent->sur_threading) {
if (preferred_su_clone_start == NULL)
su_port_set_system_preferences(getenv("SU_PORT"));
if (preferred_su_clone_start == NULL)
return -1;
return preferred_su_clone_start(parent, return_clone, magic, init, deinit);
}
svp = parent->sur_task->sut_port->sup_vtable;
if (svp->su_port_start == NULL)
if (svp->su_port_start_shared == NULL)
return su_seterrno(EINVAL);
return svp->su_port_start(parent, return_clone, magic, init, deinit);
/* Return a task sharing the same port. */
return svp->su_port_start_shared(parent, return_clone, magic, init, deinit);
}
void su_port_wait(su_clone_r rclone)
/** Get reference to a clone task.
*
* @param clone Clone pointer
*
* @return A reference to the task structure of the clone.
*/
_su_task_r su_clone_task(su_clone_r clone)
{
return su_msg_to(clone);
}
/**Forget the clone.
*
* Normally, the clone task executes until it is stopped. If the parent
* task does not need to stop the task, it can "forget" the clone. The
* clone exits independently of the parent task.
*
* @param rclone Reference to the clone.
*/
void su_clone_forget(su_clone_r rclone)
{
su_msg_destroy(rclone);
}
/** Stop the clone.
*
* This can used only if clone task has sent no report messages (messages
* with delivery report sent back to clone).
*
* @deprecated. Use su_clone_wait().
*/
void su_clone_stop(su_clone_r rclone)
{
su_msg_send(rclone);
}
/** Stop a clone and wait until it is has completed.
*
* The function su_clone_wait() is used to stop the clone task and wait
* until it has cleaned up. The clone task is destroyed asynchronously. The
* parent sends a message to clone, clone deinitializes itself and then
* replies. After the reply message is received by the parent, it will send
* a third message back to clone.
*
* The parent destroy all messages to or from clone task before calling
* su_clone_wait(). The parent task may not send any messages to the clone
* after calling su_clone_wait(). The su_clone_wait() function blocks until
* the cloned task is destroyed. During that time, the parent task must be
* prepared to process all the messages sent by clone task. This includes
* all the messages sent by clone before destroy the message reached the
* clone.
*/
void su_clone_wait(su_root_t *root, su_clone_r rclone)
{
su_port_t *parentport;
if (rclone[0]) {
assert(root == NULL || root == su_msg_from(rclone)->sut_root);
su_port_wait(rclone);
}
}
assert(su_msg_to(rclone)->sut_port);
/** Pause a clone.
*
* Obtain an exclusive lock on clone's private data.
*
* @retval 0 if successful (and clone is paused)
* @retval -1 upon an error
*
* @deprecated Never implemented.
*/
int su_clone_pause(su_clone_r rclone)
{
#if 0
su_root_t *cloneroot = su_task_root(su_msg_to(rclone));
if (!cloneroot)
return (errno = EFAULT), -1;
if (SU_ROOT_OWN_THREAD(cloneroot))
/* We own it already */
return 0;
return su_port_pause(cloneroot->sur_port);
#else
return errno = ENOSYS, -1;
#endif
}
/** Resume a clone.
*
* Give up an exclusive lock on clone's private data.
*
* @retval 0 if successful (and clone is resumed)
* @retval -1 upon an error
*
* @deprecated Never implemented.
*/
int su_clone_resume(su_clone_r rclone)
{
#if 0
su_root_t *cloneroot = su_task_root(su_msg_to(rclone));
if (!cloneroot)
return (errno = EFAULT), -1;
if (SU_ROOT_OWN_THREAD(cloneroot))
/* We cannot give it away */
return 0;
return su_port_resume(cloneroot->sur_port);
#else
return errno = ENOSYS, -1;
#endif
}
void su_port_wait(su_clone_r rclone)
{
su_port_t *cloneport;
parentport = su_msg_from(rclone)->sut_port;
assert(parentport);
assert(parentport->sup_vtable->su_port_wait);
parentport->sup_vtable->su_port_wait(rclone);
cloneport = su_msg_to(rclone)->sut_port;
cloneport->sup_vtable->su_port_wait(rclone);
}
int su_port_execute(su_task_r const task,
int (*function)(void *), void *arg,
int *return_value)
{
assert(task->sut_port->sup_vtable->su_port_execute);
if (!task->sut_port->sup_vtable->su_port_execute)
return errno = ENOSYS, -1;
return task->sut_port->sup_vtable->
su_port_execute(task, function, arg, return_value);
}
#if notyet
#if notyet && nomore
int su_port_pause(su_port_t *self)
{
assert(self->sup_vtable->su_port_pause);
......
......@@ -133,13 +133,14 @@ typedef struct su_port_vtable {
/* Extension from >= 1.12.4 */
int (*su_port_wait_events)(su_port_t *port, su_duration_t timeout);
int (*su_port_getmsgs)(su_port_t *port);
/* Extension from >= 1.12.5 - create a cloned port */
su_port_t *(*su_port_create)(void);
int (*su_port_start)(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
/* Extension from >= 1.12.5 */
int (*su_port_getmsgs_from)(su_port_t *port, su_port_t *cloneport);
char const *(*su_port_name)(su_port_t const *port);
int (*su_port_start_shared)(su_root_t *root,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
void (*su_port_wait)(su_clone_r rclone);
int (*su_port_execute)(su_task_r const task,
int (*function)(void *), void *arg,
......@@ -150,7 +151,15 @@ SOFIAPUBFUN su_port_t *su_port_create(void)
__attribute__((__malloc__));
/* Extension from >= 1.12.5 */
SOFIAPUBFUN void su_port_prefer(su_port_t *(*implementation)(void));
typedef su_port_t *su_port_create_f(void);
typedef int su_clone_start_f(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
SOFIAPUBFUN void su_port_prefer(su_port_create_f *f, su_clone_start_f *);
SOFIAPUBFUN su_port_t *su_epoll_port_create(void)
__attribute__((__malloc__));
......@@ -159,6 +168,10 @@ SOFIAPUBFUN su_port_t *su_poll_port_create(void)
SOFIAPUBFUN su_port_t *su_select_port_create(void)
__attribute__((__malloc__));
SOFIAPUBFUN su_clone_start_f su_epoll_clone_start;
SOFIAPUBFUN su_clone_start_f su_poll_clone_start;
SOFIAPUBFUN su_clone_start_f su_select_clone_start;
SOFIAPUBFUN void su_msg_delivery_report(su_msg_r msg);
SOFIAPUBFUN su_duration_t su_timer_next_expires(su_timer_t const * t,
su_time_t now);
......@@ -410,11 +423,14 @@ int su_port_getmsgs(su_port_t *self)
return base->sup_vtable->su_port_getmsgs(self);
}
SOFIAPUBFUN int su_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
static inline
int su_port_getmsgs_from(su_port_t *self, su_port_t *cloneport)
{
su_virtual_port_t *base = (su_virtual_port_t *)self;
return base->sup_vtable->su_port_getmsgs_from(self, cloneport);
}
SOFIAPUBFUN void su_port_wait(su_clone_r rclone);
SOFIAPUBFUN int su_port_execute(su_task_r const task,
......@@ -468,8 +484,8 @@ SOFIAPUBFUN struct _GSource *su_base_port_gsource(su_port_t *self);
SOFIAPUBFUN su_socket_t su_base_port_mbox(su_port_t *self);
SOFIAPUBFUN int su_base_port_send(su_port_t *self, su_msg_r rmsg);
SOFIAPUBFUN int su_base_port_getmsgs(su_port_t *self);
SOFIAPUBFUN int su_base_port_getmsgs_from_port(su_port_t *self,
su_port_t *from);
SOFIAPUBFUN int su_base_port_getmsgs_from(su_port_t *self,
su_port_t *from);
SOFIAPUBFUN void su_base_port_run(su_port_t *self);
SOFIAPUBFUN void su_base_port_break(su_port_t *self);
......@@ -489,11 +505,11 @@ SOFIAPUBFUN int su_base_port_multishot(su_port_t *self, int multishot);
SOFIAPUBFUN int su_base_port_threadsafe(su_port_t *self);
SOFIAPUBFUN int su_base_port_yield(su_port_t *self);
SOFIAPUBFUN int su_base_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
SOFIAPUBFUN int su_base_port_start_shared(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
SOFIAPUBFUN void su_base_port_wait(su_clone_r rclone);
/* ---------------------------------------------------------------------- */
......@@ -532,27 +548,33 @@ SOFIAPUBFUN int su_pthread_port_own_thread(su_port_t const *self);
SOFIAPUBFUN int su_pthread_port_send(su_port_t *self, su_msg_r rmsg);
#if 0 /* not yet */
SOFIAPUBFUN su_port_t *su_pthread_port_create(void);
SOFIAPUBFUN su_port_t *su_pthread_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
#endif
SOFIAPUBFUN int su_pthreaded_port_start(su_port_create_f *create,
su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
SOFIAPUBFUN int su_pthread_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit);
SOFIAPUBFUN void su_pthread_port_wait(su_clone_r rclone);
SOFIAPUBFUN int su_pthread_port_execute(su_task_r const task,
int (*function)(void *), void *arg,
int *return_value);
#if 0
SOFIAPUBFUN int su_pthread_port_pause(su_port_t *self);
SOFIAPUBFUN int su_pthread_port_resume(su_port_t *self);
#endif
SOFIAPUBFUN int su_pthread_port_execute(su_task_r const task,
int (*function)(void *), void *arg,
int *return_value);
#else
typedef su_base_port_t su_pthread_port_t;
......@@ -563,7 +585,6 @@ typedef su_base_port_t su_pthread_port_t;
#define su_pthread_port_unlock su_base_port_unlock
#define su_pthread_port_own_thread su_base_port_own_thread
#define su_pthread_port_send su_base_port_send
#define su_pthread_port_start su_base_port_start
#define su_pthread_port_wait su_base_port_wait
#define su_pthread_port_execute su_base_port_execute
......
......@@ -205,7 +205,6 @@ void su_pthread_port_deinit(su_port_t *self)
su_base_port_deinit(self);
}
void su_pthread_port_lock(su_port_t *self, char const *who)
{
PORT_LOCK_DEBUG(("%p at %s locking(%p)...",
......@@ -264,6 +263,7 @@ int su_pthread_port_own_thread(su_port_t const *self)
struct clone_args
{
su_port_create_f*create;
su_root_t *parent;
su_root_magic_t *magic;
su_root_init_f init;
......@@ -287,11 +287,11 @@ struct su_pthread_port_waiting_parent {
int waiting;
};
/** Start a clone task by a pthread.
/** Start a clone task running under a pthread.
*
* @internal
*
* Allocates and initializes a sub-task with its own thread. The sub-task is
* Allocates and initializes a sub-task with its own pthread. The sub-task is
* represented by clone handle to the rest of the application. The function
* su_clone_start() returns the clone handle in @a return_clone. The clone
* handle is used to communicate with the newly created clone task using
......@@ -331,13 +331,15 @@ struct su_pthread_port_waiting_parent {
* su_clone_forget().
*
*/
int su_pthread_port_start(su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
int su_pthreaded_port_start(su_port_create_f *create,
su_root_t *parent,
su_clone_r return_clone,
su_root_magic_t *magic,
su_root_init_f init,
su_root_deinit_f deinit)
{
struct clone_args arg = {
/* create: */ NULL,
/* parent: */ NULL,
/* magic: */ NULL,
/* init: */ NULL,
......@@ -351,9 +353,7 @@ int su_pthread_port_start(su_root_t *parent,
int thread_created = 0;
pthread_t tid;
if (parent && !parent->sur_threading)
return su_base_port_start(parent, return_clone, magic, init, deinit);
arg.create = create;
arg.parent = parent;
arg.magic = magic;
arg.init = init;
......@@ -388,15 +388,13 @@ static void *su_pthread_port_clone_main(void *varg)
{
struct clone_args *arg = (struct clone_args *)varg;
su_task_r task;
int zap;
int zap = 1;