All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paul Brook <paul@nowt.org>
To: qemu-devel@nongnu.org
Subject: [Qemu-devel] [4693] Implement thread creation.
Date: Sat, 07 Jun 2008 22:12:17 +0000	[thread overview]
Message-ID: <E1K56e1-0001s7-H6@cvs.savannah.gnu.org> (raw)

Revision: 4693
          http://svn.sv.gnu.org/viewvc/?view=rev&root=qemu&revision=4693
Author:   pbrook
Date:     2008-06-07 22:12:17 +0000 (Sat, 07 Jun 2008)

Log Message:
-----------
Implement thread creation.

Modified Paths:
--------------
    trunk/linux-user/syscall.c

Modified: trunk/linux-user/syscall.c
===================================================================
--- trunk/linux-user/syscall.c	2008-06-07 20:50:51 UTC (rev 4692)
+++ trunk/linux-user/syscall.c	2008-06-07 22:12:17 UTC (rev 4693)
@@ -74,6 +74,11 @@
 
 #if defined(USE_NPTL)
 #include <linux/futex.h>
+#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \
+    CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)
+#else
+/* XXX: Hardcode the above values.  */
+#define CLONE_NPTL_FLAGS2 0
 #endif
 
 //#define DEBUG
@@ -2706,6 +2711,48 @@
 
 #endif /* defined(TARGET_I386) */
 
+#if defined(USE_NPTL)
+
+#define NEW_STACK_SIZE PTHREAD_STACK_MIN
+
+static pthread_mutex_t clone_lock = PTHREAD_MUTEX_INITIALIZER;
+typedef struct {
+    CPUState *env;
+    pthread_mutex_t mutex;
+    pthread_cond_t cond;
+    pthread_t thread;
+    uint32_t tid;
+    abi_ulong child_tidptr;
+    abi_ulong parent_tidptr;
+    sigset_t sigmask;
+} new_thread_info;
+
+static void *clone_func(void *arg)
+{
+    new_thread_info *info = arg;
+    CPUState *env;
+
+    env = info->env;
+    thread_env = env;
+    info->tid = gettid();
+    if (info->child_tidptr)
+        put_user_u32(info->tid, info->child_tidptr);
+    if (info->parent_tidptr)
+        put_user_u32(info->tid, info->parent_tidptr);
+    /* Enable signals.  */
+    sigprocmask(SIG_SETMASK, &info->sigmask, NULL);
+    /* Signal to the parent that we're ready.  */
+    pthread_mutex_lock(&info->mutex);
+    pthread_cond_broadcast(&info->cond);
+    pthread_mutex_unlock(&info->mutex);
+    /* Wait until the parent has finshed initializing the tls state.  */
+    pthread_mutex_lock(&clone_lock);
+    pthread_mutex_unlock(&clone_lock);
+    cpu_loop(env);
+    /* never exits */
+    return NULL;
+}
+#else
 /* this stack is the equivalent of the kernel stack associated with a
    thread/process */
 #define NEW_STACK_SIZE 8192
@@ -2717,24 +2764,27 @@
     /* never exits */
     return 0;
 }
+#endif
 
 /* do_fork() Must return host values and target errnos (unlike most
    do_*() functions). */
