Default signal handlers are sigactions
[akaros.git] / user / parlib / dtls.c
1 /*
2  * Copyright (c) 2012 The Regents of the University of California
3  * Kevin Klues <klueska@cs.berkeley.edu>
4  *
5  * This file is part of Parlib.
6  *
7  * Parlib is free software: you can redistribute it and/or modify
8  * it under the terms of the Lesser GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  * 
12  * Parlib is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * Lesser GNU General Public License for more details.
16  * 
17  * See COPYING.LESSER for details on the GNU Lesser General Public License.
18  * See COPYING for details on the GNU General Public License.
19  */
20
21 #include <stddef.h>
22 #include <assert.h>
23 #include <spinlock.h>
24 #include <dtls.h>
25 #include <slab.h>
26
27 /* The current dymamic tls implementation uses a locked linked list
28  * to find the key for a given thread. We should probably find a better way to
29  * do this based on a custom lock-free hash table or something. */
30 #include <sys/queue.h>
31 #include <spinlock.h>
32
33 /* The dynamic tls key structure */
34 struct dtls_key {
35   spinpdrlock_t lock;
36   int ref_count;
37   bool valid;
38   void (*dtor)(void*);
39 };
40
41 /* The definition of a dtls_key list and its elements */
42 struct dtls_value {
43   TAILQ_ENTRY(dtls_value) link;
44   struct dtls_key *key;
45   void *dtls;
46 }; 
47 TAILQ_HEAD(dtls_list, dtls_value);
48
49 /* A struct containing all of the per thread (i.e. vcore or uthread) data
50  * associated with dtls */
51 typedef struct dtls_data {
52   /* A per-thread list of dtls regions */
53   struct dtls_list list;
54
55 } dtls_data_t;
56
57 /* A slab of dtls keys (global to all threads) */
58 static struct kmem_cache *__dtls_keys_cache;
59
60 /* A slab of values for use when mapping a dtls_key to its per-thread value */
61 struct kmem_cache *__dtls_values_cache;
62   
63 /* A lock protecting access to the caches above */
64 static spinpdrlock_t __dtls_lock;
65
66 static __thread dtls_data_t __dtls_data;
67 static __thread bool __dtls_initialized = false;
68
69 static dtls_key_t __allocate_dtls_key() 
70 {
71   spin_pdr_lock(&__dtls_lock);
72   dtls_key_t key = kmem_cache_alloc(__dtls_keys_cache, 0);
73   assert(key);
74   key->ref_count = 1;
75   spin_pdr_unlock(&__dtls_lock);
76   return key;
77 }
78
79 static void __maybe_free_dtls_key(dtls_key_t key)
80 {
81   if(key->ref_count == 0) {
82     spin_pdr_lock(&__dtls_lock);
83     kmem_cache_free(__dtls_keys_cache, key);
84     spin_pdr_unlock(&__dtls_lock);
85   }
86 }
87
88 /* Constructor to get a reference to the main thread's TLS descriptor */
89 int dtls_lib_init()
90 {
91   /* Make sure this only runs once */
92   static bool initialized = false;
93   if (initialized)
94       return 0;
95   initialized = true;
96   
97   /* Initialize the global cache of dtls_keys */
98   __dtls_keys_cache = kmem_cache_create("dtls_keys_cache", 
99     sizeof(struct dtls_key), __alignof__(struct dtls_key), 0, NULL, NULL);
100   
101   __dtls_values_cache = kmem_cache_create("dtls_values_cache", 
102     sizeof(struct dtls_value), __alignof__(struct dtls_value), 0, NULL, NULL);
103   
104   /* Initialize the lock that protects the cache */
105   spin_pdr_init(&__dtls_lock);
106   return 0;
107 }
108
109 dtls_key_t dtls_key_create(dtls_dtor_t dtor)
110 {
111   dtls_lib_init();
112   dtls_key_t key = __allocate_dtls_key();
113   spin_pdr_init(&key->lock);
114   key->valid = true;
115   key->dtor = dtor;
116   return key;
117 }
118
119 void dtls_key_delete(dtls_key_t key)
120 {
121   assert(key);
122
123   spin_pdr_lock(&key->lock);
124   key->valid = false;
125   key->ref_count--;
126   spin_pdr_unlock(&key->lock);
127   __maybe_free_dtls_key(key);
128 }
129
130 static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls)
131 {
132   assert(key);
133
134   spin_pdr_lock(&key->lock);
135   key->ref_count++;
136   spin_pdr_unlock(&key->lock);
137
138   struct dtls_value *v = NULL;
139   TAILQ_FOREACH(v, &dtls_data->list, link)
140     if(v->key == key) break;
141
142   if(!v) {
143     spin_pdr_lock(&__dtls_lock);
144     v = kmem_cache_alloc(__dtls_values_cache, 0);
145     spin_pdr_unlock(&__dtls_lock);
146     assert(v);
147     v->key = key;
148     TAILQ_INSERT_HEAD(&dtls_data->list, v, link);
149   }
150   v->dtls = dtls;
151 }
152
153 static inline void *__get_dtls(dtls_data_t *dtls_data, dtls_key_t key)
154 {
155   assert(key);
156
157   struct dtls_value *v = NULL;
158   TAILQ_FOREACH(v, &dtls_data->list, link)
159     if(v->key == key) return v->dtls;
160   return v;
161 }
162
163 static inline void __destroy_dtls(dtls_data_t *dtls_data)
164 {
165  struct dtls_value *v,*n;
166   v = TAILQ_FIRST(&dtls_data->list);
167   while(v != NULL) {
168     dtls_key_t key = v->key;
169     bool run_dtor = false;
170   
171     spin_pdr_lock(&key->lock);
172     if(key->valid)
173       if(key->dtor)
174         run_dtor = true;
175     spin_pdr_unlock(&key->lock);
176
177         // MUST run the dtor outside the spinlock if we want it to be able to call
178         // code that may deschedule it for a while (i.e. a mutex). Probably a
179         // good idea anyway since it can be arbitrarily long and is written by the
180         // user. Note, there is a small race here on the valid field, whereby we
181         // may run a destructor on an invalid key. At least the keys memory wont
182         // be deleted though, as protected by the ref count. Any reasonable usage
183         // of this interface should safeguard that a key is never destroyed before
184         // all of the threads that use it have exited anyway.
185     if(run_dtor) {
186           void *dtls = v->dtls;
187       v->dtls = NULL;
188       key->dtor(dtls);
189     }
190
191     spin_pdr_lock(&key->lock);
192     key->ref_count--;
193     spin_pdr_unlock(&key->lock);
194     __maybe_free_dtls_key(key);
195
196     n = TAILQ_NEXT(v, link);
197     TAILQ_REMOVE(&dtls_data->list, v, link);
198     spin_pdr_lock(&__dtls_lock);
199     kmem_cache_free(__dtls_values_cache, v);
200     spin_pdr_unlock(&__dtls_lock);
201     v = n;
202   }
203 }
204
205 void set_dtls(dtls_key_t key, void *dtls)
206 {
207   bool initialized = true;
208   dtls_data_t *dtls_data = NULL;
209   if(!__dtls_initialized) {
210     initialized = false;
211     __dtls_initialized  = true;
212   }
213   dtls_data = &__dtls_data;
214   if(!initialized) {
215     TAILQ_INIT(&dtls_data->list);
216   }
217   __set_dtls(dtls_data, key, dtls);
218 }
219
220 void *get_dtls(dtls_key_t key)
221 {
222   dtls_data_t *dtls_data = NULL;
223   if(!__dtls_initialized)
224     return NULL;
225   dtls_data = &__dtls_data;
226   return __get_dtls(dtls_data, key);
227 }
228
229 void destroy_dtls()
230 {
231   dtls_data_t *dtls_data = NULL;
232   if(!__dtls_initialized)
233     return;
234   dtls_data = &__dtls_data;
235   __destroy_dtls(dtls_data);
236 }
237