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