rthread_tls.c revision 1.2
138032Speter/*	$OpenBSD: rthread_tls.c,v 1.2 2017/08/15 06:38:41 guenther Exp $ */
238032Speter/*
364562Sgshapiro * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org>
464562Sgshapiro * All Rights Reserved.
538032Speter *
638032Speter * Permission to use, copy, modify, and distribute this software for any
738032Speter * purpose with or without fee is hereby granted, provided that the above
838032Speter * copyright notice and this permission notice appear in all copies.
938032Speter *
1038032Speter * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1138032Speter * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1238032Speter * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1338032Speter * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1438032Speter * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1538032Speter * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1638032Speter * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1738032Speter */
1838032Speter/*
1938032Speter * thread specific storage
2038032Speter */
2138032Speter
2238032Speter#include <errno.h>
2364562Sgshapiro#include <pthread.h>
2438032Speter#include <stdlib.h>
2538032Speter
2638032Speter#include "rthread.h"
2738032Speter
28static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
29static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED;
30
31int
32pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
33{
34	static int hint;
35	int i;
36
37	_spinlock(&rkeyslock);
38	if (rkeys[hint].used) {
39		for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
40			if (!rkeys[i].used)
41				break;
42		}
43		if (i == PTHREAD_KEYS_MAX) {
44			_spinunlock(&rkeyslock);
45			return (EAGAIN);
46		}
47		hint = i;
48	}
49	rkeys[hint].used = 1;
50	rkeys[hint].destructor = destructor;
51
52	*key = hint++;
53	if (hint >= PTHREAD_KEYS_MAX)
54		hint = 0;
55	_spinunlock(&rkeyslock);
56
57	return (0);
58}
59DEF_STD(pthread_key_create);
60
61int
62pthread_key_delete(pthread_key_t key)
63{
64	pthread_t thread;
65	struct rthread_storage *rs;
66	int rv = 0;
67
68	if (key < 0 || key >= PTHREAD_KEYS_MAX)
69		return (EINVAL);
70
71	_spinlock(&rkeyslock);
72	if (!rkeys[key].used) {
73		rv = EINVAL;
74		goto out;
75	}
76
77	rkeys[key].used = 0;
78	rkeys[key].destructor = NULL;
79	_spinlock(&_thread_lock);
80	LIST_FOREACH(thread, &_thread_list, threads) {
81		for (rs = thread->local_storage; rs; rs = rs->next) {
82			if (rs->keyid == key)
83				rs->data = NULL;
84		}
85	}
86	_spinunlock(&_thread_lock);
87
88out:
89	_spinunlock(&rkeyslock);
90	return (rv);
91}
92
93static struct rthread_storage *
94_rthread_findstorage(pthread_key_t key)
95{
96	struct rthread_storage *rs;
97	pthread_t self;
98
99	if (!rkeys[key].used) {
100		rs = NULL;
101		goto out;
102	}
103
104	self = pthread_self();
105
106	for (rs = self->local_storage; rs; rs = rs->next) {
107		if (rs->keyid == key)
108			break;
109	}
110	if (!rs) {
111		rs = calloc(1, sizeof(*rs));
112		if (!rs)
113			goto out;
114		rs->keyid = key;
115		rs->data = NULL;
116		rs->next = self->local_storage;
117		self->local_storage = rs;
118	}
119
120out:
121	return (rs);
122}
123
124void *
125pthread_getspecific(pthread_key_t key)
126{
127	struct rthread_storage *rs;
128
129	if (key < 0 || key >= PTHREAD_KEYS_MAX)
130		return (NULL);
131
132	rs = _rthread_findstorage(key);
133	if (!rs)
134		return (NULL);
135
136	return (rs->data);
137}
138DEF_STD(pthread_getspecific);
139
140int
141pthread_setspecific(pthread_key_t key, const void *data)
142{
143	struct rthread_storage *rs;
144
145	if (key < 0 || key >= PTHREAD_KEYS_MAX)
146		return (EINVAL);
147
148	rs = _rthread_findstorage(key);
149	if (!rs)
150		return (ENOMEM);
151	rs->data = (void *)data;
152
153	return (0);
154}
155DEF_STD(pthread_setspecific);
156
157void
158_rthread_tls_destructors(pthread_t thread)
159{
160	struct rthread_storage *rs;
161	int i;
162
163	_spinlock(&rkeyslock);
164	for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS; i++) {
165		for (rs = thread->local_storage; rs; rs = rs->next) {
166			if (!rs->data)
167				continue;
168			if (rkeys[rs->keyid].destructor) {
169				void (*destructor)(void *) =
170				    rkeys[rs->keyid].destructor;
171				void *data = rs->data;
172				rs->data = NULL;
173				_spinunlock(&rkeyslock);
174				destructor(data);
175				_spinlock(&rkeyslock);
176			}
177		}
178	}
179	for (rs = thread->local_storage; rs; rs = thread->local_storage) {
180		thread->local_storage = rs->next;
181		free(rs);
182	}
183	_spinunlock(&rkeyslock);
184}
185