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

Fix support for ReadOnly ThreadSafeReferences #4517

Closed
wants to merge 18 commits into from

Conversation

DominicFrei
Copy link
Contributor

@DominicFrei DominicFrei commented Mar 15, 2021

Fix for realm/realm-swift#5475.

What, How & Why?

Problem

This issue is caused by two checks interfering with each other:

class ThreadSafeReference::Payload {
public:
virtual ~Payload() = default;
Payload(Realm& realm)
: m_transaction(realm.is_in_read_transaction() ? realm.duplicate() : nullptr)
, m_coordinator(Realm::Internal::get_coordinator(realm).shared_from_this())
, m_created_in_write_transaction(realm.is_in_transaction())
{
}

static void check_can_create_any_transaction(const Realm* realm)
{
if (realm->config().immutable()) {
throw InvalidTransactionException("Can't perform transactions on read-only Realms.");
}
}

Within a ThreadSafeReference we duplicate() the realm if it is_in_read_transaction() which it is since the check looks like this:

bool is_in_read_transaction() const
{
return m_group != nullptr;
}

The then executed path of duplicate() which needs to get_version_of_current_transaction() tries to verify_can_create_any_transaction which subsequently crashes because we "Can't perform transactions on read-only Realms.".

Solution

  • move ReadLockInfo and get_current_version() to Group so that we can read a version for a (read-only) Group as well
  • read_transaction_version() was split into get_current_version() in general and get_version_of_current_or_frozen_transaction() specifically to account for that change
  • is_in_any_transaction() was adjusted to match the test in the more specific is_in_write_transaction()

With these preparations we can then make the two resolving changes:

  • When duplicating we have to distinguish between read-only and writable Realms:
    std::shared_ptr<Transaction> Realm::duplicate() const
    {
    VersionID version;
    if (m_config.is_immutable()) {
    version = get_current_version();
    }
    else {
    version = get_version_of_current_transaction();
    }
    std::shared_ptr<Group> group = m_coordinator->begin_read(version, is_frozen());
    std::shared_ptr<Transaction> transaction = std::static_pointer_cast<Transaction>(group);
    return transaction;
    }
  • With these changes we can then check and duplicate a SharedRealm correctly in
    : m_transaction(realm.is_in_any_transaction() || (realm.config().is_immutable() && realm.has_group())
    ? realm.duplicate()

☑️ ToDos

Notes

I was encouraged to change names when unclear and add comments, etc.
The changes I've made happened while creating this PR and helped me understanding those parts of the core better and eventually led to the solution but are not necessarily part of it. I'm happy to divide them into two PRs if preferred.

Open questions

  • bool Collection::is_valid() const
    {
    if (!m_realm)
    return false;
    m_realm->verify_is_on_thread();
    if (!m_realm->is_in_any_transaction())
    return false;
    return m_coll_base->is_attached();
    }

    Why can a List, Set or Dictionary not be valid on a read-only realm?
  • When writing the tests I noticed that there aren't any for writable realms for those Collections. Is this connected to the is_valid() function?

@jsflax
Copy link
Contributor

jsflax commented Mar 15, 2021

The title of this PR should be in the imperative: Fix support for ReadOnly ThreadSafeReferences (or something similar).

@DominicFrei DominicFrei changed the title Df/read only thread safe references Fix support for ReadOnly ThreadSafeReferences Mar 15, 2021
@jsflax jsflax requested a review from tgoyne March 23, 2021 12:19
@tgoyne
Copy link
Member

tgoyne commented Mar 23, 2021

It is difficult to find the actual change here in the giant mess of unrelated pointless renamings, but I think it's incorrect. The entire concept of a readlock or version does not apply to a read-only Realm, and it doesn't make any sense to add them to Group. import_copy_of() on Group just doesn't need to check the version at all because there's only ever one version of a non-Transaction Group.

@finnschiermer
Copy link
Contributor

finnschiermer commented Mar 23, 2021

also, as I said earlier, we can't merge this. I want to unify handling of read-only in DB instead of needing special casing with Group all over. @jsflax, for which I created #4528.

@DominicFrei DominicFrei force-pushed the df/read-only-thread-safe-references branch 2 times, most recently from b2d7deb to c246636 Compare May 11, 2021 13:08
@DominicFrei DominicFrei force-pushed the df/read-only-thread-safe-references branch from c246636 to e0bb952 Compare May 11, 2021 13:16
@DominicFrei DominicFrei force-pushed the df/read-only-thread-safe-references branch from e0bb952 to 37b2345 Compare May 11, 2021 13:21
@DominicFrei
Copy link
Contributor Author

Issue was continued and fixed by @finnschiermer in #4688.

@DominicFrei DominicFrei deleted the df/read-only-thread-safe-references branch May 12, 2021 10:29
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants