////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Mutex.cs
//
// Note:
//
using System;
using System.Threading;
using System.Runtime.CompilerServices;
using Microsoft.Singularity;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Scheduling;
namespace System.Threading
{
[CLSCompliant(false)]
public enum MutexEvent
{
AcquireAgain = 1,
Acquire = 2,
Enqueue = 3,
}
///
///
/// Mutex class implementing a mutex functionality
///
///
[NoCCtor]
[CLSCompliant(false)]
public sealed class Mutex : WaitHandle
{
///
///
/// Default constructor
///
///
public Mutex()
: this(false, true)
{
}
///
///
/// Constructor
///
///
/// Initial state of a mutex
///
/// We assume that mutex can initially be owned by current thread only
///
public Mutex(bool initiallyOwned)
: this(initiallyOwned, true)
{
}
///
///
/// Constructor
///
///
/// Initial state of a mutex
///
/// True if this mutex is created by kernel and therefore used by kernel threads.
/// False if this mutex is created by a SIP and therefore used only by the SIP.
///
/// A kernel thread is not allowed to be forcibly stopped while owning a mutex,
/// whereas SIP threads can be forcibly stopped while owning a mutex. This doesn't
/// create problems for SIPs because the only time a SIP thread is forced to stop is
/// during process torn down.
///
///
/// We assume that mutex can initially be owned by current thread only
///
public Mutex(bool initiallyOwned, bool isKernelObject)
: base(initiallyOwned ? WaitHandle.SignalState.Unsignaled :
WaitHandle.SignalState.Signaled,
WaitHandle.SignalState.Unsignaled,
SpinLock.Types.Mutex)
{
this.isKernelObject = isKernelObject;
if (initiallyOwned) {
Thread currentThread = Thread.CurrentThread;
this.owner = Thread.CurrentThread;
if (this.isKernelObject) {
// Kernel thread can't be stop if it owns mutex
currentThread.DelayStop(true);
}
this.acquired = 1;
}
}
///
///
/// Finalize method - release mutex by hand if it is going away
///
///
[CLSCompliant(false)]
public void Finalize() {
// If a thread owns mutex and mutex is pure kernel object update its dealy abort counter
if (this.owner != null && this.isKernelObject) {
this.owner.DelayStop(false);
}
}
///
///
/// Acquire mutex without time out specified
///
///
public void AcquireMutex()
{
WaitOne(SchedulerTime.MaxValue);
}
///
///
/// Acquire mutex with time out specified
///
///
/// Specified time out
///
public bool AcquireMutex(SchedulerTime stop)
{
bool result = true;;
Thread currentThread = Thread.CurrentThread;
// If we own the mutex we can by pass the wait - do wait only if we don't own
if (this.owner != currentThread) {
result = WaitOne(stop);
}
else {
// Don't forget to update recursion counter
this.acquired++;
}
return result;
}
///
///
/// Release mutex
///
///
public void ReleaseMutex()
{
// Assert precondition: Only thread that owns mutex can release it and mutex
// acquired counter should be strictly positive
VTable.Assert(Thread.CurrentThread == this.owner);
VTable.Assert(this.acquired > 0);
if (Thread.CurrentThread == this.owner && this.acquired > 0) {
// Decrement recurtion counter - first step for giving up ownership
this.acquired--;
// If we are done with mutex - give up ownership
if (this.acquired == 0) {
//Indicate that noone longer owns mutex
this.owner = null;
// Wakeup waiters
SignalOne(WaitHandle.SignalState.Signaled);
// If this is kernel object, the thread can be aborted now
if (this.isKernelObject) {
Thread.CurrentThread.DelayStop(false);
}
}
}
}
///
///
/// Check if mutex is owned by the current thread
///
///
internal bool IsOwnedByCurrentThread()
{
return (Thread.CurrentThread == owner);
}
///
///
/// Try to acquire a mutex if acquire fail entry will be enqueued onto wait queue
///
///
/// Entry represents a thread attempting to acquire mutex
///
/// Id of the handle that we are trying to acquire - is used to check if thread can be unblocked
///
///
[NoHeapAllocation]
protected override bool AcquireOrEnqueue(ThreadEntry entry, int handleId)
{
bool didAcquire = true;
// Assert preconditions: Current thread and entry's thread should be the same
VTable.Assert(Thread.CurrentThread == entry.Thread);
VTable.Assert(Thread.CurrentThread.IsAbortDelayed());
// If we currently don't own mutex try to acquire it or enqueue ourselves..
if (this.owner != entry.Thread) {
didAcquire = base.AcquireOrEnqueue (entry, handleId);
}
return didAcquire;
}
///
///
/// Complete wait - used by mutex to record ownership
///
///
/// Thread owner
///
[NoHeapAllocation]
protected override void CompleteWait(Thread ownerThread)
{
// Assert preconditions
VTable.Assert(Thread.CurrentThread == ownerThread);
// Update recursion counter
this.acquired++;
//If this is first time we acquired mutex don't forget to update owner
if (this.acquired == 1) {
if (this.isKernelObject) {
// Kernel thread can't be stop if it owns mutex
ownerThread.DelayStop(true);
}
this.owner = ownerThread;
}
}
///
/// Recursion counter indicating number of times mutex is acquired by the same
/// thread
///
private int acquired = 0;
///
/// True if this mutex is created by kernel and therefore used by kernel threads.
/// False if this mutex is created by a SIP and therefore used only by the SIP.
///
/// A kernel thread is not allowed to be forcibly stopped while owning a mutex,
/// whereas SIP threads can be forcibly stopped while owning a mutex. This doesn't
/// create problems for SIPs because the only time a SIP thread is forced to stop is
/// during process torn down.
///
private readonly bool isKernelObject;
}
}