Blame view

RIOT/sys/posix/pthread/pthread_tls.c 4.83 KB
a752c7ab   elopes   add first test an...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  /*
   * Copyright (C) 2014 Hamburg University of Applied Sciences (HAW)
   *
   * This file is subject to the terms and conditions of the GNU Lesser
   * General Public License v2.1. See the file LICENSE in the top level
   * directory for more details.
   */
  
  /**
   * @ingroup pthread
   * @{
   * @file
   * @brief       RIOT POSIX thread local storage
   * @author      Martin Landsmann <martin.landsmann@haw-hamburg.de>
   * @author      René Kijewski <rene.kijewski@fu-berlin.de>
   * @}
   */
  
  #include "pthread.h"
  
  #ifdef HAVE_MALLOC_H
  #include <malloc.h>
  #endif
  
  #define ENABLE_DEBUG (0)
  #include "debug.h"
  
  typedef struct __pthread_tls_datum {
      pthread_key_t key;
      struct __pthread_tls_datum *next;
      void *value;
  } tls_data_t;
  
  struct __pthread_tls_key {
      void (*destructor)(void *);
  };
  
  /**
   * @brief   Used while manipulating the TLS of a pthread.
   */
  static mutex_t tls_mutex;
  
  /**
   * @brief        Find a thread-specific datum.
   * @param[in]    tls    Pointer to the list of the thread-specific datums.
   * @param[in]    key    The key to look up.
   * @param[out]   prev   The datum before the result. `NULL` if the result is the first key. Spurious if the key was not found.
   * @returns      The datum or `NULL`.
   */
  static tls_data_t *find_specific(tls_data_t **tls, pthread_key_t key, tls_data_t **prev)
  {
      tls_data_t *specific = *tls;
      *prev = NULL;
  
      while (specific) {
          if (specific->key == key) {
              return specific;
          }
  
          *prev = specific;
          specific = specific->next;
      }
  
      return 0;
  }
  
  /**
   * @brief       Find or allocate a thread specific datum.
   * @details     The `key` must be initialized.
   *              The result will be the head of the thread-specific datums afterwards.
   * @param[in]   key   The key to lookup.
   * @returns     The datum. `NULL` on ENOMEM or if the caller is not a pthread.
   */
  static tls_data_t *get_specific(pthread_key_t key)
  {
      pthread_t self_id = pthread_self();
      if (self_id == 0) {
          DEBUG("ERROR called pthread_self() returned 0 in \"%s\"!\n", __func__);
          return NULL;
      }
  
      tls_data_t **tls = __pthread_get_tls_head(self_id);
      tls_data_t *prev, *specific = find_specific(tls, key, &prev);
  
      /* Did the datum already exist? */
      if (specific) {
          if (prev) {
              /* Move the datum to the front for a faster next lookup. */
              /* Let's pretend that we have a totally degenerated splay tree. ;-) */
              prev->next = specific->next;
              specific->next = *tls;
              *tls = specific;
          }
          return specific;
      }
  
      /* Allocate new datum. */
      specific = malloc(sizeof (*specific));
      if (specific) {
          specific->key = key;
          specific->next = *tls;
          specific->value = NULL;
          *tls = specific;
      }
      else {
          DEBUG("ERROR out of memory in %s!\n", __func__);
      }
      return specific;
  }
  
  int pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
  {
      *key = malloc(sizeof (**key));
      if (!*key) {
          return ENOMEM;
      }
  
      (*key)->destructor = destructor;
      return 0;
  }
  
  int pthread_key_delete(pthread_key_t key)
  {
      if (!key) {
          return EINVAL;
      }
  
      mutex_lock(&tls_mutex);
      for (unsigned i = 1; i <= MAXTHREADS; ++i) {
          tls_data_t **tls = __pthread_get_tls_head(i);
          if (!tls) {
              continue;
          }
  
          tls_data_t *prev, *specific = find_specific(tls, key, &prev);
          if (specific) {
              if (prev) {
                  prev->next = specific->next;
              }
              else {
                  *tls = specific->next;
              }
              free(specific);
          }
      }
      mutex_unlock(&tls_mutex);
  
      return 0;
  }
  
  void *pthread_getspecific(pthread_key_t key)
  {
      if (!key) {
          return NULL;
      }
  
      mutex_lock(&tls_mutex);
      tls_data_t *specific = get_specific(key);
      void *result = specific ? specific->value : NULL;
      mutex_unlock(&tls_mutex);
  
      return result;
  }
  
  int pthread_setspecific(pthread_key_t key, const void *value)
  {
      if (!key) {
          return EINVAL;
      }
  
      mutex_lock(&tls_mutex);
      tls_data_t *specific = get_specific(key);
      if (specific) {
          specific->value = (void *) value;
      }
      mutex_unlock(&tls_mutex);
  
      return specific ? 0 : ENOMEM;
  }
  
  void __pthread_keys_exit(int self_id)
  {
      tls_data_t **tls = __pthread_get_tls_head(self_id);
  
      /* Calling the dtor could cause another pthread_exit(), so we dehead and free defore calling it. */
      mutex_lock(&tls_mutex);
      for (tls_data_t *specific; (specific = *tls); ) {
          *tls = specific->next;
          void *value = specific->value;
          void (*destructor)(void *) = specific->key->destructor;
          free(specific);
  
          if (value && destructor) {
              mutex_unlock(&tls_mutex);
              destructor(value);
              mutex_lock(&tls_mutex);
          }
      }
      mutex_unlock(&tls_mutex);
  }