// Dick Porter (dick@ximian.com)
//
// (C) Ximian, Inc. http://www.ximian.com
+// Copyright (C) 2004 Novell (http://www.novell.com)
+//
+
+//
+// Copyright (C) 2004 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
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System.Runtime.Remoting.Contexts;
+using System.Security.Permissions;
using System.Security.Principal;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Collections;
namespace System.Threading
{
public sealed class Thread
{
+ #region Sync with object.h
+ // stores a thread handle
+ private IntPtr system_thread_handle;
+
+ private CultureInfo current_culture;
+ private CultureInfo current_ui_culture;
+ private bool threadpool_thread;
+ /* accessed only from unmanaged code */
+ private IntPtr name;
+ private int name_len;
+ private ThreadState state = ThreadState.Unstarted;
+ private object abort_exc;
+ internal object abort_state;
+ /* thread_id is only accessed from unmanaged code */
+ private int thread_id;
+
+ /* start_notify is used by the runtime to signal that Start()
+ * is ok to return
+ */
+ private IntPtr start_notify;
+ private IntPtr stack_ptr;
+ private IntPtr static_data;
+ private IntPtr jit_data;
+ private IntPtr lock_data;
+ private IntPtr appdomain_refs;
+ private bool interruption_requested;
+ private IntPtr suspend_event;
+ private IntPtr resume_event;
+ private object synch_lock = new Object();
+ #endregion
+
+ private ThreadStart threadstart;
+ private string thread_name=null;
+
+ private IPrincipal _principal;
+
public static Context CurrentContext {
get {
- // FIXME -
- // System.Runtime.Remoting.Context not
- // yet implemented
- return(null);
+ return(AppDomain.InternalGetContext ());
}
}
public static IPrincipal CurrentPrincipal {
get {
- // FIXME -
- // System.Security.Principal.IPrincipal
- // not yet implemented
- return(null);
+ IPrincipal p = null;
+ Thread th = CurrentThread;
+ lock (th) {
+ p = th._principal;
+ if (p == null) {
+ p = GetDomain ().DefaultPrincipal;
+ th._principal = p;
+ }
+ }
+ return p;
}
-
set {
+ new SecurityPermission (SecurityPermissionFlag.ControlPrincipal).Demand ();
+ CurrentThread._principal = value;
}
}
}
}
+ internal static int CurrentThreadId {
+ get {
+ return CurrentThread.thread_id;
+ }
+ }
+
+ // Looks up the slot hash for the current thread
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static Hashtable SlotHash_lookup();
+
+ // Stores the slot hash for the current thread
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static void SlotHash_store(Hashtable slothash);
+
+ private static Hashtable GetTLSSlotHash() {
+ Hashtable slothash=SlotHash_lookup();
+ if(slothash==null) {
+ // Not synchronised, because this is
+ // thread specific anyway.
+ slothash=new Hashtable();
+ SlotHash_store(slothash);
+ }
+
+ return(slothash);
+ }
+
+ internal static object ResetDataStoreStatus () {
+ Hashtable slothash=SlotHash_lookup();
+ SlotHash_store(null);
+ return slothash;
+ }
+
+ internal static void RestoreDataStoreStatus (object data) {
+ SlotHash_store((Hashtable)data);
+ }
+
public static LocalDataStoreSlot AllocateDataSlot() {
- // FIXME
- return(null);
+ LocalDataStoreSlot slot = new LocalDataStoreSlot();
+
+ return(slot);
}
+ // Stores a hash keyed by strings of LocalDataStoreSlot objects
+ static Hashtable datastorehash;
+ private static object datastore_lock = new object ();
+
+ private static void InitDataStoreHash () {
+ lock (datastore_lock) {
+ if (datastorehash == null) {
+ datastorehash = Hashtable.Synchronized(new Hashtable());
+ }
+ }
+ }
+
public static LocalDataStoreSlot AllocateNamedDataSlot(string name) {
- // FIXME
- return(null);
+ lock (datastore_lock) {
+ if (datastorehash == null)
+ InitDataStoreHash ();
+ 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();
+
+ datastorehash.Add(name, slot);
+
+ return(slot);
+ }
}
public static void FreeNamedDataSlot(string name) {
- // FIXME
+ lock (datastore_lock) {
+ if (datastorehash == null)
+ InitDataStoreHash ();
+ LocalDataStoreSlot slot=(LocalDataStoreSlot)datastorehash[name];
+
+ if(slot!=null) {
+ datastorehash.Remove(slot);
+ }
+ }
}
public static object GetData(LocalDataStoreSlot slot) {
- // FIXME
- return(null);
+ Hashtable slothash=GetTLSSlotHash();
+ return(slothash[slot]);
}
public static AppDomain GetDomain() {
- // FIXME
- return(null);
+ return AppDomain.CurrentDomain;
}
- public static int GetDomainID() {
- // FIXME
- return(0);
- }
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public extern static int GetDomainID();
public static LocalDataStoreSlot GetNamedDataSlot(string name) {
- // FIXME
- return(null);
+ lock (datastore_lock) {
+ if (datastorehash == null)
+ InitDataStoreHash ();
+ LocalDataStoreSlot slot=(LocalDataStoreSlot)datastorehash[name];
+
+ if(slot==null) {
+ slot=AllocateNamedDataSlot(name);
+ }
+
+ return(slot);
+ }
}
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern static void ResetAbort_internal();
- public static void ResetAbort() {
- // FIXME
+ public static void ResetAbort()
+ {
+ ResetAbort_internal();
}
+
+
+ public static void SetData(LocalDataStoreSlot slot,
+ object data) {
+ Hashtable slothash=GetTLSSlotHash();
- public static void SetData(LocalDataStoreSlot slot, object data) {
- // FIXME
+ if(slothash.Contains(slot)) {
+ slothash.Remove(slot);
+ }
+
+ slothash.Add(slot, data);
}
- // Returns milliseconds remaining (due to interrupted sleep)
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static int Sleep_internal(int ms);
+ private extern static void Sleep_internal(int ms);
- // Causes thread to give up its timeslice
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern static void Schedule_internal();
-
public static void Sleep(int millisecondsTimeout) {
- if(millisecondsTimeout<0) {
+ if((millisecondsTimeout<0) && (millisecondsTimeout != Timeout.Infinite)) {
throw new ArgumentException("Negative timeout");
}
- if(millisecondsTimeout==0) {
- // Schedule another thread
- Schedule_internal();
- } else {
- Thread thread=CurrentThread;
-
- thread.state |= ThreadState.WaitSleepJoin;
- int ms_remaining=Sleep_internal(millisecondsTimeout);
- thread.state &= ~ThreadState.WaitSleepJoin;
-
- if(ms_remaining>0) {
- throw new ThreadInterruptedException("Thread interrupted while sleeping");
- }
- }
-
+ Thread thread=CurrentThread;
+ Sleep_internal(millisecondsTimeout);
}
public static void Sleep(TimeSpan timeout) {
// LAMESPEC: says to throw ArgumentException too
- if(timeout.Milliseconds < 0 || timeout.Milliseconds > Int32.MaxValue) {
+ int ms=Convert.ToInt32(timeout.TotalMilliseconds);
+
+ if(ms < 0 || ms > Int32.MaxValue) {
throw new ArgumentOutOfRangeException("Timeout out of range");
}
- if(timeout.Milliseconds==0) {
- // Schedule another thread
- Schedule_internal();
- } else {
- Thread thread=CurrentThread;
-
- thread.state |= ThreadState.WaitSleepJoin;
- int ms_remaining=Sleep_internal(timeout.Milliseconds);
- thread.state &= ~ThreadState.WaitSleepJoin;
-
- if(ms_remaining>0) {
- throw new ThreadInterruptedException("Thread interrupted while sleeping");
- }
- }
+
+ Thread thread=CurrentThread;
+ Sleep_internal(ms);
}
- private ThreadStart start_delegate=null;
-
+ // Returns the system thread handle
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern IntPtr Thread_internal(ThreadStart start);
+
public Thread(ThreadStart start) {
if(start==null) {
throw new ArgumentNullException("Null ThreadStart");
}
-
- // Nothing actually happens here, the fun
- // begins when Thread.Start() is called. For
- // now, just record what the ThreadStart
- // delegate is.
- start_delegate=start;
+ threadstart=start;
}
+ [MonoTODO]
public ApartmentState ApartmentState {
get {
- // FIXME
return(ApartmentState.Unknown);
}
}
}
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private static extern int current_lcid ();
+
+ /* If the current_lcid() isn't known by CultureInfo,
+ * it will throw an exception which may cause
+ * String.Concat to try and recursively look up the
+ * CurrentCulture, which will throw an exception, etc.
+ * Use a boolean to short-circuit this scenario.
+ */
+ private static bool in_currentculture=false;
+
public CultureInfo CurrentCulture {
get {
- // FIXME
- return(null);
+ if (current_culture == null) {
+ lock (typeof (Thread)) {
+ if(current_culture==null) {
+ if(in_currentculture==true) {
+ /* Bail out */
+ current_culture = CultureInfo.InvariantCulture;
+ } else {
+ in_currentculture=true;
+
+ current_culture = CultureInfo.ConstructCurrentCulture ();
+ }
+ }
+
+ in_currentculture=false;
+ }
+ }
+
+ return(current_culture);
}
set {
+ current_culture = value;
}
}
public CultureInfo CurrentUICulture {
get {
- // FIXME
- return(null);
+ if (current_ui_culture == null) {
+ lock (synch_lock) {
+ if(current_ui_culture==null) {
+ /* We don't
+ * distinguish
+ * between
+ * System and
+ * UI cultures
+ */
+ current_ui_culture = CultureInfo.ConstructCurrentUICulture ();
+ }
+ }
+ }
+
+ return(current_ui_culture);
}
set {
+ current_ui_culture = value;
+ }
+ }
+
+ public bool IsThreadPoolThread {
+ get {
+ return IsThreadPoolThreadInternal;
+ }
+ }
+
+ internal bool IsThreadPoolThreadInternal {
+ get {
+ return threadpool_thread;
+ }
+ set {
+ threadpool_thread = value;
}
}
public bool IsAlive {
get {
- // LAMESPEC: is a Stopped thread dead?
- if((state & ThreadState.Running) != 0 ||
- (state & ThreadState.Stopped) != 0) {
- return(true);
- } else {
+ ThreadState curstate=state;
+
+ if((curstate & ThreadState.Aborted) != 0 ||
+ (curstate & ThreadState.Stopped) != 0 ||
+ (curstate & ThreadState.Unstarted) != 0) {
return(false);
+ } else {
+ return(true);
}
}
}
public bool IsBackground {
get {
- // FIXME
- return(false);
+ if((state & ThreadState.Background) != 0) {
+ return(true);
+ } else {
+ return(false);
+ }
}
set {
+ if(value==true) {
+ set_state(ThreadState.Background);
+ } else {
+ clr_state(ThreadState.Background);
+ }
}
}
- private string thread_name=null;
-
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern string GetName_internal ();
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void SetName_internal (String name);
+
+ /*
+ * The thread name must be shared by appdomains, so it is stored in
+ * unmanaged code.
+ */
+
public string Name {
get {
- return(thread_name);
+ return GetName_internal ();
}
set {
- thread_name=value;
+ lock (synch_lock) {
+ if(Name!=null) {
+ throw new InvalidOperationException ("Thread.Name can only be set once.");
+ }
+
+ SetName_internal (value);
+ }
}
}
+ [MonoTODO]
public ThreadPriority Priority {
get {
- // FIXME
return(ThreadPriority.Lowest);
}
}
}
- private ThreadState state=ThreadState.Unstarted;
-
public ThreadState ThreadState {
get {
return(state);
}
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void Abort_internal (object stateInfo);
+
public void Abort() {
- // FIXME
+ Abort_internal (null);
}
public void Abort(object stateInfo) {
- // FIXME
+ Abort_internal(stateInfo);
}
+
+ [MonoTODO]
public void Interrupt() {
- // FIXME
}
// The current thread joins with 'this'. Set ms to 0 to block
// until this actually exits.
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern /*bool*/ int Join_internal(int ms, UInt32 handle);
+ private extern bool Join_internal(int ms, IntPtr handle);
public void Join() {
- if(state == ThreadState.Unstarted) {
+ if((state & ThreadState.Unstarted) != 0) {
throw new ThreadStateException("Thread has not been started");
}
Thread thread=CurrentThread;
- thread.state |= ThreadState.WaitSleepJoin;
- Join_internal(0, system_thread_handle);
- thread.state &= ~ThreadState.WaitSleepJoin;
+ Join_internal(Timeout.Infinite, system_thread_handle);
}
public bool Join(int millisecondsTimeout) {
- if(millisecondsTimeout<0) {
- throw new ArgumentException("Timeout less than zero");
- }
- if(state == ThreadState.Unstarted) {
+ if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout < 0)
+ throw new ArgumentException ("Timeout less than zero", "millisecondsTimeout");
+
+ if((state & ThreadState.Unstarted) != 0) {
throw new ThreadStateException("Thread has not been started");
}
Thread thread=CurrentThread;
-
- thread.state |= ThreadState.WaitSleepJoin;
- bool ret=(Join_internal(millisecondsTimeout,
- system_thread_handle)==1);
- thread.state &= ~ThreadState.WaitSleepJoin;
-
- return(ret);
+ return Join_internal(millisecondsTimeout, system_thread_handle);
}
public bool Join(TimeSpan timeout) {
// LAMESPEC: says to throw ArgumentException too
- if(timeout.Milliseconds < 0 || timeout.Milliseconds > Int32.MaxValue) {
+ int ms=Convert.ToInt32(timeout.TotalMilliseconds);
+
+ if(ms < 0 || ms > Int32.MaxValue) {
throw new ArgumentOutOfRangeException("timeout out of range");
}
- if(state == ThreadState.Unstarted) {
+ if((state & ThreadState.Unstarted) != 0) {
throw new ThreadStateException("Thread has not been started");
}
Thread thread=CurrentThread;
-
- thread.state |= ThreadState.WaitSleepJoin;
- bool ret=(Join_internal(timeout.Milliseconds,
- system_thread_handle)==1);
- thread.state &= ~ThreadState.WaitSleepJoin;
+ return Join_internal(ms, system_thread_handle);
+ }
- return(ret);
+#if NET_1_1
+ [MonoTODO ("seems required for multi-processors systems like Itanium")]
+ public static void MemoryBarrier ()
+ {
+ throw new NotImplementedException ();
+ }
+#endif
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void Resume_internal();
+
+ public void Resume ()
+ {
+ if ((state & ThreadState.Unstarted) != 0 || !IsAlive ||
+ ((state & ThreadState.Suspended) == 0 && (state & ThreadState.SuspendRequested) == 0))
+ {
+ throw new ThreadStateException("Thread has not been started, or is dead");
+ }
+
+ Resume_internal ();
}
- public void Resume() {
- // FIXME
+ [MonoTODO]
+ public static void SpinWait (int iterations)
+ {
+ throw new NotImplementedException ();
}
-
- // stores a pthread_t, which is defined as unsigned long
- // on my system. I _think_ windows uses "unsigned int" for
- // its thread handles, so that _should_ work too.
- private UInt32 system_thread_handle;
- // Returns the system thread handle
+ // Launches the thread
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- private extern UInt32 Start_internal(ThreadStart start);
+ private extern void Start_internal(IntPtr handle);
public void Start() {
- if(state!=ThreadState.Unstarted) {
- throw new ThreadStateException("Thread has already been started");
- }
+ lock(synch_lock) {
+ if((state & ThreadState.Unstarted) == 0) {
+ throw new ThreadStateException("Thread has already been started");
+ }
+
- state=ThreadState.Running;
-
- // Don't rely on system_thread_handle being
- // set before start_delegate runs!
- system_thread_handle=Start_internal(start_delegate);
+ // Thread_internal creates the new thread, but
+ // blocks it until Start() is called later.
+ system_thread_handle=Thread_internal(threadstart);
+
+ if (system_thread_handle == (IntPtr) 0) {
+ throw new SystemException ("Thread creation failed");
+ }
+
+ // Launch this thread
+ Start_internal(system_thread_handle);
+
+ // Mark the thread state as Running
+ // (which is all bits
+ // cleared). Therefore just remove the
+ // Unstarted bit
+ clr_state(ThreadState.Unstarted);
+ }
}
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void Suspend_internal();
+
public void Suspend() {
- // FIXME
+ if((state & ThreadState.Unstarted) != 0 || !IsAlive) {
+ throw new ThreadStateException("Thread has not been started, or is dead");
+ }
+ Suspend_internal ();
}
+ // Closes the system thread handle
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ private extern void Thread_free_internal(IntPtr handle);
+
~Thread() {
- // FIXME
+ // Free up the handle
+ if (system_thread_handle != (IntPtr) 0)
+ Thread_free_internal(system_thread_handle);
+ }
+
+ private void set_state(ThreadState set) {
+ lock(synch_lock) {
+ state |= set;
+ }
+ }
+ private void clr_state(ThreadState clr) {
+ lock(synch_lock) {
+ state &= ~clr;
+ }
}
+
+#if NET_1_1
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static byte VolatileRead (ref byte address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static double VolatileRead (ref double address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static short VolatileRead (ref short address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static int VolatileRead (ref int address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static long VolatileRead (ref long address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static IntPtr VolatileRead (ref IntPtr address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static object VolatileRead (ref object address);
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static sbyte VolatileRead (ref sbyte address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static float VolatileRead (ref float address);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static ushort VolatileRead (ref ushort address);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static uint VolatileRead (ref uint address);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static ulong VolatileRead (ref ulong address);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static UIntPtr VolatileRead (ref UIntPtr address);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref byte address, byte value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref double address, double value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref short address, short value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref int address, int value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref long address, long value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref IntPtr address, IntPtr value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref object address, object value);
+
+ [CLSCompliant(false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref sbyte address, sbyte value);
+
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref float address, float value);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref ushort address, ushort value);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref uint address, uint value);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref ulong address, ulong value);
+
+ [CLSCompliant (false)]
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ extern public static void VolatileWrite (ref UIntPtr address, UIntPtr value);
+
+#endif
+
}
}