socketry / async

An awesome asynchronous event-driven reactor for Ruby.
MIT License
2.15k stars 89 forks source link

Semaphore: Any way to tell if it's held by the current Task? #330

Closed paddor closed 4 months ago

paddor commented 4 months ago

I have a shared resource that should only be changed by one task at a time and then the changes need to be committed. So far I've done that with:

def change_my_resource
  result = begin
             @change_level += 1
             yield
           ensure
             @change_level -= 1
           end

  commit if @change_level == 0

  result
end

change_my_resource do
  # ...
end

This allowed nesting calls as well, with only one final call to #commit. I used to be able to guarantee that no other top-level call to #change_my_resouce would occur, as long as there was a top-level call to #change_my_resouce ongoing.

Now with Async I can't make that guarantee any more (without limiting its potential severely), which means that a semaphore is needed:

@semaphore = Async::Semaphore.new 1

def change_my_resource
  @semaphore.async do
    result = begin
               @change_level += 1
               yield
             ensure
               @change_level -= 1
             end

    commit if @change_level == 0

    result
  end.wait
end

Unfortuntately, that makes the nested calls block forever. Is there any way to detect nested calls from the same Async::Task (and possibly its children) and to avoid blocking on the semaphore forever?

The Semaphore#waiting is a list of Semaphore::FiberNode objects, not Task objects.

ioquatix commented 4 months ago

IIUC, what you are looking for is some kind of recursive read/write lock...

There are many different kinds of locks.

In general, a semaphore is a counter, and doesn't keep track of who has locked it (could be more than one task).

What you want is some kind of recursive lock, e.g. Ruby's Monitor: https://rubyapi.org/3.3/o/monitor

paddor commented 4 months ago

That sounds exactly like what I need. I did not know about recursive locks or Monitor. Thanks a lot!

ioquatix commented 4 months ago

Be careful designing programs that use such locks, they are extremely easy to end up with unexpected deadlocks.