All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.realm.kotlin.mongodb.internal.SyncedRealmContext.kt Maven / Gradle / Ivy

Go to download

Sync Library code for Realm Kotlin. This artifact is not supposed to be consumed directly, but through 'io.realm.kotlin:gradle-plugin:1.6.2' instead.

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2022 Realm Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.realm.kotlin.mongodb.internal

import io.realm.kotlin.BaseRealm
import io.realm.kotlin.internal.BaseRealmImpl
import io.realm.kotlin.internal.RealmImpl
import io.realm.kotlin.internal.interop.RealmInterop
import io.realm.kotlin.mongodb.sync.SubscriptionSet
import io.realm.kotlin.mongodb.sync.SyncConfiguration
import io.realm.kotlin.mongodb.sync.SyncSession

/**
 * Since extension functions has limited capabilities, like not allowing backing fields. This class
 * contains all fields and functionality needed to expose public Sync API's. Meaning that the
 * extension functions only need to verify that the call is valid and otherwise just delegate
 * to this class.
 *
 * In order to work around the bootstrap problem, all public API entry points that access this
 * class must do so through the [executeInSyncContext] closure.
 */
@OptIn(ExperimentalStdlibApi::class)
internal class SyncedRealmContext(realm: T) : AutoCloseable {
    // TODO For now this can only be a RealmImpl, which is required by the SyncSessionImpl
    //  When we introduce a public DynamicRealm, this can also be a `DynamicRealmImpl`
    //  And we probably need to modify the SyncSessionImpl to take either of these two.
    private val baseRealm = realm as RealmImpl
    internal val config: SyncConfiguration = baseRealm.configuration as SyncConfiguration
    // Note: Session and Subscriptions only need a valid dbPointer when being created, after that, they
    // have their own lifecycle and can be cached.
    private val sessionDelegate: Lazy = lazy {
        SyncSessionImpl(
            baseRealm,
            RealmInterop.realm_sync_session_get(baseRealm.realmReference.dbPointer)
        )
    }
    internal val session: SyncSession by sessionDelegate

    private val subscriptionsDelegate: Lazy> = lazy {
        SubscriptionSetImpl(
            realm,
            RealmInterop.realm_sync_get_latest_subscriptionset(baseRealm.realmReference.dbPointer)
        )
    }
    internal val subscriptions: SubscriptionSet by subscriptionsDelegate

    override fun close() {
        if (sessionDelegate.isInitialized()) {
            (session as SyncSessionImpl).close()
        }
    }
}

/**
 * Helper methods that can be used by public API entry points to grant safe access to the
 * [SyncedRealmContext], or otherwise throw an appropriate exception.
 */
internal fun  executeInSyncContext(realm: R, block: (context: SyncedRealmContext) -> T): T {
    if (realm.isClosed()) {
        throw IllegalStateException("This method is not available when the Realm has been closed.")
    }
    val config = realm.configuration
    if (config is SyncConfiguration) {
        if (realm is BaseRealmImpl) {
            val context: SyncedRealmContext = initSyncContextIfNeeded(realm)
            return block(context)
        } else {
            // Should never happen. Indicates a problem with our internal architecture.
            throw IllegalStateException("This method is not available on objects of type: $realm")
        }
    } else {
        // Public error
        throw IllegalStateException("This method is only available on synchronized realms.")
    }
}

@OptIn(ExperimentalStdlibApi::class)
private fun  initSyncContextIfNeeded(realm: T): SyncedRealmContext {
    // INVARIANT: `syncContext` is only ever set once, and never to `null`.
    // This code works around the fact that `Mutex`'s can only be locked inside suspend functions on
    // Kotlin Native.
    val syncContext = (realm as RealmImpl).syncContext
    return if (syncContext.value != null) {
        syncContext.value!! as SyncedRealmContext
    } else {
        // Worst case, two SyncedRealmContext will be created and one of them will thrown
        // away. As long as SyncedRealmContext is cheap to create, this should be fine. If, at
        // some point, it start having too much state, we can consider making `lazy` properties
        // inside the class to defer the construction cost.
        syncContext.compareAndSet(null, SyncedRealmContext(realm))
        syncContext.value!! as SyncedRealmContext
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy