Skip to content

Commit 5c6f6d7

Browse files
committed
Store - add constructor from an existing C pointer to allow sharing store between multiple isolates
1 parent 4faf387 commit 5c6f6d7

File tree

3 files changed

+136
-1
lines changed

3 files changed

+136
-1
lines changed

lib/src/store.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class Store {
9292
}
9393
}
9494

95+
Store.fromPtr(this.defs, this._cStore);
96+
9597
/// Closes this store.
9698
///
9799
/// Don't try to call any other ObjectBox methods after the store is closed.

test/isolates_test.dart

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import 'dart:async';
2+
import 'dart:ffi';
3+
import 'dart:isolate';
4+
5+
import 'package:objectbox/src/bindings/bindings.dart';
6+
import 'package:test/test.dart';
7+
8+
import 'entity.dart';
9+
import 'objectbox.g.dart';
10+
import 'test_env.dart';
11+
12+
// We want to have types explicit - verifying the return types of functions.
13+
// ignore_for_file: omit_local_variable_types
14+
void main() {
15+
/// Set up a simple echo isolate with request-response communication.
16+
/// This isn't really a test, just an example of how isolates can communicate.
17+
test('isolates two-way communication example', () async {
18+
var receivePort = ReceivePort();
19+
await Isolate.spawn(echoIsolate, receivePort.sendPort);
20+
21+
Completer sendPortCompleter = Completer<SendPort>();
22+
Completer responseCompleter;
23+
receivePort.listen((data) {
24+
if (data is SendPort) {
25+
sendPortCompleter.complete(data);
26+
} else {
27+
print('Main received: $data');
28+
responseCompleter.complete(data);
29+
}
30+
});
31+
32+
// Receive the SendPort from the Isolate
33+
SendPort sendPort = await sendPortCompleter.future;
34+
35+
final call = (message) {
36+
responseCompleter = Completer<String>();
37+
sendPort.send(message);
38+
return responseCompleter.future;
39+
};
40+
41+
// Send a message to the isolate
42+
expect(await call('hello'), equals('re:hello'));
43+
expect(await call('foo'), equals('re:foo'));
44+
});
45+
46+
/// Work with a single store accross multiple isolates
47+
test('single store in multiple isolates', () async {
48+
var receivePort = ReceivePort();
49+
await Isolate.spawn(createDataIsolate, receivePort.sendPort);
50+
51+
final sendPortCompleter = Completer<SendPort>();
52+
Completer<dynamic> responseCompleter;
53+
receivePort.listen((data) {
54+
if (data is SendPort) {
55+
sendPortCompleter.complete(data);
56+
} else {
57+
print('Main received: $data');
58+
responseCompleter.complete(data);
59+
}
60+
});
61+
62+
// Receive the SendPort from the Isolate
63+
SendPort sendPort = await sendPortCompleter.future;
64+
65+
final call = (message) {
66+
responseCompleter = Completer<dynamic>();
67+
sendPort.send(message);
68+
return responseCompleter.future;
69+
};
70+
71+
// Pass the store to the isolate
72+
final env = TestEnv('isolates');
73+
expect(await call(env.store.ptr.address), equals('store set'));
74+
75+
expect(env.box.isEmpty(), isTrue);
76+
expect(await call(['put', 'Foo']), equals(1)); // returns inserted id = 1
77+
expect(env.box.get(1).tString, equals('Foo'));
78+
});
79+
}
80+
81+
// Echoes back any received message.
82+
void echoIsolate(SendPort sendPort) async {
83+
// Open the ReceivePort to listen for incoming messages
84+
var port = ReceivePort();
85+
86+
// Send the port where the main isolate can contact us
87+
sendPort.send(port.sendPort);
88+
89+
// Listen for messages
90+
await for (var data in port) {
91+
// `data` is the message received.
92+
print('Isolate received: $data');
93+
sendPort.send('re:$data');
94+
}
95+
}
96+
97+
// Creates data in the background, in the [Store] received as the first message.
98+
void createDataIsolate(SendPort sendPort) async {
99+
// Open the ReceivePort to listen for incoming messages
100+
var port = ReceivePort();
101+
102+
// Send the port where the main isolate can contact us
103+
sendPort.send(port.sendPort);
104+
105+
TestEnv env;
106+
// Listen for messages
107+
await for (var data in port) {
108+
if (env == null) {
109+
// first message data is Store's C pointer address
110+
env = TestEnv.fromPtr(Pointer<OBX_store>.fromAddress(data));
111+
sendPort.send('store set');
112+
} else {
113+
print('Isolate received: $data');
114+
if (data is! List) {
115+
sendPort.send('unknown message type, list expected');
116+
} else {
117+
switch (data[0]) {
118+
case 'put':
119+
final id = env.box.put(TestEntity(tString: data[1]));
120+
sendPort.send(id);
121+
}
122+
}
123+
}
124+
}
125+
}

test/test_env.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import 'dart:ffi';
12
import 'dart:io';
3+
import 'package:objectbox/src/bindings/bindings.dart';
4+
25
import 'entity.dart';
36
import 'objectbox.g.dart';
47

@@ -14,9 +17,14 @@ class TestEnv {
1417
box = Box<TestEntity>(store);
1518
}
1619

20+
TestEnv.fromPtr(Pointer<OBX_store> cStore) : dir = null {
21+
store = Store.fromPtr(getObjectBoxModel(), cStore);
22+
box = Box<TestEntity>(store);
23+
}
24+
1725
void close() {
1826
store.close();
19-
if (dir.existsSync()) dir.deleteSync(recursive: true);
27+
if (dir != null && dir.existsSync()) dir.deleteSync(recursive: true);
2028
}
2129
}
2230

0 commit comments

Comments
 (0)