X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Threading%2FThread.cs;h=a1e44a48687c96ef299957aa37fc8404f37c9795;hb=159314e0440139d301ec145450b73045cea8563f;hp=042f475b76349f0fe922a2937eae7942b479d6df;hpb=b585d00928892398dfbfc315ed78b8032fa14708;p=mono.git diff --git a/mcs/class/corlib/System.Threading/Thread.cs b/mcs/class/corlib/System.Threading/Thread.cs index 042f475b763..a1e44a48687 100644 --- a/mcs/class/corlib/System.Threading/Thread.cs +++ b/mcs/class/corlib/System.Threading/Thread.cs @@ -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)