Skip to content

Commit

Permalink
Expose async transaction support in the C API (#5783)
Browse files Browse the repository at this point in the history
* expose async transaction realm support in the C API

* changelog entry

* fix format checks

* code review

* appease format checks

* remove redundant realm parameter from callback
  • Loading branch information
nicola-cab authored Aug 29, 2022
1 parent e4aaa37 commit 21f67ac
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
* <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 @@ -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.
*
Expand Down Expand Up @@ -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.
*
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

0 comments on commit 21f67ac

Please sign in to comment.