Skip to content
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

Expose async transaction support in the C API #5783

Merged
merged 6 commits into from
Aug 29, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Action returned from the server in the json error messages is surfaced through the SyncError. ([PR #5690](https://github.com/realm/realm-core/pull/5690)).
* `NotificationToken` grew an `unregister()` method as an alternative to destroying it or doing `token = {};` ([PR #5776](https://github.com/realm/realm-core/pull/5776)).
* Expose `Obj::add_int()` in the CAPI. ([PR #5770](https://github.com/realm/realm-core/pull/5770)).
* Expose `Realm::async_begin_transaction`, `Realm::async_commit_transaction`, `Realm::async_cancel_transaction` in the CAPI.([PR 5783 #](https://github.com/realm/realm-core/pull/5783)).

### Fixed
* <How do the end-user experience this issue? what was the impact?> ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?)
Expand Down
22 changes: 21 additions & 1 deletion src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,8 @@ typedef void (*realm_on_object_change_func_t)(realm_userdata_t userdata, const r
typedef void (*realm_on_collection_change_func_t)(realm_userdata_t userdata, const realm_collection_changes_t*);
typedef void (*realm_on_realm_change_func_t)(realm_userdata_t userdata);
typedef void (*realm_on_realm_refresh_func_t)(realm_userdata_t userdata);
typedef void (*realm_async_begin_write_func_t)(realm_userdata_t userdata);
typedef void (*realm_async_commit_func_t)(realm_userdata_t userdata, bool error, const char* desc);

/**
* Callback for realm schema changed notifications.
Expand Down Expand Up @@ -1060,6 +1062,25 @@ RLM_API bool realm_commit(realm_t*);
*/
RLM_API bool realm_rollback(realm_t*);

/**
* start a new write transaction asynchronously for the realm passed as argument.
*/
RLM_API unsigned int realm_async_begin_write(realm_t* realm, realm_async_begin_write_func_t,
realm_userdata_t userdata, realm_free_userdata_func_t userdata_free,
bool notify_only);

/**
* commit a transaction asynchronously for the realm passed as argument.
*/
RLM_API unsigned int realm_async_commit(realm_t* realm, realm_async_commit_func_t, realm_userdata_t userdata,
realm_free_userdata_func_t userdata_free, bool allow_grouping);

/**
* Cancel the transaction referenced by the token passed as argument and set the optional boolean flag in order to
* inform the caller if the transaction was cancelled.
*/
RLM_API bool realm_async_cancel(realm_t* realm, unsigned int token, bool* cancelled);

/**
* Add a callback that will be invoked every time the view of this file is updated.
*
Expand Down Expand Up @@ -3725,7 +3746,6 @@ RLM_API uint64_t realm_async_open_task_register_download_progress_notifier(
realm_free_userdata_func_t userdata_free) RLM_API_NOEXCEPT;
RLM_API void realm_async_open_task_unregister_download_progress_notifier(realm_async_open_task_t*,
uint64_t token) RLM_API_NOEXCEPT;

/**
* Get the sync session for a specific realm.
*
Expand Down
46 changes: 44 additions & 2 deletions src/realm/object-store/c_api/realm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ realm_refresh_callback_token::~realm_refresh_callback_token()
realm::c_api::CBindingContext::get(*m_realm).realm_pending_refresh_callbacks().remove(m_token);
}


namespace realm::c_api {


Expand Down Expand Up @@ -125,7 +124,7 @@ RLM_API bool realm_is_closed(realm_t* realm)

RLM_API bool realm_is_writable(const realm_t* realm)
{
return (*realm)->is_in_transaction();
return (*realm)->is_in_transaction() || (*realm)->is_in_async_transaction();
}

RLM_API bool realm_close(realm_t* realm)
Expand Down Expand Up @@ -168,6 +167,49 @@ RLM_API bool realm_rollback(realm_t* realm)
});
}

RLM_API unsigned int realm_async_begin_write(realm_t* realm, realm_async_begin_write_func_t callback,
realm_userdata_t userdata, realm_free_userdata_func_t userdata_free,
bool notify_only)
{
auto cb = [callback, userdata = UserdataPtr{userdata, userdata_free}]() {
callback(userdata.get());
};
return wrap_err([&]() {
return (*realm)->async_begin_transaction(std::move(cb), notify_only);
});
}

RLM_API unsigned int realm_async_commit(realm_t* realm, realm_async_commit_func_t callback, realm_userdata_t userdata,
realm_free_userdata_func_t userdata_free, bool allow_grouping)
{
auto cb = [callback, userdata = UserdataPtr{userdata, userdata_free}](std::exception_ptr err) {
if (err) {
try {
std::rethrow_exception(err);
}
catch (const std::exception& e) {
callback(userdata.get(), true, e.what());
}
}
else {
callback(userdata.get(), false, nullptr);
}
};
return wrap_err([&]() {
return (*realm)->async_commit_transaction(std::move(cb), allow_grouping);
});
}

RLM_API bool realm_async_cancel(realm_t* realm, unsigned int token, bool* cancelled)
{
return wrap_err([&]() {
auto res = (*realm)->async_cancel_transaction(token);
if (cancelled)
*cancelled = res;
return true;
});
}

RLM_API realm_callback_token_t* realm_add_realm_changed_callback(realm_t* realm,
realm_on_realm_change_func_t callback,
realm_userdata_t userdata,
Expand Down
52 changes: 34 additions & 18 deletions test/object-store/c_api/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1083,31 +1083,47 @@ TEST_CASE("C API", "[c_api]") {
}

SECTION("realm refresh async pending") {
bool realm_refresh_callback_called = false;
// bool on_transaction_completed = false;
bool done = false;
struct TestingObj {
static TestingObj& get()
{
static TestingObj obj;
return obj;
}
bool done{false};
bool realm_refresh_callback_called{false};
realm_t* realm{nullptr};
};

auto wait_for_done = [&]() {
util::EventLoop::main().run_until([&] {
return done;
return TestingObj::get().done;
});
REQUIRE(done);
REQUIRE(TestingObj::get().done);
};
(*realm)->async_begin_transaction([&]() {
auto token = cptr(realm_add_realm_refresh_callback(
realm,
[](void* userdata) {
*reinterpret_cast<bool*>(userdata) = true;
},
&realm_refresh_callback_called, [](void*) {}));

(*realm)->async_commit_transaction([&](std::exception_ptr) {
done = true;
});
});
TestingObj::get().realm = realm;

realm_async_begin_write(
realm,
[](void*) {
auto realm = TestingObj::get().realm;
auto token_refresh = cptr(realm_add_realm_refresh_callback(
realm,
[](void* userdata) {
*reinterpret_cast<bool*>(userdata) = true;
},
&(TestingObj::get().realm_refresh_callback_called), [](void*) {}));

realm_async_commit(
realm,
[](void*, bool, const char*) {
TestingObj::get().done = true;
},
nullptr, nullptr, false);
},
nullptr, nullptr, false);

wait_for_done();
CHECK(realm_refresh_callback_called);
CHECK(TestingObj::get().realm_refresh_callback_called);
}

SECTION("realm async refresh - main use case") {
Expand Down