* Thread.cs: SetData() method: use Hashtable.Contains to check
[mono.git] / mcs / class / corlib / System.Threading / Thread.cs
1 //
2 // System.Threading.Thread.cs
3 //
4 // Author:
5 //   Dick Porter (dick@ximian.com)
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System.Runtime.Remoting.Contexts;
11 using System.Security.Principal;
12 using System.Globalization;
13 using System.Runtime.CompilerServices;
14 using System.Collections;
15
16 namespace System.Threading
17 {
18         public sealed class Thread
19         {
20                 // stores a thread handle
21                 private IntPtr system_thread_handle;
22                 
23                 private CultureInfo current_culture;
24                 private bool threadpool_thread = false;
25                 private ThreadState state = ThreadState.Unstarted;
26                 private object abort_exc;
27                 internal object abort_state;
28                 /* thread_id is only accessed from unmanaged code */
29                 private int thread_id;
30                 
31                 /* start_notify is used by the runtime to signal that Start()
32                  * is ok to return
33                  */
34                 private IntPtr start_notify;
35                 private IntPtr stack_ptr;
36                 private IntPtr static_data;
37                 
38                 public static Context CurrentContext {
39                         get {
40                                 return(AppDomain.InternalGetContext ());
41                         }
42                 }
43
44                 [MonoTODO]
45                 public static IPrincipal CurrentPrincipal {
46                         get {
47                                 // FIXME -
48                                 // System.Security.Principal.IPrincipal
49                                 // not yet implemented
50                                 return(null);
51                         }
52                         
53                         set {
54                         }
55                 }
56
57                 // Looks up the object associated with the current thread
58                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
59                 private extern static Thread CurrentThread_internal();
60                 
61                 public static Thread CurrentThread {
62                         get {
63                                 return(CurrentThread_internal());
64                         }
65                 }
66
67                 internal static int CurrentThreadId {
68                         get {
69                                 return CurrentThread.thread_id;
70                         }
71                 }
72
73                 // Looks up the slot hash for the current thread
74                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
75                 private extern static Hashtable SlotHash_lookup();
76
77                 // Stores the slot hash for the current thread
78                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
79                 private extern static void SlotHash_store(Hashtable slothash);
80
81                 private static Hashtable GetTLSSlotHash() {
82                         Hashtable slothash=SlotHash_lookup();
83                         if(slothash==null) {
84                                 // Not synchronised, because this is
85                                 // thread specific anyway.
86                                 slothash=new Hashtable();
87                                 SlotHash_store(slothash);
88                         }
89
90                         return(slothash);
91                 }
92
93                 public static LocalDataStoreSlot AllocateDataSlot() {
94                         LocalDataStoreSlot slot = new LocalDataStoreSlot();
95
96                         return(slot);
97                 }
98
99                 // Stores a hash keyed by strings of LocalDataStoreSlot objects
100                 static Hashtable datastorehash;
101
102                 private static void InitDataStoreHash () {
103                         lock (typeof (Thread)) {
104                                 if (datastorehash == null) {
105                                         datastorehash = Hashtable.Synchronized(new Hashtable());
106                                 }
107                         }
108                 }
109                 
110                 public static LocalDataStoreSlot AllocateNamedDataSlot(string name) {
111                         if (datastorehash == null)
112                                 InitDataStoreHash ();
113                         LocalDataStoreSlot slot = (LocalDataStoreSlot)datastorehash[name];
114                         if(slot!=null) {
115                                 // This exception isnt documented (of
116                                 // course) but .net throws it
117                                 throw new ArgumentException("Named data slot already added");
118                         }
119                         
120                         slot = new LocalDataStoreSlot();
121
122                         datastorehash.Add(name, slot);
123                         
124                         return(slot);
125                 }
126
127                 public static void FreeNamedDataSlot(string name) {
128                         if (datastorehash == null)
129                                 InitDataStoreHash ();
130                         LocalDataStoreSlot slot=(LocalDataStoreSlot)datastorehash[name];
131
132                         if(slot!=null) {
133                                 datastorehash.Remove(slot);
134                         }
135                 }
136
137                 public static object GetData(LocalDataStoreSlot slot) {
138                         Hashtable slothash=GetTLSSlotHash();
139                         return(slothash[slot]);
140                 }
141
142                 public static AppDomain GetDomain() {
143                         return AppDomain.CurrentDomain;
144                 }
145
146                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
147                 public extern static int GetDomainID();
148
149                 public static LocalDataStoreSlot GetNamedDataSlot(string name) {
150                         if (datastorehash == null)
151                                 InitDataStoreHash ();
152                         LocalDataStoreSlot slot=(LocalDataStoreSlot)datastorehash[name];
153
154                         if(slot==null) {
155                                 slot=AllocateNamedDataSlot(name);
156                         }
157                         
158                         return(slot);
159                 }
160                 
161                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
162                 private extern static void ResetAbort_internal();
163
164                 public static void ResetAbort()
165                 {
166                         Thread thread=CurrentThread;
167
168                         thread.clr_state(ThreadState.AbortRequested);
169                         ResetAbort_internal();
170                 }
171                 
172
173                 public static void SetData(LocalDataStoreSlot slot,
174                                            object data) {
175                         Hashtable slothash=GetTLSSlotHash();
176
177                         if(slothash.Contains(slot)) {
178                                 slothash.Remove(slot);
179                         }
180                         
181                         slothash.Add(slot, data);
182                 }
183
184                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
185                 private extern static void Sleep_internal(int ms);
186
187                 public static void Sleep(int millisecondsTimeout) {
188                         if((millisecondsTimeout<0) && (millisecondsTimeout != Timeout.Infinite)) {
189                                 throw new ArgumentException("Negative timeout");
190                         }
191                         Thread thread=CurrentThread;
192                                 
193                         thread.set_state(ThreadState.WaitSleepJoin);
194                                 
195                         Sleep_internal(millisecondsTimeout);
196                         thread.clr_state(ThreadState.WaitSleepJoin);
197                 }
198
199                 public static void Sleep(TimeSpan timeout) {
200                         // LAMESPEC: says to throw ArgumentException too
201                         int ms=Convert.ToInt32(timeout.TotalMilliseconds);
202                         
203                         if(ms < 0 || ms > Int32.MaxValue) {
204                                 throw new ArgumentOutOfRangeException("Timeout out of range");
205                         }
206
207                         Thread thread=CurrentThread;
208                                 
209                         thread.set_state(ThreadState.WaitSleepJoin);
210                         Sleep_internal(ms);
211                         thread.clr_state(ThreadState.WaitSleepJoin);
212                 }
213
214                 // Returns the system thread handle
215                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
216                 private extern IntPtr Thread_internal(ThreadStart start);
217
218                 private ThreadStart threadstart;
219                 
220                 public Thread(ThreadStart start) {
221                         if(start==null) {
222                                 throw new ArgumentNullException("Null ThreadStart");
223                         }
224                         threadstart=start;
225
226                         // This is a two-stage thread launch.  Thread_internal
227                         // creates the new thread, but blocks it until
228                         // Start() is called later.
229                         system_thread_handle=Thread_internal(start);
230
231                         // Should throw an exception here if
232                         // Thread_internal returns NULL
233                         if(system_thread_handle==(IntPtr)0) {
234                                 throw new SystemException("Thread creation failed");
235                         }
236                 }
237
238                 [MonoTODO]
239                 public ApartmentState ApartmentState {
240                         get {
241                                 // FIXME
242                                 return(ApartmentState.Unknown);
243                         }
244                         
245                         set {
246                         }
247                 }
248
249                 [MonoTODO]
250                 public CultureInfo CurrentCulture {
251                         get {
252                                 if (current_culture == null)
253                                         current_culture = CultureInfo.InvariantCulture;
254                                 return current_culture;
255                         }
256                         
257                         set {
258                                 current_culture = value;
259                         }
260                 }
261
262                 [MonoTODO]
263                 public CultureInfo CurrentUICulture {
264                         get {
265                                 // FIXME
266                                 return(CurrentCulture);
267                         }
268                         
269                         set {
270                                 // FIXME
271                                 CurrentCulture=value;
272                         }
273                 }
274
275                 public bool IsThreadPoolThread {
276                         get {
277                                 return IsThreadPoolThreadInternal;
278                         }
279                 }
280
281                 internal bool IsThreadPoolThreadInternal {
282                         get {
283                                 return threadpool_thread;
284                         }
285                         set {
286                                 threadpool_thread = value;
287                         }
288                 }
289
290                 public bool IsAlive {
291                         get {
292                                 ThreadState curstate=state;
293                                 
294                                 if((curstate & ThreadState.Aborted) != 0 ||
295                                    (curstate & ThreadState.Stopped) != 0 ||
296                                    (curstate & ThreadState.Unstarted) != 0) {
297                                         return(false);
298                                 } else {
299                                         return(true);
300                                 }
301                         }
302                 }
303
304                 public bool IsBackground {
305                         get {
306                                 if((state & ThreadState.Background) != 0) {
307                                         return(true);
308                                 } else {
309                                         return(false);
310                                 }
311                         }
312                         
313                         set {
314                                 if(value==true) {
315                                         set_state(ThreadState.Background);
316                                 } else {
317                                         clr_state(ThreadState.Background);
318                                 }
319                         }
320                 }
321
322                 private string thread_name=null;
323                 
324                 public string Name {
325                         get {
326                                 return(thread_name);
327                         }
328                         
329                         set {
330                                 thread_name=value;
331                         }
332                 }
333
334                 [MonoTODO]
335                 public ThreadPriority Priority {
336                         get {
337                                 // FIXME
338                                 return(ThreadPriority.Lowest);
339                         }
340                         
341                         set {
342                         }
343                 }
344
345                 public ThreadState ThreadState {
346                         get {
347                                 return(state);
348                         }
349                 }
350
351                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
352                 private extern void Abort_internal (object stateInfo);
353
354                 public void Abort() {
355                         set_state(ThreadState.AbortRequested);
356                         Abort_internal (null);
357                 }
358
359                 public void Abort(object stateInfo) {
360                         set_state(ThreadState.AbortRequested);
361                         Abort_internal(stateInfo);
362                 }
363                 
364
365                 [MonoTODO]
366                 public void Interrupt() {
367                         // FIXME
368                 }
369
370                 // The current thread joins with 'this'. Set ms to 0 to block
371                 // until this actually exits.
372                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
373                 private extern bool Join_internal(int ms, IntPtr handle);
374                 
375                 public void Join() {
376                         if((state & ThreadState.Unstarted) != 0) {
377                                 throw new ThreadStateException("Thread has not been started");
378                         }
379                         
380                         Thread thread=CurrentThread;
381                                 
382                         thread.set_state(ThreadState.WaitSleepJoin);
383                         Join_internal(Timeout.Infinite, system_thread_handle);
384                         thread.clr_state(ThreadState.WaitSleepJoin);
385                 }
386
387                 public bool Join(int millisecondsTimeout) {
388                         if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout < 0)
389                                 throw new ArgumentException ("Timeout less than zero", "millisecondsTimeout");
390
391                         if((state & ThreadState.Unstarted) != 0) {
392                                 throw new ThreadStateException("Thread has not been started");
393                         }
394
395                         Thread thread=CurrentThread;
396                                 
397                         thread.set_state(ThreadState.WaitSleepJoin);
398                         bool ret=Join_internal(millisecondsTimeout,
399                                                system_thread_handle);
400                         thread.clr_state(ThreadState.WaitSleepJoin);
401
402                         return(ret);
403                 }
404
405                 public bool Join(TimeSpan timeout) {
406                         // LAMESPEC: says to throw ArgumentException too
407                         int ms=Convert.ToInt32(timeout.TotalMilliseconds);
408                         
409                         if(ms < 0 || ms > Int32.MaxValue) {
410                                 throw new ArgumentOutOfRangeException("timeout out of range");
411                         }
412                         if((state & ThreadState.Unstarted) != 0) {
413                                 throw new ThreadStateException("Thread has not been started");
414                         }
415
416                         Thread thread=CurrentThread;
417
418                         thread.set_state(ThreadState.WaitSleepJoin);
419                         bool ret=Join_internal(ms, system_thread_handle);
420                         thread.clr_state(ThreadState.WaitSleepJoin);
421
422                         return(ret);
423                 }
424
425                 [MonoTODO]
426                 public void Resume() {
427                         throw new NotImplementedException ();
428                 }
429
430                 // Launches the thread
431                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
432                 private extern void Start_internal(IntPtr handle);
433                 
434                 public void Start() {
435                         lock(this) {
436                                 if((state & ThreadState.Unstarted) == 0) {
437                                         throw new ThreadStateException("Thread has already been started");
438                                 }
439                                 
440                                 // Launch this thread
441                                 Start_internal(system_thread_handle);
442
443                                 // Mark the thread state as Running
444                                 // (which is all bits
445                                 // cleared). Therefore just remove the
446                                 // Unstarted bit
447                                 clr_state(ThreadState.Unstarted);
448                         }
449                 }
450
451                 [MonoTODO]
452                 public void Suspend() {
453                         if((state & ThreadState.Unstarted) != 0 || !IsAlive) {
454                                 throw new ThreadStateException("Thread has not been started, or is dead");
455                         }
456
457                         set_state(ThreadState.SuspendRequested);
458                         // FIXME - somehow let the interpreter know that
459                         // this thread should now suspend
460                         Console.WriteLine ("WARNING: Thread.Suspend () partially implemented");
461                 }
462
463                 // Closes the system thread handle
464                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
465                 private extern void Thread_free_internal(IntPtr handle);
466
467                 ~Thread() {
468                         // Free up the handle
469                         Thread_free_internal(system_thread_handle);
470                 }
471
472                 private void set_state(ThreadState set) {
473                         lock(this) {
474                                 state |= set;
475                         }
476                 }
477                 private void clr_state(ThreadState clr) {
478                         lock(this) {
479                                 state &= ~clr;
480                         }
481                 }
482         }
483 }