Skip to content

Commit

Permalink
Remove unfair primitives, add error flag to BoundedSemaphore
Browse files Browse the repository at this point in the history
  • Loading branch information
Serious-senpai committed May 18, 2024
1 parent c92c731 commit 45ca7ab
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 76 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-docs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Documentation Build
name: Documentation build

on: push

Expand All @@ -8,7 +8,7 @@ permissions:

jobs:
test:
name: Build docs test
name: Build docs
runs-on: ubuntu-latest

steps:
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Package Test
name: Package test

on: push

Expand All @@ -8,11 +8,11 @@ permissions:

jobs:
test:
name: Run package tests
name: Run tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
os: [macos-latest, ubuntu-latest, windows-latest]

steps:

Expand Down
11 changes: 0 additions & 11 deletions lib/src/lock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,3 @@ class Lock extends _Lock {
@override
_FutureWaiter _getNextWaiter() => _waiters.removeFirst();
}

/// An [UnfairLock] object is identical to a [Lock] excepts that it wakes up the
/// last future that called [acquire] instead of the first (i.e. waiting futures are
/// put in a LIFO queue).
class UnfairLock extends _Lock {
/// Create a new [UnfairLock] object.
UnfairLock();

@override
_FutureWaiter _getNextWaiter() => _waiters.removeLast();
}
31 changes: 15 additions & 16 deletions lib/src/semaphore.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,32 +106,31 @@ class Semaphore extends _Semaphore {
/// a permit.
class BoundedSemaphore extends Semaphore {
final int _initial;
final bool _error = true;

/// Construct a new [BoundedSemaphore] object with the initial internal counter set to [value].
/// This provided [value] is also the upper bound of the internal counter.
BoundedSemaphore(int value)
///
/// If [error] is set to `true`, a [BoundedSemaphoreLimitException] will be thrown when the
/// internal counter exceeds the initial value. If set to `false`, this exception will be
/// suppressed.
BoundedSemaphore(int value, {bool error = true})
: _initial = value,
super(value);

/// Release a permit from the semaphore. If the value of the semaphore is greater than the
/// initial value, a [BoundedSemaphoreLimitException] is thrown.
/// Release a permit from the semaphore. If the internal value of the semaphore is greater than the
/// initial value, a [BoundedSemaphoreLimitException] may be thrown.
///
/// Whether an instance of [BoundedSemaphoreLimitException] is thrown depends on the value of the
/// `error` parameter in the constructor.
@override
void release() {
super.release();
if (_value > _initial) {
throw BoundedSemaphoreLimitException();
_value = _initial;
if (_error) {
throw BoundedSemaphoreLimitException();
}
}
}
}

/// A [UnfairSemaphore] is a synchronization primitive that limits the number of concurrent
/// accesses to a shared resource. It is similar to a [Semaphore], but it wakes up the last
/// future that called [acquire] instead of the first (i.e. waiting futures are put in a
/// LIFO queue).
class UnfairSemaphore extends _Semaphore {
/// Create a new [UnfairSemaphore] object with the initial internal counter set to [value].
UnfairSemaphore(int value) : super(value);

@override
_FutureWaiter _getNextWaiter() => _waiters.removeLast();
}
4 changes: 2 additions & 2 deletions lib/src/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ class AsyncLocksException implements Exception {}
/// Exception thrown to futures cancelled by [Event.cancelAll]
class EventCancelledException extends AsyncLocksException {}

/// Exception thrown to futures cancelled by [Lock.cancelAll] or [UnfairLock.cancelAll]
/// Exception thrown to futures cancelled by [Lock.cancelAll]
class LockAcquireFailureException extends AsyncLocksException {}

/// Exception thrown to futures cancelled by [BoundedSemaphore.cancelAll], [Semaphore.cancelAll] or [UnfairSemaphore.cancelAll]
/// Exception thrown to futures cancelled by [BoundedSemaphore.cancelAll] or [Semaphore.cancelAll]
class SemaphoreAcquireFailureException extends AsyncLocksException {}

/// Exception that may be thrown in [BoundedSemaphore.release]
Expand Down
80 changes: 39 additions & 41 deletions test/lock_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,43 @@ import "utils.dart";
const futures_count = 5;

void main() {
var locks = [Lock(), UnfairLock()];

for (var lock in locks) {
test(
"Testing control flow: $lock",
() async {
var futures = <Future<void>>[];
for (int i = 0; i < futures_count; i++) {
futures.add(lock.run(() => Future.delayed(waiting)));
}
futures.add(Future.delayed(short_waiting));

var timer = Stopwatch();
timer.start();
await Future.wait(futures);
timer.stop();

expect(lock.locked, isFalse);
expect(timer.elapsedMilliseconds, approximates(1000 * futures_count, 100));
print("Elapsed time: ${timer.elapsedMilliseconds} ms");
},
);

test(
"Test lock acquire cancellation: $lock",
() async {
var futures = <Future<void>>[];
for (int i = 0; i < futures_count; i++) {
futures.add(lock.run(() => Future.delayed(waiting)));
}

expect(
() async {
lock.cancelAll();
await Future.wait(futures);
},
throwsException,
);
},
);
}
final lock = Lock();

test(
"Testing control flow: $lock",
() async {
var futures = <Future<void>>[];
for (int i = 0; i < futures_count; i++) {
futures.add(lock.run(() => Future.delayed(waiting)));
}
futures.add(Future.delayed(short_waiting));

var timer = Stopwatch();
timer.start();
await Future.wait(futures);
timer.stop();

expect(lock.locked, isFalse);
expect(timer.elapsedMilliseconds, approximates(1000 * futures_count, 100));
print("Elapsed time: ${timer.elapsedMilliseconds} ms");
},
);

test(
"Test lock acquire cancellation: $lock",
() async {
var futures = <Future<void>>[];
for (int i = 0; i < futures_count; i++) {
futures.add(lock.run(() => Future.delayed(waiting)));
}

expect(
() async {
lock.cancelAll();
await Future.wait(futures);
},
throwsException,
);
},
);
}
2 changes: 1 addition & 1 deletion test/semaphore_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const futures_count = 20;
const concurrency = 4;

void main() {
var semaphores = [Semaphore(concurrency), BoundedSemaphore(concurrency), UnfairSemaphore(concurrency)];
final semaphores = [Semaphore(concurrency), BoundedSemaphore(concurrency)];
for (var semaphore in semaphores) {
test(
"Testing control flow: $semaphore",
Expand Down

0 comments on commit 45ca7ab

Please sign in to comment.