-int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp)
+static int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp,
+                   abi_ulong parent_tidptr, target_ulong newtls,
+                   abi_ulong child_tidptr)
 {
     int ret;
     TaskState *ts;
     uint8_t *new_stack;
     CPUState *new_env;
+#if defined(USE_NPTL)
+    unsigned int nptl_flags;
+    sigset_t sigmask;
+#endif
 
     if (flags & CLONE_VM) {
 #if defined(USE_NPTL)
-        /* qemu is not threadsafe.  Bail out immediately if application
-           tries to create a thread.  */
-        if (!(flags & CLONE_VFORK)) {
-            gemu_log ("clone(CLONE_VM) not supported\n");
-            return -EINVAL;
-        }
+        new_thread_info info;
+        pthread_attr_t attr;
 #endif
         ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
         init_task_state(ts);
@@ -2744,19 +2794,94 @@
         /* Init regs that differ from the parent.  */
         cpu_clone_regs(new_env, newsp);
         new_env->opaque = ts;
+#if defined(USE_NPTL)
+        nptl_flags = flags;
+        flags &= ~CLONE_NPTL_FLAGS2;
+
+        /* TODO: Implement CLONE_CHILD_CLEARTID.  */
+        if (nptl_flags & CLONE_SETTLS)
+            cpu_set_tls (new_env, newtls);
+
+        /* Grab a mutex so that thread setup appears atomic.  */
+        pthread_mutex_lock(&clone_lock);
+
+        memset(&info, 0, sizeof(info));
+        pthread_mutex_init(&info.mutex, NULL);
+        pthread_mutex_lock(&info.mutex);
+        pthread_cond_init(&info.cond, NULL);
+        info.env = new_env;
+        if (nptl_flags & CLONE_CHILD_SETTID)
+            info.child_tidptr = child_tidptr;
+        if (nptl_flags & CLONE_PARENT_SETTID)
+            info.parent_tidptr = parent_tidptr;
+
+        ret = pthread_attr_init(&attr);
+        ret = pthread_attr_setstack(&attr, new_stack, NEW_STACK_SIZE);
+        /* It is not safe to deliver signals until the child has finished
+           initializing, so temporarily block all signals.  */
+        sigfillset(&sigmask);
+        sigprocmask(SIG_BLOCK, &sigmask, &info.sigmask);
+
+        ret = pthread_create(&info.thread, &attr, clone_func, &info);
+
+        sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
+        pthread_attr_destroy(&attr);
+        if (ret == 0) {
+            /* Wait for the child to initialize.  */
+            pthread_cond_wait(&info.cond, &info.mutex);
+            ret = info.tid;
+            if (flags & CLONE_PARENT_SETTID)
+                put_user_u32(ret, parent_tidptr);
+        } else {
+            ret = -1;
+        }
+        pthread_mutex_unlock(&info.mutex);
+        pthread_cond_destroy(&info.cond);
+        pthread_mutex_destroy(&info.mutex);
+        pthread_mutex_unlock(&clone_lock);
+#else
+        if (flags & CLONE_NPTL_FLAGS2)
+            return -EINVAL;
+        /* This is probably going to die very quickly, but do it anyway.  */
 #ifdef __ia64__
         ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
 #else
 	ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
 #endif
+#endif
     } else {
         /* if no CLONE_VM, we consider it is a fork */
-        if ((flags & ~CSIGNAL) != 0)
+        if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0)
             return -EINVAL;
+        fork_start();
         ret = fork();
+#if defined(USE_NPTL)
+        /* There is a race condition here.  The parent process could
+           theoretically read the TID in the child process before the child
+           tid is set.  This would require using either ptrace
+           (not implemented) or having *_tidptr to point at a shared memory
+           mapping.  We can't repeat the spinlock hack used above because
+           the child process gets its own copy of the lock.  */
         if (ret == 0) {
             cpu_clone_regs(env, newsp);
+            fork_end(1);
+            /* Child Process.  */
+            if (flags & CLONE_CHILD_SETTID)
+                put_user_u32(gettid(), child_tidptr);
+            if (flags & CLONE_PARENT_SETTID)
+                put_user_u32(gettid(), parent_tidptr);
+            ts = (TaskState *)env->opaque;
+            if (flags & CLONE_SETTLS)
+                cpu_set_tls (env, newtls);
+            /* TODO: Implement CLONE_CHILD_CLEARTID.  */
+        } else {
+            fork_end(0);
         }
+#else
+        if (ret == 0) {
+            cpu_clone_regs(env, newsp);
+        }
+#endif
     }
     return ret;
 }
@@ -3153,7 +3278,7 @@
         ret = do_brk(arg1);
         break;
     case TARGET_NR_fork:
-        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
+        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0));
         break;
 #ifdef TARGET_NR_waitpid
     case TARGET_NR_waitpid:
@@ -4531,7 +4656,7 @@
         ret = get_errno(fsync(arg1));
         break;
     case TARGET_NR_clone:
-        ret = get_errno(do_fork(cpu_env, arg1, arg2));
+        ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5));
         break;
 #ifdef __NR_exit_group
         /* new thread calls */
@@ -4967,7 +5092,8 @@
 #endif
 #ifdef TARGET_NR_vfork
     case TARGET_NR_vfork:
-        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
+        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD,
+                        0, 0, 0, 0));
         break;
 #endif
 #ifdef TARGET_NR_ugetrlimit

             reply	other threads:[~2008-06-07 22:12 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-07 22:12 Paul Brook [this message]
2008-06-08 20:34 ` [Qemu-devel] [4693] Implement thread creation Kirill A. Shutemov
2008-06-09 11:53   ` Felipe Contreras
2008-06-09 12:12     ` Kirill A. Shutemov
2008-06-09 12:38       ` Laurent Desnogues
2008-06-09 12:40         ` Laurent Desnogues

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=E1K56e1-0001s7-H6@cvs.savannah.gnu.org \
    --to=paul@nowt.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.