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