xref: /minix3/minix/lib/libmthread/key.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 #include <minix/mthread.h>
2 #include <string.h>
3 #include "global.h"
4 #include "proto.h"
5 
6 static int keys_used = 0;
7 static struct {
8   int used;
9   int nvalues;
10   void *mvalue;
11   void **value;
12   void (*destr)(void *);
13 } keys[MTHREAD_KEYS_MAX];
14 
15 /*===========================================================================*
16  *				mthread_init_keys			     *
17  *===========================================================================*/
mthread_init_keys(void)18 void mthread_init_keys(void)
19 {
20 /* Initialize the table of key entries.
21  */
22   mthread_key_t k;
23 
24   for (k = 0; k < MTHREAD_KEYS_MAX; k++)
25 	keys[k].used = FALSE;
26 }
27 
28 /*===========================================================================*
29  *				mthread_key_create			     *
30  *===========================================================================*/
mthread_key_create(mthread_key_t * key,void (* destructor)(void *))31 int mthread_key_create(mthread_key_t *key, void (*destructor)(void *))
32 {
33 /* Allocate a key.
34  */
35   mthread_key_t k;
36 
37   keys_used = 1;
38 
39   /* We do not yet allocate storage space for the values here, because we can
40    * not estimate how many threads will be created in the common case that the
41    * application creates keys before spawning threads.
42    */
43   for (k = 0; k < MTHREAD_KEYS_MAX; k++) {
44 	if (!keys[k].used) {
45 		keys[k].used = TRUE;
46 		keys[k].nvalues = 0;
47 		keys[k].mvalue = NULL;
48 		keys[k].value = NULL;
49 		keys[k].destr = destructor;
50 		*key = k;
51 
52 		return(0);
53 	}
54   }
55 
56   return(EAGAIN);
57 }
58 
59 /*===========================================================================*
60  *				mthread_key_delete			     *
61  *===========================================================================*/
mthread_key_delete(mthread_key_t key)62 int mthread_key_delete(mthread_key_t key)
63 {
64 /* Free up a key, as well as any associated storage space.
65  */
66 
67   if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
68 	return(EINVAL);
69 
70   free(keys[key].value);
71 
72   keys[key].used = FALSE;
73 
74   return(0);
75 }
76 
77 /*===========================================================================*
78  *				mthread_getspecific			     *
79  *===========================================================================*/
mthread_getspecific(mthread_key_t key)80 void *mthread_getspecific(mthread_key_t key)
81 {
82 /* Get this thread's local value for the given key. The default is NULL.
83  */
84 
85   if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
86 	return(NULL);
87 
88   if (current_thread == MAIN_THREAD)
89 	return keys[key].mvalue;
90 
91   if (current_thread < keys[key].nvalues)
92 	return(keys[key].value[current_thread]);
93 
94   return(NULL);
95 }
96 
97 /*===========================================================================*
98  *				mthread_setspecific			     *
99  *===========================================================================*/
mthread_setspecific(mthread_key_t key,void * value)100 int mthread_setspecific(mthread_key_t key, void *value)
101 {
102 /* Set this thread's value for the given key. Allocate more resources as
103  * necessary.
104  */
105   void **p;
106 
107   if (key < 0 || key >= MTHREAD_KEYS_MAX || !keys[key].used)
108 	return(EINVAL);
109 
110   if (current_thread == MAIN_THREAD) {
111 	keys[key].mvalue = value;
112 
113 	return(0);
114   }
115 
116   if (current_thread >= keys[key].nvalues) {
117 	if (current_thread >= no_threads)
118 		mthread_panic("Library state corrupt");
119 
120 	if ((p = (void **) realloc(keys[key].value,
121 			sizeof(void*) * no_threads)) == NULL)
122 		return(ENOMEM);
123 
124 	memset(&p[keys[key].nvalues], 0,
125 		sizeof(void*) * (no_threads - keys[key].nvalues));
126 
127 	keys[key].nvalues = no_threads;
128 	keys[key].value = p;
129   }
130 
131   keys[key].value[current_thread] = value;
132 
133   return(0);
134 }
135 
136 /*===========================================================================*
137  *				mthread_cleanup_values			     *
138  *===========================================================================*/
mthread_cleanup_values(void)139 void mthread_cleanup_values(void)
140 {
141 /* Clean up all the values associated with an exiting thread, calling keys'
142  * destruction procedures as appropriate.
143  */
144   mthread_key_t k;
145   void *value;
146   int found;
147 
148   if (!keys_used) return;	/* Only clean up if we used any keys at all */
149 
150   /* Any of the destructors may set a new value on any key, so we may have to
151    * loop over the table of keys multiple times. This implementation has no
152    * protection against infinite loops in this case.
153    */
154   do {
155 	found = FALSE;
156 
157 	for (k = 0; k < MTHREAD_KEYS_MAX; k++) {
158 		if (!keys[k].used) continue;
159 		if (keys[k].destr == NULL) continue;
160 
161 		if (current_thread == MAIN_THREAD) {
162 			value = keys[k].mvalue;
163 
164 			keys[k].mvalue = NULL;
165 		} else {
166 			if (current_thread >= keys[k].nvalues) continue;
167 
168 			value = keys[k].value[current_thread];
169 
170 			keys[k].value[current_thread] = NULL;
171 		}
172 
173 		if (value != NULL) {
174 			/* Note: calling mthread_exit() from a destructor
175 			 * causes undefined behavior.
176 			 */
177 			keys[k].destr(value);
178 
179 			found = TRUE;
180 		}
181 	}
182   } while (found);
183 }
184 
185 /* pthread compatibility layer. */
186 __weak_alias(pthread_key_create, mthread_key_create)
187 __weak_alias(pthread_key_delete, mthread_key_delete)
188 __weak_alias(pthread_getspecific, mthread_getspecific)
189 __weak_alias(pthread_setspecific, mthread_setspecific)
190 
191