stackMeUp
stackMeUp

Reputation: 171

Releasing lock without executing a method

The system I am working on is composed of a Windows service hosting various WCF services. Multiple clients can talk to a same service, but only one client can talk to a service at time.
I am therefore using a "lock ()" to prevent multiple clients to conflict. If Client1 makes a first request to a service and then Client2 makes another requested while the former is still executing, the "lock" puts that second request on hold until the first one is done.

So far so good. Now the problem is that I have to deal with events (I don't think a simple callback would do the trick here).
In other words, while the 1st request is running, something may happen that Client2 needs to know about.
When that happens, Client2 will receive that event and should eventually stop using that service all together.
The problem here is that our 2nd request is already in the locker's queue.
So how would I prevent that second request from running. Can it get cancelled?
Maybe it is just a matter of adding a dirty flag but I am hoping there are better ways to do that.
Here is what it may look like on the service side with the dirty flag "canRun":

lock (_locker)
{ 
    if (canRun)
       return SomeMethod();
    else
       return null;
}

Upvotes: 1

Views: 146

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1064114

If you can switch to async/await and something like a SemaphoreSlim, then: CancellationToken is what you're looking for; a CancellationTokenSource can be marked as cancelled at any time, and code can respond accordingly - and most framework/library code already correctly handles cancellation, for example: during async semaphore acquisition.

If you need to stick in a synchronous world, then your best bet is probably to change to a looped Monitor mode that can re-check every timeout, for example:

bool lockTaken = false;
try
{
    do
    {
        if (!canRun) throw new OperationCanceledException();
        Monitor.TryEnter(_locker, someTimeout, ref lockTaken);
    }
    while (!lockTaken);
    // now we have the conch
    SomeMethod();
}
finally
{
    if (lockTaken) Monitor.Exit(_locker);
}

Note that if canRun is a field you would want to ensure that it is either volatile or otherwise accessed suitably to avoid being cached in a register or similar.

Upvotes: 1

Related Questions