thr_init.c revision 123859
1/*
2 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libthr/thread/thr_init.c 123859 2003-12-26 08:16:17Z mtm $
33 */
34
35/* Allocate space for global thread variables here: */
36#define GLOBAL_PTHREAD_PRIVATE
37
38#include "namespace.h"
39#include <sys/param.h>
40#include <sys/types.h>
41#include <machine/reg.h>
42
43#include <sys/ioctl.h>
44#include <sys/mount.h>
45#include <sys/uio.h>
46#include <sys/socket.h>
47#include <sys/event.h>
48#include <sys/stat.h>
49#include <sys/sysctl.h>
50#include <sys/time.h>
51#include <sys/ttycom.h>
52#include <sys/user.h>
53#include <sys/wait.h>
54#include <sys/mman.h>
55#include <dirent.h>
56#include <errno.h>
57#include <fcntl.h>
58#include <paths.h>
59#include <pthread.h>
60#include <signal.h>
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#include <unistd.h>
65#include "un-namespace.h"
66
67#include "thr_private.h"
68
69/*
70 * Early implementations of sigtimedwait interpreted the signal
71 * set incorrectly.
72 */
73#define SIGTIMEDWAIT_SET_IS_INVERTED(osreldate) \
74    ((500100 <= (osreldate) && (osreldate) <= 500113) || \
75    (osreldate) == 501000 || (osreldate) == 501100)
76
77extern void _thread_init_hack(void);
78
79/*
80 * All weak references used within libc should be in this table.
81 * This will is so that static libraries will work.
82 *
83 * XXXTHR - Check this list.
84 */
85static void *references[] = {
86	&_thread_init_hack,
87	&_thread_init,
88	&_accept,
89	&_bind,
90	&_close,
91	&_connect,
92	&_dup,
93	&_dup2,
94	&_execve,
95	&_fcntl,
96	&_flock,
97	&_flockfile,
98	&_fstat,
99	&_fstatfs,
100	&_fsync,
101	&_funlockfile,
102	&_getdirentries,
103	&_getlogin,
104	&_getpeername,
105	&_getsockname,
106	&_getsockopt,
107	&_ioctl,
108	&_kevent,
109	&_listen,
110	&_nanosleep,
111	&_open,
112	&_pthread_getspecific,
113	&_pthread_key_create,
114	&_pthread_key_delete,
115	&_pthread_mutex_destroy,
116	&_pthread_mutex_init,
117	&_pthread_mutex_lock,
118	&_pthread_mutex_trylock,
119	&_pthread_mutex_unlock,
120	&_pthread_mutexattr_init,
121	&_pthread_mutexattr_destroy,
122	&_pthread_mutexattr_settype,
123	&_pthread_once,
124	&_pthread_setspecific,
125	&_read,
126	&_readv,
127	&_recvfrom,
128	&_recvmsg,
129	&_select,
130	&_sendmsg,
131	&_sendto,
132	&_setsockopt,
133	&_sigaction,
134	&_sigprocmask,
135	&_sigsuspend,
136	&_socket,
137	&_socketpair,
138	&_wait4,
139	&_write,
140	&_writev
141};
142
143/*
144 * These are needed when linking statically.  All references within
145 * libgcc (and in the future libc) to these routines are weak, but
146 * if they are not (strongly) referenced by the application or other
147 * libraries, then the actual functions will not be loaded.
148 */
149static void *libgcc_references[] = {
150	&_thread_init_hack,
151	&_thread_init,
152	&_pthread_once,
153	&_pthread_key_create,
154	&_pthread_key_delete,
155	&_pthread_getspecific,
156	&_pthread_setspecific,
157	&_pthread_mutex_init,
158	&_pthread_mutex_destroy,
159	&_pthread_mutex_lock,
160	&_pthread_mutex_trylock,
161	&_pthread_mutex_unlock
162};
163
164int _pthread_guard_default;
165int _pthread_page_size;
166
167/*
168 * Initialize the current thread.
169 */
170void
171init_td_common(struct pthread *td, struct pthread_attr *attrp, int reinit)
172{
173	/*
174	 * Some parts of a pthread are initialized only once.
175	 */
176	if (!reinit) {
177		memset(td, 0, sizeof(struct pthread));
178		td->cancelflags = PTHREAD_CANCEL_ENABLE |
179		    PTHREAD_CANCEL_DEFERRED;
180		memcpy(&td->attr, attrp, sizeof(struct pthread_attr));
181		td->magic = PTHREAD_MAGIC;
182		TAILQ_INIT(&td->mutexq);
183	} else {
184		memset(&td->join_status, 0, sizeof(struct join_status));
185	}
186	td->joiner = NULL;
187	td->error = 0;
188	td->flags = 0;
189}
190
191/*
192 * Initialize the active and dead threads list. Any threads in the active
193 * list will be removed and the thread td * will be marked as the
194 * initial thread and inserted in the list as the only thread. Any threads
195 * in the dead threads list will also be removed.
196 */
197void
198init_tdlist(struct pthread *td, int reinit)
199{
200	struct pthread *tdTemp, *tdTemp2;
201
202	_thread_initial = td;
203	td->name = strdup("_thread_initial");
204
205	/*
206	 * If this is not the first initialization, remove any entries
207	 * that may be in the list and deallocate their memory. Also
208	 * destroy any global pthread primitives (they will be recreated).
209	 */
210	if (reinit) {
211		TAILQ_FOREACH_SAFE(tdTemp, &_thread_list, tle, tdTemp2) {
212			if (tdTemp != NULL) {
213				TAILQ_REMOVE(&_thread_list, tdTemp, tle);
214				free(tdTemp);
215			}
216		}
217		TAILQ_FOREACH_SAFE(tdTemp, &_dead_list, dle, tdTemp2) {
218			if (tdTemp != NULL) {
219				TAILQ_REMOVE(&_dead_list, tdTemp, dle);
220				free(tdTemp);
221			}
222		}
223		_pthread_mutex_destroy(&dead_list_lock);
224		_pthread_cond_destroy(&_gc_cond);
225	} else {
226		TAILQ_INIT(&_thread_list);
227		TAILQ_INIT(&_dead_list);
228	}
229
230	/* Insert this thread as the first thread in the active list */
231	TAILQ_INSERT_HEAD(&_thread_list, td, tle);
232
233	/*
234	 * Initialize the active thread list lock and the
235	 * dead threads list lock & associated condition variable.
236	 */
237	memset(&thread_list_lock, 0, sizeof(spinlock_t));
238	if (_pthread_mutex_init(&dead_list_lock,NULL) != 0 ||
239	    _pthread_cond_init(&_gc_cond,NULL) != 0)
240		PANIC("Failed to initialize garbage collector primitives");
241}
242
243/*
244 * Threaded process initialization
245 */
246void
247_thread_init(void)
248{
249	struct pthread	*pthread;
250	int		fd;
251	int             i;
252	size_t		len;
253	int		mib[2];
254	sigset_t	set;
255	int		osreldate;
256	int		error;
257
258	struct clockinfo clockinfo;
259	struct sigaction act;
260
261	/* Check if this function has already been called: */
262	if (_thread_initial)
263		/* Only initialise the threaded application once. */
264		return;
265
266	_pthread_page_size = getpagesize();
267	_pthread_guard_default = getpagesize();
268
269	pthread_attr_default.guardsize_attr = _pthread_guard_default;
270
271
272	/*
273	 * Make gcc quiescent about {,libgcc_}references not being
274	 * referenced:
275	 */
276	if ((references[0] == NULL) || (libgcc_references[0] == NULL))
277		PANIC("Failed loading mandatory references in _thread_init");
278
279	/*
280	 * Check for the special case of this process running as
281	 * or in place of init as pid = 1:
282	 */
283	if (getpid() == 1) {
284		/*
285		 * Setup a new session for this process which is
286		 * assumed to be running as root.
287		 */
288		if (setsid() == -1)
289			PANIC("Can't set session ID");
290		if (revoke(_PATH_CONSOLE) != 0)
291			PANIC("Can't revoke console");
292		if ((fd = __sys_open(_PATH_CONSOLE, O_RDWR)) < 0)
293			PANIC("Can't open console");
294		if (setlogin("root") == -1)
295			PANIC("Can't set login to root");
296		if (__sys_ioctl(fd, TIOCSCTTY, (char *) NULL) == -1)
297			PANIC("Can't set controlling terminal");
298		if (__sys_dup2(fd, 0) == -1 ||
299		    __sys_dup2(fd, 1) == -1 ||
300		    __sys_dup2(fd, 2) == -1)
301			PANIC("Can't dup2");
302	}
303
304	/* Allocate memory for the thread structure of the initial thread: */
305	if ((pthread = (pthread_t) malloc(sizeof(struct pthread))) == NULL) {
306		/*
307		 * Insufficient memory to initialise this application, so
308		 * abort:
309		 */
310		PANIC("Cannot allocate memory for initial thread");
311	}
312
313	init_tdlist(pthread, 0);
314	init_td_common(pthread, &pthread_attr_default, 0);
315	pthread->arch_id = _set_curthread(NULL, pthread, &error);
316
317	/* Get our thread id. */
318	thr_self(&pthread->thr_id);
319
320	/* Find the stack top */
321	mib[0] = CTL_KERN;
322	mib[1] = KERN_USRSTACK;
323	len = sizeof (_usrstack);
324	if (sysctl(mib, 2, &_usrstack, &len, NULL, 0) == -1)
325		_usrstack = (void *)USRSTACK;
326	/*
327	 * Create a red zone below the main stack.  All other stacks are
328	 * constrained to a maximum size by the paramters passed to
329	 * mmap(), but this stack is only limited by resource limits, so
330	 * this stack needs an explicitly mapped red zone to protect the
331	 * thread stack that is just beyond.
332	 */
333	if (mmap(_usrstack - PTHREAD_STACK_INITIAL -
334	    _pthread_guard_default, _pthread_guard_default, 0,
335	    MAP_ANON, -1, 0) == MAP_FAILED)
336		PANIC("Cannot allocate red zone for initial thread");
337
338	/* Set the main thread stack pointer. */
339	pthread->stack = _usrstack - PTHREAD_STACK_INITIAL;
340
341	/* Set the stack attributes. */
342	pthread->attr.stackaddr_attr = pthread->stack;
343	pthread->attr.stacksize_attr = PTHREAD_STACK_INITIAL;
344
345	/* Setup the context for initial thread. */
346	getcontext(&pthread->ctx);
347	pthread->ctx.uc_stack.ss_sp = pthread->stack;
348	pthread->ctx.uc_stack.ss_size = PTHREAD_STACK_INITIAL;
349
350	/* Default the priority of the initial thread: */
351	pthread->base_priority = PTHREAD_DEFAULT_PRIORITY;
352	pthread->active_priority = PTHREAD_DEFAULT_PRIORITY;
353	pthread->inherited_priority = 0;
354
355	/* Initialise the state of the initial thread: */
356	pthread->state = PS_RUNNING;
357
358	/* Enter a loop to get the existing signal status: */
359	for (i = 1; i < NSIG; i++) {
360		/* Check for signals which cannot be trapped. */
361		if (i == SIGKILL || i == SIGSTOP)
362			continue;
363
364		/* Get the signal handler details. */
365		if (__sys_sigaction(i, NULL,
366		    &_thread_sigact[i - 1]) != 0)
367			PANIC("Cannot read signal handler info");
368	}
369	act.sa_sigaction = _thread_sig_wrapper;
370	act.sa_flags = SA_SIGINFO;
371	SIGFILLSET(act.sa_mask);
372
373	if (__sys_sigaction(SIGTHR, &act, NULL))
374		PANIC("Cannot set SIGTHR handler.\n");
375
376	SIGEMPTYSET(set);
377	SIGADDSET(set, SIGTHR);
378	__sys_sigprocmask(SIG_BLOCK, &set, 0);
379
380	/*
381	 * Precompute the signal set used by _thread_suspend to wait
382	 * for SIGTHR.
383	 */
384	mib[0] = CTL_KERN;
385	mib[1] = KERN_OSRELDATE;
386	len = sizeof(osreldate);
387	if (sysctl(mib, 2, &osreldate, &len, NULL, 0) == 0 &&
388	    SIGTIMEDWAIT_SET_IS_INVERTED(osreldate)) {
389		/* Kernel bug requires an inverted signal set. */
390		SIGFILLSET(_thread_suspend_sigset);
391		SIGDELSET(_thread_suspend_sigset, SIGTHR);
392	} else {
393		SIGEMPTYSET(_thread_suspend_sigset);
394		SIGADDSET(_thread_suspend_sigset, SIGTHR);
395	}
396#ifdef _PTHREADS_INVARIANTS
397	SIGADDSET(_thread_suspend_sigset, SIGALRM);
398#endif
399
400	/* Get the kernel clockrate: */
401	mib[0] = CTL_KERN;
402	mib[1] = KERN_CLOCKRATE;
403	len = sizeof (struct clockinfo);
404	if (sysctl(mib, 2, &clockinfo, &len, NULL, 0) == 0)
405		_clock_res_usec = clockinfo.tick > CLOCK_RES_USEC_MIN ?
406		    clockinfo.tick : CLOCK_RES_USEC_MIN;
407}
408
409/*
410 * Special start up code for NetBSD/Alpha
411 */
412#if	defined(__NetBSD__) && defined(__alpha__)
413int
414main(int argc, char *argv[], char *env);
415
416int
417_thread_main(int argc, char *argv[], char *env)
418{
419	_thread_init();
420	return (main(argc, argv, env));
421}
422#endif
423