Skip to content

Commit

Permalink
Removed support for Dart 1.x.
Browse files Browse the repository at this point in the history
  • Loading branch information
hoylen committed Dec 13, 2020
1 parent a6ee875 commit 3e0be8d
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 110 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 2.0.0-nullsafety.0

- Pre-release version: updated library to null safety (Non-nullable by default).
- Removed support for Dart 1.x.

## 1.1.0

Expand Down
53 changes: 31 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,38 @@ A mutex guarantees at most only one lock can exist at any one time.
If the lock has already been acquired, attempts to acquire another
lock will be blocked until the lock has been released.

import 'package:mutex/mutex.dart';
```dart
import 'package:mutex/mutex.dart';
m = Mutex();
...
final m = Mutex();
```

Acquiring the lock before running the critical section of code,
and then releasing the lock.

await m.acquire();
// No other lock can be acquired until the lock is released
```dart
await m.acquire();
// No other lock can be acquired until the lock is released
try {
// critical section with asynchronous code
await ...
}
finally {
m.release();
}
try {
// critical section with asynchronous code
await ...
} finally {
m.release();
}
```

The following code uses the _protect_ convenience method to do the
same thing as the above code. Use the convenence method whenever
possible, since it ensures the lock will always be released.

await m.protect(() async {
// critical section
});
```dart
await m.protect(() async {
// critical section
});
```

## Read-write mutex

Expand All @@ -82,20 +89,23 @@ blocked. If there is a _write lock_, attempts to acquire any lock
A read-write mutex can also be described as a single-writer mutex,
multiple-reader mutex, or a reentrant lock.

import 'package:mutex/mutex.dart';
```dart
import 'package:mutex/mutex.dart';
m = MutexReadWrite();

Acquiring a write lock:
...
final m = MutexReadWrite();
```

Acquiring a write lock:

await m.acquireWrite();
// No other locks (read or write) can be acquired until released

try {
// critical write section with asynchronous code
await ...
}
finally {
} finally {
m.release();
}

