From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1K56e7-00069H-B1 for qemu-devel@nongnu.org; Sat, 07 Jun 2008 18:12:23 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1K56e4-00067M-3w for qemu-devel@nongnu.org; Sat, 07 Jun 2008 18:12:20 -0400 Received: from [199.232.76.173] (port=42350 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1K56e3-00067D-Jk for qemu-devel@nongnu.org; Sat, 07 Jun 2008 18:12:19 -0400 Received: from savannah.gnu.org ([199.232.41.3]:40719 helo=sv.gnu.org) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1K56e3-0005Tp-Eo for qemu-devel@nongnu.org; Sat, 07 Jun 2008 18:12:19 -0400 Received: from cvs.savannah.gnu.org ([199.232.41.69]) by sv.gnu.org with esmtp (Exim 4.63) (envelope-from ) id 1K56e1-0001sG-OK for qemu-devel@nongnu.org; Sat, 07 Jun 2008 22:12:17 +0000 Received: from pbrook by cvs.savannah.gnu.org with local (Exim 4.63) (envelope-from ) id 1K56e1-0001s7-H6 for qemu-devel@nongnu.org; Sat, 07 Jun 2008 22:12:17 +0000 MIME-Version: 1.0 Errors-To: pbrook Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Paul Brook Message-Id: Date: Sat, 07 Jun 2008 22:12:17 +0000 Subject: [Qemu-devel] [4693] Implement thread creation. Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org 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 +#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