Does SemaphoreSlim's Wait(Int32) method return immediately when passed zero?

.net-4.0, c#, concurrency, semaphore

Solution

If passed a timeout of 0 then `SemaphoreSlim.Wait` will always attempt to acquire a slot before returning, it just won't wait around for one for longer than a few operations and a `SpinOnce`.

EDIT: clarification: This is probably the same apparent behaviour as `Semaphore`. From the documentation for `Semaphore`:

It tests the state of the wait handle and returns immediately

However `SemaphoreSlim` does give a slot a chance of opening up once you are in the Wait method, through the use of `SpinOnce`.

(end edit)

Also, `SemaphoreSlim` runs a few operations before attempting to acquire a slot. One of these is a `Monitor.Enter`, so it could wait on other threads at that point who are in the process of waiting or releasing. It will not necessarily, therefore, return immediately.

As far as I know the order of events is:

- Create a `CancellationTokenRegistration`

- If the available slot count is 0, then `SpinWait.SpinOnce` (or skip this if `NextSpinWillYield` is `true`) (edit: I made this bold to emphasise that step 2 does the same test as step 5, giving it a chance to spin and have a slot become available before the final test and exit)

- Call `Monitor.Enter` to enter the lock (the same lock that Release and WaitAsync enters)

- Increment the internal waitCount by 1

- If the available slot count is 0, decrement waitCount by 1 and Exit the lock, then return `false`

- If we get to here and there is a free slot:

- decrement the available slot count by 1 (implication: acquire the slot)

- Reset the waitHandle under `AvailableWaitHandle`, if it is being used and the available slot count is now 0 again

- Decrement waitCount by 1

- Exit the lock

- Dispose of the `CancellationTokenRegistration`

- return `true`

(Note that other threads who are using non-zero timeouts intermittently release and acquire the lock used to protect the counters by calling `Monitor.Wait`, so you won't wait forever with a timeout of 0, just for a very short time.)

So `SemaphoreSlim` would not seem to share the exact same 0 timeout behaviour with `Sempahore`, as it does give a slot a chance to open up. (Maybe this is why there is `Semaphore.WaitOne` and `SemaphoreSlim.Wait` - to cause code not to compile when upgrading old code by just changing the instantiation of the Semaphore, and therefore making us check the behaviour).

The article Semaphore vs. SemaphoreSlim doesn't highlight this behaviour, just the fundamental differences between the two.

Side note:

Interestingly that reference also states

[SemaphoreSlim] does not support ... the use of a wait handle for synchronization

And yet the documentation for SemaphoreSlim.AvailableWaitHandle says differently.

Problem

In the MSDN docs for Semaphore's WaitOne(Int32) method, it says that giving it a value of zero will cause the method to return immediately without waiting for a slot to open up. The documentation for the SemaphoreSlim version does not say the same thing. Does SemaphoreSlim share this behavior? I'm not sure how to guarantee the timing to test this myself.

Original source