Skip to content

Commit

Permalink
Fixed issue #150, by better supporting background/multiple `FlutterEn…
Browse files Browse the repository at this point in the history
…gine`s
  • Loading branch information
JaffaKetchup committed Apr 29, 2024
1 parent ae1f0f5 commit 480315c
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 31 deletions.
7 changes: 7 additions & 0 deletions lib/src/backend/impls/objectbox/backend/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:io';
import 'dart:isolate';

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
Expand Down Expand Up @@ -44,19 +45,25 @@ final class FMTCObjectBoxBackend implements FMTCBackend {
/// [the ObjectBox docs](https://docs.objectbox.io/getting-started) for
/// details.
///
/// [rootIsolateToken] should only be used in exceptional circumstances where
/// this backend is being initialised in a seperate isolate (or background)
/// thread.
///
/// Avoid using [useInMemoryDatabase] outside of testing purposes.
@override
Future<void> initialise({
String? rootDirectory,
int maxDatabaseSize = 10000000,
String? macosApplicationGroup,
RootIsolateToken? rootIsolateToken,
@visibleForTesting bool useInMemoryDatabase = false,
}) =>
FMTCObjectBoxBackendInternal._instance.initialise(
rootDirectory: rootDirectory,
maxDatabaseSize: maxDatabaseSize,
macosApplicationGroup: macosApplicationGroup,
useInMemoryDatabase: useInMemoryDatabase,
rootIsolateToken: rootIsolateToken,
);

/// {@macro fmtc.backend.uninitialise}
Expand Down
36 changes: 19 additions & 17 deletions lib/src/backend/impls/objectbox/backend/internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,19 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
required int maxDatabaseSize,
required String? macosApplicationGroup,
required bool useInMemoryDatabase,
required RootIsolateToken? rootIsolateToken,
}) async {
if (_sendPort != null) throw RootAlreadyInitialised();

// Obtain the `RootIsolateToken` to enable the worker isolate to use
// ObjectBox
rootIsolateToken ??= ServicesBinding.rootIsolateToken ??
(throw StateError(
'Unable to start FMTC in a background thread without access to a '
'`RootIsolateToken`',
));

// Construct the root directory path
if (useInMemoryDatabase) {
this.rootDirectory = Store.inMemoryPrefix + (rootDirectory ?? 'fmtc');
} else {
Expand All @@ -137,16 +147,11 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
_workerResOneShot[0] = Completer();
final workerInitialRes = _workerResOneShot[0]!
.future // Completed directly by handler below
.then<({ByteData? storeRef, Object? err, StackTrace? stackTrace})>(
.then<({Object err, StackTrace stackTrace})?>(
(res) {
_workerResOneShot.remove(0);
_sendPort = res!['sendPort'];

return (
storeRef: res['storeReference'] as ByteData,
err: null,
stackTrace: null,
);
return null;
},
onError: (err, stackTrace) {
_workerHandler.cancel();
Expand All @@ -156,7 +161,7 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
_workerResOneShot.clear();
_workerResStreamed.clear();

return (storeRef: null, err: err, stackTrace: stackTrace);
return (err: err, stackTrace: stackTrace);
},
);

Expand Down Expand Up @@ -209,22 +214,19 @@ class _ObjectBoxBackendImpl implements FMTCObjectBoxBackendInternal {
rootDirectory: this.rootDirectory,
maxDatabaseSize: maxDatabaseSize,
macosApplicationGroup: macosApplicationGroup,
rootIsolateToken: ServicesBinding.rootIsolateToken!,
rootIsolateToken: rootIsolateToken,
),
onExit: receivePort.sendPort,
debugName: '[FMTC] ObjectBox Backend Worker',
);

// Wait for initial response from isolate
final initResult = await workerInitialRes;

// Check whether initialisation was successful
if (initResult.storeRef case final storeRef?) {
// Check whether initialisation was successful after initial response
if (await workerInitialRes case (:final err, :final stackTrace)) {
Error.throwWithStackTrace(err, stackTrace);
} else {
FMTCBackendAccess.internal = this;
FMTCBackendAccessThreadSafe.internal =
_ObjectBoxBackendThreadSafeImpl._(storeReference: storeRef);
} else {
Error.throwWithStackTrace(initResult.err!, initResult.stackTrace!);
_ObjectBoxBackendThreadSafeImpl._(rootDirectory: this.rootDirectory);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ Future<void> _worker(
// Open database, kill self if failed
late final Store root;
try {
root = await openStore(
directory: input.rootDirectory,
maxDBSizeInKB: input.maxDatabaseSize, // Defaults to 10 GB
macosApplicationGroup: input.macosApplicationGroup,
);
// If already opened, attach existing instance
// This can occur when a background `FlutterEngine` is in use, keeping the
// database open
if (Store.isOpen(input.rootDirectory)) {
root = Store.attach(getObjectBoxModel(), input.rootDirectory);
} else {
root = await openStore(
directory: input.rootDirectory,
maxDBSizeInKB: input.maxDatabaseSize, // Defaults to 10 GB
macosApplicationGroup: input.macosApplicationGroup,
);
}

// If the database is new, create the root statistics object
final rootBox = root.box<ObjectBoxRoot>();
Expand All @@ -47,7 +54,7 @@ Future<void> _worker(
// Respond with comms channel for future cmds
sendRes(
id: 0,
data: {'sendPort': receivePort.sendPort, 'storeReference': root.reference},
data: {'sendPort': receivePort.sendPort},
);

//! UTIL METHODS !//
Expand All @@ -58,8 +65,6 @@ Future<void> _worker(
/// Should be run within a transaction.
///
/// Specified values may be negative.
///
/// Handles cases where there is no root statistics object yet.
void updateRootStatistics({int deltaLength = 0, int deltaSize = 0}) =>
root.box<ObjectBoxRoot>().put(
root.box<ObjectBoxRoot>().get(1)!
Expand Down Expand Up @@ -836,8 +841,7 @@ Future<void> _worker(
(numExportedTiles) {
if (numExportedTiles == 0) {
throw ArgumentError(
'must include at least one tile in any of the specified '
'stores',
'Specified stores must include at least one tile total',
'storeNames',
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ part of '../backend.dart';

class _ObjectBoxBackendThreadSafeImpl implements FMTCBackendInternalThreadSafe {
_ObjectBoxBackendThreadSafeImpl._({
required this.storeReference,
required this.rootDirectory,
});

@override
String get friendlyIdentifier => 'ObjectBox';

final ByteData storeReference;
final String rootDirectory;
Store get expectInitialisedRoot => _root ?? (throw RootUnavailable());
Store? _root;

@override
void initialise() {
if (_root != null) throw RootAlreadyInitialised();
_root = Store.fromReference(getObjectBoxModel(), storeReference);
_root = Store.attach(getObjectBoxModel(), rootDirectory);
}

@override
Expand All @@ -29,7 +29,7 @@ class _ObjectBoxBackendThreadSafeImpl implements FMTCBackendInternalThreadSafe {

@override
_ObjectBoxBackendThreadSafeImpl duplicate() =>
_ObjectBoxBackendThreadSafeImpl._(storeReference: storeReference);
_ObjectBoxBackendThreadSafeImpl._(rootDirectory: rootDirectory);

@override
Future<ObjectBoxTile?> readTile({
Expand Down

0 comments on commit 480315c

Please sign in to comment.