xref: /openbsd-src/lib/libc/thread/rthread_tls.c (revision 02c3c5b9f1530a0fae1c0f58b324fa9df88236f3)
1 /*	$OpenBSD: rthread_tls.c,v 1.5 2023/04/19 12:30:09 jsg 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  * thread specific storage
20  */
21 
22 #include <errno.h>
23 #include <pthread.h>
24 #include <stdlib.h>
25 
26 #include "rthread.h"
27 
28 
29 struct rthread_key {
30 	int used;
31 	void (*destructor)(void *);
32 };
33 
34 static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
35 static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED;
36 
37 int
pthread_key_create(pthread_key_t * key,void (* destructor)(void *))38 pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
39 {
40 	static int hint;
41 	int i;
42 
43 	_spinlock(&rkeyslock);
44 	if (rkeys[hint].used) {
45 		for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
46 			if (!rkeys[i].used)
47 				break;
48 		}
49 		if (i == PTHREAD_KEYS_MAX) {
50 			_spinunlock(&rkeyslock);
51 			return (EAGAIN);
52 		}
53 		hint = i;
54 	}
55 	rkeys[hint].used = 1;
56 	rkeys[hint].destructor = destructor;
57 
58 	*key = hint++;
59 	if (hint >= PTHREAD_KEYS_MAX)
60 		hint = 0;
61 	_spinunlock(&rkeyslock);
62 
63 	return (0);
64 }
65 DEF_STRONG(pthread_key_create);
66 
67 int
pthread_key_delete(pthread_key_t key)68 pthread_key_delete(pthread_key_t key)
69 {
70 	struct rthread_storage *rs;
71 	int rv = 0;
72 
73 	if (key < 0 || key >= PTHREAD_KEYS_MAX)
74 		return (EINVAL);
75 
76 	_spinlock(&rkeyslock);
77 	if (!rkeys[key].used) {
78 		rv = EINVAL;
79 		goto out;
80 	}
81 
82 	rkeys[key].used = 0;
83 	rkeys[key].destructor = NULL;
84 	if (_thread_cb.tc_thread_key_zero != NULL)
85 		_thread_cb.tc_thread_key_zero(key);
86 	else {
87 		for (rs = _initial_thread.local_storage; rs; rs = rs->next) {
88 			if (rs->keyid == key)
89 				rs->data = NULL;
90 		}
91 	}
92 
93 out:
94 	_spinunlock(&rkeyslock);
95 	return (rv);
96 }
97 
98 static struct rthread_storage *
_rthread_findstorage(pthread_key_t key)99 _rthread_findstorage(pthread_key_t key)
100 {
101 	struct rthread_storage *rs;
102 	pthread_t self;
103 
104 	if (!rkeys[key].used) {
105 		rs = NULL;
106 		goto out;
107 	}
108 
109 	self = pthread_self();
110 
111 	for (rs = self->local_storage; rs; rs = rs->next) {
112 		if (rs->keyid == key)
113 			break;
114 	}
115 	if (!rs) {
116 		rs = calloc(1, sizeof(*rs));
117 		if (!rs)
118 			goto out;
119 		rs->keyid = key;
120 		rs->data = NULL;
121 		rs->next = self->local_storage;
122 		self->local_storage = rs;
123 	}
124 
125 out:
126 	return (rs);
127 }
128 
129 void *
pthread_getspecific(pthread_key_t key)130 pthread_getspecific(pthread_key_t key)
131 {
132 	struct rthread_storage *rs;
133 
134 	if (key < 0 || key >= PTHREAD_KEYS_MAX)
135 		return (NULL);
136 
137 	rs = _rthread_findstorage(key);
138 	if (!rs)
139 		return (NULL);
140 
141 	return (rs->data);
142 }
143 DEF_STRONG(pthread_getspecific);
144 
145 int
pthread_setspecific(pthread_key_t key,const void * data)146 pthread_setspecific(pthread_key_t key, const void *data)
147 {
148 	struct rthread_storage *rs;
149 
150 	if (key < 0 || key >= PTHREAD_KEYS_MAX)
151 		return (EINVAL);
152 
153 	rs = _rthread_findstorage(key);
154 	if (!rs)
155 		return (ENOMEM);
156 	rs->data = (void *)data;
157 
158 	return (0);
159 }
160 DEF_STRONG(pthread_setspecific);
161 
162 void
_rthread_tls_destructors(pthread_t thread)163 _rthread_tls_destructors(pthread_t thread)
164 {
165 	struct rthread_storage *rs;
166 	int i;
167 
168 	_spinlock(&rkeyslock);
169 	for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; i++) {
170 		for (rs = thread->local_storage; rs; rs = rs->next) {
171 			if (!rs->data)
172 				continue;
173 			if (rkeys[rs->keyid].destructor) {
174 				void (*destructor)(void *) =
175 				    rkeys[rs->keyid].destructor;
176 				void *data = rs->data;
177 				rs->data = NULL;
178 				_spinunlock(&rkeyslock);
179 				destructor(data);
180 				_spinlock(&rkeyslock);
181 			}
182 		}
183 	}
184 	for (rs = thread->local_storage; rs; rs = thread->local_storage) {
185 		thread->local_storage = rs->next;
186 		free(rs);
187 	}
188 	_spinunlock(&rkeyslock);
189 }
190