Skip to content

Add async variant of runInTransaction #415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .gitlab/merge_request_templates/Default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## What does this MR do?

<!-- Briefly describe what this MR is about. -->

## Author's checklist

- [ ] The MR fully addresses the requirements of the associated task.
- [ ] I did a self-review of the changes and did not spot any issues. Among others, this includes:
* I added unit tests for new/changed behavior; all test pass.
* My code conforms to our coding standards and guidelines.
* My changes are prepared in a way that makes the review straightforward for the reviewer.
- [ ] I amended [`CHANGELOG.md`](objectbox/CHANGELOG.md) if this affects users in any way.

## Review checklist

- [ ] I reviewed all changes line-by-line and addressed relevant issues
- [ ] The requirements of the associated task are fully met
- [ ] I can confirm that:
* CI passes
* Coverage percentages do not decrease
* New code conforms to standards and guidelines
* If applicable, additional checks were done for special code changes (e.g. core performance, binary size, OSS licenses)

/assign me
8 changes: 8 additions & 0 deletions objectbox/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
## latest
* Add `Store.runInTransactionAsync` to run database operations asynchronously in the background
(requires Flutter 2.8.0/Dart 2.15.0 or newer).
* Rename `Store.runIsolated` to `runAsync`, drop unused `mode` parameter, propagate errors and
handle premature isolate exit.

* The native ObjectBox library is also searched for in the `lib` subfolder on desktop OS (macOS,
Linux, Windows). This is where the [`install.sh`](/install.sh) script downloads it by default.
E.g. it is no longer necessary to install the library globally to run `dart test` or `flutter test`.

## 1.4.1 (2022-03-01)

Expand Down
4 changes: 2 additions & 2 deletions objectbox/example/flutter/objectbox_demo/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ class _MyHomePageState extends State<MyHomePage> {
final _noteInputController = TextEditingController();
final _listController = StreamController<List<Note>>(sync: true);

void _addNote() {
Future<void> _addNote() async {
if (_noteInputController.text.isEmpty) return;
objectbox.noteBox.put(Note(_noteInputController.text));
await objectbox.addNote(_noteInputController.text);
_noteInputController.text = '';
}

Expand Down
24 changes: 23 additions & 1 deletion objectbox/example/flutter/objectbox_demo/lib/objectbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,28 @@ class ObjectBox {
Note('Delete notes by tapping on one'),
Note('Write a demo app for ObjectBox')
];
noteBox.putMany(demoNotes);
store.runInTransactionAsync(TxMode.write, _putNotesInTx, demoNotes);
}

static void _putNotesInTx(Store store, List<Note> notes) =>
store.box<Note>().putMany(notes);

/// Add a note within a transaction.
///
/// To avoid frame drops, run ObjectBox operations that take longer than a
/// few milliseconds, e.g. putting many objects, in an isolate with its
/// own Store instance.
/// For this example only a single object is put which would also be fine if
/// done here directly.
Future<void> addNote(String text) =>
store.runInTransactionAsync(TxMode.write, _addNoteInTx, text);

/// Note: due to [dart-lang/sdk#36983](https://github.com/dart-lang/sdk/issues/36983)
/// not using a closure as it may capture more objects than expected.
/// These might not be send-able to an isolate. See Store.runAsync for details.
static void _addNoteInTx(Store store, String text) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<Note>().put(Note(text));
}
}
4 changes: 2 additions & 2 deletions objectbox/example/flutter/objectbox_demo_sync/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ class _MyHomePageState extends State<MyHomePage> {
final _noteInputController = TextEditingController();
final _listController = StreamController<List<Note>>(sync: true);

void _addNote() {
Future<void> _addNote() async {
if (_noteInputController.text.isEmpty) return;
objectbox.noteBox.put(Note(_noteInputController.text));
await objectbox.addNote(_noteInputController.text);
_noteInputController.text = '';
}

Expand Down
19 changes: 19 additions & 0 deletions objectbox/example/flutter/objectbox_demo_sync/lib/objectbox.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,23 @@ class ObjectBox {
);
return ObjectBox._create(store);
}

/// Add a note within a transaction.
///
/// To avoid frame drops, run ObjectBox operations that take longer than a
/// few milliseconds, e.g. putting many objects, in an isolate with its
/// own Store instance.
/// For this example only a single object is put which would also be fine if
/// done here directly.
Future<void> addNote(String text) =>
store.runInTransactionAsync(TxMode.write, _addNoteInTx, text);

/// Note: due to [dart-lang/sdk#36983](https://github.com/dart-lang/sdk/issues/36983)
/// not using a closure as it may capture more objects than expected.
/// These might not be send-able to an isolate. See Store.runAsync for details.
static void _addNoteInTx(Store store, String text) {
// Perform ObjectBox operations that take longer than a few milliseconds
// here. To keep it simple, this example just puts a single object.
store.box<Note>().put(Note(text));
}
}
62 changes: 45 additions & 17 deletions objectbox/lib/src/native/bindings/bindings.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:ffi';
import 'dart:io' show Platform;
import 'dart:io' show Directory, Platform;

import 'package:path/path.dart';

import 'helpers.dart';
import 'objectbox_c.dart';
Expand Down Expand Up @@ -31,29 +33,55 @@ ObjectBoxC? _tryObjectBoxLibProcess() {

ObjectBoxC? _tryObjectBoxLibFile() {
_lib = null;
var libName = 'objectbox';
final String libName;
if (Platform.isWindows) {
libName += '.dll';
try {
_lib = DynamicLibrary.open(libName);
} on ArgumentError {
libName = 'lib/' + libName;
}
libName = 'objectbox.dll';
} else if (Platform.isMacOS) {
libName = 'lib' + libName + '.dylib';
try {
_lib = DynamicLibrary.open(libName);
} on ArgumentError {
libName = '/usr/local/lib/' + libName;
}
libName = 'libobjectbox.dylib';
} else if (Platform.isAndroid) {
libName = 'lib' + libName + '-jni.so';
libName = 'libobjectbox-jni.so';
} else if (Platform.isLinux) {
libName = 'lib' + libName + '.so';
libName = 'libobjectbox.so';
} else {
// Other platforms not supported (for iOS see _tryObjectBoxLibProcess).
return null;
}
_lib ??= DynamicLibrary.open(libName);
// For desktop OS prefer version in 'lib' subfolder as this is where
// install.sh (which calls objectbox-c download.sh) puts the library.
if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
// Must use absolute directory as relative directory fails on macOS
// due to security restrictions ("file system relative paths not allowed in
// hardened programs").
String libPath = join(Directory.current.path, "lib", libName);
try {
_lib = DynamicLibrary.open(libPath);
} on ArgumentError {
// On macOS also try /usr/local/lib, this is where the objectbox-c
// download script installs to as well.
if (Platform.isMacOS) {
try {
_lib ??= DynamicLibrary.open('/usr/local/lib/' + libName);
} on ArgumentError {
// Ignore.
}
}
// Try default path, see below.
}
}
try {
// This will look in some standard places for shared libraries:
// - on Android in the JNI lib folder for the architecture
// - on Linux in /lib and /usr/lib
// - on macOS?
// - on Windows in the working directory and System32
_lib ??= DynamicLibrary.open(libName);
} catch (e) {
print("Failed to load ObjectBox library. For Flutter apps, check if "
"objectbox_flutter_libs is added to dependencies. "
"For unit tests and Dart apps, check if the ObjectBox library was "
"downloaded (https://docs.objectbox.io/getting-started).");
rethrow;
}
return ObjectBoxC(_lib!);
}

Expand Down
Loading