Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement my own TaS-Lock in C#?

So for an assignment we need to have the option to either use the C#-Lock or use a self-implemented TaS-Lock. What I've read about TaS-Locks is that it uses 1 atomic step to both read and write a value. It was suggested to us that we use the Interlocked class in C# for this.

So far this is what I've got, but it seems to result in inconsistent answers:

public interface Lock
{
    void Lock();
    void Unlock();
}

public class C_Sharp_Lock : Lock
{
    readonly Object myLock = new object();

    public void Lock()
    {
        Monitor.Enter(myLock);
    }

    public void Unlock()
    {
        Monitor.Exit(myLock);
    }
}

public class Tas_Lock : Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 == Interlocked.Exchange(ref L, 1)) { };
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

Does anyone know what I'm doing wrong here?

Edit: As a response to Kevin:

I've changed it to the following:

public class Tas_Lock : Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 == Interlocked.CompareExchange(ref L, 1, 0)) { };
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

However this still returns inconsistent results.

Edit #2: Changes to C# lock:

public class C_Sharp_Lock : Lock
{
    readonly Object myLock = new object();
    bool lockTaken = false;

    public void Lock()
    {
        Monitor.Enter(myLock, ref lockTaken);
    }

    public void Unlock()
    {
        if (lockTaken)
            Monitor.Exit(myLock);
    }
}
like image 264
Heijmaaans Avatar asked Nov 29 '16 17:11

Heijmaaans


1 Answers

You're misunderstanding the way Interlocked.CompareExchange works. It swaps atomatically a value if it's previously equal to the provided comparand, and returns the previous value.

In short, Interlocked.CompareExchange(ref L, 1, 0) will:

  • Check whether L is equal to 0
  • If L is equal to 0, then it will set L to 1 and return the previous value (0)
  • If L isn't equal to 0 (and therefore is equal to 1), then it will return the previous value (1)

From there, what you should do is loop until Interlocked.CompareExchange returns 0 (which means that the lock was acquired). In your code, you're waiting while Interlocked.CompareExchange returns 0.

The fixed code:

public class Tas_Lock
{
    int L = 0;

    public void Lock()
    {
        while (0 != Interlocked.CompareExchange(ref L, 1, 0)) { }
    }

    public void Unlock()
    {
        Interlocked.Exchange(ref L, 0);
    }
}

Two things to note:

  • The Interlocked.Exchange in Unlock could be replaced by a faster Volatile.Write (or even, though arguable, a simple write)
  • If it wasn't for an assignment, you could use the built-in class SpinLock, which already does all that stuff in an optimized way
like image 100
Kevin Gosse Avatar answered Sep 21 '22 11:09

Kevin Gosse