@@ -382,6 +382,39 @@ class Store {
382382 return _runInTransaction (mode, (tx) => fn ());
383383 }
384384
385+ // Isolate entry point must be static or top-level.
386+ static void _callFunctionWithStoreInIsolate <P , R >(IsoPass <P , R > isoPass) {
387+ final store = Store .attach (isoPass.model, isoPass.dbDirectoryPath,
388+ queriesCaseSensitiveDefault: isoPass.queriesCaseSensitiveDefault);
389+ final result = isoPass.runFn (store);
390+ store.close ();
391+ // Note: maybe replace with Isolate.exit once min Dart SDK 2.15.
392+ isoPass.resultPort? .send (result);
393+ }
394+
395+ /// Spawns an isolate, runs [callback] in that isolate passing it [param] with
396+ /// its own Store and returns the result of callback.
397+ ///
398+ /// Instances of [callback] must be top-level functions or static methods
399+ /// of classes, not closures or instance methods of objects.
400+ Future <R > runIsolated <P , R >(
401+ TxMode mode, R Function (Store , P ) callback, P param) async {
402+ final resultPort = ReceivePort ();
403+ // Await isolate spawn to avoid waiting forever if it fails to spawn.
404+ await Isolate .spawn (
405+ _callFunctionWithStoreInIsolate,
406+ IsoPass (_defs, directoryPath, _queriesCaseSensitiveDefault,
407+ resultPort.sendPort, callback, param));
408+ // Use Completer to return result so type is not lost.
409+ final result = Completer <R >();
410+ resultPort.listen ((dynamic message) {
411+ result.complete (message as R );
412+ });
413+ await result.future;
414+ resultPort.close ();
415+ return result.future;
416+ }
417+
385418 /// Internal only - bypasses the main checks for async functions, you may
386419 /// only pass synchronous callbacks!
387420 R _runInTransaction <R >(TxMode mode, R Function (Transaction ) fn) {
@@ -495,3 +528,40 @@ final _openStoreDirectories = HashSet<String>();
495528/// Otherwise, it's we can distinguish at runtime whether a function is async.
496529final _nullSafetyEnabled = _nullReturningFn is ! Future Function ();
497530final _nullReturningFn = () => null ;
531+
532+ /// Captures everything required to create a "copy" of a store in an isolate
533+ /// and run user code.
534+ @immutable
535+ class IsoPass <P , R > {
536+ ///
537+ final ModelDefinition model;
538+
539+ /// Used to attach to store in separate isolate
540+ /// (may be replaced in the future).
541+ final String dbDirectoryPath;
542+
543+ /// Config
544+ final bool queriesCaseSensitiveDefault;
545+
546+ /// Non-void functions can use this port to receive the result
547+ final SendPort ? resultPort;
548+
549+ /// Parameter passed to the function
550+ final P param;
551+
552+ /// Function to be called in isolate
553+ final R Function (Store , P ) fn;
554+
555+ /// creates everything that needs to be passed to the isolate.
556+ const IsoPass (
557+ this .model,
558+ this .dbDirectoryPath,
559+ // ignore: avoid_positional_boolean_parameters
560+ this .queriesCaseSensitiveDefault,
561+ this .resultPort,
562+ this .fn,
563+ this .param);
564+
565+ /// Called inside this class so types are not lost (dynamic instead of P and R).
566+ R runFn (Store store) => fn (store, param);
567+ }
0 commit comments