mirror of
https://github.com/status-im/realm-js.git
synced 2025-01-12 15:24:18 +00:00
Run RealmCoordinator::on_change() on a different thread when using tsan
This commit is contained in:
parent
059f907a4a
commit
7f5277a97b
@ -61,8 +61,7 @@ TEST_CASE("list") {
|
||||
f();
|
||||
r->commit_transaction();
|
||||
|
||||
coordinator.on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
};
|
||||
|
||||
auto require_change = [&] {
|
||||
@ -199,8 +198,7 @@ TEST_CASE("list") {
|
||||
// Each of the Lists now has a different source version and state at
|
||||
// that version, so they should all see different changes despite
|
||||
// being for the same LinkList
|
||||
coordinator.on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
REQUIRE_INDICES(changes[0].insertions, 0, 1, 2);
|
||||
REQUIRE(changes[0].modifications.empty());
|
||||
@ -213,8 +211,7 @@ TEST_CASE("list") {
|
||||
|
||||
// After making another change, they should all get the same notification
|
||||
change_list();
|
||||
coordinator.on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
REQUIRE_INDICES(changes[i].insertions, 3);
|
||||
|
@ -58,15 +58,13 @@ TEST_CASE("Results") {
|
||||
++notification_calls;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
auto write = [&](auto&& f) {
|
||||
r->begin_transaction();
|
||||
f();
|
||||
r->commit_transaction();
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
};
|
||||
|
||||
SECTION("initial results are delivered") {
|
||||
@ -79,8 +77,7 @@ TEST_CASE("Results") {
|
||||
r->commit_transaction();
|
||||
|
||||
REQUIRE(notification_calls == 1);
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
REQUIRE(notification_calls == 2);
|
||||
}
|
||||
|
||||
@ -91,8 +88,7 @@ TEST_CASE("Results") {
|
||||
|
||||
REQUIRE(notification_calls == 1);
|
||||
token = {};
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
REQUIRE(notification_calls == 1);
|
||||
}
|
||||
|
||||
@ -117,9 +113,7 @@ TEST_CASE("Results") {
|
||||
});
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
|
||||
advance_and_notify(*r);
|
||||
REQUIRE(called);
|
||||
}
|
||||
|
||||
@ -132,8 +126,7 @@ TEST_CASE("Results") {
|
||||
REQUIRE(false);
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
}
|
||||
|
||||
SECTION("removing the current callback does not stop later ones from being called") {
|
||||
@ -146,8 +139,7 @@ TEST_CASE("Results") {
|
||||
called = true;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
REQUIRE(called);
|
||||
}
|
||||
@ -307,15 +299,13 @@ TEST_CASE("Results") {
|
||||
++notification_calls;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
auto write = [&](auto&& f) {
|
||||
r->begin_transaction();
|
||||
f();
|
||||
r->commit_transaction();
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
};
|
||||
|
||||
SECTION("modifications that leave a non-matching row non-matching do not send notifications") {
|
||||
@ -392,8 +382,7 @@ TEST_CASE("Results") {
|
||||
r->commit_transaction();
|
||||
|
||||
REQUIRE(notification_calls == 1);
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
REQUIRE(notification_calls == 2);
|
||||
}
|
||||
}
|
||||
@ -458,8 +447,7 @@ TEST_CASE("Async Results error handling") {
|
||||
called = true;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
bool called2 = false;
|
||||
auto token2 = results.add_notification_callback([&](CollectionChangeIndices, std::exception_ptr err) {
|
||||
@ -468,9 +456,7 @@ TEST_CASE("Async Results error handling") {
|
||||
called2 = true;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
|
||||
advance_and_notify(*r);
|
||||
REQUIRE(called2);
|
||||
}
|
||||
}
|
||||
@ -500,8 +486,7 @@ TEST_CASE("Async Results error handling") {
|
||||
});
|
||||
OpenFileLimiter limiter;
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
bool called2 = false;
|
||||
auto token2 = results.add_notification_callback([&](CollectionChangeIndices, std::exception_ptr err) {
|
||||
@ -510,8 +495,7 @@ TEST_CASE("Async Results error handling") {
|
||||
called2 = true;
|
||||
});
|
||||
|
||||
coordinator->on_change();
|
||||
r->notify();
|
||||
advance_and_notify(*r);
|
||||
|
||||
REQUIRE(called2);
|
||||
}
|
||||
|
@ -1,10 +1,18 @@
|
||||
#include "util/test_file.hpp"
|
||||
|
||||
#include "impl/realm_coordinator.hpp"
|
||||
|
||||
#include <realm/disable_sync_to_disk.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__has_feature) && __has_feature(thread_sanitizer)
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
TestFile::TestFile()
|
||||
{
|
||||
static std::string tmpdir = [] {
|
||||
@ -29,3 +37,62 @@ InMemoryTestFile::InMemoryTestFile()
|
||||
{
|
||||
in_memory = true;
|
||||
}
|
||||
|
||||
#if defined(__has_feature) && __has_feature(thread_sanitizer)
|
||||
// A helper which synchronously runs on_change() on a fixed background thread
|
||||
// so that ThreadSanitizer can potentially detect issues
|
||||
// This deliberately uses an unsafe spinlock for synchronization to ensure that
|
||||
// the code being tested has to supply all required safety
|
||||
static class TsanNotifyWorker {
|
||||
public:
|
||||
TsanNotifyWorker()
|
||||
{
|
||||
m_thread = std::thread([&] { work(); });
|
||||
}
|
||||
|
||||
void work()
|
||||
{
|
||||
while (true) {
|
||||
auto value = m_signal.load(std::memory_order_relaxed);
|
||||
if (value == 0 || value == 1)
|
||||
continue;
|
||||
if (value == 2)
|
||||
return;
|
||||
|
||||
auto c = reinterpret_cast<realm::_impl::RealmCoordinator *>(value);
|
||||
c->on_change();
|
||||
m_signal.store(1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
~TsanNotifyWorker()
|
||||
{
|
||||
m_signal = 2;
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
void on_change(realm::_impl::RealmCoordinator* c)
|
||||
{
|
||||
m_signal.store(reinterpret_cast<uintptr_t>(c), std::memory_order_relaxed);
|
||||
while (m_signal.load(std::memory_order_relaxed) != 1) ;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<uintptr_t> m_signal{0};
|
||||
std::thread m_thread;
|
||||
} s_worker;
|
||||
|
||||
void advance_and_notify(realm::Realm& realm)
|
||||
{
|
||||
s_worker.on_change(realm::_impl::RealmCoordinator::get_existing_coordinator(realm.config().path).get());
|
||||
realm.notify();
|
||||
}
|
||||
|
||||
#else // __has_feature(thread_sanitizer)
|
||||
|
||||
void advance_and_notify(realm::Realm& realm)
|
||||
{
|
||||
realm::_impl::RealmCoordinator::get_existing_coordinator(realm.config().path)->on_change();
|
||||
realm.notify();
|
||||
}
|
||||
#endif
|
||||
|
@ -12,4 +12,6 @@ struct InMemoryTestFile : TestFile {
|
||||
InMemoryTestFile();
|
||||
};
|
||||
|
||||
void advance_and_notify(realm::Realm& realm);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user