// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

/// Mutual exclusion.
///
/// Usage:
///
///     var m = new Mutex();
///
///     await m.acquire();
///     try {
///       // critical section
///     }
///     finally {
///       m.release();
///     }
class Mutex {
  Completer<Null> _lock;

  /// Acquire a lock.
  ///
  /// Returns a [Future] that will be completed when the lock has been acquired.
  Future<Null> acquire() async {
    // TODO(brianwilkerson) Determine whether this await is necessary.
    await null;
    while (_lock != null) {
      await _lock.future;
    }
    _lock = new Completer<Null>();
  }

  /// Run the given [criticalSection] with acquired mutex.
  Future<T> guard<T>(Future<T> criticalSection()) async {
    // TODO(brianwilkerson) Determine whether this await is necessary.
    await null;
    await acquire();
    try {
      return await criticalSection();
    } finally {
      release();
    }
  }

  /// Release a lock.
  ///
  /// Release a lock that has been acquired.
  void release() {
    if (_lock == null) {
      throw new StateError('No lock to release.');
    }
    _lock.complete();
    _lock = null;
  }
}
