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
34 int _rthread_debug_level;
35
36 static int _threads_inited;
37
38 struct pthread _initial_thread = {
39 .flags_lock = _SPINLOCK_UNLOCKED,
40 .name = "Original thread",
41 };
42
43 /*
44 * internal support functions
45 */
46 void
_spinlock(volatile _atomic_lock_t * lock)47 _spinlock(volatile _atomic_lock_t *lock)
48 {
49 while (_atomic_lock(lock))
50 sched_yield();
51 membar_enter_after_atomic();
52 }
53 DEF_STRONG(_spinlock);
54
55 int
_spinlocktry(volatile _atomic_lock_t * lock)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
65 void
_spinunlock(volatile _atomic_lock_t * lock)66 _spinunlock(volatile _atomic_lock_t *lock)
67 {
68 membar_exit();
69 *lock = _ATOMIC_LOCK_UNLOCKED;
70 }
71 DEF_STRONG(_spinunlock);
72
73 static void
_rthread_init(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 */
111 pthread_t
pthread_self(void)112 pthread_self(void)
113 {
114 if (__predict_false(!_threads_inited))
115 _rthread_init();
116
117 return TIB_GET()->tib_thread;
118 }
119 DEF_STRONG(pthread_self);
120
121 void
pthread_exit(void * retval)122 pthread_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 }
158 DEF_STRONG(pthread_exit);
159
160 int
pthread_equal(pthread_t t1,pthread_t t2)161 pthread_equal(pthread_t t1, pthread_t t2)
162 {
163 return (t1 == t2);
164 }
165
166