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 3 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
26 changes: 25 additions & 1 deletion src/realm.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,16 @@ typedef enum realm_property_flags {
typedef struct realm_notification_token realm_notification_token_t;
typedef struct realm_callback_token realm_callback_token_t;
typedef struct realm_refresh_callback_token realm_refresh_callback_token_t;
typedef struct realm_async_transaction_token realm_async_transaction_token_t;
typedef struct realm_object_changes realm_object_changes_t;
typedef struct realm_collection_changes realm_collection_changes_t;
typedef void (*realm_on_object_change_func_t)(realm_userdata_t userdata, const realm_object_changes_t*);
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_on_realm_async_begin_transaction_func_t)(realm_t*, realm_userdata_t userdata);
nicola-cab marked this conversation as resolved.
Show resolved Hide resolved
typedef void (*realm_on_realm_async_commit_transaction_funct_t)(realm_t*, realm_userdata_t userdata, bool error,
const char* desc);

/**
* Callback for realm schema changed notifications.
Expand Down Expand Up @@ -1060,6 +1064,27 @@ 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 realm_async_transaction_token_t*
realm_async_begin_transaction(realm_t* realm, realm_on_realm_async_begin_transaction_func_t,
realm_userdata_t userdata, realm_free_userdata_func_t userdata_free, bool* notify_only);
nicola-cab marked this conversation as resolved.
Show resolved Hide resolved

/**
* commit a transaction asynchronously for the realm passed as argument.
*/
RLM_API realm_async_transaction_token_t*
realm_async_commit_transaction(realm_t* realm, realm_on_realm_async_commit_transaction_funct_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_transaction(realm_t* realm, realm_async_transaction_token_t* 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 +3750,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
52 changes: 50 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,55 @@ RLM_API bool realm_rollback(realm_t* realm)
});
}

RLM_API realm_async_transaction_token_t*
realm_async_begin_transaction(realm_t* realm, realm_on_realm_async_begin_transaction_func_t callback,
realm_userdata_t userdata, realm_free_userdata_func_t userdata_free, bool* notify_only)
{
const auto notify_only_value = notify_only ? *notify_only : false;
auto cb = [realm, callback, userdata = UserdataPtr{userdata, userdata_free}]() {
callback(realm, userdata.get());
};
return wrap_err([&]() {
auto handle = (*realm)->async_begin_transaction(std::move(cb), notify_only_value);
return new realm_async_transaction_token_t{handle};
});
}

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

RLM_API bool realm_async_cancel_transaction(realm_t* realm, realm_async_transaction_token_t* token, bool* cancelled)
{
return wrap_err([&]() {
auto res = (*realm)->async_cancel_transaction(token->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
15 changes: 15 additions & 0 deletions src/realm/object-store/c_api/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,21 @@ struct realm_refresh_callback_token : realm_callback_token {
~realm_refresh_callback_token() override;
};

struct realm_async_transaction_token : realm::c_api::WrapC {
nicola-cab marked this conversation as resolved.
Show resolved Hide resolved
realm_async_transaction_token(unsigned token)
: m_token(token)
{
}
~realm_async_transaction_token() override = default;
unsigned token() const
{
return m_token;
}

private:
unsigned m_token;
};

struct realm_query : realm::c_api::WrapC {
realm::Query query;
std::weak_ptr<realm::Realm> weak_realm;
Expand Down
50 changes: 31 additions & 19 deletions test/object-store/c_api/c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1083,31 +1083,43 @@ 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};
};

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;
});
});

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

auto token_commit = cptr(realm_async_commit_transaction(
realm,
[](realm_t*, void*, bool, const char*) {
TestingObj::get().done = true;
},
nullptr, nullptr, nullptr));
},
nullptr, nullptr, nullptr));

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

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