hoylen/dart-mutex

Can not acquire write lock.

Closed this issue · 2 comments

This test below will timed out.

import 'package:mutex/mutex.dart';
import 'package:test/test.dart';

import 'dart:async';

void main() {
  group('Exclusive lock tests', () {
    test('test5', () async {
      var mutex = ReadWriteMutex();
      unawaited(sharedLoop1(mutex));
      await mySleep(10);
      unawaited(sharedLoop1(mutex));
      await mySleep(10);
      unawaited(sharedLoop1(mutex));
      await mySleep(10);
      unawaited(sharedLoop1(mutex));
      await mySleep(10);
      unawaited(sharedLoop1(mutex));
      await mySleep(1000);
      await mutex.acquireWrite();
      mutex.release();
      expect('a', 'a');
    });
  });
}

Future<void> sharedLoop1(ReadWriteMutex mutex) async {
  while (true) {
    await mutex.protectRead(() async {
      await mySleep(50);
    });
  }
}

Future<void> mySleep([int ms = 1000]) async {
  await Future<void>.delayed(Duration(milliseconds: ms));
}

Thank you very much for reporting this bug.

It has now been fixed in version 3.0.1.

The new requests for read operations were incorrectly being allowed to acquire a mutex, even though there was a write request waiting. While it is permitted to have multiple read mutexes simultaneously active, there can be at most one write mutex active. The request for a write mutex was never allowed to be acquired, because there was always one or more read mutex active. The non-stopping sequence of read mutexes ensures there is never a time when there are zero mutexes active, so the write operation is never allowed to acquire the mutex.

This can be the correct behaviour for some implementation of a mutex. In some situations, it is desirable to let quick read operations be processed in parallel with already running read operations, before yielding to "blocking" write operations. Obviously, those implementations have to be used carefully, since they risk the "starvation" behaviour that you discovered.

But it was the incorrect behaviour for this particular implementation, which is designed to acquire mutexes in the requested order (i.e. first-in first-out).

I have fixed the bug and added a modified version of your code as another unit test.

LGFM