Maintain thread safety while preventing deadlock from possibly synchronous callback

Viewed 174

I have an API like the following, where Baz is the worker implementation. This Bar needs to be thread-safe, and this gets tricky when interacting with Baz's callbacks.

The current baz instance needs to be referenced in the callback (which can be called on a worker thread or synchronously). The comments should show the problem:

final class Bar {
  final Lock lock = new ReentrantLock();
  Baz baz; // Guarded by lock.

  void run() { // Called by any thread.
    lock.lock();
    if (baz.isRunning()) {
      lock.unlock();
      return;
    }
    baz = new Baz();
    // If it unlocks here, the next line may execute on the wrong Baz.
    // If it doesn't unlock here, there will be a deadlock when done() is called synchronously.
    // lock.unlock();
    baz.run(new Baz.Callback() { // May be called synchronously or by Baz worker thread.
      @Override
      public void done() {
        lock.lock();
        baz = new Baz();
        lock.unlock();
      }
    });
  }
}

Is there a good way to make this work correctly while also not causing a deadlock?

Edit: more succinctly:

final class Foo {
  final Lock lock = new ReentrantLock();

  void run() {
    lock.lock();
    worker.enqueue(new Callback() {
      @Override void complete() {
        lock.lock(); // Could cause deadlock.
      }
    });
    lock.unlock();
  }
}
1 Answers
Related