1/*	$OpenBSD: rthread.c,v 1.9 2020/10/12 22:06:51 deraadt Exp $ */
2/*
3 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18/*
19 * The infrastructure of rthreads
20 */
21
22#include <sys/types.h>
23#include <sys/atomic.h>
24
25#include <pthread.h>
26#include <stdlib.h>
27#include <tib.h>
28#include <unistd.h>
29
30#include "rthread.h"
31
32#define RTHREAD_ENV_DEBUG	"RTHREAD_DEBUG"
33
34int _rthread_debug_level;
35
36static int _threads_inited;
37
38struct pthread _initial_thread = {
39	.flags_lock = _SPINLOCK_UNLOCKED,
40	.name = "Original thread",
41};
42
43/*
44 * internal support functions
45 */
46void
47_spinlock(volatile _atomic_lock_t *lock)
48{
49	while (_atomic_lock(lock))
50		sched_yield();
51	membar_enter_after_atomic();
52}
53DEF_STRONG(_spinlock);
54
55int
56_spinlocktry(volatile _atomic_lock_t *lock)
57{
58	if (_atomic_lock(lock) == 0) {
59		membar_enter_after_atomic();
60		return 1;
61	}
62	return 0;
63}
64
65void
66_spinunlock(volatile _atomic_lock_t *lock)
67{
68	membar_exit();
69	*lock = _ATOMIC_LOCK_UNLOCKED;
70}
71DEF_STRONG(_spinunlock);
72
73static void
74_rthread_init(void)
75{
76	pthread_t thread = &_initial_thread;
77	struct tib *tib;
78
79	if (_threads_inited)
80		return;
81
82	tib = TIB_GET();
83	tib->tib_thread = thread;
84	thread->tib = tib;
85
86	thread->donesem.lock = _SPINLOCK_UNLOCKED;
87	tib->tib_thread_flags = TIB_THREAD_INITIAL_STACK;
88
89	/*
90	 * Set the debug level from an environment string.
91	 * Bogus values are silently ignored.
92	 */
93	if (!issetugid()) {
94		char *envp = getenv(RTHREAD_ENV_DEBUG);
95
96		if (envp != NULL) {
97			char *rem;
98
99			_rthread_debug_level = (int) strtol(envp, &rem, 0);
100			if (*rem != '\0' || _rthread_debug_level < 0)
101				_rthread_debug_level = 0;
102		}
103	}
104
105	_threads_inited = 1;
106}
107
108/*
109 * real pthread functions
110 */
111pthread_t
112pthread_self(void)
113{
114	if (__predict_false(!_threads_inited))
115		_rthread_init();
116
117	return TIB_GET()->tib_thread;
118}
119DEF_STRONG(pthread_self);
120
121void
122pthread_exit(void *retval)
123{
124	struct rthread_cleanup_fn *clfn;
125	struct tib *tib;
126	pthread_t thread = pthread_self();
127
128	tib = thread->tib;
129
130	if (tib->tib_cantcancel & CANCEL_DYING) {
131		/*
132		 * Called pthread_exit() from destructor or cancelation
133		 * handler: blow up.  XXX write something to stderr?
134		 */
135		abort();
136		//_exit(42);
137	}
138
139	tib->tib_cantcancel |= CANCEL_DYING;
140
141	thread->retval = retval;
142
143	for (clfn = thread->cleanup_fns; clfn; ) {
144		struct rthread_cleanup_fn *oclfn = clfn;
145		clfn = clfn->next;
146		oclfn->fn(oclfn->arg);
147		free(oclfn);
148	}
149	_thread_finalize();
150	_rthread_tls_destructors(thread);
151
152	if (_thread_cb.tc_thread_release != NULL)
153		_thread_cb.tc_thread_release(thread);
154
155	__threxit(&tib->tib_tid);
156	for(;;);
157}
158DEF_STRONG(pthread_exit);
159
160int
161pthread_equal(pthread_t t1, pthread_t t2)
162{
163	return (t1 == t2);
164}
165
166