| // Copyright (c) 2016, 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. |
| |
| // This test works by spawning a new process running |
| // file_blocking_lock_script.dart, trading the file lock back and forth, |
| // writing bytes 1 ... 25 in order to the file. There are checks to ensure |
| // that the bytes are written in order, that one process doesn't write all the |
| // bytes and that a non-blocking lock fails such that a blocking lock must |
| // be taken, which succeeds. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import "package:async_helper/async_helper.dart"; |
| import "package:expect/expect.dart"; |
| import "package:path/path.dart"; |
| |
| // Check whether the file is locked or not. |
| runPeer(String path, int len, FileLock mode) { |
| var script = Platform.script.resolve( |
| 'file_blocking_lock_script.dart').toFilePath(); |
| var arguments = [] |
| ..addAll(Platform.executableArguments) |
| ..add(script) |
| ..add(path) |
| ..add(len.toString()); |
| return Process.start(Platform.executable, arguments).then((process) { |
| process.stdout |
| .transform(UTF8.decoder) |
| .listen((data) { print(data); }); |
| process.stderr |
| .transform(UTF8.decoder) |
| .listen((data) { print(data); }); |
| return process; |
| }); |
| } |
| |
| testLockWholeFile() async { |
| const int length = 25; |
| Directory directory = await Directory.systemTemp.createTemp('dart_file_lock'); |
| File file = new File(join(directory.path, "file")); |
| await file.writeAsBytes(new List.filled(length, 0)); |
| var raf = await file.open(mode: APPEND); |
| await raf.setPosition(0); |
| await raf.lock(FileLock.BLOCKING_EXCLUSIVE, 0, length); |
| Process peer = await runPeer(file.path, length, FileLock.BLOCKING_EXCLUSIVE); |
| |
| int nextToWrite = 1; |
| int at = 0; |
| List iWrote = new List.filled(length, 0); |
| bool nonBlockingFailed = false; |
| while (nextToWrite <= length) { |
| int p = await raf.position(); |
| await raf.writeByte(nextToWrite); |
| await raf.flush(); |
| // Record which bytes this process wrote so that we can check that the |
| // other process was able to take the lock and write some bytes. |
| iWrote[nextToWrite-1] = nextToWrite; |
| nextToWrite++; |
| // Let the other process get the lock at least once by spinning until the |
| // non-blocking lock fails. |
| while (!nonBlockingFailed) { |
| await raf.unlock(0, length); |
| try { |
| await raf.lock(FileLock.EXCLUSIVE, 0, length); |
| } catch(e) { |
| // Check that at some point the non-blocking lock fails. |
| nonBlockingFailed = true; |
| await raf.lock(FileLock.BLOCKING_EXCLUSIVE, 0, length); |
| } |
| } |
| while (true) { |
| p = await raf.position(); |
| at = await raf.readByte(); |
| if (at == 0 || at == -1) break; |
| nextToWrite++; |
| } |
| await raf.setPosition(p); |
| } |
| |
| await raf.setPosition(0); |
| for (int i = 1; i <= length; i++) { |
| Expect.equals(i, await raf.readByte()); |
| } |
| await raf.unlock(0, length); |
| |
| bool wroteAll = true; |
| for (int i = 0; i < length; i++) { |
| // If there's a 0 entry, this process didn't write all bytes. |
| wroteAll = wroteAll && (iWrote[i] == 0); |
| } |
| Expect.equals(false, wroteAll); |
| |
| Expect.equals(true, nonBlockingFailed); |
| |
| await peer.exitCode.then((v) async { |
| Expect.equals(0, v); |
| await raf.close(); |
| await directory.delete(recursive: true); |
| }); |
| } |
| |
| main() async { |
| asyncStart(); |
| await testLockWholeFile(); |
| asyncEnd(); |
| } |