Expand All @@ -108,8 +118,7 @@ Acquiring a read lock:
try {
// critical read section with asynchronous code
await ...
}
finally {
} finally {
m.release();
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/mutex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ part of mutex;
///
/// Usage:
///
/// m = new Mutex();
/// m = Mutex();
///
/// await m.protect(() {
/// // critical section
Expand All @@ -19,7 +19,7 @@ part of mutex;
/// have been used. Failure to release the lock will prevent other code for
/// ever acquiring the lock.
///
/// m = new Mutex();
/// m = Mutex();
///
/// await m.acquire();
/// try {
Expand Down Expand Up @@ -50,7 +50,7 @@ class Mutex {
/// Release a lock.
///
/// Release a lock that has been acquired.
///
void release() => _rwMutex.release();

/// Convenience method for protecting a function with a lock.
Expand Down
71 changes: 50 additions & 21 deletions lib/src/read_write_mutex.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
part of mutex;

/// Represents a request for a lock.
//################################################################
/// Internal representation of a request for a lock.
///
/// This is instantiated for each acquire and, if necessary, it is added
/// to the waiting queue.
///
class _ReadWriteMutexRequest {
/// Internal constructor.
///
/// The [isRead] indicates if this is a read lock (true) or a write lock (false).
/// The [isRead] indicates if this is a request for a read lock (true) or a
/// request for a write lock (false).
_ReadWriteMutexRequest({required this.isRead});

Expand All @@ -18,15 +20,12 @@ class _ReadWriteMutexRequest {

/// The job's completer.
///
/// This [Completer] will complete when the job has acquired a lock.
///
/// This should be defined as Completer<void>, but void is not supported in
/// Dart 1 (it only appeared in Dart 2). A type must be defined, otherwise
/// the Dart 2 dartanalyzer complains.
/// This [Completer] will complete when the job has acquired the lock.
final Completer<int> completer = Completer<int>();
final Completer<void> completer = Completer<void>();
}

//################################################################
/// Mutual exclusion that supports read and write locks.
///
/// Multiple read locks can be simultaneously acquired, but at most only
Expand All @@ -40,7 +39,7 @@ class _ReadWriteMutexRequest {
///
/// Create the mutex:
///
/// m = new ReadWriteMutex();
/// m = ReadWriteMutex();
///
/// Code protected by a write lock:
///
Expand All @@ -64,16 +63,15 @@ class _ReadWriteMutexRequest {
///
/// Create the mutex:
///
/// m = new ReadWriteMutex();
/// m = ReadWriteMutex();
///
/// Some code can acquire a write lock:
///
/// await m.acquireWrite();
/// try {
/// // critical write section
/// assert(m.isWriteLocked);
/// }
/// finally {
/// } finally {
/// m.release();
/// }
///
Expand All @@ -83,8 +81,7 @@ class _ReadWriteMutexRequest {
/// try {
/// // critical read section
/// assert(m.isReadLocked);
/// }
/// finally {
/// } finally {
/// m.release();
/// }
///
Expand All @@ -94,10 +91,20 @@ class _ReadWriteMutexRequest {
/// request issue, if there is a need for another scheduling algorithm.
class ReadWriteMutex {
//================================================================
// Members

/// List of requests waiting for a lock on this mutex.
final _waiting = <_ReadWriteMutexRequest>[];

/// State of the mutex
int _state = 0; // -1 = write lock, +ve = number of read locks; 0 = no lock

//================================================================
// Methods

/// Indicates if a lock (read or write) has been acquired and not released.
bool get isLocked => (_state != 0);

Expand All @@ -111,28 +118,41 @@ class ReadWriteMutex {
///
/// Returns a future that will be completed when the lock has been acquired.
///
/// A read lock can not be acquired when there is a write lock on the mutex.
/// But it can be acquired if there are other read locks.
///
/// Consider using the convenience method [protectRead], otherwise the caller
/// is responsible for making sure the lock is released after it is no longer
/// needed. Failure to release the lock means no other code can acquire a
/// write lock.
Future acquireRead() => _acquire(true);
Future acquireRead() => _acquire(isRead: true);

/// Acquire a write lock
///
/// Returns a future that will be completed when the lock has been acquired.
///
/// A write lock can only be acquired when there are no other locks (neither
/// read locks nor write locks) on the mutex.
///
/// Consider using the convenience method [protectWrite], otherwise the caller
/// is responsible for making sure the lock is released after it is no longer
/// needed. Failure to release the lock means no other code can acquire the
/// lock (neither a read lock or a write lock).
Future acquireWrite() => _acquire(false);
Future acquireWrite() => _acquire(isRead: false);

/// Release a lock.
///
/// Release a lock that has been acquired.
/// Release the lock that was previously acquired.
///
/// When the lock is released, locks waiting to be acquired can be acquired
/// depending on the type of lock waiting and if other locks have been
/// acquired.
///
/// A [StateError] is thrown if the mutex does not currently have a lock on
/// it.
void release() {
if (_state == -1) {
// Write lock released
Expand All @@ -143,7 +163,7 @@ class ReadWriteMutex {
} else if (_state == 0) {
throw StateError('no lock to release');
} else {
assert(false);
assert(false, 'invalid state');
}

// Let all jobs that can now acquire a lock do so.
Expand Down Expand Up @@ -196,7 +216,12 @@ class ReadWriteMutex {

/// Internal acquire method.
///
Future _acquire(bool isRead) {
/// Used to acquire a read lock (when [isRead] is true) or a write lock
/// (when [isRead] is false).
///
/// Returns a Future that completes when the lock has been acquired.
Future<void> _acquire({required bool isRead}) {
final newJob = _ReadWriteMutexRequest(isRead: isRead);
if (!_jobAcquired(newJob)) {
_waiting.add(newJob);
Expand All @@ -209,12 +234,16 @@ class ReadWriteMutex {
/// If it can acquire the lock, the job's completer is completed, the
/// state updated, and true is returned. If not, false is returned.
///
/// A job for a read lock can only be acquired if there are no other locks
/// or there are read lock(s). A job for a write lock can only be acquired
/// if there are no other locks.
bool _jobAcquired(_ReadWriteMutexRequest job) {
assert(-1 <= _state);
if (_state == 0 || (0 < _state && job.isRead)) {
// Can acquire
_state = (job.isRead) ? (_state + 1) : -1;
job.completer.complete(0); // dummy value
job.completer.complete();
return true;
} else {
return false;
Expand Down
5 changes: 1 addition & 4 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,5 @@ homepage: https://github.com/hoylen/dart-mutex
environment:
sdk: '>=2.12.0-0 <3.0.0'

#dependencies:
# lib_name: any

dev_dependencies:
test: '>=0.12.0 <2.0.0'
test: ^1.16.0-nullsafety.13
Loading

0 comments on commit 3e0be8d

Please sign in to comment.