Abstract atomicFu usages to native only

Due to the library being unstable a common abstraction for Room is used instead that uses JVM atomics for Android and JVM and atomicFu for native.

Bug: 318697762
Test: Existing
Relnote: "Add atomicFu abstraction APIs"
Change-Id: I06bc592277f1ce70c4ac4a3bc96beb08282c02df
diff --git a/room/room-paging/build.gradle b/room/room-paging/build.gradle
index e0ab2c1..3805343 100644
--- a/room/room-paging/build.gradle
+++ b/room/room-paging/build.gradle
@@ -41,7 +41,6 @@
                 api(libs.kotlinStdlib)
                 api("androidx.paging:paging-common:3.3.2")
                 api(project(":room:room-runtime"))
-                implementation(libs.atomicFu)
             }
         }
 
@@ -66,6 +65,9 @@
 
         nativeMain {
             dependsOn(jvmNativeMain)
+            dependencies {
+                implementation(libs.atomicFu)
+            }
         }
 
         androidInstrumentedTest {
diff --git a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.android.kt b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.android.kt
index 4128526..66cbfa3 100644
--- a/room/room-paging/src/androidMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.android.kt
+++ b/room/room-paging/src/androidMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.android.kt
@@ -61,7 +61,7 @@
     private val implementation = CommonLimitOffsetImpl(tables, this, ::convertRows)
 
     public actual val itemCount: Int
-        get() = implementation.itemCount.value
+        get() = implementation.itemCount.get()
 
     override val jumpingSupported: Boolean
         get() = true
diff --git a/room/room-paging/src/commonMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.kt b/room/room-paging/src/commonMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.kt
index edd5950..9c5783d4 100644
--- a/room/room-paging/src/commonMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.kt
+++ b/room/room-paging/src/commonMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.kt
@@ -24,11 +24,12 @@
 import androidx.room.RoomDatabase
 import androidx.room.RoomRawQuery
 import androidx.room.Transactor.SQLiteTransactionType
+import androidx.room.concurrent.AtomicBoolean
+import androidx.room.concurrent.AtomicInt
 import androidx.room.paging.util.INITIAL_ITEM_COUNT
 import androidx.room.paging.util.queryDatabase
 import androidx.room.paging.util.queryItemCount
 import androidx.room.useReaderConnection
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
@@ -70,9 +71,9 @@
     private val db = pagingSource.db
     private val sourceQuery = pagingSource.sourceQuery
 
-    internal val itemCount = atomic(INITIAL_ITEM_COUNT)
+    internal val itemCount = AtomicInt(INITIAL_ITEM_COUNT)
 
-    private val invalidationFlowStarted = atomic(false)
+    private val invalidationFlowStarted = AtomicBoolean(false)
     private var invalidationFlowJob: Job? = null
 
     init {
@@ -80,7 +81,7 @@
     }
 
     suspend fun load(params: LoadParams<Int>): LoadResult<Int, Value> {
-        if (invalidationFlowStarted.compareAndSet(expect = false, update = true)) {
+        if (invalidationFlowStarted.compareAndSet(false, true)) {
             invalidationFlowJob =
                 db.getCoroutineScope().launch {
                     db.invalidationTracker.createFlow(*tables, emitInitialState = false).collect {
@@ -92,7 +93,7 @@
                 }
         }
 
-        val tempCount = itemCount.value
+        val tempCount = itemCount.get()
         // if itemCount is < 0, then it is initial load
         return try {
             if (tempCount == INITIAL_ITEM_COUNT) {
@@ -119,7 +120,7 @@
         return db.useReaderConnection { connection ->
             connection.withTransaction(SQLiteTransactionType.DEFERRED) {
                 val tempCount = queryItemCount(sourceQuery, db)
-                itemCount.value = tempCount
+                itemCount.set(tempCount)
                 queryDatabase(
                     params = params,
                     sourceQuery = sourceQuery,
diff --git a/room/room-paging/src/jvmNativeMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.jvmNative.kt b/room/room-paging/src/jvmNativeMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.jvmNative.kt
index 1f4235e..77f4bd2 100644
--- a/room/room-paging/src/jvmNativeMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.jvmNative.kt
+++ b/room/room-paging/src/jvmNativeMain/kotlin/androidx/room/paging/LimitOffsetPagingSource.jvmNative.kt
@@ -41,7 +41,7 @@
     private val implementation = CommonLimitOffsetImpl(tables, this, ::convertRows)
 
     public actual val itemCount: Int
-        get() = implementation.itemCount.value
+        get() = implementation.itemCount.get()
 
     override val jumpingSupported: Boolean
         get() = true
diff --git a/room/room-runtime/bcv/native/current.txt b/room/room-runtime/bcv/native/current.txt
index 984bd87..89d7a96 100644
--- a/room/room-runtime/bcv/native/current.txt
+++ b/room/room-runtime/bcv/native/current.txt
@@ -222,6 +222,24 @@
     final object Companion // androidx.room/EntityUpsertAdapter.Companion|null[0]
 }
 
+final class androidx.room.concurrent/AtomicBoolean { // androidx.room.concurrent/AtomicBoolean|null[0]
+    constructor <init>(kotlin/Boolean) // androidx.room.concurrent/AtomicBoolean.<init>|<init>(kotlin.Boolean){}[0]
+
+    final fun compareAndSet(kotlin/Boolean, kotlin/Boolean): kotlin/Boolean // androidx.room.concurrent/AtomicBoolean.compareAndSet|compareAndSet(kotlin.Boolean;kotlin.Boolean){}[0]
+    final fun get(): kotlin/Boolean // androidx.room.concurrent/AtomicBoolean.get|get(){}[0]
+}
+
+final class androidx.room.concurrent/AtomicInt { // androidx.room.concurrent/AtomicInt|null[0]
+    constructor <init>(kotlin/Int) // androidx.room.concurrent/AtomicInt.<init>|<init>(kotlin.Int){}[0]
+
+    final fun compareAndSet(kotlin/Int, kotlin/Int): kotlin/Boolean // androidx.room.concurrent/AtomicInt.compareAndSet|compareAndSet(kotlin.Int;kotlin.Int){}[0]
+    final fun decrementAndGet(): kotlin/Int // androidx.room.concurrent/AtomicInt.decrementAndGet|decrementAndGet(){}[0]
+    final fun get(): kotlin/Int // androidx.room.concurrent/AtomicInt.get|get(){}[0]
+    final fun getAndIncrement(): kotlin/Int // androidx.room.concurrent/AtomicInt.getAndIncrement|getAndIncrement(){}[0]
+    final fun incrementAndGet(): kotlin/Int // androidx.room.concurrent/AtomicInt.incrementAndGet|incrementAndGet(){}[0]
+    final fun set(kotlin/Int) // androidx.room.concurrent/AtomicInt.set|set(kotlin.Int){}[0]
+}
+
 final class androidx.room.util/ByteArrayWrapper { // androidx.room.util/ByteArrayWrapper|null[0]
     constructor <init>(kotlin/ByteArray) // androidx.room.util/ByteArrayWrapper.<init>|<init>(kotlin.ByteArray){}[0]
 
diff --git a/room/room-runtime/build.gradle b/room/room-runtime/build.gradle
index dc736fd..4fd3768 100644
--- a/room/room-runtime/build.gradle
+++ b/room/room-runtime/build.gradle
@@ -110,7 +110,6 @@
                 api("androidx.collection:collection:1.4.2")
                 api("androidx.annotation:annotation:1.8.1")
                 api(libs.kotlinCoroutinesCore)
-                implementation(libs.atomicFu)
             }
         }
         commonTest {
@@ -180,6 +179,7 @@
             dependsOn(jvmNativeMain)
             dependencies {
                 api(project(":sqlite:sqlite-framework"))
+                implementation(libs.atomicFu)
             }
         }
         nativeTest {
diff --git a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/coroutines/BundledSQLiteConnectionPoolTest.kt b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/coroutines/BundledSQLiteConnectionPoolTest.kt
index ab9a93b..f876d37 100644
--- a/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/coroutines/BundledSQLiteConnectionPoolTest.kt
+++ b/room/room-runtime/src/androidInstrumentedTest/kotlin/androidx/room/coroutines/BundledSQLiteConnectionPoolTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.kruth.assertThat
 import androidx.room.Transactor
+import androidx.room.concurrent.AtomicInt
 import androidx.sqlite.SQLiteDriver
 import androidx.sqlite.driver.bundled.BundledSQLiteDriver
 import androidx.test.filters.LargeTest
@@ -27,7 +28,6 @@
 import kotlin.test.AfterTest
 import kotlin.test.BeforeTest
 import kotlin.test.Test
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.InternalCoroutinesApi
@@ -130,7 +130,7 @@
 
     /** A CoroutineDispatcher that dispatches every block into a new thread */
     private class NewThreadDispatcher : CoroutineDispatcher() {
-        private val idCounter = atomic(0)
+        private val idCounter = AtomicInt(0)
 
         @OptIn(InternalCoroutinesApi::class)
         override fun dispatchYield(context: CoroutineContext, block: Runnable) {
diff --git a/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt b/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
index 04caeb7..9b5e5d8 100644
--- a/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
+++ b/room/room-runtime/src/androidMain/kotlin/androidx/room/InvalidationTracker.android.kt
@@ -21,12 +21,12 @@
 import androidx.annotation.WorkerThread
 import androidx.lifecycle.LiveData
 import androidx.room.InvalidationTracker.Observer
+import androidx.room.concurrent.ReentrantLock
+import androidx.room.concurrent.withLock
 import androidx.room.support.AutoCloser
 import androidx.sqlite.SQLiteConnection
 import java.lang.ref.WeakReference
 import java.util.concurrent.Callable
-import kotlinx.atomicfu.locks.reentrantLock
-import kotlinx.atomicfu.locks.withLock
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.runBlocking
@@ -65,7 +65,7 @@
         )
 
     private val observerMap = mutableMapOf<Observer, ObserverWrapper>()
-    private val observerMapLock = reentrantLock()
+    private val observerMapLock = ReentrantLock()
 
     private var autoCloser: AutoCloser? = null
 
diff --git a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
index bbe94dc..c5e76f9 100644
--- a/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
+++ b/room/room-runtime/src/androidUnitTest/kotlin/androidx/room/InvalidationTrackerTest.kt
@@ -19,6 +19,8 @@
 import androidx.annotation.RequiresApi
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
+import androidx.room.concurrent.AtomicBoolean
+import androidx.room.concurrent.AtomicInt
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteDriver
 import androidx.sqlite.SQLiteStatement
@@ -27,7 +29,6 @@
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
 import kotlin.collections.removeFirst as removeFirstKt
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.cancelAndJoin
@@ -346,12 +347,12 @@
     fun refreshAndCloseDbWithSlowObserver() = runTest {
         // Validates that a slow observer will finish notification after database closing
         val invalidatedLatch = CountDownLatch(1)
-        val invalidated = atomic(false)
+        val invalidated = AtomicBoolean(false)
         tracker.addObserver(
             object : InvalidationTracker.Observer("a") {
                 override fun onInvalidated(tables: Set<String>) {
                     invalidatedLatch.countDown()
-                    assertThat(invalidated.compareAndSet(expect = false, update = true)).isTrue()
+                    assertThat(invalidated.compareAndSet(false, true)).isTrue()
                     runBlocking { delay(100) }
                 }
             }
@@ -361,7 +362,7 @@
         testScheduler.advanceUntilIdle()
         invalidatedLatch.await()
         roomDatabase.close()
-        assertThat(invalidated.value).isTrue()
+        assertThat(invalidated.get()).isTrue()
     }
 
     @Test
@@ -473,7 +474,7 @@
 
     @Test
     fun weakObserver() = runTest {
-        val invalidated = atomic(0)
+        val invalidated = AtomicInt(0)
         var observer: InvalidationTracker.Observer? =
             object : InvalidationTracker.Observer("a") {
                 override fun onInvalidated(tables: Set<String>) {
@@ -484,7 +485,7 @@
 
         sqliteDriver.setInvalidatedTables(0)
         tracker.awaitRefreshAsync()
-        assertThat(invalidated.value).isEqualTo(1)
+        assertThat(invalidated.get()).isEqualTo(1)
 
         // Attempt to perform garbage collection in a loop so that weak observer is discarded
         // and it stops receiving invalidation notifications. If GC fails to collect the observer
@@ -511,7 +512,7 @@
 
         sqliteDriver.setInvalidatedTables(0)
         tracker.awaitRefreshAsync()
-        assertThat(invalidated.value).isEqualTo(1)
+        assertThat(invalidated.get()).isEqualTo(1)
     }
 
     @Test
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
index 1997321..5730b60 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/InvalidationTracker.kt
@@ -18,16 +18,16 @@
 
 import androidx.annotation.RestrictTo
 import androidx.room.Transactor.SQLiteTransactionType
+import androidx.room.concurrent.AtomicBoolean
+import androidx.room.concurrent.ReentrantLock
 import androidx.room.concurrent.ifNotClosed
+import androidx.room.concurrent.withLock
 import androidx.room.util.getCoroutineContext
 import androidx.sqlite.SQLiteConnection
 import androidx.sqlite.SQLiteException
 import androidx.sqlite.execSQL
 import kotlin.jvm.JvmOverloads
 import kotlin.jvm.JvmSuppressWildcards
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.locks.reentrantLock
-import kotlinx.atomicfu.locks.withLock
 import kotlinx.coroutines.CoroutineName
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.FlowCollector
@@ -162,7 +162,7 @@
      * queue to be done asynchronously, this flag is used to control excessive scheduling of
      * refreshes.
      */
-    private val pendingRefresh = atomic(false)
+    private val pendingRefresh = AtomicBoolean(false)
 
     /** Callback to allow or disallow [refreshInvalidation] from proceeding. */
     internal var onAllowRefresh: () -> Boolean = { true }
@@ -484,7 +484,7 @@
  */
 internal class ObservedTableStates(size: Int) {
 
-    private val lock = reentrantLock()
+    private val lock = ReentrantLock()
 
     // The number of observers per table
     private val tableObserversCount = LongArray(size)
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Atomics.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Atomics.kt
new file mode 100644
index 0000000..04abb13
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Atomics.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+
+package androidx.room.concurrent
+
+import androidx.annotation.RestrictTo
+
+expect class AtomicInt {
+    constructor(initialValue: Int)
+
+    fun get(): Int
+
+    fun set(value: Int)
+
+    fun compareAndSet(expect: Int, update: Int): Boolean
+
+    fun incrementAndGet(): Int
+
+    fun getAndIncrement(): Int
+
+    fun decrementAndGet(): Int
+}
+
+internal inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing {
+    while (true) {
+        action(get())
+    }
+}
+
+expect class AtomicBoolean {
+    constructor(initialValue: Boolean)
+
+    fun get(): Boolean
+
+    fun compareAndSet(expect: Boolean, update: Boolean): Boolean
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/CloseBarrier.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/CloseBarrier.kt
index 8bf4a24..e499315 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/CloseBarrier.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/CloseBarrier.kt
@@ -16,11 +16,6 @@
 
 package androidx.room.concurrent
 
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.locks.SynchronizedObject
-import kotlinx.atomicfu.locks.synchronized
-import kotlinx.atomicfu.loop
-
 /**
  * A barrier that can be used to perform a cleanup action once, waiting for registered parties
  * (blockers) to finish using the protected resource.
@@ -40,9 +35,10 @@
  *   blockers.
  */
 internal class CloseBarrier(private val closeAction: () -> Unit) : SynchronizedObject() {
-    private val blockers = atomic(0)
-    private val closeInitiated = atomic(false)
-    private val isClosed by closeInitiated
+    private val blockers = AtomicInt(0)
+    private val closeInitiated = AtomicBoolean(false)
+    private val isClosed: Boolean
+        get() = closeInitiated.get()
 
     /**
      * Blocks the [closeAction] from occurring.
@@ -72,7 +68,7 @@
     internal fun unblock(): Unit =
         synchronized(this) {
             blockers.decrementAndGet()
-            check(blockers.value >= 0) { "Unbalanced call to unblock() detected." }
+            check(blockers.get() >= 0) { "Unbalanced call to unblock() detected." }
         }
 
     /**
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt
index de1c2e5..2d34dd7 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ExclusiveLock.kt
@@ -16,11 +16,6 @@
 
 package androidx.room.concurrent
 
-import kotlinx.atomicfu.locks.ReentrantLock
-import kotlinx.atomicfu.locks.SynchronizedObject
-import kotlinx.atomicfu.locks.reentrantLock
-import kotlinx.atomicfu.locks.synchronized
-
 /**
  * An exclusive lock for in-process and multi-process synchronization.
  *
@@ -59,7 +54,7 @@
 
         private fun getThreadLock(key: String): ReentrantLock =
             synchronized(this) {
-                return threadLocksMap.getOrPut(key) { reentrantLock() }
+                return threadLocksMap.getOrPut(key) { ReentrantLock() }
             }
 
         private fun getFileLock(key: String) = FileLock(key)
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ReentrantLock.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ReentrantLock.kt
new file mode 100644
index 0000000..444ef7c
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/ReentrantLock.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal expect class ReentrantLock() {
+    fun lock(): Unit
+
+    fun tryLock(): Boolean
+
+    fun unlock(): Unit
+}
+
+internal inline fun <T> ReentrantLock.withLock(block: () -> T): T {
+    lock()
+    try {
+        return block()
+    } finally {
+        unlock()
+    }
+}
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Synchronized.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Synchronized.kt
new file mode 100644
index 0000000..1515bdf
--- /dev/null
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/concurrent/Synchronized.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal expect open class SynchronizedObject()
+
+internal expect inline fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T
diff --git a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
index 185e412..ae49a2e 100644
--- a/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
+++ b/room/room-runtime/src/commonMain/kotlin/androidx/room/coroutines/ConnectionPoolImpl.kt
@@ -19,6 +19,8 @@
 import androidx.room.TransactionScope
 import androidx.room.Transactor
 import androidx.room.Transactor.SQLiteTransactionType
+import androidx.room.concurrent.AtomicBoolean
+import androidx.room.concurrent.AtomicInt
 import androidx.room.concurrent.ThreadLocal
 import androidx.room.concurrent.asContextElement
 import androidx.room.concurrent.currentThreadId
@@ -35,7 +37,6 @@
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.coroutineContext
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.sync.Mutex
@@ -50,8 +51,9 @@
 
     private val threadLocal = ThreadLocal<PooledConnectionImpl>()
 
-    private val _isClosed = atomic(false)
-    private val isClosed by _isClosed
+    private val _isClosed = AtomicBoolean(false)
+    private val isClosed: Boolean
+        get() = _isClosed.get()
 
     // Amount of time to wait to acquire a connection before throwing, Android uses 30 seconds in
     // its pool, so we do too here, but IDK if that is a good number. This timeout is unrelated to
@@ -195,7 +197,7 @@
 }
 
 private class Pool(val capacity: Int, val connectionFactory: () -> SQLiteConnection) {
-    private val size = atomic(0)
+    private val size = AtomicInt(0)
     private val connections = arrayOfNulls<ConnectionWithLock>(capacity)
     private val channel =
         Channel<ConnectionWithLock>(capacity = capacity, onUndeliveredElement = { recycle(it) })
@@ -211,7 +213,7 @@
     }
 
     private fun tryOpenNewConnection() {
-        val currentSize = size.value
+        val currentSize = size.get()
         if (currentSize >= capacity) {
             // Capacity reached
             return
@@ -322,8 +324,9 @@
 ) : Transactor, RawConnectionAccessor {
     private val transactionStack = ArrayDeque<TransactionItem>()
 
-    private val _isRecycled = atomic(false)
-    private val isRecycled by _isRecycled
+    private val _isRecycled = AtomicBoolean(false)
+    private val isRecycled: Boolean
+        get() = _isRecycled.get()
 
     override val rawConnection: SQLiteConnection
         get() = delegate
diff --git a/room/room-runtime/src/commonTest/kotlin/androidx/room/concurrent/CloseBarrierTest.kt b/room/room-runtime/src/commonTest/kotlin/androidx/room/concurrent/CloseBarrierTest.kt
index 75f64d1..3133b3a 100644
--- a/room/room-runtime/src/commonTest/kotlin/androidx/room/concurrent/CloseBarrierTest.kt
+++ b/room/room-runtime/src/commonTest/kotlin/androidx/room/concurrent/CloseBarrierTest.kt
@@ -19,7 +19,6 @@
 import androidx.kruth.assertThat
 import androidx.kruth.assertThrows
 import kotlin.test.Test
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.launch
@@ -34,9 +33,9 @@
     @Test
     @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
     fun oneBlocker() = runTest {
-        val actionPerformed = atomic(false)
+        val actionPerformed = AtomicBoolean(false)
         val closeBarrier = CloseBarrier {
-            assertThat(actionPerformed.compareAndSet(expect = false, update = true)).isTrue()
+            assertThat(actionPerformed.compareAndSet(false, true)).isTrue()
         }
         val jobLaunched = Mutex(locked = true)
 
@@ -52,14 +51,14 @@
 
         // yield for launch and verify the close action has not been performed
         yield()
-        jobLaunched.withLock { assertThat(actionPerformed.value).isFalse() }
+        jobLaunched.withLock { assertThat(actionPerformed.get()).isFalse() }
 
         // unblock the barrier, close job should complete
         closeBarrier.unblock()
         closeJob.join()
 
         // verify action was performed
-        assertThat(actionPerformed.value).isTrue()
+        assertThat(actionPerformed.get()).isTrue()
 
         // verify a new block is not granted since the barrier is already close
         assertThat(closeBarrier.block()).isFalse()
@@ -67,15 +66,15 @@
 
     @Test
     fun noBlockers() = runTest {
-        val actionPerformed = atomic(false)
+        val actionPerformed = AtomicBoolean(false)
         val closeBarrier = CloseBarrier {
-            assertThat(actionPerformed.compareAndSet(expect = false, update = true)).isTrue()
+            assertThat(actionPerformed.compareAndSet(false, true)).isTrue()
         }
 
         // Validate close action is performed immediately if there are no blockers
         closeBarrier.close()
 
-        assertThat(actionPerformed.value).isTrue()
+        assertThat(actionPerformed.get()).isTrue()
     }
 
     @Test
@@ -89,9 +88,9 @@
     @Test
     @OptIn(ExperimentalCoroutinesApi::class, DelicateCoroutinesApi::class)
     fun noStarvation() = runTest {
-        val actionPerformed = atomic(false)
+        val actionPerformed = AtomicBoolean(false)
         val closeBarrier = CloseBarrier {
-            assertThat(actionPerformed.compareAndSet(expect = false, update = true)).isTrue()
+            assertThat(actionPerformed.compareAndSet(false, true)).isTrue()
         }
         val jobLaunched = Mutex(locked = true)
 
@@ -111,12 +110,12 @@
         // yield for launch and verify the close action has not been performed in an attempt to
         // get the block / unblock loop going
         yield()
-        jobLaunched.withLock { assertThat(actionPerformed.value).isFalse() }
+        jobLaunched.withLock { assertThat(actionPerformed.get()).isFalse() }
 
         // initiate the close action, test should not deadlock (or timeout) meaning the barrier
         // will not cause the caller to starve
         closeBarrier.close()
         blockerJob.join()
-        assertThat(actionPerformed.value).isTrue()
+        assertThat(actionPerformed.get()).isTrue()
     }
 }
diff --git a/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt b/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
index cff6dbd..f86b7b6 100644
--- a/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
+++ b/room/room-runtime/src/commonTest/kotlin/androidx/room/coroutines/BaseConnectionPoolTest.kt
@@ -19,6 +19,7 @@
 import androidx.kruth.assertThat
 import androidx.room.PooledConnection
 import androidx.room.Transactor
+import androidx.room.concurrent.AtomicInt
 import androidx.room.deferredTransaction
 import androidx.room.exclusiveTransaction
 import androidx.room.execSQL
@@ -34,7 +35,6 @@
 import kotlin.test.Test
 import kotlin.test.assertFailsWith
 import kotlin.test.fail
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.DelicateCoroutinesApi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -463,7 +463,7 @@
     @Test
     fun singleConnectionPool() = runTest {
         val multiThreadContext = newFixedThreadPoolContext(2, "Test-Threads")
-        val connectionsOpened = atomic(0)
+        val connectionsOpened = AtomicInt(0)
         val actualDriver = setupDriver()
         val driver =
             object : SQLiteDriver by actualDriver {
@@ -487,12 +487,12 @@
         jobs.joinAll()
         pool.close()
         multiThreadContext.close()
-        assertThat(connectionsOpened.value).isEqualTo(1)
+        assertThat(connectionsOpened.get()).isEqualTo(1)
     }
 
     @Test
     fun openOneConnectionWhenUsedSerially() = runTest {
-        val connectionsOpened = atomic(0)
+        val connectionsOpened = AtomicInt(0)
         val actualDriver = setupDriver()
         val driver =
             object : SQLiteDriver by actualDriver {
@@ -518,7 +518,7 @@
             }
         }
         pool.close()
-        assertThat(connectionsOpened.value).isEqualTo(1)
+        assertThat(connectionsOpened.get()).isEqualTo(1)
     }
 
     @Test
@@ -689,7 +689,7 @@
                 actual.close()
             }
         }
-        val connectionArrCount = atomic(0)
+        val connectionArrCount = AtomicInt(0)
         val connectionsArr = arrayOfNulls<CloseAwareConnection>(4)
         val actualDriver = setupDriver()
         val driver =
@@ -714,7 +714,7 @@
                 launch(multiThreadContext) { pool.useReaderConnection { barrier.withLock {} } }
             jobs.add(job)
         }
-        while (connectionArrCount.value < 4) {
+        while (connectionArrCount.get() < 4) {
             delay(100)
         }
         barrier.unlock()
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Atomics.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Atomics.jvmAndroid.kt
new file mode 100644
index 0000000..00255dc
--- /dev/null
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Atomics.jvmAndroid.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+
+package androidx.room.concurrent
+
+import androidx.annotation.RestrictTo
+
+actual typealias AtomicInt = java.util.concurrent.atomic.AtomicInteger
+
+actual typealias AtomicBoolean = java.util.concurrent.atomic.AtomicBoolean
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/ReentrantLock.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/ReentrantLock.jvmAndroid.kt
new file mode 100644
index 0000000..dc8fdab
--- /dev/null
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/ReentrantLock.jvmAndroid.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal actual typealias ReentrantLock = java.util.concurrent.locks.ReentrantLock
diff --git a/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Synchronized.jvmAndroid.kt b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Synchronized.jvmAndroid.kt
new file mode 100644
index 0000000..fae30d5
--- /dev/null
+++ b/room/room-runtime/src/jvmAndroidMain/kotlin/androidx/room/concurrent/Synchronized.jvmAndroid.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal actual typealias SynchronizedObject = Any
+
+internal actual inline fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T =
+    kotlin.synchronized(lock, block)
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Atomics.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Atomics.native.kt
new file mode 100644
index 0000000..46b4012
--- /dev/null
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Atomics.native.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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.
+ */
+
+@file:RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+
+package androidx.room.concurrent
+
+import androidx.annotation.RestrictTo
+import kotlin.concurrent.AtomicInt as KotlinAtomicInt
+
+actual class AtomicInt actual constructor(initialValue: Int) {
+    private val delegate: KotlinAtomicInt = KotlinAtomicInt(initialValue)
+
+    actual fun get(): Int = delegate.value
+
+    actual fun set(value: Int) {
+        delegate.value = value
+    }
+
+    actual fun compareAndSet(expect: Int, update: Int): Boolean =
+        delegate.compareAndSet(expect, update)
+
+    actual fun incrementAndGet(): Int = delegate.incrementAndGet()
+
+    actual fun getAndIncrement(): Int = delegate.getAndIncrement()
+
+    actual fun decrementAndGet(): Int = delegate.decrementAndGet()
+}
+
+actual class AtomicBoolean actual constructor(initialValue: Boolean) {
+    private val delegate: KotlinAtomicInt = KotlinAtomicInt(toInt(initialValue))
+
+    actual fun get(): Boolean = delegate.value == 1
+
+    actual fun compareAndSet(expect: Boolean, update: Boolean): Boolean =
+        delegate.compareAndSet(toInt(expect), toInt(update))
+
+    private fun toInt(value: Boolean) = if (value) 1 else 0
+}
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/ReentrantLock.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/ReentrantLock.native.kt
new file mode 100644
index 0000000..544fa01
--- /dev/null
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/ReentrantLock.native.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal actual typealias ReentrantLock = kotlinx.atomicfu.locks.SynchronizedObject
diff --git a/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Synchronized.native.kt b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Synchronized.native.kt
new file mode 100644
index 0000000..7691045
--- /dev/null
+++ b/room/room-runtime/src/nativeMain/kotlin/androidx/room/concurrent/Synchronized.native.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * 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 androidx.room.concurrent
+
+internal actual typealias SynchronizedObject = kotlinx.atomicfu.locks.SynchronizedObject
+
+internal actual inline fun <T> synchronized(lock: SynchronizedObject, block: () -> T): T =
+    kotlinx.atomicfu.locks.synchronized(lock, block)