root/trunk/infrastructure/st/tls.d

Revision 40, 10.0 kB (checked in by KirkMcDonald, 2 years ago)

Meta-programming library replaced; large re-write. StackThreads? updated to 0.3.2. Iteration updates.

Line 
1 /**
2  * Thread Local Storage for DigitalMars D.
3  * This module adds a simple wrapper for system specific thread local storage
4  * facilities.
5  *
6  * Authors: Mikola Lysenko, (mclysenk@mtu.edu)
7  * License: Public Domain (100% free)
8  * Date: 9-11-2006
9  * Version: 0.3
10  *
11  * History:
12  *  v0.3 - Fixed some clean up bugs and added fallback code for other platforms.
13  *
14  *  v0.2 - Merged with stackthreads.
15  *
16  *  v0.1 - Initial release.
17  *
18  * Bugs:
19  *  On non-windows & non-posix systems, the implementation uses a synchronized
20  *  block which may negatively impact performance.
21  *
22  *  Windows thread local storage is not correctly garbage collected.  Works
23  *  fine on Posix and other environments.
24  *
25  * Example:
26  * <code><pre>
27  * //Create a counter initially set to 5
28  * auto tls_counter = new ThreadLocal!(int)(5);
29  *
30  * //Create a thread
31  * Thread t = new Thread(
32  * {
33  *   //Just decrement the counter until it reaches 0.
34  *   while(tls_counter.val > 0)
35  *   {
36  *       writefln("Countdown ... %d", tls_counter.val);
37  *       tls_counter.val = tls_counter.val - 1;
38  *   }
39  *   writefln("Blast off!");
40  *   return 0;
41  * });
42  *
43  * //
44  * // Mess with the counter
45  * //
46  * assert(tls_counter.val == 5);
47  * tls_counter.val = 20;
48  * //
49  * // Invoke the thread
50  * //
51  * t.start();
52  * t.wait();
53  * //
54  * // This will print out:
55  * //
56  * //  Countdown ... 5
57  * //  Countdown ... 4
58  * //  Countdown ... 3
59  * //  Countdown ... 2
60  * //  Countdown ... 1
61  * //  Blast off!
62  * //
63  * // After the thread completes, the counter is untouched
64  * //
65  * assert(tls_counter.val == 20);
66  * </pre></code>
67  */
68 module st.tls;
69
70 private import
71     std.stdio,
72     std.string,
73     std.thread;
74
75 /**
76  * A ThreadLocalException is generated if the runtime fails to properly
77  * handle a ThreadLocal action.
78  */
79 class ThreadLocalException : Exception
80 {
81     this(char[] msg) { super(msg); }
82 }
83
84 version(linux)
85     version = TLS_UsePthreads;
86 version(darwin)
87     version = TLS_UsePthreads;
88 version(Win32)
89     version = TLS_UseWinAPI;
90
91
92 version(TLS_UsePthreads)
93 {
94 private import std.gc;
95
96 private extern(C)
97 {
98     typedef uint pthread_key_t;
99    
100     int pthread_key_delete(pthread_key_t);
101     int pthread_key_create(pthread_key_t*, void function(void*));
102     int pthread_setspecific(pthread_key_t, void*);
103     void *pthread_getspecific(pthread_key_t);
104    
105     const int EAGAIN = 11;
106     const int ENOMEM = 12;
107     const int EINVAL = 22;
108 }
109
110 /**
111  * Thread Local Storage is a mechanism for associating variables with specific
112  * threads.  This can be used to ensure the principle of confinement, and is
113  * useful for many sorts of algorithms.
114  */
115 public class ThreadLocal(T)
116 {
117     /**
118      * Allocates the thread local storage.
119      *
120      * Params:
121      *  def = An optional default value for the thread local storage.
122      *
123      * Throws:
124      *  A ThreadLocalException if the system could not allocate the storage.
125      */
126     public this(T def = T.init)
127     {
128         this.def = def;
129        
130         switch(pthread_key_create(&tls_key, &clean_up))
131         {
132             case 0: break; //Success
133            
134             case EAGAIN: throw new ThreadLocalException
135                 ("Out of keys for thread local storage");
136            
137             case ENOMEM: throw new ThreadLocalException
138                 ("Out of memory for thread local storage");
139            
140             default: throw new ThreadLocalException
141                 ("Undefined error while creating thread local storage");
142         }
143        
144         debug (ThreadLocal) writefln("TLS: Created %s", toString);
145     }
146    
147     /**
148      * Deallocates the thread local storage.
149      */
150     public ~this()
151     {
152         debug (ThreadLocal) writefln("TLS: Deleting %s", toString);
153         pthread_key_delete(tls_key);
154     }
155    
156     /**
157      * Returns: The current value of the thread local storage.
158      */
159     public T val()
160     {
161         debug (ThreadLocal) writefln("TLS: Accessing %s", toString);
162        
163         TWrapper * w = cast(TWrapper*)pthread_getspecific(tls_key);
164        
165         if(w is null)
166         {
167             debug(ThreadLocal) writefln("TLS: Not found");
168             return def;
169         }
170        
171         debug(ThreadLocal) writefln("TLS: Found %s", w);
172        
173         return w.val;
174     }
175    
176     /**
177      * Sets the thread local storage.  Can be used with property syntax.
178      *
179      * Params:
180      *  nv = The new value of the thread local storage.
181      *
182      * Returns:
183      *  nv upon success
184      *
185      * Throws:
186      *  ThreadLocalException if the system could not set the ThreadLocalStorage.
187      */
188     public T val(T nv)
189     {
190         debug (ThreadLocal) writefln("TLS: Setting %s", toString);
191        
192         void * w_old = pthread_getspecific(tls_key);
193        
194         if(w_old !is null)
195         {
196             (cast(TWrapper*)w_old).val = nv;
197         }
198         else
199         {
200             switch(pthread_setspecific(tls_key, TWrapper(nv)))
201             {
202                 case 0: break;
203                    
204                 case ENOMEM: throw new ThreadLocalException
205                     ("Insufficient memory to set new thread local storage");
206                
207                 case EINVAL: throw new ThreadLocalException
208                     ("Invalid thread local storage");
209                
210                 default: throw new ThreadLocalException
211                     ("Undefined error when setting thread local storage");
212             }
213         }
214        
215         debug(ThreadLocal) writefln("TLS: Set %s", toString);
216         return nv;
217     }
218    
219     /**
220      * Converts the thread local storage into a stringified representation.
221      * Can be useful for debugging.
222      *
223      * Returns:
224      *  A string representing the thread local storage.
225      */
226     public char[] toString()
227     {
228         return format("ThreadLocal[%8x]", tls_key);
229     }
230
231    
232     // Clean up thread local resources
233     private extern(C) static void clean_up(void * obj)
234     {
235         if(obj is null)
236             return;
237        
238         std.gc.removeRoot(obj);
239         delete obj;
240     }
241    
242     // The wrapper manages the thread local attributes`
243     private struct TWrapper
244     {
245         T val;
246        
247         static void * opCall(T nv)
248         {
249             TWrapper * res = new TWrapper;
250             std.gc.addRoot(cast(void*)res);
251             res.val = nv;
252             return cast(void*)res;
253         }
254     }
255    
256     private pthread_key_t tls_key;
257     private T def;
258 }
259
260 } else version(TLS_UseWinAPI) {
261
262 private import std.gc;
263    
264 private extern(Windows)
265 {
266     uint TlsAlloc();
267     bool TlsFree(uint);
268     bool TlsSetValue(uint, void*);
269     void* TlsGetValue(uint);
270 }
271
272 public class ThreadLocal(T)
273 {
274     public this(T def = T.init)
275     {
276         this.def = def;
277         this.tls_key = TlsAlloc();
278     }
279    
280     public ~this()
281     {
282         TlsFree(tls_key);
283     }
284    
285     public T val()
286     {
287         void * v = TlsGetValue(tls_key);
288        
289         if(v is null)
290             return def;
291        
292         return (cast(TWrapper*)v).val;
293     }
294    
295     public T val(T nv)
296     {
297         TWrapper * w_old = cast(TWrapper*)TlsGetValue(tls_key);
298        
299         if(w_old is null)
300         {
301             TlsSetValue(tls_key, TWrapper(nv));
302         }
303         else
304         {
305             w_old.val = nv;
306         }
307        
308         return nv;
309     }
310    
311     private uint tls_key;
312     private T def;
313        
314     private struct TWrapper
315     {
316         T val;
317        
318         static void * opCall(T nv)
319         {
320             TWrapper * res = new TWrapper;
321             std.gc.addRoot(cast(void*)res);
322             res.val = nv;
323             return cast(void*)res;
324         }
325     }
326 }
327
328 } else {
329
330 //Use a terrible hack instead...
331 //Performance will be bad, but at least we can fake the result.
332 public class ThreadLocal(T)
333 {
334     public this(T def = T.init)
335     {
336         this.def = def;
337     }
338    
339     public T val()
340     {
341         synchronized(this)
342         {
343             Thread t = Thread.getThis;
344            
345             if(t in tls_map)
346                 return tls_map[t];
347             return def;
348         }
349     }
350    
351     public T val(T nv)
352     {
353         synchronized(this)
354         {
355             return tls_map[Thread.getThis] = nv;
356         }
357     }
358    
359     private T def;
360     private T[Thread] tls_map;
361 }
362    
363 }
364
365 unittest
366 {
367     //Attempt to test out the tls
368     auto tls = new ThreadLocal!(int);
369    
370     //Make sure default values work
371     assert(tls.val == 0);
372    
373     //Init tls to something
374     tls.val = 333;
375    
376     //Create some threads to mess with the tls
377     Thread a = new Thread(
378     {
379         tls.val = 10;
380         Thread.yield;
381         assert(tls.val == 10);
382        
383         tls.val = 1010;
384         Thread.yield;
385         assert(tls.val == 1010);
386        
387         return 0;
388     });
389    
390     Thread b = new Thread(
391     {
392         tls.val = 20;
393         Thread.yield;
394         assert(tls.val == 20);
395        
396         tls.val = 2020;
397         Thread.yield;
398         assert(tls.val == 2020);
399        
400         return 0;
401     });
402    
403     a.start;
404     b.start;
405    
406     //Wait until they have have finished
407     a.wait;
408     b.wait;
409    
410     //Make sure the value was preserved
411     assert(tls.val == 333);
412    
413     //Try out structs
414     struct TestStruct
415     {
416         int x = 10;
417         real r = 20.0;
418         byte b = 3;
419     }
420    
421     auto tls2 = new ThreadLocal!(TestStruct);
422    
423     assert(tls2.val.x == 10);
424     assert(tls2.val.r == 20.0);
425     assert(tls2.val.b == 3);
426    
427     Thread x = new Thread(
428     {
429         assert(tls2.val.x == 10);
430        
431         TestStruct nv;
432         nv.x = 20;
433         tls2.val = nv;
434        
435         assert(tls2.val.x == 20);
436        
437         return 0;
438     });
439    
440     x.start();
441     x.wait();
442    
443     assert(tls2.val.x == 10);
444    
445     //Try out objects
446     static class TestClass
447     {
448         int x = 10;
449     }
450    
451     auto tls3 = new ThreadLocal!(TestClass)(new TestClass);
452    
453     assert(tls3.val.x == 10);
454    
455     Thread y = new Thread(
456     {
457         tls3.val.x ++;
458        
459         tls3.val = new TestClass;
460         tls3.val.x = 2020;
461        
462         assert(tls3.val.x == 2020);
463        
464         return 0;
465     });
466    
467     y.start;
468     y.wait;
469    
470     assert(tls3.val.x == 11);
471 }
Note: See TracBrowser for help on using the browser.