Send serve messages in wasm setup

This commit is contained in:
Simon Binder 2023-05-29 17:56:15 +02:00
parent 54b1b707a9
commit 0060e0ea44
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
4 changed files with 93 additions and 15 deletions

View File

@ -11,15 +11,21 @@ import 'dart:async';
import 'dart:html';
import 'package:async/async.dart';
import 'package:drift/remote.dart';
import 'package:drift/wasm.dart';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
import 'package:sqlite3/wasm.dart';
import 'channel.dart';
import 'wasm_setup/protocol.dart';
/// Whether the `crossOriginIsolated` JavaScript property is true in the current
/// context.
@JS()
external bool get crossOriginIsolated;
/// Whether shared workers can be constructed in the current context.
bool get supportsSharedWorkers => hasProperty(globalThis, 'SharedWorker');
Future<WasmDatabaseResult> openWasmDatabase({
@ -27,6 +33,24 @@ Future<WasmDatabaseResult> openWasmDatabase({
required Uri driftWorkerUri,
required String databaseName,
}) async {
final missingFeatures = <MissingBrowserFeature>{};
Future<WasmDatabaseResult> connect(WasmStorageImplementation impl,
void Function(WasmInitializationMessage) send) async {
final channel = MessageChannel();
final local = channel.port1.channel();
final message = ServeDriftDatabase(
sqlite3WasmUri: sqlite3WasmUri,
port: channel.port2,
storage: impl,
databaseName: databaseName,
);
send(message);
final connection = await connectToRemoteAndInitialize(local);
return WasmDatabaseResult(connection, impl, missingFeatures);
}
// First, let's see if we can spawn dedicated workers in shared workers, which
// would enable us to efficiently share a OPFS database.
if (supportsSharedWorkers) {
@ -39,17 +63,47 @@ Future<WasmDatabaseResult> openWasmDatabase({
// First, the shared worker will tell us which features it supports.
final sharedFeatures = await sharedMessages.next as SharedWorkerStatus;
// Can we use the shared OPFS implementation?
if (sharedFeatures.canSpawnDedicatedWorkers &&
sharedFeatures.dedicatedWorkersCanUseOpfs) {
return connect(
WasmStorageImplementation.opfsShared, (msg) => msg.sendToPort(port));
} else {
missingFeatures.addAll(sharedFeatures.missingFeatures);
await sharedMessages.cancel();
port.close();
}
} else {
// If we don't support shared workers, we might still have support for
// OPFS in dedicated workers.
final dedicatedWorker = Worker(driftWorkerUri.toString());
DedicatedWorkerCompatibilityCheck().sendToWorker(dedicatedWorker);
missingFeatures.add(MissingBrowserFeature.sharedWorkers);
}
final workerMessages = StreamQueue(
dedicatedWorker.onMessage.map(WasmInitializationMessage.fromJs));
final dedicatedWorker = Worker(driftWorkerUri.toString());
DedicatedWorkerCompatibilityCheck().sendToWorker(dedicatedWorker);
final status =
await workerMessages.next as DedicatedWorkerCompatibilityResult;
final workerMessages = StreamQueue(
dedicatedWorker.onMessage.map(WasmInitializationMessage.fromJs));
final status =
await workerMessages.next as DedicatedWorkerCompatibilityResult;
missingFeatures.addAll(status.missingFeatures);
if (status.canAccessOpfs && status.supportsSharedArrayBuffers) {
// todo send second worker to first one
} else if (status.supportsIndexedDb) {
return connect(WasmStorageImplementation.unsafeIndexedDb,
(msg) => msg.sendToWorker(dedicatedWorker));
} else {
// Nothing works on this browser, so we'll fall back to an in-memory
// database.
final sqlite3 = await WasmSqlite3.loadFromUrl(sqlite3WasmUri);
sqlite3.registerVirtualFileSystem(InMemoryFileSystem());
return WasmDatabaseResult(
WasmDatabase(sqlite3: sqlite3, path: '/app.db'),
WasmStorageImplementation.inMemory,
missingFeatures,
);
}
throw 'todo';

View File

@ -6,6 +6,7 @@ import 'dart:html';
import 'package:drift/drift.dart';
import 'package:drift/remote.dart';
import 'package:drift/wasm.dart';
import 'package:js/js_util.dart';
import 'package:sqlite3/wasm.dart';
import '../channel.dart';
@ -36,6 +37,8 @@ class DedicatedDriftWorker {
DedicatedWorkerCompatibilityResult(
canAccessOpfs: supportsOpfs,
supportsIndexedDb: supportsIndexedDb,
supportsSharedArrayBuffers:
hasProperty(globalThis, 'SharedArrayBuffer'),
).sendToClient(self);
case ServeDriftDatabase():
final server = _servers.putIfAbsent(message.databaseName, () {

View File

@ -62,12 +62,10 @@ final class SharedWorkerStatus extends WasmInitializationMessage {
final bool canSpawnDedicatedWorkers;
final bool dedicatedWorkersCanUseOpfs;
final bool canUseIndexedDb;
SharedWorkerStatus({
required this.canSpawnDedicatedWorkers,
required this.dedicatedWorkersCanUseOpfs,
required this.canUseIndexedDb,
});
factory SharedWorkerStatus.fromJsPayload(Object payload) {
@ -76,7 +74,6 @@ final class SharedWorkerStatus extends WasmInitializationMessage {
return SharedWorkerStatus(
canSpawnDedicatedWorkers: data[0],
dedicatedWorkersCanUseOpfs: data[1],
canUseIndexedDb: data[2],
);
}
@ -85,9 +82,18 @@ final class SharedWorkerStatus extends WasmInitializationMessage {
sender.sendTyped(type, [
canSpawnDedicatedWorkers,
dedicatedWorkersCanUseOpfs,
canUseIndexedDb
]);
}
Iterable<MissingBrowserFeature> get missingFeatures sync* {
if (!canSpawnDedicatedWorkers) {
yield MissingBrowserFeature.dedicatedWorkersInSharedWorkers;
}
if (!dedicatedWorkersCanUseOpfs) {
yield MissingBrowserFeature.fileSystemAccess;
}
}
}
/// A message sent by a worker when an error occurred.
@ -168,16 +174,20 @@ final class DedicatedWorkerCompatibilityResult
static const type = 'DedicatedWorkerCompatibilityResult';
final bool canAccessOpfs;
final bool supportsSharedArrayBuffers;
final bool supportsIndexedDb;
DedicatedWorkerCompatibilityResult({
required this.canAccessOpfs,
required this.supportsSharedArrayBuffers,
required this.supportsIndexedDb,
});
factory DedicatedWorkerCompatibilityResult.fromJsPayload(Object payload) {
return DedicatedWorkerCompatibilityResult(
canAccessOpfs: getProperty(payload, 'canAccessOpfs'),
supportsSharedArrayBuffers:
getProperty(payload, 'supportsSharedArrayBuffers'),
supportsIndexedDb: getProperty(payload, 'supportsIndexedDb'),
);
}
@ -187,9 +197,23 @@ final class DedicatedWorkerCompatibilityResult
final object = newObject<Object>();
setProperty(object, 'canAccessOpfs', canAccessOpfs);
setProperty(object, 'supportsIndexedDb', supportsIndexedDb);
setProperty(
object, 'supportsSharedArrayBuffers', supportsSharedArrayBuffers);
sender.sendTyped(type, object);
}
Iterable<MissingBrowserFeature> get missingFeatures sync* {
if (!canAccessOpfs) {
yield MissingBrowserFeature.fileSystemAccess;
}
if (!supportsSharedArrayBuffers) {
yield MissingBrowserFeature.sharedArrayBuffers;
}
if (!supportsIndexedDb) {
yield MissingBrowserFeature.indexedDb;
}
}
}
final class StartFileSystemServer extends WasmInitializationMessage {

View File

@ -61,13 +61,11 @@ class SharedDriftWorker {
Future<SharedWorkerStatus> _startFeatureDetection() async {
// First, let's see if this shared worker can spawn dedicated workers.
final hasWorker = hasProperty(self, 'Worker');
final canUseIndexedDb = await checkIndexedDbSupport();
if (!hasWorker) {
return SharedWorkerStatus(
canSpawnDedicatedWorkers: false,
dedicatedWorkersCanUseOpfs: false,
canUseIndexedDb: canUseIndexedDb,
);
} else {
final worker = _dedicatedWorker = Worker(Uri.base.toString());
@ -83,7 +81,6 @@ class SharedDriftWorker {
completer.complete(SharedWorkerStatus(
canSpawnDedicatedWorkers: true,
dedicatedWorkersCanUseOpfs: result,
canUseIndexedDb: canUseIndexedDb,
));
messageSubscription?.cancel();