thr_create.c revision 55193
11556Srgrimes/*
21556Srgrimes * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
31556Srgrimes * All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by John Birrell.
161556Srgrimes * 4. Neither the name of the author nor the names of any co-contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes *
321556Srgrimes * $FreeBSD: head/lib/libkse/thread/thr_create.c 55193 1999-12-28 18:12:07Z deischen $
331556Srgrimes */
341556Srgrimes#include <errno.h>
351556Srgrimes#include <stdlib.h>
361556Srgrimes#include <string.h>
371556Srgrimes#include <fcntl.h>
381556Srgrimes#include <unistd.h>
3935773Scharnier#include <stddef.h>
4036007Scharnier#include <sys/time.h>
4135773Scharnier#include <sys/param.h>
4235773Scharnier#include <sys/mman.h>
4350471Speter#ifdef _THREAD_SAFE
441556Srgrimes#include <machine/reg.h>
451556Srgrimes#include <pthread.h>
461556Srgrimes#include "pthread_private.h"
471556Srgrimes#include "libc_private.h"
481556Srgrimes
491556Srgrimesstatic u_int64_t next_uniqueid = 1;
501556Srgrimes
511556Srgrimes#define OFF(f)	offsetof(struct pthread, f)
521556Srgrimesint _thread_next_offset			= OFF(tle.tqe_next);
531556Srgrimesint _thread_uniqueid_offset		= OFF(uniqueid);
541556Srgrimesint _thread_state_offset		= OFF(state);
551556Srgrimesint _thread_name_offset			= OFF(name);
561556Srgrimesint _thread_sig_saved_offset		= OFF(sig_saved);
571556Srgrimesint _thread_saved_sigcontext_offset	= OFF(saved_sigcontext);
581556Srgrimesint _thread_saved_jmp_buf_offset	= OFF(saved_jmp_buf);
591556Srgrimes#undef OFF
601556Srgrimes
611556Srgrimesint _thread_PS_RUNNING_value		= PS_RUNNING;
621556Srgrimesint _thread_PS_DEAD_value		= PS_DEAD;
631556Srgrimes
641556Srgrimesint
651556Srgrimespthread_create(pthread_t * thread, const pthread_attr_t * attr,
661556Srgrimes	       void *(*start_routine) (void *), void *arg)
671556Srgrimes{
681556Srgrimes	int		f_gc = 0;
691556Srgrimes	int             ret = 0;
7051137Sgreen	pthread_t       gc_thread;
7151208Sgreen	pthread_t       new_thread;
721556Srgrimes	pthread_attr_t	pattr;
7351208Sgreen	void           *stack;
7454245Sgreen
751556Srgrimes	/*
761556Srgrimes	 * Locking functions in libc are required when there are
771556Srgrimes	 * threads other than the initial thread.
781556Srgrimes	 */
791556Srgrimes	__isthreaded = 1;
801556Srgrimes
811556Srgrimes	/* Allocate memory for the thread structure: */
821556Srgrimes	if ((new_thread = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
831556Srgrimes		/* Insufficient memory to create a thread: */
841556Srgrimes		ret = EAGAIN;
8557523Sgreen	} else {
861556Srgrimes		/* Check if default thread attributes are required: */
871556Srgrimes		if (attr == NULL || *attr == NULL) {
8857523Sgreen			/* Use the default thread attributes: */
891556Srgrimes			pattr = &pthread_attr_default;
901556Srgrimes		} else {
911556Srgrimes			pattr = *attr;
921556Srgrimes		}
931556Srgrimes		/* Check if a stack was specified in the thread attributes: */
941556Srgrimes		if ((stack = pattr->stackaddr_attr) != NULL) {
951556Srgrimes		}
961556Srgrimes		/* Allocate memory for a default-size stack: */
971556Srgrimes		else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
981556Srgrimes			struct stack	*spare_stack;
991556Srgrimes
1001556Srgrimes			/* Allocate or re-use a default-size stack. */
1011556Srgrimes
1021556Srgrimes			/*
1031556Srgrimes			 * Use the garbage collector mutex for synchronization
1041556Srgrimes			 * of the spare stack list.
1051556Srgrimes			 */
1061556Srgrimes			if (pthread_mutex_lock(&_gc_mutex) != 0)
10719720Sphk				PANIC("Cannot lock gc mutex");
10830230Seivind
10948051Sgreen			if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
1101556Srgrimes				/* Use the spare stack. */
1111556Srgrimes				SLIST_REMOVE_HEAD(&_stackq, qe);
1121556Srgrimes
1131556Srgrimes				/* Unlock the garbage collector mutex. */
1141556Srgrimes				if (pthread_mutex_unlock(&_gc_mutex) != 0)
1151556Srgrimes					PANIC("Cannot unlock gc mutex");
11648051Sgreen
11748051Sgreen				stack = sizeof(struct stack)
11848051Sgreen				    + (void *) spare_stack
1191556Srgrimes				    - PTHREAD_STACK_DEFAULT;
1201556Srgrimes			} else {
12148051Sgreen				/* Allocate a new stack. */
12248051Sgreen				stack = _next_stack + PTHREAD_STACK_GUARD;
1231556Srgrimes
1241556Srgrimes				/*
1251556Srgrimes				 * Even if stack allocation fails, we don't want
1261556Srgrimes				 * to try to use this location again, so
1271556Srgrimes				 * unconditionally decrement _next_stack.  Under
1281556Srgrimes				 * normal operating conditions, the most likely
1291556Srgrimes				 * reason for an mmap() error is a stack
1301556Srgrimes				 * overflow of the adjacent thread stack.
1311556Srgrimes				 */
1321556Srgrimes				_next_stack -= (PTHREAD_STACK_DEFAULT
1331556Srgrimes				    + PTHREAD_STACK_GUARD);
1341556Srgrimes
1351556Srgrimes				/* Unlock the garbage collector mutex. */
1361556Srgrimes				if (pthread_mutex_unlock(&_gc_mutex) != 0)
1371556Srgrimes					PANIC("Cannot unlock gc mutex");
1381556Srgrimes
1391556Srgrimes				/* Red zone: */
1401556Srgrimes				if (mmap(stack - PTHREAD_STACK_GUARD,
1411556Srgrimes				    PTHREAD_STACK_GUARD, 0, MAP_ANON,
1421556Srgrimes				    -1, 0) == MAP_FAILED) {
1431556Srgrimes					ret = EAGAIN;
1441556Srgrimes					free(new_thread);
1451556Srgrimes				}
1461556Srgrimes				/* Stack: */
14751249Sgreen				else if (mmap(stack, PTHREAD_STACK_DEFAULT,
1481556Srgrimes				    PROT_READ | PROT_WRITE, MAP_STACK,
1491556Srgrimes				    -1, 0) == MAP_FAILED) {
1501556Srgrimes					ret = EAGAIN;
1511556Srgrimes					munmap(stack - PTHREAD_STACK_GUARD,
1521556Srgrimes					    PTHREAD_STACK_GUARD);
1531556Srgrimes					free(new_thread);
15451249Sgreen				}
1551556Srgrimes			}
1561556Srgrimes		}
1571556Srgrimes		/*
1581556Srgrimes		 * The user wants a stack of a particular size.  Lets hope they
1591556Srgrimes		 * really know what they want, and simply malloc the stack.
1601556Srgrimes		 */
1611556Srgrimes		else if ((stack = (void *) malloc(pattr->stacksize_attr))
1621556Srgrimes		    == NULL) {
1631556Srgrimes			/* Insufficient memory to create a thread: */
1641556Srgrimes			ret = EAGAIN;
1651556Srgrimes			free(new_thread);
16651249Sgreen		}
16751208Sgreen
16851208Sgreen		/* Check for errors: */
16951208Sgreen		if (ret != 0) {
17051208Sgreen		} else {
17151208Sgreen			/* Initialise the thread structure: */
1721556Srgrimes			memset(new_thread, 0, sizeof(struct pthread));
1731556Srgrimes			new_thread->slice_usec = -1;
1741556Srgrimes			new_thread->sig_saved = 0;
1751556Srgrimes			new_thread->stack = stack;
1761556Srgrimes			new_thread->start_routine = start_routine;
1771556Srgrimes			new_thread->arg = arg;
1781556Srgrimes
17954278Sgreen			new_thread->cancelflags = PTHREAD_CANCEL_ENABLE |
18054278Sgreen			    PTHREAD_CANCEL_DEFERRED;
1811556Srgrimes
1821556Srgrimes			/*
1831556Srgrimes			 * Write a magic value to the thread structure
1841556Srgrimes			 * to help identify valid ones:
1851556Srgrimes			 */
1861556Srgrimes			new_thread->magic = PTHREAD_MAGIC;
18751208Sgreen
1881556Srgrimes			/* Initialise the thread for signals: */
18951208Sgreen			new_thread->sigmask = _thread_run->sigmask;
19051208Sgreen
19151208Sgreen			/* Initialise the jump buffer: */
19251208Sgreen			setjmp(new_thread->saved_jmp_buf);
1931556Srgrimes
1941556Srgrimes			/*
1951556Srgrimes			 * Set up new stack frame so that it looks like it
1961556Srgrimes			 * returned from a longjmp() to the beginning of
1971556Srgrimes			 * _thread_start().
1981556Srgrimes			 */
19951208Sgreen#if	defined(__FreeBSD__)
2001556Srgrimes#if	defined(__alpha__)
20151208Sgreen			new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start;
20251208Sgreen			new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0;
20351208Sgreen			new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start;
20451208Sgreen#else
2051556Srgrimes			new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start;
2061556Srgrimes#endif
2071556Srgrimes#elif	defined(__NetBSD__)
2081556Srgrimes#if	defined(__alpha__)
2091556Srgrimes			new_thread->saved_jmp_buf[2] = (long) _thread_start;
2101556Srgrimes			new_thread->saved_jmp_buf[4 + R_RA] = 0;
2111556Srgrimes			new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start;
21251137Sgreen#else
21351326Sgreen			new_thread->saved_jmp_buf[0] = (long) _thread_start;
21451326Sgreen#endif
21551335Sgreen#else
21651335Sgreen#error	"Don't recognize this operating system!"
2171556Srgrimes#endif
2181556Srgrimes
2191556Srgrimes			/* The stack starts high and builds down: */
2201556Srgrimes#if	defined(__FreeBSD__)
2211556Srgrimes#if	defined(__alpha__)
2221556Srgrimes			new_thread->saved_jmp_buf[0]._jb[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
2231556Srgrimes#else
22451137Sgreen			new_thread->saved_jmp_buf[0]._jb[2] = (int) (new_thread->stack + pattr->stacksize_attr - sizeof(double));
22551249Sgreen#endif
22651249Sgreen#elif	defined(__NetBSD__)
2271556Srgrimes#if	defined(__alpha__)
2281556Srgrimes			new_thread->saved_jmp_buf[4 + R_SP] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
2291556Srgrimes#else
2301556Srgrimes			new_thread->saved_jmp_buf[2] = (long) new_thread->stack + pattr->stacksize_attr - sizeof(double);
2311556Srgrimes#endif
2321556Srgrimes#else
23351208Sgreen#error	"Don't recognize this operating system!"
2341556Srgrimes#endif
23548051Sgreen
23651208Sgreen			/* Copy the thread attributes: */
23751208Sgreen			memcpy(&new_thread->attr, pattr, sizeof(struct pthread_attr));
23851249Sgreen
23951249Sgreen			/*
24048051Sgreen			 * Check if this thread is to inherit the scheduling
2411556Srgrimes			 * attributes from its parent:
2421556Srgrimes			 */
2431556Srgrimes			if (new_thread->attr.flags & PTHREAD_INHERIT_SCHED) {
2441556Srgrimes				/* Copy the scheduling attributes: */
2451556Srgrimes				new_thread->base_priority
2461556Srgrimes				    = _thread_run->base_priority;
2471556Srgrimes				new_thread->attr.prio
2481556Srgrimes				    = _thread_run->base_priority;
2491556Srgrimes				new_thread->attr.sched_policy
2501556Srgrimes				    = _thread_run->attr.sched_policy;
2511556Srgrimes			} else {
2521556Srgrimes				/*
2531556Srgrimes				 * Use just the thread priority, leaving the
2541556Srgrimes				 * other scheduling attributes as their
25551208Sgreen				 * default values:
2561556Srgrimes				 */
25748051Sgreen				new_thread->base_priority
25851208Sgreen				    = new_thread->attr.prio;
25951208Sgreen			}
26051208Sgreen			new_thread->active_priority = new_thread->base_priority;
26151208Sgreen			new_thread->inherited_priority = 0;
26248051Sgreen
2631556Srgrimes			/* Initialise the join queue for the new thread: */
2641556Srgrimes			TAILQ_INIT(&(new_thread->join_queue));
2651556Srgrimes
2661556Srgrimes			/* Initialize the mutex queue: */
2671556Srgrimes			TAILQ_INIT(&new_thread->mutexq);
2681556Srgrimes
2691556Srgrimes			/* Initialise hooks in the thread structure: */
2701556Srgrimes			new_thread->specific_data = NULL;
2711556Srgrimes			new_thread->cleanup = NULL;
2721556Srgrimes			new_thread->flags = 0;
2731556Srgrimes			new_thread->poll_data.nfds = 0;
2741556Srgrimes			new_thread->poll_data.fds = NULL;
2751556Srgrimes
2761556Srgrimes			/*
2771556Srgrimes			 * Defer signals to protect the scheduling queues
27851208Sgreen			 * from access by the signal handler:
2791556Srgrimes			 */
2801556Srgrimes			_thread_kern_sig_defer();
2811556Srgrimes
2821556Srgrimes			/*
2831556Srgrimes			 * Initialise the unique id which GDB uses to
2841556Srgrimes			 * track threads.
2851556Srgrimes			 */
28651208Sgreen			new_thread->uniqueid = next_uniqueid++;
2871556Srgrimes
2881556Srgrimes			/*
28951208Sgreen			 * Check if the garbage collector thread
29054245Sgreen			 * needs to be started.
2911556Srgrimes			 */
29251208Sgreen			f_gc = (TAILQ_FIRST(&_thread_list) == _thread_initial);
2931556Srgrimes
2941556Srgrimes			/* Add the thread to the linked list of all threads: */
2951556Srgrimes			TAILQ_INSERT_HEAD(&_thread_list, new_thread, tle);
2961556Srgrimes
2971556Srgrimes			if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
2981556Srgrimes				new_thread->state = PS_SUSPENDED;
2991556Srgrimes				PTHREAD_WAITQ_INSERT(new_thread);
3001556Srgrimes			} else {
3011556Srgrimes				new_thread->state = PS_RUNNING;
3021556Srgrimes				PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
3031556Srgrimes			}
30431120Sjoerg
30530312Sjoerg			/*
3061556Srgrimes			 * Undefer and handle pending signals, yielding
3071556Srgrimes			 * if necessary.
3081556Srgrimes			 */
3091556Srgrimes			_thread_kern_sig_undefer();
3101556Srgrimes
3111556Srgrimes			/* Return a pointer to the thread structure: */
3121556Srgrimes			(*thread) = new_thread;
3131556Srgrimes
3141556Srgrimes			/* Schedule the new user thread: */
3151556Srgrimes			_thread_kern_sched(NULL);
3161556Srgrimes
3171556Srgrimes			/*
3181556Srgrimes			 * Start a garbage collector thread
3191556Srgrimes			 * if necessary.
32051208Sgreen			 */
32151208Sgreen			if (f_gc && pthread_create(&gc_thread,NULL,
32251208Sgreen				    _thread_gc,NULL) != 0)
3231556Srgrimes				PANIC("Can't create gc thread");
3241556Srgrimes		}
3251556Srgrimes	}
3261556Srgrimes
3271556Srgrimes	/* Return the status: */
3281556Srgrimes	return (ret);
3291556Srgrimes}
3301556Srgrimes
3311556Srgrimesvoid
3321556Srgrimes_thread_start(void)
3331556Srgrimes{
3341556Srgrimes	/* We just left the scheduler via longjmp: */
3351556Srgrimes	_thread_kern_in_sched = 0;
3361556Srgrimes
33754278Sgreen	/* Run the current thread's start routine with argument: */
33854278Sgreen	pthread_exit(_thread_run->start_routine(_thread_run->arg));
3391556Srgrimes
3401556Srgrimes	/* This point should never be reached. */
3411556Srgrimes	PANIC("Thread has resumed after exit");
34248051Sgreen}
3431556Srgrimes#endif
34448051Sgreen