Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Atomic.h>
8#include <errno.h>
9#include <pthread.h>
10#include <unistd.h>
11
12#ifndef _DYNAMIC_LOADER
13extern "C" {
14
15static constexpr int max_keys = PTHREAD_KEYS_MAX;
16
17struct KeyTable {
18 KeyDestructor destructors[max_keys] { nullptr };
19 int next { 0 };
20 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
21};
22
23struct SpecificTable {
24 void* values[max_keys] { nullptr };
25};
26
27static KeyTable s_keys;
28
29__thread SpecificTable t_specifics;
30
31// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html
32int pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
33{
34 int ret = 0;
35 pthread_mutex_lock(&s_keys.mutex);
36 if (s_keys.next >= max_keys) {
37 ret = EAGAIN;
38 } else {
39 *key = s_keys.next++;
40 s_keys.destructors[*key] = destructor;
41 ret = 0;
42 }
43 pthread_mutex_unlock(&s_keys.mutex);
44 return ret;
45}
46
47// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_delete.html
48int pthread_key_delete(pthread_key_t key)
49{
50 if (key < 0 || key >= max_keys)
51 return EINVAL;
52 pthread_mutex_lock(&s_keys.mutex);
53 s_keys.destructors[key] = nullptr;
54 pthread_mutex_unlock(&s_keys.mutex);
55 return 0;
56}
57
58// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_getspecific.html
59void* pthread_getspecific(pthread_key_t key)
60{
61 if (key < 0)
62 return nullptr;
63 if (key >= max_keys)
64 return nullptr;
65 return t_specifics.values[key];
66}
67
68// https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_setspecific.html
69int pthread_setspecific(pthread_key_t key, void const* value)
70{
71 if (key < 0)
72 return EINVAL;
73 if (key >= max_keys)
74 return EINVAL;
75
76 t_specifics.values[key] = const_cast<void*>(value);
77 return 0;
78}
79
80void __pthread_key_destroy_for_current_thread()
81{
82 // This function will either be called during exit_thread, for a pthread, or
83 // during global program shutdown for the main thread.
84
85 pthread_mutex_lock(&s_keys.mutex);
86 size_t num_used_keys = s_keys.next;
87
88 // Dr. POSIX accounts for weird key destructors setting their own key again.
89 // Or even, setting other unrelated keys? Odd, but whatever the Doc says goes.
90
91 for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) {
92 bool any_nonnull_destructors = false;
93 for (size_t key_index = 0; key_index < num_used_keys; ++key_index) {
94 void* value = exchange(t_specifics.values[key_index], nullptr);
95
96 if (value && s_keys.destructors[key_index]) {
97 any_nonnull_destructors = true;
98 (*s_keys.destructors[key_index])(value);
99 }
100 }
101 if (!any_nonnull_destructors)
102 break;
103 }
104 pthread_mutex_unlock(&s_keys.mutex);
105}
106}
107#endif