Abstract out alloc/free from the key/value caches
[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 <parlib/spinlock.h>
24 #include <parlib/dtls.h>
25 #include <parlib/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 <parlib/spinlock.h>
32
33 /* The dynamic tls key structure */
34 struct dtls_key {
35   int ref_count;
36   bool valid;
37   void (*dtor)(void*);
38 };
39
40 /* The definition of a dtls_key list and its elements */
41 struct dtls_value {
42   TAILQ_ENTRY(dtls_value) link;
43   struct dtls_key *key;
44   void *dtls;
45 }; 
46 TAILQ_HEAD(dtls_list, dtls_value);
47
48 /* A struct containing all of the per thread (i.e. vcore or uthread) data
49  * associated with dtls */
50 typedef struct dtls_data {
51   /* A per-thread list of dtls regions */
52   struct dtls_list list;
53
54 } dtls_data_t;
55
56 /* A slab of dtls keys (global to all threads) */
57 static struct kmem_cache *__dtls_keys_cache;
58
59 /* A slab of values for use when mapping a dtls_key to its per-thread value */
60 struct kmem_cache *__dtls_values_cache;
61   
62 static __thread dtls_data_t __dtls_data;
63 static __thread bool __dtls_initialized = false;
64
65 static dtls_key_t __allocate_dtls_key() 
66 {
67   dtls_key_t key = kmem_cache_alloc(__dtls_keys_cache, 0);
68   assert(key);
69   key->ref_count = 1;
70   return key;
71 }
72
73 static void __maybe_free_dtls_key(dtls_key_t key)
74 {
75   int ref_count = __sync_add_and_fetch(&key->ref_count, -1);
76   if (ref_count == 0)
77     kmem_cache_free(__dtls_keys_cache, key);
78 }
79
80 static struct dtls_value *__allocate_dtls_value()
81 {
82   struct dtls_value *v;
83   v = kmem_cache_alloc(__dtls_values_cache, 0);
84   assert(v);
85   return v;
86 }
87
88 static void __free_dtls_value(struct dtls_value *v)
89 {
90   kmem_cache_free(__dtls_values_cache, v);
91 }
92
93 /* Constructor to get a reference to the main thread's TLS descriptor */
94 int dtls_lib_init()
95 {
96   /* Make sure this only runs once */
97   static bool initialized = false;
98   if (initialized)
99       return 0;
100   initialized = true;
101   
102   /* Initialize the global cache of dtls_keys */
103   __dtls_keys_cache = kmem_cache_create("dtls_keys_cache", 
104     sizeof(struct dtls_key), __alignof__(struct dtls_key), 0, NULL, NULL);
105   
106   __dtls_values_cache = kmem_cache_create("dtls_values_cache", 
107     sizeof(struct dtls_value), __alignof__(struct dtls_value), 0, NULL, NULL);
108   
109   return 0;
110 }
111
112 dtls_key_t dtls_key_create(dtls_dtor_t dtor)
113 {
114   dtls_lib_init();
115   dtls_key_t key = __allocate_dtls_key();
116   key->valid = true;
117   key->dtor = dtor;
118   return key;
119 }
120
121 void dtls_key_delete(dtls_key_t key)
122 {
123   assert(key);
124   key->valid = false;
125   __maybe_free_dtls_key(key);
126 }
127
128 static inline void __set_dtls(dtls_data_t *dtls_data, dtls_key_t key, void *dtls)
129 {
130   assert(key);
131   __sync_fetch_and_add(&key->ref_count, 1);
132
133   struct dtls_value *v = NULL;
134   TAILQ_FOREACH(v, &dtls_data->list, link)
135     if(v->key == key) break;
136
137   if (!v) {
138     v = __allocate_dtls_value();
139     v->key = key;
140     TAILQ_INSERT_HEAD(&dtls_data->list, v, link);
141   }
142   v->dtls = dtls;
143 }
144
145 static inline void *__get_dtls(dtls_data_t *dtls_data, dtls_key_t key)
146 {
147   assert(key);
148
149   struct dtls_value *v = NULL;
150   TAILQ_FOREACH(v, &dtls_data->list, link)
151     if(v->key == key) return v->dtls;
152   return v;
153 }
154
155 static inline void __destroy_dtls(dtls_data_t *dtls_data)
156 {
157  struct dtls_value *v,*n;
158   v = TAILQ_FIRST(&dtls_data->list);
159   while(v != NULL) {
160     dtls_key_t key = v->key;
161   
162         // The dtor must be called outside of a spinlock so that it can call
163         // code that may deschedule it for a while (i.e. a mutex). Probably a
164         // good idea anyway since it can be arbitrarily long and is written by the
165         // user. Note, there is a small race here on the valid field, whereby we
166         // may run a destructor on an invalid key. At least the keys memory wont
167         // be deleted though, as protected by the ref count. Any reasonable usage
168         // of this interface should safeguard that a key is never destroyed before
169         // all of the threads that use it have exited anyway.
170     if (key->valid && key->dtor) {
171           void *dtls = v->dtls;
172       v->dtls = NULL;
173       key->dtor(dtls);
174     }
175     __maybe_free_dtls_key(key);
176
177     n = TAILQ_NEXT(v, link);
178     TAILQ_REMOVE(&dtls_data->list, v, link);
179     __free_dtls_value(v);
180     v = n;
181   }
182 }
183
184 void set_dtls(dtls_key_t key, void *dtls)
185 {
186   bool initialized = true;
187   dtls_data_t *dtls_data = NULL;
188   if(!__dtls_initialized) {
189     initialized = false;
190     __dtls_initialized  = true;
191   }
192   dtls_data = &__dtls_data;
193   if(!initialized) {
194     TAILQ_INIT(&dtls_data->list);
195   }
196   __set_dtls(dtls_data, key, dtls);
197 }
198
199 void *get_dtls(dtls_key_t key)
200 {
201   dtls_data_t *dtls_data = NULL;
202   if(!__dtls_initialized)
203     return NULL;
204   dtls_data = &__dtls_data;
205   return __get_dtls(dtls_data, key);
206 }
207
208 void destroy_dtls()
209 {
210   dtls_data_t *dtls_data = NULL;
211   if(!__dtls_initialized)
212     return;
213   dtls_data = &__dtls_data;
214   __destroy_dtls(dtls_data);
215 }
216