diff --git a/mcs/class/System/System.Threading/Semaphore.cs.new b/mcs/class/System/System.Threading/Semaphore.cs.new new file mode 100644 index 0000000000000..3d1d1ee01622b --- /dev/null +++ b/mcs/class/System/System.Threading/Semaphore.cs.new @@ -0,0 +1,112 @@ +// +// System.Threading.Semaphore.cs +// +// Copyright 2011 Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#if NET_2_0 + +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security.AccessControl; + +namespace System.Threading +{ + [ComVisible (false)] + public sealed class Semaphore : WaitHandle + { + internal Semaphore (StWaitable waitable) + { + Waitable = waitable; + } + + public Semaphore (int initialCount, int maximumCount) + { + if (initialCount < 0) + throw new ArgumentOutOfRangeException("initialCount", "< 0"); + if (maximumCount < 1) + throw new ArgumentOutOfRangeException("maximumCount", "< 1"); + if (initialCount > maximumCount) + throw new ArgumentException("initialCount > maximumCount"); + + Waitable = new StSemaphore (initialCount, maximumCount); + } + + public Semaphore (int initialCount, int maximumCount, string name) + { + throw new NotImplementedException (); + } + + public Semaphore (int initialCount, int maximumCount, string name, out bool createdNew) + : this (initialCount, maximumCount, name, out createdNew, null) { } + + [MonoTODO ("Does not support access control, semaphoreSecurity is ignored")] + public Semaphore (int initialCount, int maximumCount, string name, out bool createdNew, + SemaphoreSecurity semaphoreSecurity) + { + throw new NotImplementedException (); + } + + public static Semaphore OpenExisting (string name) + { + return OpenExisting(name, SemaphoreRights.Synchronize | SemaphoreRights.Modify); + } + + public static Semaphore OpenExisting (string name, SemaphoreRights rights) + { + throw new NotImplementedException (); + } + + [MonoTODO] + public SemaphoreSecurity GetAccessControl () + { + throw new NotImplementedException (); + } + + [PrePrepareMethod] + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] + public int Release () + { + return (Release (1)); + } + + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] + public int Release (int releaseCount) + { + var sem = Waitable as StSemaphore; + return sem != null ? sem.Release(releaseCount) : 0; + } + + internal override bool _WaitOne(int timeout) + { + var sem = Waitable as StSemaphore; + return sem != null ? sem.Wait (1, new StCancelArgs (timeout)) : false; + } + + [MonoTODO] + public void SetAccessControl (SemaphoreSecurity semaphoreSecurity) + { + if (semaphoreSecurity == null) { + throw new ArgumentNullException ("semaphoreSecurity"); + } + + throw new NotImplementedException (); + } + } +} + +#endif diff --git a/mcs/class/corlib/Microsoft.Win32.SafeHandles/SafeWaitHandle.cs b/mcs/class/corlib/Microsoft.Win32.SafeHandles/SafeWaitHandle.cs index c6f4df5fe094b..8812c31448beb 100644 --- a/mcs/class/corlib/Microsoft.Win32.SafeHandles/SafeWaitHandle.cs +++ b/mcs/class/corlib/Microsoft.Win32.SafeHandles/SafeWaitHandle.cs @@ -1,51 +1,44 @@ // -// Microsoft.Win32.SafeHandles.SafeWaitHandle +// System.Threading.SafeWaitHandle.cs // -// Author: -// Sebastien Pouliot -// Miguel de Icaza -// -// Copyright (C) 2005, 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 -// "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: +// Copyright 2011 Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. +// http://www.apache.org/licenses/LICENSE-2.0 // -// 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. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) // using System; -using System.IO; -using System.Threading; using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; namespace Microsoft.Win32.SafeHandles { public sealed class SafeWaitHandle : SafeHandleZeroOrMinusOneIsInvalid { + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public SafeWaitHandle (IntPtr existingHandle, bool ownsHandle) : base (ownsHandle) + public SafeWaitHandle (IntPtr existingHandle, bool ownsHandle) + : base (ownsHandle) { SetHandle (existingHandle); } protected override bool ReleaseHandle () { - NativeEventCalls.CloseEvent_internal (handle); - return true; + if (handle != IntPtr.Zero) { + GCHandle.FromIntPtr (handle).Free (); + } + return true; } - } -} +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/CancelArgs.cs b/mcs/class/corlib/System.Threading/CancelArgs.cs deleted file mode 100644 index d7fc078aeadde..0000000000000 --- a/mcs/class/corlib/System.Threading/CancelArgs.cs +++ /dev/null @@ -1,157 +0,0 @@ -// -// System.Threading.CancelArgs.cs -// -// Copyright 2011 Carlos Martins, Duarte Nunes -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Author: Duarte Nunes (duarte.m.nunes@gmail.com) -// - -using System; - -namespace System.Threading -{ - - // - // This structure is used to specify the cancellers for - // blocking acquire operations. - // - - public struct StCancelArgs - { - - // - // The specified timeout, disable if set to -1. - // - - public int Timeout { get; private set; } - - // - // The alerter. Can be null. - // - - public StAlerter Alerter { get; private set; } - - // - // Indicates if the wait can be cancelled by thread interruption. - // - - public bool Interruptible { get; private set; } - - // - // CancelArgs used to specify no canceller. - // - - public static readonly StCancelArgs None = new StCancelArgs(-1, null, false); - - // - // Constructors. - // - - public StCancelArgs (int timeout, StAlerter alerter, bool interruptible) - { - if (timeout < -1) { - throw new ArgumentOutOfRangeException ("timeout", timeout, "Wrong timeout value"); - } - - this = new StCancelArgs (); - Timeout = timeout; - Alerter = alerter; - Interruptible = interruptible; - } - - public StCancelArgs (int timeout) - : this (timeout, null, false) { } - - public StCancelArgs (TimeSpan timeout) - : this (timeout.Milliseconds, null, false) { } - - public StCancelArgs (StAlerter alerter) - : this (-1, alerter, false) { } - - public StCancelArgs (bool interruptible) - : this (-1, null, interruptible) { } - - public StCancelArgs (int timeout, bool interruptible) - : this (timeout, null, interruptible) { } - - public StCancelArgs (TimeSpan timeout, bool interruptible) - : this (timeout.Milliseconds, null, interruptible) { } - - public StCancelArgs (int timeout, StAlerter alerter) - : this (timeout, alerter, false) { } - - public StCancelArgs (TimeSpan timeout, StAlerter alerter) - : this (timeout.Milliseconds, alerter, false) { } - - public StCancelArgs (StAlerter alerter, bool interruptible) - : this (-1, alerter, interruptible) { } - - // - // Adjusts the timeout value, returning false if the timeout - // has expired. - // - - public bool AdjustTimeout (ref int lastTime) - { - if (Timeout == System.Threading.Timeout.Infinite) { - return true; - } - - int now = Environment.TickCount; - int e = now == lastTime ? 1 : now - lastTime; - if (Timeout <= e) { - return false; - } - Timeout -= e; - lastTime = now; - return true; - } - - // - // Thows the cancellation exception, if appropriate; - // otherwise, does noting. - // - - public static void ThrowIfException (int ws) - { - switch (ws) { - case StParkStatus.Alerted: throw new StThreadAlertedException (); - case StParkStatus.Interrupted: throw new ThreadInterruptedException (); - default: return; - } - } - - // - // Postpones the cancellation due to the specifed wait status. - // - - internal static void PostponeCancellation (int ws) - { - if (ws == StParkStatus.Interrupted) { - Thread.CurrentThread.Interrupt (); - } - } - - // - // Resets the implicit cancellers. - // - - internal void ResetImplicitCancellers () - { - Timeout = -1; - Interruptible = false; - } - } -} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/EventWaitHandle.cs b/mcs/class/corlib/System.Threading/EventWaitHandle.cs index de747e506bb1f..739087ef6ba58 100644 --- a/mcs/class/corlib/System.Threading/EventWaitHandle.cs +++ b/mcs/class/corlib/System.Threading/EventWaitHandle.cs @@ -1,33 +1,23 @@ // // System.Threading.EventWaitHandle.cs // -// Author: -// Dick Porter (dick@ximian.com) -// -// (C) Ximian, Inc. (http://www.ximian.com) -// Copyright (C) 2005 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: +// Copyright 2011 Duarte Nunes // -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// 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. +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) // -using System.IO; using System.Runtime.InteropServices; #if !NET_2_1 using System.Security.AccessControl; @@ -38,47 +28,43 @@ namespace System.Threading [ComVisible (true)] public class EventWaitHandle : WaitHandle { - private EventWaitHandle (IntPtr handle) - { - Handle = handle; - } - - private bool IsManualReset (EventResetMode mode) + private static bool IsManualReset (EventResetMode mode) { if ((mode < EventResetMode.AutoReset) || (mode > EventResetMode.ManualReset)) throw new ArgumentException ("mode"); return (mode == EventResetMode.ManualReset); } + internal EventWaitHandle (StWaitable waitable) + { + Waitable = waitable; + } + public EventWaitHandle (bool initialState, EventResetMode mode) { - bool created; - bool manual = IsManualReset (mode); - Handle = NativeEventCalls.CreateEvent_internal (manual, initialState, null, out created); + Waitable = IsManualReset (mode) + ? (StWaitable) new StNotificationEvent(initialState) + : new StSynchronizationEvent(initialState); } - public EventWaitHandle (bool initialState, EventResetMode mode, - string name) + public EventWaitHandle (bool initialState, EventResetMode mode, string name) { - bool created; - bool manual = IsManualReset (mode); - Handle = NativeEventCalls.CreateEvent_internal (manual, initialState, name, out created); + throw new NotImplementedException (); } public EventWaitHandle (bool initialState, EventResetMode mode, - string name, out bool createdNew) + string name, out bool createdNew) { - bool manual = IsManualReset (mode); - Handle = NativeEventCalls.CreateEvent_internal (manual, initialState, name, out createdNew); + throw new NotImplementedException (); } + #if !NET_2_1 [MonoTODO ("Implement access control")] public EventWaitHandle (bool initialState, EventResetMode mode, - string name, out bool createdNew, - EventWaitHandleSecurity eventSecurity) + string name, out bool createdNew, + EventWaitHandleSecurity eventSecurity) { - bool manual = IsManualReset (mode); - Handle = NativeEventCalls.CreateEvent_internal (manual, initialState, name, out createdNew); + throw new NotImplementedException (); } [MonoTODO] @@ -89,47 +75,53 @@ public EventWaitHandleSecurity GetAccessControl () public static EventWaitHandle OpenExisting (string name) { - return(OpenExisting (name, EventWaitHandleRights.Synchronize | EventWaitHandleRights.Modify)); + throw new NotImplementedException (); } public static EventWaitHandle OpenExisting (string name, EventWaitHandleRights rights) { - if (name == null) { - throw new ArgumentNullException ("name"); - } - if ((name.Length == 0) || - (name.Length > 260)) { - throw new ArgumentException ("name", Locale.GetText ("Invalid length [1-260].")); - } - - MonoIOError error; - IntPtr handle = NativeEventCalls.OpenEvent_internal (name, rights, out error); - if (handle == (IntPtr)null) { - if (error == MonoIOError.ERROR_FILE_NOT_FOUND) { - throw new WaitHandleCannotBeOpenedException (Locale.GetText ("Named Event handle does not exist: ") + name); - } else if (error == MonoIOError.ERROR_ACCESS_DENIED) { - throw new UnauthorizedAccessException (); - } else { - throw new IOException (Locale.GetText ("Win32 IO error: ") + error.ToString ()); - } - } - - return(new EventWaitHandle (handle)); + throw new NotImplementedException (); } #endif public bool Reset () { - CheckDisposed (); - - return (NativeEventCalls.ResetEvent_internal (Handle)); + if (Waitable is StSynchronizationEvent) { + return ((StSynchronizationEvent)Waitable).Reset (); + } + + if (Waitable is StNotificationEvent) { + return ((StNotificationEvent)Waitable).Reset(); + } + + return false; } public bool Set () - { - CheckDisposed (); - - return (NativeEventCalls.SetEvent_internal (Handle)); + { + if (Waitable is StSynchronizationEvent) { + return ((StSynchronizationEvent)Waitable).Set (); + } + + if (Waitable is StNotificationEvent) { + return ((StNotificationEvent)Waitable).Set(); + } + + return false; } + + internal override bool _WaitOne(int timeout) + { + if (Waitable is StSynchronizationEvent) { + return ((StSynchronizationEvent)Waitable).Wait (new StCancelArgs (timeout)); + } + + if (Waitable is StNotificationEvent) { + return ((StNotificationEvent)Waitable).Wait (new StCancelArgs (timeout)); + } + + return false; + } + #if !NET_2_1 [MonoTODO] public void SetAccessControl (EventWaitHandleSecurity eventSecurity) diff --git a/mcs/class/corlib/System.Threading/ManualResetEventSlim.cs b/mcs/class/corlib/System.Threading/ManualResetEventSlim.cs index 50debca78cc0e..04a6536e5ebe5 100644 --- a/mcs/class/corlib/System.Threading/ManualResetEventSlim.cs +++ b/mcs/class/corlib/System.Threading/ManualResetEventSlim.cs @@ -1,88 +1,75 @@ -// ManuelResetEventSlim.cs // -// Copyright (c) 2008 Jérémie "Garuma" Laval +// System.Threading.ManualResetEventSlim.cs // -// 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. +// Copyright 2011 Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) // #if NET_4_0 || MOBILE -using System; - namespace System.Threading { - [System.Diagnostics.DebuggerDisplayAttribute ("Set = {IsSet}")] + [Diagnostics.DebuggerDisplayAttribute ("Set = {IsSet}")] public class ManualResetEventSlim : IDisposable { - const int isSet = 1; - const int isNotSet = 0; - const int defaultSpinCount = 100; + private const int defaultSpinCount = 100; - int state; - readonly int spinCount; + private readonly StNotificationEvent evt; - ManualResetEvent handle; + public ManualResetEventSlim () + : this (false, defaultSpinCount) { } - readonly static Watch sw = Watch.StartNew (); - - public ManualResetEventSlim () : this (false, defaultSpinCount) - { - } - - public ManualResetEventSlim (bool initialState) : this (initialState, defaultSpinCount) - { - } + public ManualResetEventSlim (bool initialState) + : this (initialState, defaultSpinCount) { } public ManualResetEventSlim (bool initialState, int spinCount) { if (spinCount < 0) throw new ArgumentOutOfRangeException ("spinCount is less than 0", "spinCount"); - this.state = initialState ? isSet : isNotSet; - this.spinCount = spinCount; + evt = new StNotificationEvent (initialState, spinCount); } public bool IsSet { - get { - return state == isSet; - } + get { return evt.IsSet; } } public int SpinCount { - get { - return spinCount; - } + get { return evt.waitEvent.spinCount; } + } + + // + // Just return a new EventWaitHandle. We're assuming that the object + // will be short lived, so we don't hang on to it. The user might + // try to set the Handle/SafeWaitHandle properties, but that leads + // to undefined behavior so we don't even worry about it. + // + + public WaitHandle WaitHandle { + get { return new EventWaitHandle (evt); } } public void Reset () { - state = isNotSet; - if (handle != null) - handle.Reset (); + evt.Reset (); } public void Set () { - state = isSet; - if (handle != null) - handle.Set (); + evt.Set (); } public void Wait () @@ -111,28 +98,16 @@ public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken) throw new ArgumentOutOfRangeException ("millisecondsTimeout", "millisecondsTimeout is a negative number other than -1"); - long start = millisecondsTimeout == -1 ? 0 : sw.ElapsedMilliseconds; - SpinWait wait = new SpinWait (); + var alerter = cancellationToken.CanBeCanceled ? new StAlerter () : null; - while (state == isNotSet) { - cancellationToken.ThrowIfCancellationRequested (); - - if (millisecondsTimeout > -1 && (sw.ElapsedMilliseconds - start) > millisecondsTimeout) - return false; - - if (wait.Count < spinCount) { - wait.SpinOnce (); - } else { - int waitTime = millisecondsTimeout == -1 ? -1 : Math.Max (millisecondsTimeout - (int)(sw.ElapsedMilliseconds - start) , 1); - WaitHandle handle = WaitHandle; - if (state == isSet) - return true; - if (WaitHandle.WaitAny (new[] { handle, cancellationToken.WaitHandle }, waitTime, false) == 0) - return true; - } - } - - return true; + using (cancellationToken.Register(StAlerter.CancellationTokenCallback, alerter)) { + try { + return evt.Wait(new StCancelArgs (millisecondsTimeout, alerter)); + } catch (StThreadAlertedException) { + cancellationToken.ThrowIfCancellationRequested (); + return false; /* Shut the compiler up */ + } + } } public bool Wait (TimeSpan timeout, CancellationToken cancellationToken) @@ -140,36 +115,17 @@ public bool Wait (TimeSpan timeout, CancellationToken cancellationToken) return Wait ((int)timeout.TotalMilliseconds, cancellationToken); } - public WaitHandle WaitHandle { - get { - if (handle != null) { - if (state == isSet) - handle.Set (); - - return handle; - } - - var result = LazyInitializer.EnsureInitialized (ref handle, - () => new ManualResetEvent (state == isSet ? true : false)); - if (state == isSet) - result.Set (); - - return result; - } - } - #region IDisposable implementation - public void Dispose () + + public void Dispose () { - Dispose(true); + Dispose (true); } protected virtual void Dispose (bool disposing) - { + { } - } #endregion - } } #endif diff --git a/mcs/class/corlib/System.Threading/Mutex.cs b/mcs/class/corlib/System.Threading/Mutex.cs index cd14807281cae..589aff5244c8e 100644 --- a/mcs/class/corlib/System.Threading/Mutex.cs +++ b/mcs/class/corlib/System.Threading/Mutex.cs @@ -1,95 +1,57 @@ // // System.Threading.Mutex.cs // -// Author: -// Dick Porter (dick@ximian.com) -// Veronica De Santis (veron78@interfree.it) -// -// (C) Ximian, Inc. http://www.ximian.com -// Copyright (C) 2004-2005 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: +// Copyright 2011 Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. +// http://www.apache.org/licenses/LICENSE-2.0 // -// 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. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) // - -using System.Runtime.CompilerServices; -using System.Security.Permissions; using System.Runtime.ConstrainedExecution; -using System.IO; using System.Runtime.InteropServices; #if !NET_2_1 using System.Security.AccessControl; #endif +using System.Security.Permissions; namespace System.Threading { [ComVisible (true)] public sealed class Mutex : WaitHandle { - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern IntPtr CreateMutex_internal( - bool initiallyOwned, - string name, - out bool created); - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern bool ReleaseMutex_internal(IntPtr handle); - -#if !NET_2_1 - [MethodImplAttribute (MethodImplOptions.InternalCall)] - private static extern IntPtr OpenMutex_internal (string name, MutexRights rights, out MonoIOError error); - - private Mutex (IntPtr handle) - { - Handle = handle; - } -#endif - [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] - public Mutex() { - bool created; - - Handle=CreateMutex_internal(false, null, out created); - } + public Mutex () + : this(false) { } [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] - public Mutex(bool initiallyOwned) { - bool created; - - Handle=CreateMutex_internal(initiallyOwned, null, - out created); + public Mutex (bool initiallyOwned) + { + Waitable = new StReentrantFairLock(initiallyOwned); } [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] public Mutex (bool initiallyOwned, string name) { - bool created; - Handle = CreateMutex_internal (initiallyOwned, name, out created); + throw new NotImplementedException (); } [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] public Mutex (bool initiallyOwned, string name, out bool createdNew) { - Handle = CreateMutex_internal (initiallyOwned, name, out createdNew); + throw new NotImplementedException (); } #if !NET_2_1 @@ -97,7 +59,7 @@ public Mutex (bool initiallyOwned, string name, out bool createdNew) [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] public Mutex (bool initiallyOwned, string name, out bool createdNew, MutexSecurity mutexSecurity) { - Handle = CreateMutex_internal (initiallyOwned, name, out createdNew); + throw new NotImplementedException (); } public MutexSecurity GetAccessControl () @@ -107,44 +69,34 @@ public MutexSecurity GetAccessControl () public static Mutex OpenExisting (string name) { - return(OpenExisting (name, MutexRights.Synchronize | - MutexRights.Modify)); + return OpenExisting (name, MutexRights.Synchronize | MutexRights.Modify); } - public static Mutex OpenExisting (string name, - MutexRights rights) + public static Mutex OpenExisting (string name, MutexRights rights) { - if (name == null) { - throw new ArgumentNullException ("name"); - } - if ((name.Length == 0) || - (name.Length > 260)) { - throw new ArgumentException ("name", Locale.GetText ("Invalid length [1-260].")); - } - - MonoIOError error; - IntPtr handle = OpenMutex_internal (name, rights, - out error); - if (handle == (IntPtr)null) { - if (error == MonoIOError.ERROR_FILE_NOT_FOUND) { - throw new WaitHandleCannotBeOpenedException (Locale.GetText ("Named Mutex handle does not exist: ") + name); - } else if (error == MonoIOError.ERROR_ACCESS_DENIED) { - throw new UnauthorizedAccessException (); - } else { - throw new IOException (Locale.GetText ("Win32 IO error: ") + error.ToString ()); - } - } - - return(new Mutex (handle)); + throw new NotImplementedException (); } #endif + internal override bool _WaitOne (int timeout) + { + var flock = Waitable as StReentrantFairLock; + return flock != null ? flock.Enter(new StCancelArgs (timeout)) : false; + } + [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)] - public void ReleaseMutex() { - bool success = ReleaseMutex_internal(Handle); - if (!success) { - throw new ApplicationException ("Mutex is not owned"); - } + public void ReleaseMutex () + { + var flock = Waitable as StReentrantFairLock; + if (flock == null) { + return; + } + + try { + flock.Exit(); + } catch (InvalidOperationException) { + throw new ApplicationException("Mutex is not owned"); + } } #if !NET_2_1 diff --git a/mcs/class/corlib/System.Threading/SemaphoreSlim.cs.new b/mcs/class/corlib/System.Threading/SemaphoreSlim.cs.new new file mode 100644 index 0000000000000..990f1155c4032 --- /dev/null +++ b/mcs/class/corlib/System.Threading/SemaphoreSlim.cs.new @@ -0,0 +1,129 @@ +// +// System.Threading.SemaphoreSlim.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#if NET_4_0 || MOBILE + +namespace System.Threading +{ + [Diagnostics.DebuggerDisplayAttribute ("Current Count = {currCount}")] + public class SemaphoreSlim : IDisposable + { + private const int defaultSpinCount = 100; + + private readonly StSemaphore sem; + + public SemaphoreSlim (int initialCount) + : this (initialCount, int.MaxValue) { } + + public SemaphoreSlim (int initialCount, int maxCount) + { + if (initialCount < 0 || initialCount > maxCount || maxCount < 0) { + throw new ArgumentOutOfRangeException ("initialCount"); + } + + if (maxCount < 0) { + throw new ArgumentOutOfRangeException ("maxCount"); + } + + sem = new StSemaphore (initialCount, maxCount, defaultSpinCount); + } + + public int CurrentCount { + get { return sem.CurrentCount; } + } + + // + // Just return a new Semaphore. We're assuming that the object + // will be short lived, so we don't hang on to it. The user might + // try to set the Handle/SafeWaitHandle properties, but that leads + // to undefined behavior so we don't even worry about it. + // + + public WaitHandle AvailableWaitHandle { + get { return new Semaphore (sem); } + } + + public int Release () + { + return Release(1); + } + + public int Release (int releaseCount) + { + return sem.Release (releaseCount); + } + + public void Wait () + { + Wait (CancellationToken.None); + } + + public bool Wait (TimeSpan timeout) + { + return Wait ((int)timeout.TotalMilliseconds, CancellationToken.None); + } + + public bool Wait (int millisecondsTimeout) + { + return Wait (millisecondsTimeout, CancellationToken.None); + } + + public void Wait (CancellationToken cancellationToken) + { + Wait (-1, cancellationToken); + } + + public bool Wait (TimeSpan timeout, CancellationToken cancellationToken) + { + return Wait ((int)timeout.TotalMilliseconds, cancellationToken); + } + + public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken) + { + if (millisecondsTimeout < -1) + throw new ArgumentOutOfRangeException ("millisecondsTimeout", + "millisecondsTimeout is a negative number other than -1"); + + var alerter = cancellationToken.CanBeCanceled ? new StAlerter () : null; + + using (cancellationToken.Register(StAlerter.CancellationTokenCallback, alerter)) { + try { + return sem.Wait(1, new StCancelArgs (millisecondsTimeout, alerter)); + } catch (StThreadAlertedException) { + cancellationToken.ThrowIfCancellationRequested (); + return false; /* Shut the compiler up */ + } + } + } + + #region IDisposable implementation + + public void Dispose () + { + Dispose(true); + } + + protected virtual void Dispose (bool disposing) + { } + + #endregion + } +} +#endif diff --git a/mcs/class/corlib/System.Threading/Alerter.cs b/mcs/class/corlib/System.Threading/StAlerter.cs similarity index 86% rename from mcs/class/corlib/System.Threading/Alerter.cs rename to mcs/class/corlib/System.Threading/StAlerter.cs index 1d1a22f147dea..94a1f3089df01 100644 --- a/mcs/class/corlib/System.Threading/Alerter.cs +++ b/mcs/class/corlib/System.Threading/StAlerter.cs @@ -1,5 +1,5 @@ // -// System.Threading.Alerter.cs +// System.Threading.StAlerter.cs // // Copyright 2011 Carlos Martins, Duarte Nunes // @@ -18,26 +18,31 @@ // Author: Duarte Nunes (duarte.m.nunes@gmail.com) // -using System; - #pragma warning disable 0420 namespace System.Threading { - public class StThreadAlertedException : SystemException - { - public StThreadAlertedException () - : base ("Wait alerted") { } + internal class StThreadAlertedException : SystemException + { + internal StThreadAlertedException() + : base ("Wait alerted") { } - public StThreadAlertedException (string msg) - : base (msg) { } + internal StThreadAlertedException(string msg) + : base (msg) { } - public StThreadAlertedException (string msg, Exception innerEx) - : base (msg, innerEx) {} + internal StThreadAlertedException(string msg, Exception innerEx) + : base (msg, innerEx) {} } - - public class StAlerter - { + + internal class StAlerter + { + public static readonly Action CancellationTokenCallback = obj => + { + var alerter = obj as StAlerter; + if (alerter != null) { + alerter.Set (); + } + }; // // The state of the alerter is a non-blocking stack that links @@ -49,10 +54,10 @@ public class StAlerter internal volatile StParker state; public StAlerter () - { } + { } internal StAlerter (bool alerted) - { + { state = alerted ? ALERTED : null; } @@ -60,7 +65,8 @@ internal StAlerter (bool alerted) // Returns true if the alerter is set. // - public bool IsSet { + internal bool IsSet + { get { return state == ALERTED; } } @@ -69,7 +75,7 @@ internal StAlerter (bool alerted) // internal bool RegisterParker (StParker pk) - { + { do { StParker s; @@ -98,7 +104,7 @@ internal bool RegisterParker (StParker pk) // private void SlowDeregisterParker (StParker pk) - { + { // // Absorb the locked parkers at top of the stack. @@ -141,7 +147,7 @@ private void SlowDeregisterParker (StParker pk) // internal void DeregisterParker (StParker pk) - { + { // // Very often there is only a parker inserted in the alerter @@ -159,8 +165,8 @@ internal void DeregisterParker (StParker pk) // Sets the alerter. // - public bool Set () - { + internal bool Set() + { do { StParker s; if ((s = state) == ALERTED) { diff --git a/mcs/class/corlib/System.Threading/StCancelArgs.cs b/mcs/class/corlib/System.Threading/StCancelArgs.cs new file mode 100644 index 0000000000000..9b2fa92881c23 --- /dev/null +++ b/mcs/class/corlib/System.Threading/StCancelArgs.cs @@ -0,0 +1,108 @@ +// +// System.Threading.CancelArgs.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +namespace System.Threading +{ + internal struct StCancelArgs + { + internal int Timeout { get; private set; } + + internal StAlerter Alerter { get; private set; } + + internal bool Interruptible { get; private set; } + + internal static readonly StCancelArgs None = new StCancelArgs(-1, null, false); + + internal StCancelArgs (int timeout, StAlerter alerter, bool interruptible) + { + if (timeout < -1) { + throw new ArgumentOutOfRangeException ("timeout", timeout, "Wrong timeout value"); + } + + this = new StCancelArgs (); + Timeout = timeout; + Alerter = alerter; + Interruptible = interruptible; + } + + internal StCancelArgs (int timeout) + : this (timeout, null, false) { } + + internal StCancelArgs (TimeSpan timeout) + : this (timeout.Milliseconds, null, false) { } + + internal StCancelArgs (StAlerter alerter) + : this (-1, alerter, false) { } + + internal StCancelArgs (bool interruptible) + : this (-1, null, interruptible) { } + + internal StCancelArgs (int timeout, bool interruptible) + : this (timeout, null, interruptible) { } + + internal StCancelArgs (TimeSpan timeout, bool interruptible) + : this (timeout.Milliseconds, null, interruptible) { } + + internal StCancelArgs (int timeout, StAlerter alerter) + : this (timeout, alerter, false) { } + + internal StCancelArgs (TimeSpan timeout, StAlerter alerter) + : this (timeout.Milliseconds, alerter, false) { } + + internal StCancelArgs (StAlerter alerter, bool interruptible) + : this (-1, alerter, interruptible) { } + + // + // Adjusts the timeout value, returning false if the timeout + // has expired. + // + + internal bool AdjustTimeout (ref int lastTime) + { + if (Timeout == Threading.Timeout.Infinite) { + return true; + } + + int now = Environment.TickCount; + int e = now == lastTime ? 1 : now - lastTime; + if (Timeout <= e) { + return false; + } + + Timeout -= e; + lastTime = now; + return true; + } + + // + // Thows the cancellation exception, if appropriate; + // otherwise, does noting. + // + + internal static void ThrowIfException (int ws) + { + switch (ws) { + case StParkStatus.Alerted: throw new StThreadAlertedException (); + case StParkStatus.Interrupted: throw new ThreadInterruptedException (); + default: return; + } + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StFairLock.cs b/mcs/class/corlib/System.Threading/StFairLock.cs new file mode 100644 index 0000000000000..2f3f0266b3cdd --- /dev/null +++ b/mcs/class/corlib/System.Threading/StFairLock.cs @@ -0,0 +1,56 @@ +// +// System.Threading.StFairLock.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal class StFairLock : StMutant + { + internal StFairLock(bool initiallyOwned) + : base(!initiallyOwned, 0) { } + + internal StFairLock(int spinCount) + : base(true, spinCount) { } + + internal StFairLock() + : base(true, 0) { } + + internal bool TryEnter() + { + return _TryAcquire(); + } + + internal bool Enter(StCancelArgs cargs) + { + return SlowTryAcquire(cargs); + } + + internal void Enter() + { + SlowTryAcquire(StCancelArgs.None); + } + + internal void Exit() + { + _Release(); + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StMutant.cs b/mcs/class/corlib/System.Threading/StMutant.cs new file mode 100644 index 0000000000000..07ab12630c02d --- /dev/null +++ b/mcs/class/corlib/System.Threading/StMutant.cs @@ -0,0 +1,358 @@ +// +// System.Threading.StMutant.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal class StMutant : StWaitable + { + // + // The boolean state of the lock (signalled/non-signalled) is stored + // in the *head.next* field, as follows: + // - head.next == UNLOCKED: the lock is signalled and its queue is empty; + // - head.next == null: the lock is non-signalled and its queue is empty; + // - others: the lock isn't signalled and its queue is non-empty. + // + + private static readonly StWaitBlock UNLOCKED = new StWaitBlock(); + + private volatile StWaitBlock head; + private volatile StWaitBlock tail; + + // + // The predecessor of a wait block that must be unlinked + // when the right conditions are met. + // + + private volatile StWaitBlock toUnlink; + + private readonly int spinCount; + + internal StMutant (bool initialState, int sc) + { + head = tail = new StWaitBlock (); + if (initialState) { + head.next = UNLOCKED; + } + spinCount = Environment.ProcessorCount > 0 ? sc : 0; + } + + internal override bool _AllowsAcquire { + get { return head.next == UNLOCKED; } + } + + internal override bool _TryAcquire () + { + return (head.next == UNLOCKED && + Interlocked.CompareExchange (ref head.next, null, UNLOCKED) == UNLOCKED); + } + + internal bool SlowTryAcquire (StCancelArgs cargs) + { + StWaitBlock wb = null, pred; + do { + if (head.next == UNLOCKED && + Interlocked.CompareExchange (ref head.next, null, UNLOCKED) == UNLOCKED) { + return true; + } + + if (cargs.Timeout == 0) { + return false; + } + + if (wb == null) { + wb = new StWaitBlock (WaitType.WaitAny); + } + + // + // Do the necessary consistency checks before trying to insert + // the wait block in the lock's queue; if the queue is in a + // quiescent state, try to perform the insertion. + // + + StWaitBlock t, tn; + if ((tn = (t = tail).next) == UNLOCKED) { + continue; + } + if (tn != null) { + AdvanceTail (t, tn); + continue; + } + + if (Interlocked.CompareExchange (ref t.next, wb, null) == null) { + AdvanceTail (t, wb); + + // + // Save the predecessor of the wait block and exit the loop. + // + + pred = t; + break; + } + } + while (true); + + int ws = wb.parker.Park ((head == pred) ? spinCount : 0, cargs); + + if (ws == StParkStatus.Success) { + return true; + } + + Unlink (wb, pred); + StCancelArgs.ThrowIfException (ws); + return false; + } + + internal override bool _Release () + { + do { + StWaitBlock h, hn; + if ((hn = (h = head).next) == UNLOCKED) { + return true; + } + + if (hn == null) { + if (Interlocked.CompareExchange (ref head.next, UNLOCKED, null) == null) { + return false; + } + continue; + } + + if (AdvanceHead (h, hn)) { + StParker pk; + if ((pk = hn.parker).TryLock ()) { + pk.Unpark (hn.waitKey); + + // + // If this is a wait-any wait block, we are done; + // otherwise, keep trying to release another waiter. + // + + if (hn.waitType == WaitType.WaitAny) { + return false; + } + } + } + } + while (true); + } + + internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, + ref StWaitBlock hint, ref int sc) + { + StWaitBlock wb = null; + do { + if (head.next == UNLOCKED) { + if (Interlocked.CompareExchange (ref head.next, null, UNLOCKED) == UNLOCKED) { + if (pk.TryLock ()) { + pk.UnparkSelf (key); + } + else { + + // + // The parker is already lock, which means that the + // wait-any operation was already accomplished. So, + // release the lock, undoing the previous acquire. + // + + _Release (); + } + return null; + } + continue; + } + + if (wb == null) { + wb = new StWaitBlock (pk, WaitType.WaitAny, 0, key); + } + + StWaitBlock t, tn; + if ((tn = (t = tail).next) == UNLOCKED) { + continue; + } + if (tn != null) { + AdvanceTail (t, tn); + continue; + } + + if (Interlocked.CompareExchange (ref t.next, wb, null) == null) { + AdvanceTail (t, wb); + + // + // Return the inserted wait block, its predecessor and + // the sugested spin count. + // + + sc = ((hint = t) == head) ? spinCount : 0; + return wb; + } + } + while (true); + } + + internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, + ref int sc) + { + StWaitBlock wb = null; + do { + // + // If the lock can be immediately acquired, lock our parker + // and if this is the last cooperative release, self unpark + // the current thread. + // + + if (_AllowsAcquire) { + if (pk.TryLock ()) { + pk.UnparkSelf (StParkStatus.StateChange); + } + return null; + } + + if (wb == null) { + wb = new StWaitBlock (pk, WaitType.WaitAll, 0, StParkStatus.StateChange); + } + + StWaitBlock t, tn; + if ((tn = (t = tail).next) == UNLOCKED) { + continue; + } + if (tn != null) { + AdvanceTail (t, tn); + continue; + } + if (Interlocked.CompareExchange (ref t.next, wb, null) == null) { + AdvanceTail (t, wb); + + // + // Return the inserted wait block, its predecessor and + // the spin count for this wait block. + // + + sc = ((hint = t) == head) ? spinCount : 0; + return wb; + } + } + while (true); + } + + internal override void _UndoAcquire () + { + _Release (); + } + + internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock hint) + { + Unlink (wb, hint); + } + + private void Unlink (StWaitBlock wb, StWaitBlock pred) + { + while (pred.next == wb) { + // + // Remove the cancelled wait blocks that are at the front + // of the queue. + // + + StWaitBlock h, hn; + if (((hn = (h = head).next) != null && hn != UNLOCKED) && + (hn.parker.IsLocked && hn.request > 0)) { + AdvanceHead (h, hn); + continue; + } + + // + // If the queue is empty, return. + // + + StWaitBlock t, tn; + if ((t = tail) == h) { + return; + } + + // + // Do the necessary consistency checks before trying to + // unlink the wait block. + // + + if (t != tail) { + continue; + } + + if ((tn = t.next) != null) { + AdvanceTail (t, tn); + continue; + } + + // + // If the wait block is not at the tail of the queue, try + // to unlink it. + // + + if (wb != t) { + StWaitBlock wbn; + if ((wbn = wb.next) == wb || pred.CasNext (wb, wbn)) { + return; + } + } + + // + // The wait block is at the tail of the queue; so, take + // into account the *toUnlink* wait block. + // + + StWaitBlock dp; + if ((dp = toUnlink) != null) { + StWaitBlock d, dn; + if ((d = dp.next) == dp || ((dn = d.next) != null && dp.CasNext (d, dn))) { + CasToUnlink (dp, null); + } + if (dp == pred) { + return; // *wb* is an already the saved node. + } + } + else if (CasToUnlink (null, pred)) { + return; + } + } + } + + private bool AdvanceHead (StWaitBlock h, StWaitBlock nh) + { + if (h == head && Interlocked.CompareExchange (ref head, nh, h) == h) { + h.next = h; // Mark the old head as unlinked. + return true; + } + return false; + } + + private void AdvanceTail (StWaitBlock t, StWaitBlock nt) + { + if (t == tail) { + Interlocked.CompareExchange (ref tail, nt, t); + } + } + + private bool CasToUnlink (StWaitBlock tu, StWaitBlock ntu) + { + return toUnlink == tu && Interlocked.CompareExchange (ref toUnlink, ntu, tu) == tu; + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StNotificationEvent.cs b/mcs/class/corlib/System.Threading/StNotificationEvent.cs new file mode 100644 index 0000000000000..f439621610790 --- /dev/null +++ b/mcs/class/corlib/System.Threading/StNotificationEvent.cs @@ -0,0 +1,374 @@ +// +// System.Threading.StNotificationEvent.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal struct NotificationEvent + { + // + // The value of the *state* field when the event is signalled. + // + + internal static readonly StWaitBlock SET = StWaitBlock.SENTINEL; + + // + // The state of the event and the event's queue (i.e., a non-blocking + // stack) are stored on the *state* field as follows: + // - *state* == SET: the event is signalled; + // - *state* == null: the event is non-signalled the queue is empty; + // - *state* != null && *state* != SET: the event is non-signalled + // and its queue is non-empty. + // + + internal volatile StWaitBlock state; + + // + // The number of spin cycles executed by the first waiter + // thread before it blocks on the park spot. + // + + internal readonly int spinCount; + + internal NotificationEvent (bool initialState, int sc) + { + state = initialState ? SET : null; + spinCount = Environment.ProcessorCount > 0 ? sc : 0; + } + + internal NotificationEvent (bool initialState) + : this (initialState, 0) { } + + // + // Returns true if the event is set. + // + + internal bool IsSet { + get { return state == SET; } + } + + // + // Sets the event to the signalled state. + // + + internal bool Set () + { + // + // If the event is already signalled, return true. + // + + if (state == SET) { + return true; + } + + // + // Atomically signal the event and grab the wait queue. + // + + StWaitBlock p = Interlocked.Exchange (ref state, SET); + + // + // If the event queue is empty, return the previous state of the event. + // + + if (p == null || p == SET) { + return p == SET; + } + + // + // If spinning is configured and there is more than one thread in the + // wait queue, we first release the thread that is spinning. As only + // one thread spins, we maximize the chances of unparking that thread + // before it blocks. + // + + StParker pk; + if (spinCount != 0 && p.next != null) { + StWaitBlock pv = p, n; + while ((n = pv.next) != null && n.next != null) { + pv = n; + } + + if (n != null) { + pv.next = null; + if ((pk = n.parker).TryLock ()) { + pk.Unpark (n.waitKey); + } + } + } + + // + // Lock and unpark all waiting threads. + // + + do { + if ((pk = p.parker).TryLock ()) { + pk.Unpark (p.waitKey); + } + } + while ((p = p.next) != null); + + // + // Return the previous state of the event. + // + + return false; + } + + // + // Resets the event to the non-signalled state. + // + + internal bool Reset () + { + return state == SET && Interlocked.CompareExchange (ref state, null, SET) == SET; + } + + // + // Waits until the event is signalled, activating the specified cancellers. + // + + internal int Wait (StCancelArgs cargs) + { + return state == SET + ? StParkStatus.Success + : cargs.Timeout == 0 + ? StParkStatus.Timeout + : SlowWait (cargs, new StWaitBlock (WaitType.WaitAny)); + } + + private int SlowWait (StCancelArgs cargs, StWaitBlock wb) + { + do { + // + // If the event is now signalled, return success. + // + + StWaitBlock s; + if ((s = state) == SET) { + return StParkStatus.Success; + } + + wb.next = s; + if (Interlocked.CompareExchange (ref state, wb, s) == s) { + break; + } + } + while (true); + + // + // Park the current thread, activating the specified cancellers and spinning + // if appropriate. + // + + int ws = wb.parker.Park (wb.next == null ? spinCount : 0, cargs); + + // + // If the wait was cancelled, unlink the wait block from the + // event's queue. + // + + if (ws != StParkStatus.Success) { + Unlink (wb); + } + + return ws; + } + + // + // Waits until the event is signalled, using the specified parker object. + // + + internal StWaitBlock WaitWithParker (StParker pk, WaitType type, int key, ref int sc) + { + StWaitBlock wb = null; + do { + StWaitBlock s; + if ((s = state) == SET) { + // + // The event is signalled. Try to lock it and self unpark the current thread. + // Anyway, return null to signal that no wait block was queued. + // + + if (pk.TryLock ()) { + pk.UnparkSelf (key); + } + return null; + } + + // + // The event seems closed; so, if this is the first loop iteration, + // create a wait block. + // + + if (wb == null) { + wb = new StWaitBlock (pk, type, 0, key); + } + + // + // Try to insert the wait block in the event's queue, if the + // event remains non-signalled. + // + + wb.next = s; + if (Interlocked.CompareExchange (ref state, wb, s) == s) { + // + // Return the inserted wait block and the suggested spin count. + // + + sc = s == null ? spinCount : 0; + return wb; + } + } + while (true); + } + + // + // Unlinks the wait block from the event's wait queue. + // + + internal void Unlink (StWaitBlock wb) + { + StWaitBlock s; + if ((s = state) == SET || s == null || + (wb.next == null && s == wb && + Interlocked.CompareExchange (ref state, null, s) == s)) { + return; + } + SlowUnlink (wb); + } + + // + // Slow path to unlink the wait block from the event's + // wait queue. + // + + internal void SlowUnlink (StWaitBlock wb) + { + StWaitBlock next; + if ((next = wb.next) != null && next.parker.IsLocked) { + next = next.next; + } + + StWaitBlock p = state; + + while (p != null && p != next && state != null && state != SET) { + StWaitBlock n; + if ((n = p.next) != null && n.parker.IsLocked) { + p.CasNext (n, n.next); + } + else { + p = n; + } + } + } + } + + internal abstract class StNotificationEventBase : StWaitable + { + internal NotificationEvent waitEvent; + + protected StNotificationEventBase (bool initialState, int sc) + { + id = NOTIFICATION_EVENT_ID; + waitEvent = new NotificationEvent (initialState, sc); + } + + protected StNotificationEventBase (bool initialState) + : this (initialState, 0) { } + + internal override bool _AllowsAcquire + { + get { return waitEvent.IsSet; } + } + + internal override bool _TryAcquire () + { + return waitEvent.IsSet; + } + + internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, + ref StWaitBlock hint, ref int sc) + { + return waitEvent.WaitWithParker (pk, WaitType.WaitAny, key, ref sc); + } + + internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, + ref int sc) + { + return waitEvent.WaitWithParker (pk, WaitType.WaitAll, StParkStatus.StateChange, ref sc); + } + + + internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock ignored) + { + waitEvent.Unlink (wb); + } + } + + internal sealed class StNotificationEvent : StNotificationEventBase + { + internal StNotificationEvent (bool initialState, int spinCount) + : base (initialState, spinCount) { } + + internal StNotificationEvent (bool initialState) + : this (initialState, 0) { } + + internal StNotificationEvent () + : this (false, 0) { } + + public bool IsSet { + get { return waitEvent.IsSet; } + } + + public bool Set () + { + return waitEvent.Set (); + } + + public bool Reset () + { + return waitEvent.Reset (); + } + + public bool Wait (StCancelArgs cargs) + { + int ws = waitEvent.Wait (cargs); + if (ws == StParkStatus.Success) { + return true; + } + + StCancelArgs.ThrowIfException (ws); + return false; + } + + public void Wait () + { + Wait (StCancelArgs.None); + } + + internal override bool _Release () + { + waitEvent.Set (); + return true; + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/ParkSpot.cs b/mcs/class/corlib/System.Threading/StParkSpot.cs similarity index 85% rename from mcs/class/corlib/System.Threading/ParkSpot.cs rename to mcs/class/corlib/System.Threading/StParkSpot.cs index 584d7786d95bc..5bddaf0a55c52 100644 --- a/mcs/class/corlib/System.Threading/ParkSpot.cs +++ b/mcs/class/corlib/System.Threading/StParkSpot.cs @@ -1,5 +1,5 @@ // -// System.Threading.ParkSpot.cs +// System.Threading.StParkSpot.cs // // Copyright 2011 Duarte Nunes // @@ -22,34 +22,33 @@ namespace System.Threading { - - internal struct ParkSpot + internal struct StParkSpot { private IntPtr ps; internal void Alloc () - { + { ps = Alloc_internal (); } internal void Free () - { + { Free_internal (ps); } internal void Set () - { + { Set_internal (ps); } internal void Wait (StParker pk, StCancelArgs cargs) - { + { int ws; bool interrupted = false; do { try { ws = Wait_internal (ps, cargs.Timeout) - ? StParkStatus.Success + ? StParkStatus.Success : StParkStatus.Timeout; break; } catch (ThreadInterruptedException) { @@ -57,7 +56,7 @@ internal void Wait (StParker pk, StCancelArgs cargs) ws = StParkStatus.Interrupted; break; } - interrupted = true; + interrupted = true; } } while (true); @@ -71,9 +70,9 @@ internal void Wait (StParker pk, StCancelArgs cargs) if (pk.TryCancel()) { pk.UnparkSelf(ws); } else { - if (ws == StParkStatus.Interrupted) { - interrupted = true; - } + if (ws == StParkStatus.Interrupted) { + interrupted = true; + } do { try { @@ -97,15 +96,15 @@ internal void Wait (StParker pk, StCancelArgs cargs) } [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static IntPtr Alloc_internal (); + private static extern IntPtr Alloc_internal (); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static void Free_internal (IntPtr ps); + private static extern void Free_internal (IntPtr ps); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static void Set_internal (IntPtr ps); + private static extern void Set_internal (IntPtr ps); [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern static bool Wait_internal (IntPtr ps, int timeout); + private static extern bool Wait_internal (IntPtr ps, int timeout); } } diff --git a/mcs/class/corlib/System.Threading/ParkStatus.cs b/mcs/class/corlib/System.Threading/StParkStatus.cs similarity index 66% rename from mcs/class/corlib/System.Threading/ParkStatus.cs rename to mcs/class/corlib/System.Threading/StParkStatus.cs index cf45fb22710ca..fae95e077303f 100644 --- a/mcs/class/corlib/System.Threading/ParkStatus.cs +++ b/mcs/class/corlib/System.Threading/StParkStatus.cs @@ -1,5 +1,5 @@ // -// System.Threading.ParkStatus.cs +// System.Threading.StParkStatus.cs // // Copyright 2011 Carlos Martins, Duarte Nunes // @@ -18,18 +18,14 @@ // Author: Duarte Nunes (duarte.m.nunes@gmail.com) // -using System; -namespace System.Threading { - - // - // Integer values used as park wait status. - // - - public static class StParkStatus { - public const int Success = 0; - public const int Timeout = -2; - public const int Alerted = -3; - public const int Interrupted = -4; +namespace System.Threading +{ + internal static class StParkStatus { + internal const int Success = 0; + internal const int Timeout = -2; + internal const int Alerted = -3; + internal const int Interrupted = -4; + internal const int StateChange = Int32.MaxValue; } } diff --git a/mcs/class/corlib/System.Threading/Parker.cs b/mcs/class/corlib/System.Threading/StParker.cs similarity index 86% rename from mcs/class/corlib/System.Threading/Parker.cs rename to mcs/class/corlib/System.Threading/StParker.cs index 9d9d2a6092dda..b0ac7d97bd8de 100644 --- a/mcs/class/corlib/System.Threading/Parker.cs +++ b/mcs/class/corlib/System.Threading/StParker.cs @@ -1,5 +1,5 @@ // -// System.Threading.Parker.cs +// System.Threading.StParker.cs // // Copyright 2011 Carlos Martins, Duarte Nunes // @@ -18,20 +18,12 @@ // Author: Duarte Nunes (duarte.m.nunes@gmail.com) // -using System; - #pragma warning disable 0420 namespace System.Threading { - public class StParker - { - - // - // Constants. - // - + { private const int WAIT_IN_PROGRESS_BIT = 31; private const int WAIT_IN_PROGRESS = (1 << WAIT_IN_PROGRESS_BIT); private const int LOCK_COUNT_MASK = (1 << 16) - 1; @@ -53,7 +45,7 @@ public class StParker // The park spot used to block the parker's owner thread. // - internal ParkSpot parkSpot; + internal StParkSpot parkSpot; // // The park wait status. @@ -61,30 +53,26 @@ public class StParker internal int waitStatus; - // - // Constructors. - // - - public StParker(int releasers) - { + internal StParker(int releasers) + { state = releasers | WAIT_IN_PROGRESS; } - public StParker () - : this (1) { } + internal StParker() + : this (1) { } // // Resets the parker. // - public void Reset (int releasers) - { + internal void Reset(int releasers) + { pnext = null; state = releasers | WAIT_IN_PROGRESS; } - public void Reset () - { + internal void Reset() + { Reset (1); } @@ -93,7 +81,7 @@ public void Reset () // internal bool TestAndClearInProgress () - { + { do { int s; if ((s = state) >= 0) { @@ -109,7 +97,8 @@ internal bool TestAndClearInProgress () // Returns true if the parker is locked. // - public bool IsLocked { + internal bool IsLocked + { get { return (state & LOCK_COUNT_MASK) == 0; } } @@ -117,8 +106,8 @@ internal bool TestAndClearInProgress () // Tries to lock the parker. // - public bool TryLock () - { + internal bool TryLock() + { do { // @@ -149,8 +138,8 @@ public bool TryLock () // Tries to cancel the parker. // - public bool TryCancel () - { + internal bool TryCancel() + { do { // @@ -180,8 +169,8 @@ public bool TryCancel () // owner thread. // - public void SelfCancel () - { + internal void SelfCancel() + { state = WAIT_IN_PROGRESS; } @@ -189,8 +178,8 @@ public void SelfCancel () // Unparks the parker's owner thread if the wait is still in progress. // - public bool UnparkInProgress (int ws) - { + internal bool UnparkInProgress(int ws) + { waitStatus = ws; return (state & WAIT_IN_PROGRESS) != 0 && (Interlocked.Exchange (ref state, 0) & WAIT_IN_PROGRESS) != 0; @@ -200,8 +189,8 @@ public bool UnparkInProgress (int ws) // Unparks the parker owner thread. // - public void Unpark (int status) - { + internal void Unpark(int status) + { if (UnparkInProgress (status)) { return; } @@ -213,8 +202,8 @@ public void Unpark (int status) // Unparks the parker's owner thread. // - public void UnparkSelf (int status) - { + internal void UnparkSelf(int status) + { waitStatus = status; state = 0; } @@ -224,10 +213,10 @@ public void UnparkSelf (int status) // specified cancellers and spinning if specified. // - public int Park (int spinCount, StCancelArgs cargs) - { + internal int Park(int spinCount, StCancelArgs cargs) + { #if NET_4_0 - SpinWait spinWait; + SpinWait spinWait; #endif // @@ -248,7 +237,7 @@ public int Park (int spinCount, StCancelArgs cargs) #if NET_4_0 spinWait.SpinOnce (); #else - Thread.SpinWait (1); + Thread.SpinWait (1); #endif } while (true); @@ -295,7 +284,7 @@ public int Park (int spinCount, StCancelArgs cargs) // the park spot until it is set. // - cargs.ResetImplicitCancellers (); + cargs = StCancelArgs.None; } } @@ -317,14 +306,14 @@ public int Park (int spinCount, StCancelArgs cargs) return waitStatus; } - public int Park (StCancelArgs cargs) - { + internal int Park(StCancelArgs cargs) + { return Park (0, cargs); } - public int Park () - { + internal int Park() + { return Park (0, StCancelArgs.None); } @@ -333,8 +322,9 @@ public int Park () // the specified cancellers. // - public static int Sleep (StCancelArgs cargs) { - StParker pk = new StParker (); + internal static int Sleep(StCancelArgs cargs) + { + var pk = new StParker (); int ws = pk.Park (0, cargs); StCancelArgs.ThrowIfException (ws); return ws; @@ -345,19 +335,19 @@ public int Park () // internal bool CasNext (StParker n, StParker nn) - { - return (pnext == n && - Interlocked.CompareExchange (ref pnext, nn, n) == n); + { + return pnext == n && + Interlocked.CompareExchange (ref pnext, nn, n) == n; } } // // This class implements a parker that is used as sentinel. // - - public class SentinelParker : StParker - { - public SentinelParker () - : base(0) { } + + internal class SentinelParker : StParker + { + internal SentinelParker() + : base(0) { } } } \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StReentrantFairLock.cs b/mcs/class/corlib/System.Threading/StReentrantFairLock.cs new file mode 100644 index 0000000000000..c292a5ce12d1a --- /dev/null +++ b/mcs/class/corlib/System.Threading/StReentrantFairLock.cs @@ -0,0 +1,185 @@ +// +// System.Threading.StReentrantFairLock.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal sealed class StReentrantFairLock : StWaitable + { + private readonly StFairLock flock; + + private const int UNOWNED = 0; + + // + // The *owner* variable does not need to be volatile. + // + + private int owner; + private int count; + + public StReentrantFairLock (int spinCount) + { + flock = new StFairLock (spinCount); + } + + internal StReentrantFairLock () + { + flock = new StFairLock (); + } + + internal StReentrantFairLock (bool initiallyOwned) + { + flock = new StFairLock (initiallyOwned); + if (initiallyOwned) { + owner = Thread.CurrentThreadId; + } + } + + internal bool TryEnter () + { + int tid = Thread.CurrentThreadId; + + if (owner == tid) { + count++; + return true; + } + + if (flock.TryEnter ()) { + owner = tid; + return true; + } + + return false; + } + + internal bool Enter (StCancelArgs cargs) + { + int tid = Thread.CurrentThreadId; + + if (owner == tid) { + count++; + return true; + } + + if (flock.Enter (cargs)) { + owner = tid; + return true; + } + + return false; + } + + internal void Enter () + { + Enter (StCancelArgs.None); + } + + internal void Exit () + { + if (Thread.CurrentThreadId != owner) { + throw new InvalidOperationException (); + } + + if (count != 0) { + count--; + return; + } + + owner = UNOWNED; + flock.Exit (); + } + + internal override bool _AllowsAcquire + { + get { return flock._AllowsAcquire || owner == Thread.CurrentThreadId; } + } + + internal override bool _TryAcquire () + { + return TryEnter (); + } + + internal override bool _Release () + { + if (owner != Thread.CurrentThreadId) { + return false; + } + + Exit (); + return true; + } + + internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, + ref StWaitBlock hint, ref int sc) + { + if (TryEnter ()) { + if (pk.TryLock ()) { + pk.UnparkSelf (key); + } + else { + Exit (); + } + return null; + } + + // + // The lock is busy, so execute the WaitAny prologue on the + // associated non-reentrant lock. + // + + return flock._WaitAnyPrologue (pk, key, ref hint, ref sc); + } + + internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock hint, + ref int sc) + { + if (_AllowsAcquire) { + if (pk.TryLock ()) { + pk.UnparkSelf (StParkStatus.StateChange); + } + + // + // Return null to signal that no wait block was inserted + // in the lock's queue. + // + + return null; + } + + return flock._WaitAllPrologue (pk, ref hint, ref sc); + } + + internal override void _WaitEpilogue () + { + owner = Thread.CurrentThreadId; + } + + internal override void _UndoAcquire () + { + Exit (); + } + + internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock hint) + { + flock._CancelAcquire (wb, hint); + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StSemaphore.cs b/mcs/class/corlib/System.Threading/StSemaphore.cs new file mode 100644 index 0000000000000..f1412d5aa8d11 --- /dev/null +++ b/mcs/class/corlib/System.Threading/StSemaphore.cs @@ -0,0 +1,353 @@ +// +// System.Threading.StSemaphore.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + + #if NET_2_0 + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal sealed class StSemaphore : StWaitable + { + private volatile int state; + private LockedWaitQueue queue; + + private readonly int maximumCount; + + private readonly int spinCount; + + internal StSemaphore (int count, int maximumCount, int spinCount) + { + if (count < 0 || count > maximumCount) { + throw new ArgumentException ("\"count\": incorrect value"); + } + if (maximumCount <= 0) { + throw new ArgumentException ("\"maximumCount\": incorrect value"); + } + queue.Init (); + state = count; + this.maximumCount = maximumCount; + this.spinCount = Environment.ProcessorCount > 0 ? spinCount : 0; + } + + internal StSemaphore (int count, int maximumCount) + : this (count, maximumCount, 0) { } + + internal StSemaphore (int count) + : this (count, Int32.MaxValue, 0) { } + + internal int CurrentCount { + get { return state; } + } + + internal bool Wait (int acquireCount, StCancelArgs cargs) + { + if (acquireCount <= 0 || acquireCount > maximumCount) { + throw new ArgumentException ("acquireCount"); + } + + if (TryAcquireInternal (acquireCount)) { + return true; + } + + if (cargs.Timeout == 0) { + return false; + } + + var wb = new StWaitBlock (WaitType.WaitAny, acquireCount); + int sc = EnqueueAcquire (wb, acquireCount); + + int ws = wb.parker.Park (sc, cargs); + + if (ws == StParkStatus.Success) { + return true; + } + + CancelAcquire (wb); + StCancelArgs.ThrowIfException (ws); + return false; + } + + internal void Wait (int acount) + { + Wait (acount, StCancelArgs.None); + } + + internal int Release(int releaseCount) + { + if (releaseCount < 1) { + throw new ArgumentOutOfRangeException("releaseCount"); + } + + int prevCount = state; + if (!ReleaseInternal (releaseCount)) { + //throw new SemaphoreFullException (); + } + if (IsReleasePending) { + ReleaseWaitersAndUnlockQueue (); + } + return prevCount; + } + + private bool TryAcquireInternal (int acquireCount) + { + do { + int s; + int ns = (s = state) - acquireCount; + if (ns < 0 || !queue.IsEmpty) { + return false; + } + if (Interlocked.CompareExchange (ref state, ns, s) == s) { + return true; + } + } + while (true); + } + + private bool TryAcquireInternalQueued (int acquireCount) + { + do { + int s; + int ns = (s = state) - acquireCount; + if (ns < 0) { + return false; + } + if (Interlocked.CompareExchange (ref state, ns, s) == s) { + return true; + } + } + while (true); + } + + private void UndoAcquire (int undoCount) + { + do { + int s = state; + if (Interlocked.CompareExchange (ref state, s + undoCount, s) == s) { + return; + } + } + while (true); + } + + private bool ReleaseInternal (int releaseCount) + { + do { + int s; + int ns = (s = state) + releaseCount; + if (ns < 0 || ns > maximumCount) { + return false; + } + if (Interlocked.CompareExchange (ref state, ns, s) == s) { + return true; + } + } + while (true); + } + + private bool IsReleasePending { + get + { + StWaitBlock w = queue.First; + return (w != null && (state >= w.request || w.parker.IsLocked) && queue.TryLock ()); + } + } + + private void ReleaseWaitersAndUnlockQueue () + { + do { + StWaitBlock qh = queue.head; + StWaitBlock w; + while (state > 0 && (w = qh.next) != null) { + StParker pk = w.parker; + if (w.waitType == WaitType.WaitAny) { + + // + // Try to acquire the requested permits on behalf of the + // queued waiter. + // + + if (!TryAcquireInternalQueued (w.request)) { + break; + } + + + if (pk.TryLock ()) { + pk.Unpark (w.waitKey); + } + else { + UndoAcquire (w.request); + } + } + else { + + // + // Wait-all: since that the semaphore seems to have at least + // one available permit, lock the parker and, if this is the last + // cooperative release, unpark its owner thread. + // + + if (pk.TryLock ()) { + pk.Unpark (w.waitKey); + } + } + + // + // Remove the wait block from the semaphore's queue, + // marking the previous head as unlinked, and advance + // the head of the local queues. + // + + qh.next = qh; + qh = w; + } + + // + // It seems that no more waiters can be released; so, + // set the new semaphore queue's head and unlock it. + // + + queue.SetHeadAndUnlock (qh); + + // + // If, after the semaphore's queue is unlocked, it seems + // that more waiters can be released, repeat the release + // processing. + // + + if (!IsReleasePending) { + return; + } + } + while (true); + } + + private void CancelAcquire (StWaitBlock wb) + { + // + // If the wait block is still linked and it isn't the last wait block + // of the queue and the queue's lock is free unlink the wait block. + // + + StWaitBlock wbn; + if ((wbn = wb.next) != wb && wbn != null && queue.TryLock ()) { + queue.Unlink (wb); + ReleaseWaitersAndUnlockQueue (); + } + } + + private int EnqueueAcquire (StWaitBlock wb, int acquireCount) + { + bool isFirst = queue.Enqueue (wb); + + // + // If the wait block was inserted at front of the semaphore's + // queue, re-check if the current thread is at front of the + // queue and can now acquire the requested permits; if so, + // try lock the queue and execute the release processing. + // + + if (isFirst && state >= acquireCount && queue.TryLock ()) { + ReleaseWaitersAndUnlockQueue (); + } + + return (isFirst ? spinCount : 0); + } + + internal override bool _AllowsAcquire + { + get { return (state != 0 && queue.IsEmpty); } + } + + internal override bool _TryAcquire () + { + return TryAcquireInternal (1); + } + + internal override bool _Release () + { + if (!ReleaseInternal (1)) { + return false; + } + if (IsReleasePending) { + ReleaseWaitersAndUnlockQueue (); + } + return true; + } + + internal override StWaitBlock _WaitAnyPrologue (StParker pk, int key, + ref StWaitBlock ignored, ref int sc) + { + if (TryAcquireInternal (1)) { + if (pk.TryLock ()) { + pk.UnparkSelf (key); + } + else { + UndoAcquire (1); + if (IsReleasePending) { + ReleaseWaitersAndUnlockQueue (); + } + } + + // + // Return null because no wait block was inserted on the + // semaphore's wait queue. + // + + return null; + } + + var wb = new StWaitBlock (pk, WaitType.WaitAny, 1, key); + sc = EnqueueAcquire (wb, 1); + return wb; + } + + internal override StWaitBlock _WaitAllPrologue (StParker pk, ref StWaitBlock ignored, + ref int sc) + { + if (_AllowsAcquire) { + if (pk.TryLock ()) { + pk.UnparkSelf (StParkStatus.StateChange); + } + return null; + } + + var wb = new StWaitBlock (pk, WaitType.WaitAll, 1, StParkStatus.StateChange); + sc = EnqueueAcquire (wb, 1); + + return wb; + } + + internal override void _UndoAcquire () + { + UndoAcquire (1); + if (IsReleasePending) { + ReleaseWaitersAndUnlockQueue (); + } + } + + internal override void _CancelAcquire (StWaitBlock wb, StWaitBlock ignored) + { + CancelAcquire (wb); + } + } +} + +#endif \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StSynchronizationEvent.cs b/mcs/class/corlib/System.Threading/StSynchronizationEvent.cs new file mode 100644 index 0000000000000..2df105309b903 --- /dev/null +++ b/mcs/class/corlib/System.Threading/StSynchronizationEvent.cs @@ -0,0 +1,56 @@ +// +// System.Threading.StSynchronizationEvent.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal sealed class StSynchronizationEvent : StMutant + { + internal StSynchronizationEvent (bool initialState, int spinCount) + : base (initialState, spinCount) { } + + internal StSynchronizationEvent (bool initialState) + : base (initialState, 0) { } + + internal StSynchronizationEvent () + : base (false, 0) { } + + public bool Wait (StCancelArgs cargs) + { + return SlowTryAcquire (cargs); + } + + public void Wait () + { + SlowTryAcquire (StCancelArgs.None); + } + + public bool Set () + { + return _Release (); + } + + public bool Reset () + { + return _TryAcquire (); + } + } +} \ No newline at end of file diff --git a/mcs/class/corlib/System.Threading/StWaitBlock.cs b/mcs/class/corlib/System.Threading/StWaitBlock.cs new file mode 100644 index 0000000000000..7f666c8db191a --- /dev/null +++ b/mcs/class/corlib/System.Threading/StWaitBlock.cs @@ -0,0 +1,450 @@ +// +// System.Threading.StWaitBlock.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + // + // The enumerate type used to defines the two possible types of wait. + // + + internal enum WaitType + { + WaitAll, + WaitAny + } ; + + // + // The wait block used with waitables, locks and condition variables. + // + + internal sealed class StWaitBlock + { + // + // A wait block used as sentinel to several purposes. + // + + internal static readonly StWaitBlock SENTINEL = new StWaitBlock (); + + // + // The link field used by the wait queues. + // + + internal volatile StWaitBlock next; + + // + // The associated parker. + // + + internal readonly StParker parker; + + // + // The type of wait. + // + + internal readonly WaitType waitType; + + // + // The *request* field is enconded as follows: + // - bit 31 - set one the request is a locked request. + // - bit 30 - set when the request is a special request. + // - bits 29, 0 - request type. + // + + internal const int LOCKED_REQUEST = (1 << 31); + internal const int SPECIAL_REQUEST = (1 << 30); + internal const int MAX_REQUEST = (SPECIAL_REQUEST - 1); + internal volatile int request; + + // + // The wait status specified when the owner thread of the wait + // block is unparked. + // + + internal readonly int waitKey; + + // + // Constructor used with sentinel wait blocks. + // + + internal StWaitBlock () + { + waitType = WaitType.WaitAny; + request = 0x13081953; + waitKey = StParkStatus.Success; + } + + internal StWaitBlock (StParker pk, WaitType t, int r, int k) + { + parker = pk; + waitType = t; + request = r; + waitKey = k; + } + + internal StWaitBlock (WaitType t, int r, int k) + { + parker = new StParker (); + waitType = t; + request = r; + waitKey = k; + } + + internal StWaitBlock (WaitType t, int r) + { + parker = new StParker (); + waitType = t; + request = r; + } + + internal StWaitBlock (WaitType t) + { + parker = new StParker (); + waitType = t; + } + + internal StWaitBlock (int r) + { + parker = new StParker (); + request = r; + } + + internal StWaitBlock (StParker pk, int r) + { + parker = pk; + request = r; + } + + // + // CASes on the *next* field. + // + + internal bool CasNext (StWaitBlock n, StWaitBlock nn) + { + return (next == n && Interlocked.CompareExchange (ref next, nn, n) == n); + } + } + + // + // A non-thread-safe queue of wait blocks. + // + + internal struct WaitBlockQueue + { + // + // The first and last wait blocks. + // + + internal StWaitBlock head; + private StWaitBlock tail; + + // + // Clears the queue. + // + + internal void Clear () + { + head = tail = null; + } + + // + // Enqueues a wait block at the tail of the queue. + // + + internal void Enqueue (StWaitBlock wb) + { + if (head == null) { + head = wb; + } + else { + tail.next = wb; + } + tail = wb; + } + + // + // Enqueues a wait block at head of the queue. + // + + internal void EnqueueHead (StWaitBlock wb) + { + if ((wb.next = head) == null) { + tail = wb; + } + head = wb; + } + + // + // Dequeues a wait node from a non-empty queue. + // + + internal StWaitBlock Dequeue () + { + StWaitBlock wb = head; + if ((head = wb.next) == null) { + tail = null; + } + wb.next = wb; // Mark the wait block as unlinked. + return wb; + } + + // + // Returns true if the queue is empty. + // + + internal bool IsEmpty + { + get { return head == null; } + } + + // + // Removes the specified wait node from the queue. + // + + internal void Remove (StWaitBlock wb) + { + // + // If the wait block was already unlinked, return. + // + + if (wb.next == wb) { + return; + } + + // + // Compute the previous wait block and perform the removal. + // + + StWaitBlock p = head; + StWaitBlock pv = null; + while (p != null) { + if (p == wb) { + if (pv == null) { + if ((head = wb.next) == null) { + tail = null; + } + } + else { + if ((pv.next = wb.next) == null) { + tail = pv; + } + } + wb.next = wb; + return; + } + pv = p; + p = p.next; + } + throw new InvalidOperationException (); + } + } + + // + // A queue of wait blocks that allows non-blocking enqueue + // and lock-protected dequeue. + // + + internal struct LockedWaitQueue + { + // + // The head and tail of the queue. + // + + internal volatile StWaitBlock head; + internal volatile StWaitBlock tail; + + // + // The queue lock's state. This lock has no wait queue because + // it is always acquired with TryEnter. + // + + private const int FREE = 0; + private const int BUSY = 1; + private volatile int qlock; + + // + // Initializes the queue. + // + + internal void Init () + { + head = tail = new StWaitBlock (); + } + + // + // Advances the queue's head. + // + + private bool AdvanceHead (StWaitBlock h, StWaitBlock nh) + { + if (head == h && Interlocked.CompareExchange (ref head, nh, h) == h) { + h.next = h; // Mark the previous head's wait block as unlinked. + return true; + } + return false; + } + + // + // Advances the queue's tail. + // + + private bool AdvanceTail (StWaitBlock t, StWaitBlock nt) + { + return (tail == t && Interlocked.CompareExchange (ref tail, nt, t) == t); + } + + // + // Enqueues the specified wait block and returns its + // predecessor. + // + + internal bool Enqueue (StWaitBlock wb) + { + do { + StWaitBlock t = tail; + StWaitBlock tn = t.next; + + // + // Do the necessary consistency checks. + // + + if (t != tail) { + continue; + } + if (tn != null) { + AdvanceTail (t, tn); + continue; + } + + // + // Queue in quiescent state, try to insert the wait block. + // + + if (t.CasNext (null, wb)) { + // + // Enqueue succeed; So, try to swing tail to the inserted + // wait block and return. + // + + AdvanceTail (t, wb); + return (t == head); + } + } + while (true); + } + + // + // If the queue is not locked, returns the wait block that + // is at the front of the queue; otherwise, returns always null. + // + + internal StWaitBlock First + { + get { return (qlock == FREE) ? head.next : null; } + } + + // + // Returns true if the waiting queue seems empty. + // + + internal bool IsEmpty + { + get { return head.next == null; } + } + + // + // Tries to lock the queue if it is free. + // + + internal bool TryLock () + { + return (qlock == FREE && Interlocked.CompareExchange (ref qlock, BUSY, FREE) == FREE); + } + + // + // Sets the new head and unlocks the queue. + // + + internal void SetHeadAndUnlock (StWaitBlock nh) + { + // + // First, remove the cancelled wait blocks that follow the + // new queue's head. + // + + do { + StWaitBlock w; + if ((w = nh.next) == null || !w.parker.IsLocked || w.request < 0) { + break; + } + nh.next = nh; // Mark old head's wait block as unlinked. + nh = w; + } + while (true); + + // + // Set the new head and release the queue lock, making + // the lock and queue changes visible to all processors. + // + + head = nh; + Interlocked.Exchange (ref qlock, FREE); + } + + // + // Unlinks the specified wait block. + // + + internal void Unlink (StWaitBlock wb) + { + if (wb.next == wb || wb == head) { + return; + } + + // + // Remove the cancelled wait nodes from *head* till *wb*. + // + + StWaitBlock n; + StWaitBlock pv = head; + while ((n = pv.next) != wb) { + if (n.parker.IsLocked) { + pv.next = n.next; + n.next = n; + } + else { + pv = n; + } + } + + // + // Remove the wait block *wb* and also the cancelled wait + // blocks that follow it. + // + + do { + pv.next = n.next; + n.next = n; + } + while ((n = pv.next).next != null && n.parker.IsLocked); + } + } +} diff --git a/mcs/class/corlib/System.Threading/StWaitable.cs b/mcs/class/corlib/System.Threading/StWaitable.cs new file mode 100644 index 0000000000000..d8396f1059c83 --- /dev/null +++ b/mcs/class/corlib/System.Threading/StWaitable.cs @@ -0,0 +1,649 @@ +// +// System.Threading.StWaitable.cs +// +// Copyright 2011 Carlos Martins, Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) +// + +#pragma warning disable 0420 + +namespace System.Threading +{ + internal abstract class StWaitable + { + // + // Seed used to generate identifiers that don't need to be strictly + // unique, and the identifier shared by all notification events. + // + + private static int idSeed = Int32.MinValue; + internal const int NOTIFICATION_EVENT_ID = Int32.MaxValue; + + // + // The synchronizer ID is used to sort the synchronizers + // in the WaitAll method in order to prevent livelock. + // + + internal volatile int id; + + // + // Returns the "unique" waitable identifier. + // + + private int Id + { + get + { + if (id == 0) { + int nid; + while ((nid = Interlocked.Increment (ref idSeed)) == 0 || + nid == NOTIFICATION_EVENT_ID) { + ; + } + Interlocked.CompareExchange (ref id, nid, 0); + } + return id; + } + } + + // + // Returns true if the waitable's state allows an immediate acquire. + // + + internal abstract bool _AllowsAcquire { get; } + + // + // Tries the waitable acquire operation. + // + + internal abstract bool _TryAcquire (); + + // + // Executes the release semantics associated with the Signal operation. + // + + internal virtual bool _Release () + { + return false; + } + + // + // Executes the prologue for the WaitAny method. + // + + internal abstract StWaitBlock _WaitAnyPrologue (StParker pk, int key, + ref StWaitBlock hint, ref int sc); + + // + // Executes the prologue for the WaitAll method. + // + + internal abstract StWaitBlock _WaitAllPrologue (StParker pk, + ref StWaitBlock hint, ref int sc); + + // + // Excutes the epilogue for WaitOne and WaitAny methods. + // + + internal virtual void _WaitEpilogue () + {} + + // + // Undoes a previous acquire operation. + // + + internal virtual void _UndoAcquire () + {} + + // + // Cancels an acquire attempt. + // + + internal abstract void _CancelAcquire (StWaitBlock wb, StWaitBlock hint); + + // + // Waits until the synchronizer allows the acquire, activating + // the specified cancellers. + // + + internal bool WaitOne (StCancelArgs cargs) + { + // + // Try to acquire and, if succeed, return success. + // + + if (_TryAcquire ()) { + return true; + } + + // + // Return failure, if a null timeout was specified. + // + + if (cargs.Timeout == 0) { + return false; + } + + // + // Create a parker and execute the WaitAny prologue. + // + + var pk = new StParker (); + StWaitBlock hint = null; + int sc = 0; + StWaitBlock wb; + if ((wb = _WaitAnyPrologue (pk, StParkStatus.Success, ref hint, ref sc)) == null) { + return true; + } + + // + // Park the current thread, activating the specified cancellers + // and spinning if appropriate. + // + + int ws = pk.Park (sc, cargs); + + // + // If the acquire succeed; so, execute the Wait epilogue + // and return success. + // + + if (ws == StParkStatus.Success) { + _WaitEpilogue (); + return true; + } + + // + // The acquire was cancelled; so, cancel the acquire attempt + // and report the failure appropriately. + // + + _CancelAcquire (wb, hint); + StCancelArgs.ThrowIfException (ws); + return false; + } + + // + // Waits unconditionally until the waitable allows the acquire. + // + + internal void WaitOne() + { + WaitOne (StCancelArgs.None); + } + + /// + /// Waits until one of the specified waitables allows the acquire, + /// activating the specified cancellers. + /// + /// The array of Waitables. + /// The cancellation arguments. + /// + internal static int WaitAny(StWaitable[] ws, StCancelArgs cargs) + { + // + // Validate the parameters. + // + // NOTE: We support null references on the *ws* array, provided + // that the array contains at least a non-null entry. + // + + int len = ws.Length; + int def = 0; + + // + // First, we scan the *ws* array trying to acquire one of the + // synchronizers and computing the number of specified waitables. + // + + for (int i = 0; i < len; i++) { + StWaitable w = ws[i]; + if (w != null) { + if (w._TryAcquire ()) { + // + // The acquire succedd, so return success. + // + + return (StParkStatus.Success + i); + } + def++; + } + } + + // + // If the *ws* array doesn't contain non-null references, + // throw ArgumentOutOfRangeException. + // + + if (def == 0) { + throw new ArgumentOutOfRangeException ("ws: array is empty"); + } + + // + // Return failure, if a null timeout was specified. + // + + if (cargs.Timeout == 0) { + return StParkStatus.Timeout; + } + + // + // Create a parker and execute the WaitAny prologue on all + // waitables. We stop executing prologues as soon as we detect + // that the acquire operation was accomplished. + // + + StParker pk = new StParker (1); + StWaitBlock[] wbs = new StWaitBlock[len]; + StWaitBlock[] hints = new StWaitBlock[len]; + int lv = -1; + int sc = 0; + int gsc = 0; + for (int i = 0; !pk.IsLocked && i < len; i++) { + StWaitable w = ws[i]; + if (w != null) { + if ((wbs[i] = w._WaitAnyPrologue (pk, i, ref hints[i], ref sc)) == null) { + break; + } + + // + // Adjust the global spin count. + // + + if (gsc < sc) { + gsc = sc; + } + lv = i; + } + } + + // + // Park the current thread, activating the specified cancellers + // and spinning if appropriate. + // + + int wst = pk.Park (gsc, cargs); + + // + // Compute the acquired waitable, if any. + // + + StWaitable acq = (wst >= StParkStatus.Success && wst < len) ? ws[wst] : null; + + // + // Cancel the acquire attempt on all waitables where we + // executed the WaitAny prologue, except the one where the + // acquire was satisfied. + // + + for (int i = 0; i <= lv; i++) { + StWaitable w = ws[i]; + if (w != null && w != acq) { + w._CancelAcquire (wbs[i], hints[i]); + } + } + + // + // If the WaitAny succeed, execute the wait epilogue and + // return success. + // + + if (acq != null) { + acq._WaitEpilogue (); + return wst; + } + + // + // The WaitAny failed, so report the failure appropriately. + // + + StCancelArgs.ThrowIfException (wst); + return StParkStatus.Timeout; + } + + // + // Waits unconditionally until one of the specified waitables + // allows the acquire. + // + + internal static int WaitAny(StWaitable[] ws) + { + return WaitAny (ws, StCancelArgs.None); + } + + // + // Sorts the waitable array by the waitable id and, at the same time, + // check if all waitables allow an immediate acquire operation. + // + // NOTE: The notification events are not sorted, because they don't + // have acquire side-effect. The notification events are grouped + // at the end of the sorted array. + // + + internal static int SortAndCheckAllowAcquire(StWaitable[] ws, StWaitable[] sws) + { + int i, jj; + StWaitable w; + bool acqAll = true; + int len = ws.Length; + + // + // Find the first waitable that isn't a notification event, + // in order to start insertion sort. + // + + jj = len; + for (i = 0; i < len; i++) { + w = ws[i]; + acqAll &= w._AllowsAcquire; + + // + // If the current waitable is a notification event, insert + // it at the end of the ordered array; otherwise, insert it + // on the begin of the array and break the loop. + // + + if (w.id == NOTIFICATION_EVENT_ID) { + sws[--jj] = w; + } + else { + sws[0] = w; + break; + } + } + + // + // If all synchronizers are notification events, return. + // + + if (i == len) { + return acqAll ? 1 : 0; + } + + // + // Sort the remaining synchronizers using the insertion sort + // algorithm but only with the non-notification event waitables. + // + + int k = 1; + for (i++; i < len; i++, k++) { + w = ws[i]; + acqAll &= w._AllowsAcquire; + if (w.id == NOTIFICATION_EVENT_ID) { + sws[--jj] = w; + } + else { + // + // Find the insertion position for *w*. + // + + sws[k] = w; + int j = k - 1; + while (j >= 0 && sws[j].Id > w.Id) { + sws[j + 1] = sws[j]; + j--; + } + + // + // Insert at j+1 position. + // + + sws[j + 1] = w; + + // + // Check for duplicates. + // + + if (sws[k - 1] == sws[k]) { + return -1; + } + } + } + return acqAll ? 1 : 0; + } + + /// + /// Waits until all the specified waitable allow the acquire, activating the + /// specified cancellers. + /// + /// + /// + /// + internal static bool WaitAll(StWaitable[] ws, StCancelArgs cargs) + { + int len = ws.Length; + StWaitable[] sws = new StWaitable[len]; + + int waitHint = SortAndCheckAllowAcquire (ws, sws); + + // + // If there are duplicates, throw ArgumentException. + // + + if (waitHint < 0) { + throw new ArgumentException ("ws: contains duplicates"); + } + + // + // Return failure if the WaitAll can't be satisfied immediately + // and a null timeout was specified. + // + + if (waitHint == 0 && cargs.Timeout == 0) { + return false; + } + + // + // If a timeout was specified, get the current time in order + // to adjust the timeout value later, if we re-wait. + // + + int lastTime = (cargs.Timeout != Timeout.Infinite) ? Environment.TickCount : 0; + StWaitBlock[] wbs = null; + StWaitBlock[] hints = null; + do { + if (waitHint == 0) { + // + // Create the wait block arrays if this is the first time + // that we execute the acquire-all prologue. + // + + if (wbs == null) { + wbs = new StWaitBlock[len]; + hints = new StWaitBlock[len]; + } + + // + // Create a parker for cooperative release, specifying + // as many releasers as the number of waitables. + // + // NOTE: The wait blocks and the parker can't be reused. + // + + StParker pk = new StParker (len); + + // + // Execute the WaitAll prologue on all waitables. + // + + int gsc = 1; + int sc = 0; + for (int i = 0; i < len; i++) { + if ((wbs[i] = sws[i]._WaitAllPrologue (pk, ref hints[i], ref sc)) != null) { + // + // Adjust the global spin count. + // + + if (gsc != 0) { + if (sc == 0) { + gsc = 0; + } + else if (sc > gsc) { + gsc = sc; + } + } + } + } + + int wst = pk.Park (gsc, cargs); + + // + // If the wait was cancelled due to timeout, alert or interrupt, + // cancel the acquire attempt on all waitables where we actually + // inserted wait blocks. + // + + if (wst != StParkStatus.StateChange) { + for (int i = 0; i < len; i++) { + StWaitBlock wb = wbs[i]; + if (wb != null) { + sws[i]._CancelAcquire (wb, hints[i]); + } + } + + StCancelArgs.ThrowIfException (wst); + return false; + } + } + + // + // All waitables where we inserted wait blocks seem to + // allow an immediate acquire operation; so, try to acquire + // all them. + // + + int idx; + for (idx = 0; idx < len; idx++) { + if (!sws[idx]._TryAcquire ()) { + break; + } + } + + // + // If all synchronizers were acquired, return success. + // + + if (idx == len) { + return true; + } + + // + // We failed to acquire all waitables, so undo the acquires + // that we did above. + // + + while (--idx >= 0) { + sws[idx]._UndoAcquire (); + } + + // + // If a timeout was specified, adjust the timeout value + // that will be used on the next wait. + // + + if (!cargs.AdjustTimeout (ref lastTime)) { + return false; + } + + waitHint = 0; + } + while (true); + } + + // + // Wait unconditionally until all the specified waitables + // allow the acquire. + // + + internal static void WaitAll(StWaitable[] ws) + { + WaitAll (ws, StCancelArgs.None); + } + + // + // Signals a waitable and waits on another as an atomic + // operation, activating the specified cancellers. + // + + internal static bool SignalAndWait(StWaitable tos, StWaitable tow, StCancelArgs cargs) + { + // + // Create a parker to execute the WaitAny prologue on the + // *tow* waitable. + // + + StParker pk = new StParker (); + StWaitBlock hint = null; + int sc = 0; + StWaitBlock wb = tow._WaitAnyPrologue (pk, StParkStatus.Success, ref hint, ref sc); + + // + // Signal the *tos* waitable. + // + + if (!tos._Release ()) { + // + // The signal operation failed. So, try to cancel the parker and, + // if successful, cancel the acquire attempt; otherwise, wait until + // the thread is unparked and, then, undo the acquire. + // + + if (pk.TryCancel ()) { + tow._CancelAcquire (wb, hint); + } + else { + pk.Park (); + tow._UndoAcquire (); + } + } + + // + // Park the current thread, activating the specified cancellers + // and spinning if appropriate. + // + + int ws = pk.Park (sc, cargs); + + // + // If we acquired, execute the Wait epilogue and return success. + // + + if (ws == StParkStatus.Success) { + tow._WaitEpilogue (); + return true; + } + + // + // The acquire operation was cancelled; so, cancel the acquire + // attempt and report the failure appropriately. + // + + tow._CancelAcquire (wb, hint); + StCancelArgs.ThrowIfException (ws); + return false; + } + + // + // Signals a waitable and waits unconditionally on another + // as an atomic operation. + // + + internal static void SignalAndWait(StWaitable tos, StWaitable tow) + { + SignalAndWait (tos, tow, StCancelArgs.None); + } + } +} diff --git a/mcs/class/corlib/System.Threading/WaitHandle.cs b/mcs/class/corlib/System.Threading/WaitHandle.cs index 0dc01237dc8f8..5128df4f016fc 100644 --- a/mcs/class/corlib/System.Threading/WaitHandle.cs +++ b/mcs/class/corlib/System.Threading/WaitHandle.cs @@ -1,36 +1,23 @@ // // System.Threading.WaitHandle.cs // -// Author: -// Dick Porter (dick@ximian.com) -// Gonzalo Paniagua Javier (gonzalo@ximian.com -// -// (C) 2002,2003 Ximian, Inc. (http://www.ximian.com) -// Copyright (C) 2004-2005 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: +// Copyright 2011 Duarte Nunes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at // -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. +// http://www.apache.org/licenses/LICENSE-2.0 // -// 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. +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Duarte Nunes (duarte.m.nunes@gmail.com) // -using System; -using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.Remoting.Contexts; using System.Security.Permissions; using System.Runtime.InteropServices; @@ -47,24 +34,25 @@ public abstract class WaitHandle : MarshalByRefObject, IDisposable #endif { - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern bool WaitAll_internal(WaitHandle[] handles, int ms, bool exitContext); - - static void CheckArray (WaitHandle [] handles, bool waitAll) + static StWaitable[] CheckArray (WaitHandle [] waitHandles, bool waitAll) { - if (handles == null) - throw new ArgumentNullException ("waitHandles"); - - int length = handles.Length; - if (length > 64) - throw new NotSupportedException ("Too many handles"); - - if (handles.Length == 0) { - // MS throws different exceptions from the different methods. - if (waitAll) - throw new ArgumentNullException ("waitHandles"); - else - throw new ArgumentException (); + if (waitHandles == null) { + throw new ArgumentNullException ("waitHandles"); + } + + /* + int length = handles.Length; + if (length > 64) { + throw new NotSupportedException ("Too many handles"); + } + */ + + if (waitHandles.Length == 0) { + // MS throws different exceptions from the different methods. + if (waitAll) { + throw new ArgumentNullException ("waitHandles"); + } + throw new ArgumentException (); } #if false @@ -78,14 +66,21 @@ static void CheckArray (WaitHandle [] handles, bool waitAll) if (waitAll && length > 1 && IsSTAThread) throw new NotSupportedException ("WaitAll for multiple handles is not allowed on an STA thread."); #endif - foreach (WaitHandle w in handles) { - if (w == null) - throw new ArgumentNullException ("waitHandles", "null handle"); - - if (w.safe_wait_handle == null) - throw new ArgumentException ("null element found", "waitHandle"); - + foreach (WaitHandle w in waitHandles) { + if (w == null) { + throw new ArgumentNullException ("waitHandles", "null handle"); + } + + if (w.Waitable == null) { + throw new ArgumentException ("null element found", "waitHandles"); + } } + + var waitables = new StWaitable[waitHandles.Length]; + for (int i = 0; i < waitables.Length; ++i) { + waitables[i] = waitHandles[i].Waitable; + } + return waitables; } #if false // usage of property is commented - see above @@ -106,259 +101,220 @@ static void CheckArray (WaitHandle [] handles, bool waitAll) } } #endif - public static bool WaitAll(WaitHandle[] waitHandles) - { - CheckArray (waitHandles, true); - return(WaitAll_internal(waitHandles, Timeout.Infinite, false)); - } + public const int WaitTimeout = 258; - public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) - { - CheckArray (waitHandles, true); - // check negative - except for -1 (which is Timeout.Infinite) - if (millisecondsTimeout < Timeout.Infinite) - throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + protected static readonly IntPtr InvalidHandle = (IntPtr) (-1); + + bool disposed; - try { - if (exitContext) SynchronizationAttribute.ExitContext (); - return(WaitAll_internal(waitHandles, millisecondsTimeout, false)); - } - finally { - if (exitContext) SynchronizationAttribute.EnterContext (); + // + // In 2.0 we use SafeWaitHandles instead of IntPtrs + // + + private SafeWaitHandle handle; + private readonly StFairLock handleLock = new StFairLock(); + + [Obsolete ("In the profiles > 2.x, use SafeHandle instead of Handle")] + public virtual IntPtr Handle { + get { return SafeWaitHandle.DangerousGetHandle (); } + + [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] + [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)] + set { + SafeWaitHandle = value == InvalidHandle + ? new SafeWaitHandle (InvalidHandle, false) + : new SafeWaitHandle (value, true); } } - public static bool WaitAll(WaitHandle[] waitHandles, - TimeSpan timeout, - bool exitContext) - { - CheckArray (waitHandles, true); - long ms = (long) timeout.TotalMilliseconds; - - if (ms < -1 || ms > Int32.MaxValue) - throw new ArgumentOutOfRangeException ("timeout"); - - try { - if (exitContext) SynchronizationAttribute.ExitContext (); - return (WaitAll_internal (waitHandles, (int) ms, exitContext)); + public SafeWaitHandle SafeWaitHandle { + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + get { + handleLock.Enter (); + try { + SafeWaitHandle swh; + if ((swh = handle) == null) { + IntPtr h = GCHandle.Alloc (Waitable, GCHandleType.Pinned).AddrOfPinnedObject (); + handle = swh = new SafeWaitHandle (h, true); + } + return swh; + } finally { + handleLock.Exit(); + } } - finally { - if (exitContext) SynchronizationAttribute.EnterContext (); + + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] + set { + SafeWaitHandle owh = null; + handleLock.Enter (); + try { + owh = handle; + IntPtr h = (handle = value).DangerousGetHandle (); + + Waitable = h == InvalidHandle + ? null + : GCHandle.FromIntPtr (h).Target as StWaitable; + } finally { + handleLock.Exit(); + if (owh != null) { + owh.Dispose (); + } + } } } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern int WaitAny_internal(WaitHandle[] handles, int ms, bool exitContext); + internal StWaitable Waitable { get; set; } - // LAMESPEC: Doesn't specify how to signal failures - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public static int WaitAny(WaitHandle[] waitHandles) + public static bool WaitAll (WaitHandle[] waitHandles) { - CheckArray (waitHandles, false); - return(WaitAny_internal(waitHandles, Timeout.Infinite, false)); + return WaitAll(waitHandles, Timeout.Infinite, false); } - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public static int WaitAny(WaitHandle[] waitHandles, - int millisecondsTimeout, - bool exitContext) + public static bool WaitAll (WaitHandle[] waitHandles, int millisecondsTimeout) { - CheckArray (waitHandles, false); - // check negative - except for -1 (which is Timeout.Infinite) - if (millisecondsTimeout < Timeout.Infinite) - throw new ArgumentOutOfRangeException ("millisecondsTimeout"); - - try { - if (exitContext) SynchronizationAttribute.ExitContext (); - return(WaitAny_internal(waitHandles, millisecondsTimeout, exitContext)); - } - finally { - if (exitContext) SynchronizationAttribute.EnterContext (); - } + return WaitAll (waitHandles, millisecondsTimeout, false); } - - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout) + + public static bool WaitAll (WaitHandle[] waitHandles, TimeSpan timeout) { - return WaitAny (waitHandles, timeout, false); + return WaitAll (waitHandles, timeout, false); } - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout) + public static bool WaitAll (WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) { - return WaitAny (waitHandles, millisecondsTimeout, false); + double ms = timeout.TotalMilliseconds; + if (ms > Int32.MaxValue) { + throw new ArgumentOutOfRangeException ("timeout"); + } + + return WaitAll (waitHandles, Convert.ToInt32 (ms), exitContext); } - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - public static int WaitAny(WaitHandle[] waitHandles, - TimeSpan timeout, bool exitContext) + public static bool WaitAll (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) { - CheckArray (waitHandles, false); - long ms = (long) timeout.TotalMilliseconds; - - if (ms < -1 || ms > Int32.MaxValue) - throw new ArgumentOutOfRangeException ("timeout"); + if (millisecondsTimeout < Timeout.Infinite) { + throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + } - try { - if (exitContext) SynchronizationAttribute.ExitContext (); - return (WaitAny_internal(waitHandles, (int) ms, exitContext)); - } - finally { - if (exitContext) SynchronizationAttribute.EnterContext (); + StWaitable[] waitables = CheckArray (waitHandles, true); + + try { + if (exitContext) { + SynchronizationAttribute.ExitContext (); + } + + return StWaitable.WaitAll (waitables, new StCancelArgs (millisecondsTimeout)); + } finally { + if (exitContext) { + SynchronizationAttribute.EnterContext (); + } } } - - protected WaitHandle() + + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + public static int WaitAny (WaitHandle[] waitHandles) { - // FIXME + return WaitAny (waitHandles, Timeout.Infinite, false); } - public virtual void Close() { - Dispose(true); - GC.SuppressFinalize (this); + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + public static int WaitAny (WaitHandle[] waitHandles, TimeSpan timeout) + { + return WaitAny (waitHandles, timeout, false); } -#if NET_4_0 || MOBILE || MOONLIGHT - public void Dispose () -#else - void IDisposable.Dispose () -#endif + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + public static int WaitAny (WaitHandle[] waitHandles, int millisecondsTimeout) { - Close (); + return WaitAny (waitHandles, millisecondsTimeout, false); } - public const int WaitTimeout = 258; - - // - // In 2.0 we use SafeWaitHandles instead of IntPtrs - // - SafeWaitHandle safe_wait_handle; - - [Obsolete ("In the profiles > 2.x, use SafeHandle instead of Handle")] - public virtual IntPtr Handle { - get { - return safe_wait_handle.DangerousGetHandle (); - } + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + public static int WaitAny (WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) + { + double ms = timeout.TotalMilliseconds; + if (ms > Int32.MaxValue) { + throw new ArgumentOutOfRangeException ("timeout"); + } - [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)] - [SecurityPermission (SecurityAction.InheritanceDemand, UnmanagedCode = true)] - set { - if (value == InvalidHandle) - safe_wait_handle = new SafeWaitHandle (InvalidHandle, false); - else - safe_wait_handle = new SafeWaitHandle (value, true); - } + return WaitAny (waitHandles, Convert.ToInt32 (ms), exitContext); } - - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private extern bool WaitOne_internal(IntPtr handle, int ms, bool exitContext); - protected virtual void Dispose (bool explicitDisposing) + [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] + public static int WaitAny (WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) { - if (!disposed){ - disposed = true; - - // - // This is only the case if the handle was never properly initialized - // most likely a bug in the derived class - // - if (safe_wait_handle == null) - return; - - lock (this){ - if (safe_wait_handle != null) - safe_wait_handle.Dispose (); + if (millisecondsTimeout < Timeout.Infinite) { + throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + } + + StWaitable[] waitables = CheckArray (waitHandles, false); + + try { + if (exitContext) { + SynchronizationAttribute.ExitContext (); } - } - } - public SafeWaitHandle SafeWaitHandle { - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.MayFail)] - get { - return safe_wait_handle; - } + int ws = StWaitable.WaitAny (waitables, new StCancelArgs (millisecondsTimeout)); + return ws == StParkStatus.Timeout ? WaitTimeout : ws; - [ReliabilityContract (Consistency.WillNotCorruptState, Cer.Success)] - set { - if (value == null) - safe_wait_handle = new SafeWaitHandle (InvalidHandle, false); - else - safe_wait_handle = value; + } finally { + if (exitContext) { + SynchronizationAttribute.EnterContext (); + } } } - public static bool SignalAndWait (WaitHandle toSignal, - WaitHandle toWaitOn) + public static bool SignalAndWait (WaitHandle toSignal, WaitHandle toWaitOn) { return SignalAndWait (toSignal, toWaitOn, -1, false); } - - public static bool SignalAndWait (WaitHandle toSignal, - WaitHandle toWaitOn, - int millisecondsTimeout, - bool exitContext) - { - if (toSignal == null) - throw new ArgumentNullException ("toSignal"); - if (toWaitOn == null) - throw new ArgumentNullException ("toWaitOn"); - if (millisecondsTimeout < -1) - throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + public static bool SignalAndWait (WaitHandle toSignal, WaitHandle toWaitOn, + TimeSpan timeout, bool exitContext) + { + double ms = timeout.TotalMilliseconds; + if (ms > Int32.MaxValue) { + throw new ArgumentOutOfRangeException ("timeout"); + } - return SignalAndWait_Internal (toSignal.Handle, toWaitOn.Handle, millisecondsTimeout, exitContext); + return SignalAndWait (toSignal, toWaitOn, Convert.ToInt32 (ms), false); } - public static bool SignalAndWait (WaitHandle toSignal, - WaitHandle toWaitOn, - TimeSpan timeout, - bool exitContext) + public static bool SignalAndWait (WaitHandle toSignal, WaitHandle toWaitOn, + int millisecondsTimeout, bool exitContext) { - double ms = timeout.TotalMilliseconds; - if (ms > Int32.MaxValue) - throw new ArgumentOutOfRangeException ("timeout"); + if (toSignal == null) { + throw new ArgumentNullException ("toSignal"); + } - return SignalAndWait (toSignal, toWaitOn, Convert.ToInt32 (ms), false); - } + if (toWaitOn == null) { + throw new ArgumentNullException ("toWaitOn"); + } - [MethodImplAttribute(MethodImplOptions.InternalCall)] - static extern bool SignalAndWait_Internal (IntPtr toSignal, IntPtr toWaitOn, int ms, bool exitContext); + if (millisecondsTimeout < Timeout.Infinite) { + throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + } - public virtual bool WaitOne() - { - CheckDisposed (); - bool release = false; - try { - safe_wait_handle.DangerousAddRef (ref release); - return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), Timeout.Infinite, false)); - } finally { - if (release) - safe_wait_handle.DangerousRelease (); + try { + if (exitContext) { + SynchronizationAttribute.ExitContext (); + } + + return StWaitable.SignalAndWait (toSignal.Waitable, toWaitOn.Waitable, + new StCancelArgs (millisecondsTimeout)); + } finally { + if (exitContext) { + SynchronizationAttribute.EnterContext (); + } } } - public virtual bool WaitOne(int millisecondsTimeout, bool exitContext) + public virtual bool WaitOne () { - CheckDisposed (); - // check negative - except for -1 (which is Timeout.Infinite) - if (millisecondsTimeout < Timeout.Infinite) - throw new ArgumentOutOfRangeException ("millisecondsTimeout"); - - bool release = false; - try { - if (exitContext) - SynchronizationAttribute.ExitContext (); - safe_wait_handle.DangerousAddRef (ref release); - return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), millisecondsTimeout, exitContext)); - } finally { - if (exitContext) - SynchronizationAttribute.EnterContext (); - if (release) - safe_wait_handle.DangerousRelease (); - } + return WaitOne (Timeout.Infinite, false); } - public virtual bool WaitOne (int millisecondsTimeout) + public virtual bool WaitOne (int millisecondsTimeout) { return WaitOne (millisecondsTimeout, false); } @@ -368,46 +324,73 @@ public virtual bool WaitOne (TimeSpan timeout) return WaitOne (timeout, false); } - public virtual bool WaitOne(TimeSpan timeout, bool exitContext) + public virtual bool WaitOne (TimeSpan timeout, bool exitContext) + { + double ms = timeout.TotalMilliseconds; + if (ms > Int32.MaxValue) { + throw new ArgumentOutOfRangeException ("timeout"); + } + + return WaitOne (Convert.ToInt32 (ms), exitContext); + } + + public virtual bool WaitOne (int millisecondsTimeout, bool exitContext) { CheckDisposed (); - long ms = (long) timeout.TotalMilliseconds; - if (ms < -1 || ms > Int32.MaxValue) - throw new ArgumentOutOfRangeException ("timeout"); - bool release = false; + if (millisecondsTimeout < Timeout.Infinite) { + throw new ArgumentOutOfRangeException ("millisecondsTimeout"); + } + try { - if (exitContext) - SynchronizationAttribute.ExitContext (); - safe_wait_handle.DangerousAddRef (ref release); - return (WaitOne_internal(safe_wait_handle.DangerousGetHandle (), (int) ms, exitContext)); - } - finally { - if (exitContext) - SynchronizationAttribute.EnterContext (); - if (release) - safe_wait_handle.DangerousRelease (); + if (exitContext) { + SynchronizationAttribute.ExitContext (); + } + + return _WaitOne (millisecondsTimeout); + } finally { + if (exitContext) { + SynchronizationAttribute.EnterContext (); + } } } - internal void CheckDisposed () + internal virtual bool _WaitOne (int timeout) + { + return false; + } + + public virtual void Close() { + Dispose(true); + GC.SuppressFinalize (this); + } + +#if NET_4_0 || MOBILE || MOONLIGHT + public void Dispose () +#else + void IDisposable.Dispose () +#endif { - if (disposed || safe_wait_handle == null) - throw new ObjectDisposedException (GetType ().FullName); + Close (); } - public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout) + protected virtual void Dispose (bool explicitDisposing) { - return WaitAll (waitHandles, millisecondsTimeout, false); + if (!disposed) { + disposed = true; + + if (handle != null) { + handle.Dispose (); + } + } } - public static bool WaitAll(WaitHandle[] waitHandles, TimeSpan timeout) + internal void CheckDisposed () { - return WaitAll (waitHandles, timeout, false); + if (disposed) { + throw new ObjectDisposedException (GetType ().FullName); + } } - - protected static readonly IntPtr InvalidHandle = (IntPtr) (-1); - bool disposed = false; ~WaitHandle() { Dispose(false); diff --git a/mcs/class/corlib/corlib-build.csproj b/mcs/class/corlib/corlib-build.csproj index a7d25f3cd7909..9eba713fb7c14 100644 --- a/mcs/class/corlib/corlib-build.csproj +++ b/mcs/class/corlib/corlib-build.csproj @@ -1504,12 +1504,10 @@ - - + - - + @@ -1529,14 +1527,24 @@ - - - - + - + + + + + + + + + + + + + + diff --git a/mcs/class/corlib/corlib-net_2_0.csproj b/mcs/class/corlib/corlib-net_2_0.csproj index 7fc18fc071aa3..2164803b1a49c 100644 --- a/mcs/class/corlib/corlib-net_2_0.csproj +++ b/mcs/class/corlib/corlib-net_2_0.csproj @@ -1504,12 +1504,10 @@ - - + - - + @@ -1529,14 +1527,24 @@ - - - - + - + + + + + + + + + + + + + + diff --git a/mcs/class/corlib/corlib-net_4_0.csproj b/mcs/class/corlib/corlib-net_4_0.csproj index 9b42ceaa828fb..04a0d7519765b 100644 --- a/mcs/class/corlib/corlib-net_4_0.csproj +++ b/mcs/class/corlib/corlib-net_4_0.csproj @@ -1504,12 +1504,10 @@ - - + - - + @@ -1530,9 +1528,19 @@ - - - + + + + + + + + + + + + + diff --git a/mcs/class/corlib/corlib.dll.sources b/mcs/class/corlib/corlib.dll.sources index 237c8c0f2e127..2faa632d96668 100644 --- a/mcs/class/corlib/corlib.dll.sources +++ b/mcs/class/corlib/corlib.dll.sources @@ -1462,11 +1462,9 @@ System.Text/UTF7Encoding.cs System.Text/UTF8Encoding.cs System.Text/UTF32Encoding.cs System.Threading/AbandonedMutexException.cs -System.Threading/Alerter.cs System.Threading/ApartmentState.cs System.Threading/AsyncFlowControl.cs System.Threading/AutoResetEvent.cs -System.Threading/CancelArgs.cs System.Threading/CompressedStack.cs System.Threading/ContextCallback.cs System.Threading/EventResetMode.cs @@ -1487,13 +1485,23 @@ System.Threading/NativeEventCalls.cs System.Threading/NativeOverlapped.cs System.Threading/Overlapped.cs System.Threading/ParameterizedThreadStart.cs -System.Threading/Parker.cs -System.Threading/ParkSpot.cs -System.Threading/ParkStatus.cs System.Threading/ReaderWriterLock.cs System.Threading/RegisteredWaitHandle.cs System.Threading/SendOrPostCallback.cs ../System/System.Threading/SemaphoreFullException.cs +System.Threading/StAlerter.cs +System.Threading/StCancelArgs.cs +System.Threading/StFairLock.cs +System.Threading/StMutant.cs +System.Threading/StNotificationEvent.cs +System.Threading/StParker.cs +System.Threading/StParkSpot.cs +System.Threading/StParkStatus.cs +System.Threading/StReentrantFairLock.cs +System.Threading/StSemaphore.cs +System.Threading/StSynchronizationEvent.cs +System.Threading/StWaitable.cs +System.Threading/StWaitBlock.cs System.Threading/SynchronizationContext.cs System.Threading/SynchronizationLockException.cs System.Threading/Thread.cs