Implemented fast version of ThreadLocal<T>.
[mono.git] / mcs / class / corlib / System.Threading / ThreadLocal.cs
1 // 
2 // ThreadLocal.cs
3 //  
4 // Author:
5 //       Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
6 //       Rewritten by Paolo Molaro (lupus@ximian.com)
7 // 
8 // Copyright (c) 2009 Jérémie "Garuma" Laval
9 // 
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27
28 #if NET_4_0 || MOBILE
29 using System;
30 using System.Runtime.Serialization;
31 using System.Runtime.InteropServices;
32 using System.Security.Permissions;
33
34 namespace System.Threading
35 {
36         [HostProtection (SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)]
37         [System.Diagnostics.DebuggerDisplay ("IsValueCreated={IsValueCreated}, Value={ValueForDebugDisplay}")]
38         [System.Diagnostics.DebuggerTypeProxy ("System.Threading.SystemThreading_ThreadLocalDebugView`1")]
39         public class ThreadLocal<T> : IDisposable
40         {
41                 struct TlsDatum {
42                         internal sbyte state; /* 0 uninitialized, < 0 initializing, > 0 inited */
43                         internal Exception cachedException; /* this is per-thread */
44                         internal T data;
45                 }
46
47                 Func<T> valueFactory;
48                 /* The tlsdata field is handled magically by the JIT
49                  * It must be a struct and it is always accessed by ldflda: the JIT, instead of
50                  * computing the address inside the instance, will return the address of the variable
51                  * for the current thread (based on tls_offset). This magic wouldn't be needed if C#
52                  * let us declare an icall with a TlsDatum& return type...
53                  * For this same reason, we must check tls_offset for != 0 to make sure it's valid before accessing tlsdata
54                  * The address of the tls var is cached per method at the first IL ldflda instruction, so care must be taken
55                  * not to cause it to be conditionally executed.
56                  */
57                 uint tls_offset;
58                 TlsDatum tlsdata;
59                 
60                 public ThreadLocal ()
61                 {
62                         tls_offset = Thread.AllocTlsData (typeof (TlsDatum));
63                 }
64
65                 public ThreadLocal (Func<T> valueFactory) : this ()
66                 {
67                         if (valueFactory == null)
68                                 throw new ArgumentNullException ("valueFactory");
69                         this.valueFactory = valueFactory;
70                 }
71
72                 public void Dispose ()
73                 {
74                         Dispose (true);
75                 }
76                 
77                 protected virtual void Dispose (bool disposing)
78                 {
79                         if (tls_offset != 0) {
80                                 uint o = tls_offset;
81                                 tls_offset = 0;
82                                 if (disposing)
83                                         valueFactory = null;
84                                 Thread.DestroyTlsData (o);
85                                 GC.SuppressFinalize (this);
86                         }
87                 }
88
89                 ~ThreadLocal ()
90                 {
91                         Dispose (false);
92                 }
93                 
94                 public bool IsValueCreated {
95                         get {
96                                 if (tls_offset == 0)
97                                         throw new ObjectDisposedException ("ThreadLocal object");
98                                 /* ALERT! magic tlsdata JIT access redirects to TLS value instead of instance field */
99                                 return tlsdata.state > 0;
100                         }
101                 }
102
103                 T GetSlowPath () {
104                         /* ALERT! magic tlsdata JIT access redirects to TLS value instead of instance field */
105                         if (tlsdata.cachedException != null)
106                                 throw tlsdata.cachedException;
107                         if (tlsdata.state < 0)
108                                 throw new InvalidOperationException ("The initialization function attempted to reference Value recursively");
109                         tlsdata.state = -1;
110                         if (valueFactory != null) {
111                                 try {
112                                         tlsdata.data = valueFactory ();
113                                 } catch (Exception ex) {
114                                         tlsdata.cachedException = ex;
115                                         throw ex;
116                                 }
117                         } else {
118                                 tlsdata.data = default (T);
119                         }
120                         tlsdata.state = 1;
121                         return tlsdata.data;
122                 }
123
124                 [System.Diagnostics.DebuggerBrowsableAttribute (System.Diagnostics.DebuggerBrowsableState.Never)]
125                 public T Value {
126                         get {
127                                 if (tls_offset == 0)
128                                         throw new ObjectDisposedException ("ThreadLocal object");
129                                 /* ALERT! magic tlsdata JIT access redirects to TLS value instead of instance field */
130                                 if (tlsdata.state > 0)
131                                         return tlsdata.data;
132                                 return GetSlowPath ();
133                         }
134                         set {
135                                 if (tls_offset == 0)
136                                         throw new ObjectDisposedException ("ThreadLocal object");
137                                 /* ALERT! magic tlsdata JIT access redirects to TLS value instead of instance field */
138                                 tlsdata.state = 1;
139                                 tlsdata.data = value;
140                         }
141                 }
142                 
143                 public override string ToString ()
144                 {
145                         return string.Format ("[ThreadLocal: IsValueCreated={0}, Value={1}]", IsValueCreated, Value);
146                 }
147                 
148         }
149 }
150 #endif