Warnings cleanup
[mono.git] / mcs / class / corlib / System.Threading / Thread.cs
index 042f475b76349f0fe922a2937eae7942b479d6df..a1e44a48687c96ef299957aa37fc8404f37c9795 100644 (file)
@@ -5,7 +5,7 @@
 //   Dick Porter (dick@ximian.com)
 //
 // (C) Ximian, Inc.  http://www.ximian.com
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -49,16 +49,20 @@ namespace System.Threading {
 #if NET_2_0
        [ComVisible (true)]
        [ComDefaultInterface (typeof (_Thread))]
-#endif
+       public sealed class Thread : CriticalFinalizerObject, _Thread {
+#else
        public sealed class Thread : _Thread {
+#endif
 
+#pragma warning disable 169, 414, 649
                #region Sync with metadata/object-internals.h
                int lock_thread_id;
                // stores a thread handle
                private IntPtr system_thread_handle;
-               
-               private IntPtr culture_info;
-               private IntPtr ui_culture_info;
+
+               /* Note this is an opaque object (an array), not a CultureInfo */
+               private object cached_culture_info;
+               private IntPtr unused0;
                private bool threadpool_thread;
                /* accessed only from unmanaged code */
                private IntPtr name;
@@ -74,45 +78,58 @@ namespace System.Threading {
                 */
                private IntPtr start_notify;
                private IntPtr stack_ptr;
-               private IntPtr static_data;
+               private UIntPtr static_data; /* GC-tracked */
                private IntPtr jit_data;
                private IntPtr lock_data;
                Context current_appcontext;
                int stack_size;
                object start_obj;
                private IntPtr appdomain_refs;
-               private bool interruption_requested;
+               private int interruption_requested;
                private IntPtr suspend_event;
                private IntPtr suspended_event;
                private IntPtr resume_event;
-               /* Don't lock on synch_lock in managed code, since it can result in deadlocks */
-               private object synch_lock = new Object();
+               private IntPtr synch_cs;
                private IntPtr serialized_culture_info;
                private int serialized_culture_info_len;
                private IntPtr serialized_ui_culture_info;
                private int serialized_ui_culture_info_len;
                private ExecutionContext _ec;
+               private bool thread_dump_requested;
+               private IntPtr end_stack;
+               private bool thread_interrupt_requested;
+               private byte apartment_state = (byte)ApartmentState.Unknown;
+               volatile int critical_region_level;
+               private int small_id;
+               private IntPtr manage_callback;
+               private object pending_exception;
                /* 
                 * These fields are used to avoid having to increment corlib versions
                 * when a new field is added to the unmanaged MonoThread structure.
                 */
-               private IntPtr unused1;
                private IntPtr unused2;
                private IntPtr unused3;
                private IntPtr unused4;
                private IntPtr unused5;
                private IntPtr unused6;
-               private IntPtr unused7;
                #endregion
+#pragma warning restore 169, 414, 649
 
+               // the name of local_slots is important as it's used by the runtime.
                [ThreadStatic] 
-               static Hashtable slothash;
+               static object[] local_slots;
 
                // can be both a ThreadSart and a ParameterizedThreadStart
                private MulticastDelegate threadstart;
-               private string thread_name=null;
+               //private string thread_name=null;
+
+#if NET_2_0            
+               private static int _managed_id_counter;
+               private int managed_id;
+#endif         
                
                private IPrincipal _principal;
+               internal NumberFormatter _numberFormatter;
 
                public static Context CurrentContext {
                        [SecurityPermission (SecurityAction.LinkDemand, Infrastructure=true)]
@@ -171,56 +188,76 @@ namespace System.Threading {
                        }
                }
                
-               public static LocalDataStoreSlot AllocateNamedDataSlot(string name) {
+               public static LocalDataStoreSlot AllocateNamedDataSlot (string name) {
                        lock (datastore_lock) {
                                if (datastorehash == null)
                                        InitDataStoreHash ();
-                               LocalDataStoreSlot slot = (LocalDataStoreSlot)datastorehash[name];
-                               if(slot!=null) {
+                               LocalDataStoreSlot slot = (LocalDataStoreSlot)datastorehash [name];
+                               if (slot != null) {
                                        // This exception isnt documented (of
                                        // course) but .net throws it
                                        throw new ArgumentException("Named data slot already added");
                                }
                        
-                               slot = new LocalDataStoreSlot();
+                               slot = AllocateDataSlot ();
 
-                               datastorehash.Add(name, slot);
+                               datastorehash.Add (name, slot);
 
-                               return(slot);
+                               return slot;
                        }
                }
 
-               public static void FreeNamedDataSlot(string name) {
+               public static void FreeNamedDataSlot (string name) {
                        lock (datastore_lock) {
                                if (datastorehash == null)
                                        InitDataStoreHash ();
-                               LocalDataStoreSlot slot=(LocalDataStoreSlot)datastorehash[name];
+                               LocalDataStoreSlot slot = (LocalDataStoreSlot)datastorehash [name];
 
-                               if(slot!=null) {
-                                       datastorehash.Remove(slot);
+                               if (slot != null) {
+                                       datastorehash.Remove (slot);
                                }
                        }
                }
 
-               private static Hashtable SlotHash {
-                       get {
-                               if (slothash == null)
-                                       slothash = new Hashtable ();
-                               return slothash;
-                       }
+               public static LocalDataStoreSlot AllocateDataSlot () {
+                       return new LocalDataStoreSlot (true);
                }
 
-               public static LocalDataStoreSlot AllocateDataSlot() {
-                       return new LocalDataStoreSlot();
+               public static object GetData (LocalDataStoreSlot slot) {
+                       object[] slots = local_slots;
+                       if (slot == null)
+                               throw new ArgumentNullException ("slot");
+                       if (slots != null && slot.slot < slots.Length)
+                               return slots [slot.slot];
+                       return null;
                }
 
-               public static object GetData(LocalDataStoreSlot slot) {
-                       return SlotHash [slot];
+               public static void SetData (LocalDataStoreSlot slot, object data) {
+                       object[] slots = local_slots;
+                       if (slot == null)
+                               throw new ArgumentNullException ("slot");
+                       if (slots == null) {
+                               slots = new object [slot.slot + 2];
+                               local_slots = slots;
+                       } else if (slot.slot >= slots.Length) {
+                               object[] nslots = new object [slot.slot + 2];
+                               slots.CopyTo (nslots, 0);
+                               slots = nslots;
+                               local_slots = slots;
+                       }
+                       slots [slot.slot] = data;
                }
 
-               public static void SetData(LocalDataStoreSlot slot,
-                                          object data) {
-                       SlotHash [slot] = data;
+               internal NumberFormatter AcquireNumberFormatter() {
+                       NumberFormatter res = _numberFormatter;
+                       _numberFormatter = null;
+                       if (res == null)
+                               return new NumberFormatter (this);
+                       return res;
+               }
+
+               internal void ReleaseNumberFormatter (NumberFormatter formatter) {
+                       _numberFormatter = formatter;
                }
 
                public static AppDomain GetDomain() {
@@ -230,6 +267,9 @@ namespace System.Threading {
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                public extern static int GetDomainID();
 
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static void FreeLocalSlotValues (int slot, bool thread_local);
+
                public static LocalDataStoreSlot GetNamedDataSlot(string name) {
                        lock (datastore_lock) {
                                if (datastorehash == null)
@@ -278,28 +318,52 @@ namespace System.Threading {
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                private extern IntPtr Thread_internal (MulticastDelegate start);
 
+               [MethodImplAttribute(MethodImplOptions.InternalCall)]
+               private extern void Thread_init ();
+
                public Thread(ThreadStart start) {
                        if(start==null) {
                                throw new ArgumentNullException("Null ThreadStart");
                        }
                        threadstart=start;
+
+                       Thread_init ();
                }
 
-               [MonoTODO]
 #if NET_2_0
                [Obsolete ("Deprecated in favor of GetApartmentState, SetApartmentState and TrySetApartmentState.")]
 #endif
                public ApartmentState ApartmentState {
                        get {
-                               return(ApartmentState.Unknown);
+                               if ((ThreadState & ThreadState.Stopped) != 0)
+                                       throw new ThreadStateException ("Thread is dead; state can not be accessed.");
+
+                               return (ApartmentState)apartment_state;
                        }
-                       
-                       set {
+
+                       set     {
+#if NET_2_0
+                               TrySetApartmentState (value);
+#else
+                               /* Only throw this exception when
+                                * changing the state of another
+                                * thread.  See bug 324338
+                                */
+                               if ((this != CurrentThread) &&
+                                   (ThreadState & ThreadState.Unstarted) == 0)
+                                       throw new ThreadStateException ("Thread was in an invalid state for the operation being executed.");
+
+                               if (value != ApartmentState.STA && value != ApartmentState.MTA)
+                                       throw new ArgumentOutOfRangeException ("value is not a valid apartment state.");
+
+                               if ((ApartmentState)apartment_state == ApartmentState.Unknown)
+                                       apartment_state = (byte)value;
+#endif
                        }
                }
 
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               private static extern int current_lcid ();
+               //[MethodImplAttribute (MethodImplOptions.InternalCall)]
+               //private static extern int current_lcid ();
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern CultureInfo GetCachedCurrentCulture ();
@@ -331,7 +395,7 @@ namespace System.Threading {
                 * CurrentCulture, which will throw an exception, etc.
                 * Use a boolean to short-circuit this scenario.
                 */
-               private static bool in_currentculture=false;
+               private bool in_currentculture=false;
 
                static object culture_lock = new object ();
                
@@ -366,6 +430,8 @@ namespace System.Threading {
                                                //
                                                SetCachedCurrentCulture (culture);
                                                in_currentculture = false;
+                                               if (_numberFormatter != null)
+                                                       _numberFormatter.CurrentCulture = culture;
                                                return culture;
                                        }
                                }
@@ -384,6 +450,8 @@ namespace System.Threading {
                                        in_currentculture = false;
                                }
 
+                               if (_numberFormatter != null)
+                                       _numberFormatter.CurrentCulture = culture;
                                return culture;
                        }
                        
@@ -392,18 +460,35 @@ namespace System.Threading {
                                if (value == null)
                                        throw new ArgumentNullException ("value");
 
+                               CultureInfo culture = GetCachedCurrentCulture ();
+                               if (culture == value)
+                                       return;
+
                                value.CheckNeutral ();
                                in_currentculture = true;
                                try {
-                                       BinaryFormatter bf = new BinaryFormatter();
-                                       MemoryStream ms = new MemoryStream ();
-                                       bf.Serialize (ms, value);
-
                                        SetCachedCurrentCulture (value);
-                                       SetSerializedCurrentCulture (ms.GetBuffer ());
+
+                                       byte[] serialized_form = null;
+
+                                       if (value.IsReadOnly && value.cached_serialized_form != null) {
+                                               serialized_form = value.cached_serialized_form;
+                                       } else {
+                                               BinaryFormatter bf = new BinaryFormatter();
+                                               MemoryStream ms = new MemoryStream ();
+                                               bf.Serialize (ms, value);
+
+                                               serialized_form = ms.GetBuffer ();
+                                               if (value.IsReadOnly)
+                                                       value.cached_serialized_form = serialized_form;
+                                       }
+                                               
+                                       SetSerializedCurrentCulture (serialized_form);
                                } finally {
                                        in_currentculture = false;
                                }
+                               if (_numberFormatter != null)
+                                       _numberFormatter.CurrentCulture = value;
                        }
                }
 
@@ -463,13 +548,28 @@ namespace System.Threading {
                                if (value == null)
                                        throw new ArgumentNullException ("value");
 
-                               try {
-                                       BinaryFormatter bf = new BinaryFormatter();
-                                       MemoryStream ms = new MemoryStream ();
-                                       bf.Serialize (ms, value);
+                               CultureInfo culture = GetCachedCurrentUICulture ();
+                               if (culture == value)
+                                       return;
 
+                               try {
                                        SetCachedCurrentUICulture (value);
-                                       SetSerializedCurrentUICulture (ms.GetBuffer ());
+
+                                       byte[] serialized_form = null;
+
+                                       if (value.IsReadOnly && value.cached_serialized_form != null) {
+                                               serialized_form = value.cached_serialized_form;
+                                       } else {
+                                               BinaryFormatter bf = new BinaryFormatter();
+                                               MemoryStream ms = new MemoryStream ();
+                                               bf.Serialize (ms, value);
+
+                                               serialized_form = ms.GetBuffer ();
+                                               if (value.IsReadOnly)
+                                                       value.cached_serialized_form = serialized_form;
+                                       }
+                                               
+                                       SetSerializedCurrentUICulture (serialized_form);
                                } finally {
                                        in_currentculture = false;
                                }
@@ -507,7 +607,11 @@ namespace System.Threading {
 
                public bool IsBackground {
                        get {
-                               return (GetState () & ThreadState.Background) != 0;
+                               ThreadState thread_state = GetState ();
+                               if ((thread_state & ThreadState.Stopped) != 0)
+                                       throw new ThreadStateException ("Thread is dead; state can not be accessed.");
+
+                               return (thread_state & ThreadState.Background) != 0;
                        }
                        
                        set {
@@ -540,13 +644,13 @@ namespace System.Threading {
                        }
                }
 
-               [MonoTODO]
                public ThreadPriority Priority {
                        get {
                                return(ThreadPriority.Lowest);
                        }
                        
                        set {
+                               // FIXME: Implement setter.
                        }
                }
 
@@ -571,12 +675,13 @@ namespace System.Threading {
                        Abort_internal (stateInfo);
                }
                
-
-               [MonoTODO]
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern void Interrupt_internal ();
+               
                [SecurityPermission (SecurityAction.Demand, ControlThread=true)]
                public void Interrupt ()
                {
-                       throw new NotImplementedException ();
+                       Interrupt_internal ();
                }
 
                // The current thread joins with 'this'. Set ms to 0 to block
@@ -624,13 +729,16 @@ namespace System.Threading {
                        Resume_internal ();
                }
 
-               [MonoTODO]
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static void SpinWait_internal (int iterations);
+
+
 #if NET_2_0
                [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
 #endif
                public static void SpinWait (int iterations) 
                {
-                       throw new NotImplementedException ();
+                       SpinWait_internal (iterations);
                }
 
                public void Start() {
@@ -779,6 +887,10 @@ namespace System.Threading {
 #endif
 
 #if NET_2_0
+               private static int GetNewManagedId() {
+                       return Interlocked.Increment(ref _managed_id_counter);
+               }
+
                public Thread (ThreadStart start, int maxStackSize)
                {
                        if (start == null)
@@ -788,6 +900,7 @@ namespace System.Threading {
 
                        threadstart = start;
                        stack_size = maxStackSize;
+                       Thread_init ();
                }
 
                public Thread (ParameterizedThreadStart start)
@@ -796,6 +909,7 @@ namespace System.Threading {
                                throw new ArgumentNullException ("start");
 
                        threadstart = start;
+                       Thread_init ();
                }
 
                public Thread (ParameterizedThreadStart start, int maxStackSize)
@@ -807,9 +921,11 @@ namespace System.Threading {
 
                        threadstart = start;
                        stack_size = maxStackSize;
+                       Thread_init ();
                }
 
                [MonoTODO ("limited to CompressedStack support")]
+               // FIXME: We share the _ec object between appdomains
                public ExecutionContext ExecutionContext {
                        [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
                        get {
@@ -820,80 +936,74 @@ namespace System.Threading {
                }
 
                public int ManagedThreadId {
-               [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
-                       get { return (int)thread_id; }
+                       [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
+                       get {
+                               if (managed_id == 0) {
+                                       int new_managed_id = GetNewManagedId ();
+                                       
+                                       Interlocked.CompareExchange (ref managed_id, new_managed_id, 0);
+                               }
+                               
+                               return managed_id;
+                       }
                }
 
-               [MonoTODO]
                [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)]
                public static void BeginCriticalRegion ()
                {
-                       throw new NotImplementedException ();
+                       CurrentThread.critical_region_level++;
                }
 
-               [MonoTODO]
                [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)]
                public static void EndCriticalRegion ()
                {
-                       throw new NotImplementedException ();
+                       CurrentThread.critical_region_level--;
                }
 
-               [MonoTODO]
                [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
                public static void BeginThreadAffinity ()
                {
-                       throw new NotImplementedException ();
+                       // Managed and native threads are currently bound together.
                }
 
-               [MonoTODO]
                [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
                public static void EndThreadAffinity ()
                {
-                       throw new NotImplementedException ();
+                       // Managed and native threads are currently bound together.
                }
-
-               //
-               // We disable warning 618, because we are accessing the
-               // empty property ApartmentState which produces an Obsolete
-               // message, but since its an empty routine needed for 1.x
-               // we use it.
-               //
-               // Maybe we should later turn these into internal methods for 1.x
-               // instead and have the property call these.
                
-               
-               [MonoTODO]
                public ApartmentState GetApartmentState ()
                {
-                       return this.ApartmentState;
+                       return (ApartmentState)apartment_state;
                }
 
-               [MonoTODO]
                public void SetApartmentState (ApartmentState state)
                {
-                       this.ApartmentState = state;
+                       if (!TrySetApartmentState (state))
+                               throw new InvalidOperationException ("Failed to set the specified COM apartment state.");
                }
 
-               [MonoTODO]
-               public bool TrySetApartmentState (ApartmentState state)
+               public bool TrySetApartmentState (ApartmentState state) 
                {
-                       try {
-                               this.ApartmentState = state;
-                               return true;
-                       }
-                       catch (ArgumentException) {
-                               throw;
-                       }
-                       catch {
+                       /* Only throw this exception when changing the
+                        * state of another thread.  See bug 324338
+                        */
+                       if ((this != CurrentThread) &&
+                           (ThreadState & ThreadState.Unstarted) == 0)
+                               throw new ThreadStateException ("Thread was in an invalid state for the operation being executed.");
+
+                       if ((ApartmentState)apartment_state != ApartmentState.Unknown)
                                return false;
-                       }
+
+                       apartment_state = (byte)state;
+
+                       return true;
                }
                
                [ComVisible (false)]
                public override int GetHashCode ()
                {
-                       // ??? overridden but not guaranteed to be unique ???
-                       return (int)thread_id;
+                       return ManagedThreadId;
                }
 
                public void Start (object parameter)