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