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