Tue May 13 15:34:29 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                 
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[slot]!=null) {
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                 }
234
235                 [MonoTODO]
236                 public ApartmentState ApartmentState {
237                         get {
238                                 // FIXME
239                                 return(ApartmentState.Unknown);
240                         }
241                         
242                         set {
243                         }
244                 }
245
246                 [MonoTODO]
247                 public CultureInfo CurrentCulture {
248                         get {
249                                 if (current_culture == null)
250                                         current_culture = CultureInfo.InvariantCulture;
251                                 return current_culture;
252                         }
253                         
254                         set {
255                                 current_culture = value;
256                         }
257                 }
258
259                 [MonoTODO]
260                 public CultureInfo CurrentUICulture {
261                         get {
262                                 // FIXME
263                                 return(CurrentCulture);
264                         }
265                         
266                         set {
267                                 // FIXME
268                                 CurrentCulture=value;
269                         }
270                 }
271
272                 public bool IsThreadPoolThread {
273                         get {
274                                 return IsThreadPoolThreadInternal;
275                         }
276                 }
277
278                 internal bool IsThreadPoolThreadInternal {
279                         get {
280                                 return threadpool_thread;
281                         }
282                         set {
283                                 threadpool_thread = value;
284                         }
285                 }
286
287                 public bool IsAlive {
288                         get {
289                                 ThreadState curstate=state;
290                                 
291                                 if((curstate & ThreadState.Aborted) != 0 ||
292                                    (curstate & ThreadState.Stopped) != 0 ||
293                                    (curstate & ThreadState.Unstarted) != 0) {
294                                         return(false);
295                                 } else {
296                                         return(true);
297                                 }
298                         }
299                 }
300
301                 public bool IsBackground {
302                         get {
303                                 if((state & ThreadState.Background) != 0) {
304                                         return(true);
305                                 } else {
306                                         return(false);
307                                 }
308                         }
309                         
310                         set {
311                                 if(value==true) {
312                                         set_state(ThreadState.Background);
313                                 } else {
314                                         clr_state(ThreadState.Background);
315                                 }
316                         }
317                 }
318
319                 private string thread_name=null;
320                 
321                 public string Name {
322                         get {
323                                 return(thread_name);
324                         }
325                         
326                         set {
327                                 thread_name=value;
328                         }
329                 }
330
331                 [MonoTODO]
332                 public ThreadPriority Priority {
333                         get {
334                                 // FIXME
335                                 return(ThreadPriority.Lowest);
336                         }
337                         
338                         set {
339                         }
340                 }
341
342                 public ThreadState ThreadState {
343                         get {
344                                 return(state);
345                         }
346                 }
347
348                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
349                 private extern void Abort_internal (object stateInfo);
350
351                 public void Abort() {
352                         set_state(ThreadState.AbortRequested);
353                         Abort_internal (null);
354                 }
355
356                 public void Abort(object stateInfo) {
357                         set_state(ThreadState.AbortRequested);
358                         Abort_internal(stateInfo);
359                 }
360                 
361
362                 [MonoTODO]
363                 public void Interrupt() {
364                         // FIXME
365                 }
366
367                 // The current thread joins with 'this'. Set ms to 0 to block
368                 // until this actually exits.
369                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
370                 private extern bool Join_internal(int ms, IntPtr handle);
371                 
372                 public void Join() {
373                         if((state & ThreadState.Unstarted) != 0) {
374                                 throw new ThreadStateException("Thread has not been started");
375                         }
376                         
377                         Thread thread=CurrentThread;
378                                 
379                         thread.set_state(ThreadState.WaitSleepJoin);
380                         Join_internal(Timeout.Infinite, system_thread_handle);
381                         thread.clr_state(ThreadState.WaitSleepJoin);
382                 }
383
384                 public bool Join(int millisecondsTimeout) {
385                         if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout < 0)
386                                 throw new ArgumentException ("Timeout less than zero", "millisecondsTimeout");
387
388                         if((state & ThreadState.Unstarted) != 0) {
389                                 throw new ThreadStateException("Thread has not been started");
390                         }
391
392                         Thread thread=CurrentThread;
393                                 
394                         thread.set_state(ThreadState.WaitSleepJoin);
395                         bool ret=Join_internal(millisecondsTimeout,
396                                                system_thread_handle);
397                         thread.clr_state(ThreadState.WaitSleepJoin);
398
399                         return(ret);
400                 }
401
402                 public bool Join(TimeSpan timeout) {
403                         // LAMESPEC: says to throw ArgumentException too
404                         int ms=Convert.ToInt32(timeout.TotalMilliseconds);
405                         
406                         if(ms < 0 || ms > Int32.MaxValue) {
407                                 throw new ArgumentOutOfRangeException("timeout out of range");
408                         }
409                         if((state & ThreadState.Unstarted) != 0) {
410                                 throw new ThreadStateException("Thread has not been started");
411                         }
412
413                         Thread thread=CurrentThread;
414
415                         thread.set_state(ThreadState.WaitSleepJoin);
416                         bool ret=Join_internal(ms, system_thread_handle);
417                         thread.clr_state(ThreadState.WaitSleepJoin);
418
419                         return(ret);
420                 }
421
422                 [MonoTODO]
423                 public void Resume() {
424                         throw new NotImplementedException ();
425                 }
426
427                 // Launches the thread
428                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
429                 private extern void Start_internal(IntPtr handle);
430                 
431                 public void Start() {
432                         lock(this) {
433                                 if((state & ThreadState.Unstarted) == 0) {
434                                         throw new ThreadStateException("Thread has already been started");
435                                 }
436                                 
437                                 // Launch this thread
438                                 Start_internal(system_thread_handle);
439
440                                 // Mark the thread state as Running
441                                 // (which is all bits
442                                 // cleared). Therefore just remove the
443                                 // Unstarted bit
444                                 clr_state(ThreadState.Unstarted);
445                         }
446                 }
447
448                 [MonoTODO]
449                 public void Suspend() {
450                         if((state & ThreadState.Unstarted) != 0 || !IsAlive) {
451                                 throw new ThreadStateException("Thread has not been started, or is dead");
452                         }
453
454                         set_state(ThreadState.SuspendRequested);
455                         // FIXME - somehow let the interpreter know that
456                         // this thread should now suspend
457                         Console.WriteLine ("WARNING: Thread.Suspend () partially implemented");
458                 }
459
460                 // Closes the system thread handle
461                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
462                 private extern void Thread_free_internal(IntPtr handle);
463
464                 ~Thread() {
465                         // Free up the handle
466                         Thread_free_internal(system_thread_handle);
467                 }
468
469                 private void set_state(ThreadState set) {
470                         lock(this) {
471                                 state |= set;
472                         }
473                 }
474                 private void clr_state(ThreadState clr) {
475                         lock(this) {
476                                 state &= ~clr;
477                         }
478                 }
479         }
480 }