rthread_tls.c revision 1.4
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
32struct rthread_key {
33	int used;
34	void (*destructor)(void *);
35};
36
37static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
38static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED;
39
40int
41pthread_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}
68DEF_STRONG(pthread_key_create);
69
70int
71pthread_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
96out:
97	_spinunlock(&rkeyslock);
98	return (rv);
99}
100
101static 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
128out:
129	return (rs);
130}
131
132void *
133pthread_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}
146DEF_STRONG(pthread_getspecific);
147
148int
149pthread_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}
163DEF_STRONG(pthread_setspecific);
164
165void
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