Merge pull request #4146 from element-hq/feature/fga/suspend_unsafe_sdk_method

fix(coroutine) : make sure to switch coroutine context
This commit is contained in:
ganfra 2025-01-15 11:50:04 +01:00 committed by GitHub
commit 2ae511ee33
4 changed files with 118 additions and 86 deletions

View file

@ -137,7 +137,11 @@ class RustMatrixClient(
private val innerRoomListService = innerSyncService.roomListService() private val innerRoomListService = innerSyncService.roomListService()
private val rustSyncService = RustSyncService(innerSyncService, sessionCoroutineScope) private val rustSyncService = RustSyncService(
inner = innerSyncService,
dispatcher = sessionDispatcher,
sessionCoroutineScope = sessionCoroutineScope
)
private val pushersService = RustPushersService( private val pushersService = RustPushersService(
client = innerClient, client = innerClient,
dispatchers = dispatchers, dispatchers = dispatchers,
@ -283,8 +287,8 @@ class RustMatrixClient(
} }
} }
override suspend fun findDM(userId: UserId): RoomId? { override suspend fun findDM(userId: UserId): RoomId? = withContext(sessionDispatcher) {
return innerClient.getDmRoom(userId.value)?.use { RoomId(it.id()) } innerClient.getDmRoom(userId.value)?.use { RoomId(it.id()) }
} }
override suspend fun ignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) { override suspend fun ignoreUser(userId: UserId): Result<Unit> = withContext(sessionDispatcher) {

View file

@ -329,7 +329,7 @@ class RustMatrixRoom(
} }
} }
override suspend fun updateRoomNotificationSettings(): Result<Unit> = withContext(coroutineDispatchers.io) { override suspend fun updateRoomNotificationSettings(): Result<Unit> = withContext(roomDispatcher) {
val currentState = _roomNotificationSettingsStateFlow.value val currentState = _roomNotificationSettingsStateFlow.value
val currentRoomNotificationSettings = currentState.roomNotificationSettings() val currentRoomNotificationSettings = currentState.roomNotificationSettings()
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings) _roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
@ -345,7 +345,7 @@ class RustMatrixRoom(
} }
} }
override suspend fun userRole(userId: UserId): Result<RoomMember.Role> = withContext(coroutineDispatchers.io) { override suspend fun userRole(userId: UserId): Result<RoomMember.Role> = withContext(roomDispatcher) {
runCatching { runCatching {
RoomMemberMapper.mapRole(innerRoom.suggestedRoleForUser(userId.value)) RoomMemberMapper.mapRole(innerRoom.suggestedRoleForUser(userId.value))
} }
@ -429,56 +429,56 @@ class RustMatrixRoom(
} }
} }
override suspend fun canUserInvite(userId: UserId): Result<Boolean> { override suspend fun canUserInvite(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserInvite(userId.value) innerRoom.canUserInvite(userId.value)
} }
} }
override suspend fun canUserKick(userId: UserId): Result<Boolean> { override suspend fun canUserKick(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserKick(userId.value) innerRoom.canUserKick(userId.value)
} }
} }
override suspend fun canUserBan(userId: UserId): Result<Boolean> { override suspend fun canUserBan(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserBan(userId.value) innerRoom.canUserBan(userId.value)
} }
} }
override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> { override suspend fun canUserRedactOwn(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserRedactOwn(userId.value) innerRoom.canUserRedactOwn(userId.value)
} }
} }
override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> { override suspend fun canUserRedactOther(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserRedactOther(userId.value) innerRoom.canUserRedactOther(userId.value)
} }
} }
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> { override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserSendState(userId.value, type.map()) innerRoom.canUserSendState(userId.value, type.map())
} }
} }
override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> { override suspend fun canUserSendMessage(userId: UserId, type: MessageEventType): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserSendMessage(userId.value, type.map()) innerRoom.canUserSendMessage(userId.value, type.map())
} }
} }
override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> { override suspend fun canUserTriggerRoomNotification(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserTriggerRoomNotification(userId.value) innerRoom.canUserTriggerRoomNotification(userId.value)
} }
} }
override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> { override suspend fun canUserPinUnpin(userId: UserId): Result<Boolean> = withContext(roomDispatcher) {
return runCatching { runCatching {
innerRoom.canUserPinUnpin(userId.value) innerRoom.canUserPinUnpin(userId.value)
} }
} }
@ -676,20 +676,25 @@ class RustMatrixRoom(
return liveTimeline.sendVoiceMessage(file, audioInfo, waveform, progressCallback) return liveTimeline.sendVoiceMessage(file, audioInfo, waveform, progressCallback)
} }
override suspend fun typingNotice(isTyping: Boolean) = runCatching { override suspend fun typingNotice(isTyping: Boolean) = withContext(roomDispatcher) {
runCatching {
innerRoom.typingNotice(isTyping) innerRoom.typingNotice(isTyping)
} }
}
override suspend fun generateWidgetWebViewUrl( override suspend fun generateWidgetWebViewUrl(
widgetSettings: MatrixWidgetSettings, widgetSettings: MatrixWidgetSettings,
clientId: String, clientId: String,
languageTag: String?, languageTag: String?,
theme: String?, theme: String?,
) = runCatching { ) = withContext(roomDispatcher) {
runCatching {
widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme) widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme)
} }
}
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = runCatching { override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> {
return runCatching {
RustWidgetDriver( RustWidgetDriver(
widgetSettings = widgetSettings, widgetSettings = widgetSettings,
room = innerRoom, room = innerRoom,
@ -700,18 +705,25 @@ class RustMatrixRoom(
}, },
) )
} }
}
override suspend fun getPermalink(): Result<String> = runCatching { override suspend fun getPermalink(): Result<String> = withContext(roomDispatcher) {
runCatching {
innerRoom.matrixToPermalink() innerRoom.matrixToPermalink()
} }
override suspend fun getPermalinkFor(eventId: EventId): Result<String> = runCatching {
innerRoom.matrixToEventPermalink(eventId.value)
} }
override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = runCatching { override suspend fun getPermalinkFor(eventId: EventId): Result<String> = withContext(roomDispatcher) {
runCatching {
innerRoom.matrixToEventPermalink(eventId.value)
}
}
override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = withContext(roomDispatcher) {
runCatching {
innerRoom.sendCallNotificationIfNeeded() innerRoom.sendCallNotificationIfNeeded()
} }
}
override suspend fun setSendQueueEnabled(enabled: Boolean) { override suspend fun setSendQueueEnabled(enabled: Boolean) {
withContext(roomDispatcher) { withContext(roomDispatcher) {
@ -722,22 +734,29 @@ class RustMatrixRoom(
} }
} }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = runCatching { override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = withContext(roomDispatcher) {
runCatching {
Timber.d("saveComposerDraft: $composerDraft into $roomId") Timber.d("saveComposerDraft: $composerDraft into $roomId")
innerRoom.saveComposerDraft(composerDraft.into()) innerRoom.saveComposerDraft(composerDraft.into())
} }
}
override suspend fun loadComposerDraft(): Result<ComposerDraft?> = runCatching { override suspend fun loadComposerDraft(): Result<ComposerDraft?> = withContext(roomDispatcher) {
runCatching {
Timber.d("loadComposerDraft for $roomId") Timber.d("loadComposerDraft for $roomId")
innerRoom.loadComposerDraft()?.into() innerRoom.loadComposerDraft()?.into()
} }
}
override suspend fun clearComposerDraft(): Result<Unit> = runCatching { override suspend fun clearComposerDraft(): Result<Unit> = withContext(roomDispatcher) {
runCatching {
Timber.d("clearComposerDraft for $roomId") Timber.d("clearComposerDraft for $roomId")
innerRoom.clearComposerDraft() innerRoom.clearComposerDraft()
} }
}
override suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle) = runCatching { override suspend fun ignoreDeviceTrustAndResend(devices: Map<UserId, List<DeviceId>>, sendHandle: SendHandle) = withContext(roomDispatcher) {
runCatching {
innerRoom.ignoreDeviceTrustAndResend( innerRoom.ignoreDeviceTrustAndResend(
devices = devices.entries.associate { entry -> devices = devices.entries.associate { entry ->
entry.key.value to entry.value.map { it.value } entry.key.value to entry.value.map { it.value }
@ -745,13 +764,16 @@ class RustMatrixRoom(
sendHandle = (sendHandle as RustSendHandle).inner, sendHandle = (sendHandle as RustSendHandle).inner,
) )
} }
}
override suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle) = runCatching { override suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle) = withContext(roomDispatcher) {
runCatching {
innerRoom.withdrawVerificationAndResend( innerRoom.withdrawVerificationAndResend(
userIds = userIds.map { it.value }, userIds = userIds.map { it.value },
sendHandle = (sendHandle as RustSendHandle).inner, sendHandle = (sendHandle as RustSendHandle).inner,
) )
} }
}
private fun createTimeline( private fun createTimeline(
timeline: InnerTimeline, timeline: InnerTimeline,

View file

@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.sync
import io.element.android.libraries.matrix.api.sync.SyncService import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.sync.SyncState
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@ -24,32 +25,37 @@ import java.util.concurrent.atomic.AtomicBoolean
import org.matrix.rustcomponents.sdk.SyncService as InnerSyncService import org.matrix.rustcomponents.sdk.SyncService as InnerSyncService
class RustSyncService( class RustSyncService(
private val innerSyncService: InnerSyncService, private val inner: InnerSyncService,
private val dispatcher: CoroutineDispatcher,
sessionCoroutineScope: CoroutineScope sessionCoroutineScope: CoroutineScope
) : SyncService { ) : SyncService {
private val isServiceReady = AtomicBoolean(true) private val isServiceReady = AtomicBoolean(true)
override suspend fun startSync() = runCatching { override suspend fun startSync() = withContext(dispatcher) {
runCatching {
if (!isServiceReady.get()) { if (!isServiceReady.get()) {
Timber.d("Can't start sync: service is not ready") Timber.d("Can't start sync: service is not ready")
return@runCatching return@runCatching
} }
Timber.i("Start sync") Timber.i("Start sync")
innerSyncService.start() inner.start()
}.onFailure { }.onFailure {
Timber.d("Start sync failed: $it") Timber.d("Start sync failed: $it")
} }
}
override suspend fun stopSync() = runCatching { override suspend fun stopSync() = withContext(dispatcher) {
runCatching {
if (!isServiceReady.get()) { if (!isServiceReady.get()) {
Timber.d("Can't stop sync: service is not ready") Timber.d("Can't stop sync: service is not ready")
return@runCatching return@runCatching
} }
Timber.i("Stop sync") Timber.i("Stop sync")
innerSyncService.stop() inner.stop()
}.onFailure { }.onFailure {
Timber.d("Stop sync failed: $it") Timber.d("Stop sync failed: $it")
} }
}
suspend fun destroy() = withContext(NonCancellable) { suspend fun destroy() = withContext(NonCancellable) {
// If the service was still running, stop it // If the service was still running, stop it
@ -59,7 +65,7 @@ class RustSyncService(
} }
override val syncState: StateFlow<SyncState> = override val syncState: StateFlow<SyncState> =
innerSyncService.stateFlow() inner.stateFlow()
.map(SyncServiceState::toSyncState) .map(SyncServiceState::toSyncState)
.onEach { state -> .onEach { state ->
Timber.i("Sync state=$state") Timber.i("Sync state=$state")

View file

@ -158,8 +158,8 @@ class RustTimeline(
override val membershipChangeEventReceived: Flow<Unit> = timelineDiffProcessor.membershipChangeEventReceived override val membershipChangeEventReceived: Flow<Unit> = timelineDiffProcessor.membershipChangeEventReceived
override suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit> { override suspend fun sendReadReceipt(eventId: EventId, receiptType: ReceiptType): Result<Unit> = withContext(dispatcher) {
return runCatching { runCatching {
inner.sendReadReceipt(receiptType.toRustReceiptType(), eventId.value) inner.sendReadReceipt(receiptType.toRustReceiptType(), eventId.value)
} }
} }
@ -590,8 +590,8 @@ class RustTimeline(
} }
} }
private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> { private suspend fun fetchDetailsForEvent(eventId: EventId): Result<Unit> = withContext(dispatcher) {
return runCatching { runCatching {
inner.fetchDetailsForEvent(eventId.value) inner.fetchDetailsForEvent(eventId.value)
} }
} }