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
29struct rthread_key {
30	int used;
31	void (*destructor)(void *);
32};
33
34static struct rthread_key rkeys[PTHREAD_KEYS_MAX];
35static _atomic_lock_t rkeyslock = _SPINLOCK_UNLOCKED;
36
37int
38pthread_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}
65DEF_STRONG(pthread_key_create);
66
67int
68pthread_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
93out:
94	_spinunlock(&rkeyslock);
95	return (rv);
96}
97
98static struct rthread_storage *
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
125out:
126	return (rs);
127}
128
129void *
130pthread_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}
143DEF_STRONG(pthread_getspecific);
144
145int
146pthread_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}
160DEF_STRONG(pthread_setspecific);
161
162void
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