diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad9b5a1a67..3d5dd99a615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * `NotificationToken` grew an `unregister()` method as an alternative to destroying it or doing `token = {};` ([PR #5776](https://github.com/realm/realm-core/pull/5776)). * Automatic handling backlinks for schema migrations where a class changes to being embedded. ([PR #5737](https://github.com/realm/realm-core/pull/5737)). * 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 * ([#????](https://github.com/realm/realm-core/issues/????), since v?.?.?) diff --git a/src/realm.h b/src/realm.h index 0e864f1ae22..be071bcdd18 100644 --- a/src/realm.h +++ b/src/realm.h @@ -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. @@ -1065,6 +1067,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. * @@ -3730,7 +3751,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. * diff --git a/src/realm/object-store/c_api/realm.cpp b/src/realm/object-store/c_api/realm.cpp index b359ee77d2c..5f5689ab309 100644 --- a/src/realm/object-store/c_api/realm.cpp +++ b/src/realm/object-store/c_api/realm.cpp @@ -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 { @@ -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) @@ -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, diff --git a/test/object-store/c_api/c_api.cpp b/test/object-store/c_api/c_api.cpp index 3e9aa3766e6..8d4473a5c52 100644 --- a/test/object-store/c_api/c_api.cpp +++ b/test/object-store/c_api/c_api.cpp @@ -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(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(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") {