Skip to content

Commit

Permalink
Merge branch 'main' into interface-abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
Craftplacer committed Nov 10, 2024
2 parents 0968085 + 0e5c7b9 commit 40d1495
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 41 deletions.
4 changes: 3 additions & 1 deletion packages/sane/example/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'dart:typed_data';

import 'package:logging/logging.dart';
import 'package:sane/sane.dart';
import 'package:sane/src/impl/sane_dev.dart';
import 'package:sane/src/impl/sane_mock.dart';
import 'package:sane/src/impl/sane_native.dart';

void main(List<String> args) async {
Expand Down Expand Up @@ -79,4 +79,6 @@ void main(List<String> args) async {
);
final rawPixelData = mergeUint8Lists(rawPixelDataList);
file.writeAsBytesSync(rawPixelData, mode: FileMode.append);

await sane.dispose();
}
14 changes: 14 additions & 0 deletions packages/sane/lib/src/extensions.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:sane/src/bindings.g.dart';
import 'package:sane/src/exceptions.dart';
Expand All @@ -12,3 +13,16 @@ extension SaneStatusExtension on SANE_Status {
}
}
}

@internal
extension LoggerExtension on Logger {
void redirect(LogRecord record) {
log(
record.level,
record.message,
record.error,
record.stackTrace,
record.zone,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'dart:typed_data';
import 'package:logging/logging.dart';
import 'package:sane/sane.dart';

final _logger = Logger('sane.dev');
final _logger = Logger('sane.mock');

class MockSane implements Sane {
@override
Expand Down
15 changes: 8 additions & 7 deletions packages/sane/lib/src/impl/sane_native.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class NativeSane implements Sane {
SaneIsolate? _isolate;

Future<SaneIsolate> _getIsolate() async {
if (_isolate?.exited == true || _disposed) throw SaneDisposedError();
if (_disposed) throw SaneDisposedError();
return _isolate ??= await SaneIsolate.spawn(backingSane);
}

Expand All @@ -81,17 +81,18 @@ class NativeSane implements Sane {
Future<void> dispose({bool force = false}) async {
final isolate = _isolate;

if (force) {
isolate?.kill();
return;
}

if (_disposed) return;

_disposed = true;
_instance = null;

await isolate?.sendMessage(ExitMessage());
if (isolate == null) return;

if (force) {
isolate.kill();
} else {
await isolate.sendMessage(ExitMessage());
}

_isolate = null;
}
Expand Down
12 changes: 8 additions & 4 deletions packages/sane/lib/src/impl/sane_sync.dart
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ class SyncSaneDevice implements SaneDevice, ffi.Finalizable {
SaneOptionResult<bool> controlBoolOption(
int index,
SaneAction action,
bool? value,) {
bool? value,
) {
return _controlOption<bool>(
index: index,
action: action,
Expand All @@ -405,7 +406,8 @@ class SyncSaneDevice implements SaneDevice, ffi.Finalizable {
SaneOptionResult<int> controlIntOption(
int index,
SaneAction action,
int? value,) {
int? value,
) {
return _controlOption<int>(
index: index,
action: action,
Expand All @@ -417,7 +419,8 @@ class SyncSaneDevice implements SaneDevice, ffi.Finalizable {
SaneOptionResult<double> controlFixedOption(
int index,
SaneAction action,
double? value,) {
double? value,
) {
return _controlOption<double>(
index: index,
action: action,
Expand All @@ -429,7 +432,8 @@ class SyncSaneDevice implements SaneDevice, ffi.Finalizable {
SaneOptionResult<String> controlStringOption(
int index,
SaneAction action,
String? value,) {
String? value,
) {
return _controlOption<String>(
index: index,
action: action,
Expand Down
58 changes: 37 additions & 21 deletions packages/sane/lib/src/isolate.dart
Original file line number Diff line number Diff line change
@@ -1,49 +1,53 @@
import 'dart:async';
import 'dart:isolate';

import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:sane/src/exceptions.dart';
import 'package:sane/src/extensions.dart';
import 'package:sane/src/isolate_messages/exception.dart';
import 'package:sane/src/isolate_messages/exit.dart';
import 'package:sane/src/isolate_messages/interface.dart';
import 'package:sane/src/sane.dart';

final _logger = Logger('sane.isolate');

@internal
class SaneIsolate {
SaneIsolate._(
this._isolate,
this._sendPort,
this._exitReceivePort,
) : _exited = false {
_exitReceivePort.listen((message) {
assert(message == null);
_exited = true;
});
}
this._sendPort,);

final Isolate _isolate;
final SendPort _sendPort;
final ReceivePort _exitReceivePort;

bool _exited;

bool get exited => _exited;

static Future<SaneIsolate> spawn(Sane sane) async {
final receivePort = ReceivePort();
final exitReceivePort = ReceivePort();

final isolate = await Isolate.spawn(
_entryPoint,
(receivePort.sendPort, sane),
onExit: exitReceivePort.sendPort,
onExit: receivePort.sendPort,
);

final sendPort = await receivePort.first as SendPort;
return SaneIsolate._(isolate, sendPort, exitReceivePort);
}
final sendPortCompleter = Completer<SendPort>();
receivePort.listen((message) {
switch (message) {
case SendPort():
sendPortCompleter.complete(message);
case LogRecord():
_logger.redirect(message);
case null:
receivePort.close();
}
});

void kill() {
_isolate.kill(priority: Isolate.immediate);
final sendPort = await sendPortCompleter.future;
return SaneIsolate._(isolate, sendPort);
}

void kill() => _isolate.kill(priority: Isolate.immediate);

Future<T> sendMessage<T extends IsolateResponse>(
IsolateMessage<T> message,
) async {
Expand Down Expand Up @@ -75,10 +79,16 @@ typedef _EntryPointArgs = (SendPort sendPort, Sane sane);
void _entryPoint(_EntryPointArgs args) {
final (sendPort, sane) = args;

Logger.root.level = Level.ALL;
Logger.root.onRecord.forEach(sendPort.send);

final receivePort = ReceivePort();
sendPort.send(receivePort.sendPort);

receivePort.cast<_IsolateMessageEnvelope>().listen((envelope) async {
late StreamSubscription<_IsolateMessageEnvelope> subscription;

subscription =
receivePort.cast<_IsolateMessageEnvelope>().listen((envelope) async {
final _IsolateMessageEnvelope(:message, :replyPort) = envelope;

IsolateResponse response;
Expand All @@ -93,6 +103,10 @@ void _entryPoint(_EntryPointArgs args) {
}

replyPort.send(response);

if (message is ExitMessage) {
await subscription.cancel();
}
});
}

Expand All @@ -108,8 +122,10 @@ class _IsolateMessageEnvelope {

late Map<String, SaneDevice> _devices;

@internal
SaneDevice getDevice(String name) => _devices[name]!;

@internal
void setDevices(Iterable<SaneDevice> devices) {
_devices = {
for (final device in devices) device.name: device,
Expand Down
9 changes: 5 additions & 4 deletions packages/sane/lib/src/isolate_messages/get_devices.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:sane/src/isolate.dart';
import 'package:sane/src/isolate_messages/interface.dart';
import 'package:sane/src/sane.dart';

Expand All @@ -8,14 +9,14 @@ class GetDevicesMessage implements IsolateMessage<GetDevicesResponse> {

@override
Future<GetDevicesResponse> handle(Sane sane) async {
return GetDevicesResponse(
devices: await sane.getDevices(localOnly: localOnly),
);
final devices = await sane.getDevices(localOnly: localOnly);
setDevices(devices);
return GetDevicesResponse(devices);
}
}

class GetDevicesResponse implements IsolateResponse {
GetDevicesResponse({required this.devices});
GetDevicesResponse(this.devices);

final List<SaneDevice> devices;
}
4 changes: 2 additions & 2 deletions packages/sane/lib/src/sane.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:typed_data';

import 'package:meta/meta.dart';
import 'package:sane/sane.dart';
import 'package:sane/src/impl/sane_dev.dart';
import 'package:sane/src/impl/sane_mock.dart';
import 'package:sane/src/impl/sane_native.dart';
import 'package:sane/src/impl/sane_sync.dart';

Expand All @@ -15,7 +15,7 @@ abstract interface class Sane {
/// See also:
///
/// - [Sane.sync]
factory Sane() => NativeSane();
factory Sane([Sane? backingSane]) => NativeSane(backingSane);

/// Instantiates a new synchronous SANE instance.
factory Sane.sync() => SyncSane();
Expand Down
11 changes: 10 additions & 1 deletion packages/sane/test/sane_singleton_test.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import 'package:logging/logging.dart';
import 'package:sane/sane.dart';
import 'package:test/test.dart';

void main() {
late Sane sane;

setUp(() {
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
// ignore: avoid_print
print('${record.level.name}: ${record.time}: ${record.message}');
});
});

test('can instantiate', () {
sane = Sane();
});
Expand All @@ -25,7 +34,7 @@ void main() {
});

test('can reinstiate with new instance', () {
final newSane = Sane();
final newSane = Sane(Sane.mock());
expect(sane, isNot(newSane));
sane = newSane;
});
Expand Down

0 comments on commit 40d1495

Please sign in to comment.