Make RustMatrixClient.close asynchronous (#4513)

* Make `RustMatrixClient.close` asynchronous

This is a safer way to destroy the Rust instances associated to it. Since `MatrixClient` doesn't implement `Closeable` anymore, the method has been renamed to `destroy` to follow the existing naming in the project.
This commit is contained in:
Jorge Martin Espinosa 2025-04-02 11:52:37 +02:00 committed by GitHub
parent e45151d9d1
commit 19c58489d0
5 changed files with 20 additions and 25 deletions

View file

@ -42,10 +42,9 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import java.io.Closeable
import java.util.Optional import java.util.Optional
interface MatrixClient : Closeable { interface MatrixClient {
val sessionId: SessionId val sessionId: SessionId
val deviceId: DeviceId val deviceId: DeviceId
val userProfile: StateFlow<MatrixUser> val userProfile: StateFlow<MatrixUser>

View file

@ -476,16 +476,13 @@ class RustMatrixClient(
override fun roomDirectoryService(): RoomDirectoryService = roomDirectoryService override fun roomDirectoryService(): RoomDirectoryService = roomDirectoryService
override fun close() { internal suspend fun destroy() {
innerNotificationClient.close() innerNotificationClient.close()
appCoroutineScope.launch { roomFactory.destroy()
roomFactory.destroy() rustSyncService.destroy()
rustSyncService.destroy() notificationSettingsService.destroy()
notificationSettingsService.destroy() notificationProcessSetup.destroy()
// This is sync, but it can destroy the `Client` instance and block stopping the sync service
notificationProcessSetup.destroy()
}
sessionCoroutineScope.cancel() sessionCoroutineScope.cancel()
clientDelegateTaskHandle?.cancelAndDestroy() clientDelegateTaskHandle?.cancelAndDestroy()
@ -504,7 +501,7 @@ class RustMatrixClient(
override suspend fun clearCache() { override suspend fun clearCache() {
innerClient.clearCaches() innerClient.clearCaches()
close() destroy()
} }
override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) { override suspend fun logout(userInitiated: Boolean, ignoreSdkError: Boolean) {
@ -527,7 +524,7 @@ class RustMatrixClient(
} }
} }
} }
close() destroy()
deleteSessionDirectory() deleteSessionDirectory()
if (userInitiated) { if (userInitiated) {
@ -577,7 +574,7 @@ class RustMatrixClient(
throw it throw it
} }
} }
close() destroy()
deleteSessionDirectory() deleteSessionDirectory()
sessionStore.removeSession(sessionId.value) sessionStore.removeSession(sessionId.value)
}.onFailure { }.onFailure {

View file

@ -31,7 +31,7 @@ class RustMatrixClientFactoryTest {
val sut = createRustMatrixClientFactory() val sut = createRustMatrixClientFactory()
val result = sut.create(aSessionData()) val result = sut.create(aSessionData())
assertThat(result.sessionId).isEqualTo(SessionId("@alice:server.org")) assertThat(result.sessionId).isEqualTo(SessionId("@alice:server.org"))
result.close() result.destroy()
} }
} }

View file

@ -28,9 +28,10 @@ import java.io.File
class RustMatrixClientTest { class RustMatrixClientTest {
@Test @Test
fun `ensure that sessionId and deviceId can be retrieved from the client`() = runTest { fun `ensure that sessionId and deviceId can be retrieved from the client`() = runTest {
createRustMatrixClient().use { sut -> createRustMatrixClient().run {
assertThat(sut.sessionId).isEqualTo(A_USER_ID) assertThat(sessionId).isEqualTo(A_USER_ID)
assertThat(sut.deviceId).isEqualTo(A_DEVICE_ID) assertThat(deviceId).isEqualTo(A_DEVICE_ID)
destroy()
} }
} }
@ -38,16 +39,16 @@ class RustMatrixClientTest {
fun `clear cache invokes the method clearCaches from the client and close it`() = runTest { fun `clear cache invokes the method clearCaches from the client and close it`() = runTest {
val clearCachesResult = lambdaRecorder<Unit> { } val clearCachesResult = lambdaRecorder<Unit> { }
val closeResult = lambdaRecorder<Unit> { } val closeResult = lambdaRecorder<Unit> { }
createRustMatrixClient( val client = createRustMatrixClient(
client = FakeRustClient( client = FakeRustClient(
clearCachesResult = clearCachesResult, clearCachesResult = clearCachesResult,
closeResult = closeResult, closeResult = closeResult,
) )
).use { sut -> )
sut.clearCache() client.clearCache()
clearCachesResult.assertions().isCalledOnce() clearCachesResult.assertions().isCalledOnce()
closeResult.assertions().isCalledOnce() closeResult.assertions().isCalledOnce()
} client.destroy()
} }
private fun TestScope.createRustMatrixClient( private fun TestScope.createRustMatrixClient(

View file

@ -181,8 +181,6 @@ class FakeMatrixClient(
deactivateAccountResult(password, eraseData) deactivateAccountResult(password, eraseData)
} }
override fun close() = Unit
override suspend fun getUserProfile(): Result<MatrixUser> = simulateLongTask { override suspend fun getUserProfile(): Result<MatrixUser> = simulateLongTask {
val result = getProfileResults[sessionId]?.getOrNull() ?: MatrixUser(sessionId, userDisplayName, userAvatarUrl) val result = getProfileResults[sessionId]?.getOrNull() ?: MatrixUser(sessionId, userDisplayName, userAvatarUrl)
_userProfile.tryEmit(result) _userProfile.tryEmit(result)