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,8 +676,10 @@ 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) {
innerRoom.typingNotice(isTyping) runCatching {
innerRoom.typingNotice(isTyping)
}
} }
override suspend fun generateWidgetWebViewUrl( override suspend fun generateWidgetWebViewUrl(
@ -685,32 +687,42 @@ class RustMatrixRoom(
clientId: String, clientId: String,
languageTag: String?, languageTag: String?,
theme: String?, theme: String?,
) = runCatching { ) = withContext(roomDispatcher) {
widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme) runCatching {
widgetSettings.generateWidgetWebViewUrl(innerRoom, clientId, languageTag, theme)
}
} }
override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> = runCatching { override fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver> {
RustWidgetDriver( return runCatching {
widgetSettings = widgetSettings, RustWidgetDriver(
room = innerRoom, widgetSettings = widgetSettings,
widgetCapabilitiesProvider = object : WidgetCapabilitiesProvider { room = innerRoom,
override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities { widgetCapabilitiesProvider = object : WidgetCapabilitiesProvider {
return getElementCallRequiredPermissions(sessionId.value, deviceId.value) override fun acquireCapabilities(capabilities: WidgetCapabilities): WidgetCapabilities {
} return getElementCallRequiredPermissions(sessionId.value, deviceId.value)
}, }
) },
)
}
} }
override suspend fun getPermalink(): Result<String> = runCatching { override suspend fun getPermalink(): Result<String> = withContext(roomDispatcher) {
innerRoom.matrixToPermalink() runCatching {
innerRoom.matrixToPermalink()
}
} }
override suspend fun getPermalinkFor(eventId: EventId): Result<String> = runCatching { override suspend fun getPermalinkFor(eventId: EventId): Result<String> = withContext(roomDispatcher) {
innerRoom.matrixToEventPermalink(eventId.value) runCatching {
innerRoom.matrixToEventPermalink(eventId.value)
}
} }
override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = runCatching { override suspend fun sendCallNotificationIfNeeded(): Result<Unit> = withContext(roomDispatcher) {
innerRoom.sendCallNotificationIfNeeded() runCatching {
innerRoom.sendCallNotificationIfNeeded()
}
} }
override suspend fun setSendQueueEnabled(enabled: Boolean) { override suspend fun setSendQueueEnabled(enabled: Boolean) {
@ -722,35 +734,45 @@ class RustMatrixRoom(
} }
} }
override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = runCatching { override suspend fun saveComposerDraft(composerDraft: ComposerDraft): Result<Unit> = withContext(roomDispatcher) {
Timber.d("saveComposerDraft: $composerDraft into $roomId") runCatching {
innerRoom.saveComposerDraft(composerDraft.into()) Timber.d("saveComposerDraft: $composerDraft into $roomId")
innerRoom.saveComposerDraft(composerDraft.into())
}
} }
override suspend fun loadComposerDraft(): Result<ComposerDraft?> = runCatching { override suspend fun loadComposerDraft(): Result<ComposerDraft?> = withContext(roomDispatcher) {
Timber.d("loadComposerDraft for $roomId") runCatching {
innerRoom.loadComposerDraft()?.into() Timber.d("loadComposerDraft for $roomId")
innerRoom.loadComposerDraft()?.into()
}
} }
override suspend fun clearComposerDraft(): Result<Unit> = runCatching { override suspend fun clearComposerDraft(): Result<Unit> = withContext(roomDispatcher) {
Timber.d("clearComposerDraft for $roomId") runCatching {
innerRoom.clearComposerDraft() Timber.d("clearComposerDraft for $roomId")
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) {
innerRoom.ignoreDeviceTrustAndResend( runCatching {
devices = devices.entries.associate { entry -> innerRoom.ignoreDeviceTrustAndResend(
entry.key.value to entry.value.map { it.value } devices = devices.entries.associate { entry ->
}, entry.key.value to entry.value.map { it.value }
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) {
innerRoom.withdrawVerificationAndResend( runCatching {
userIds = userIds.map { it.value }, innerRoom.withdrawVerificationAndResend(
sendHandle = (sendHandle as RustSendHandle).inner, userIds = userIds.map { it.value },
) sendHandle = (sendHandle as RustSendHandle).inner,
)
}
} }
private fun createTimeline( private fun createTimeline(

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,31 +25,36 @@ 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) {
if (!isServiceReady.get()) { runCatching {
Timber.d("Can't start sync: service is not ready") if (!isServiceReady.get()) {
return@runCatching Timber.d("Can't start sync: service is not ready")
return@runCatching
}
Timber.i("Start sync")
inner.start()
}.onFailure {
Timber.d("Start sync failed: $it")
} }
Timber.i("Start sync")
innerSyncService.start()
}.onFailure {
Timber.d("Start sync failed: $it")
} }
override suspend fun stopSync() = runCatching { override suspend fun stopSync() = withContext(dispatcher) {
if (!isServiceReady.get()) { runCatching {
Timber.d("Can't stop sync: service is not ready") if (!isServiceReady.get()) {
return@runCatching Timber.d("Can't stop sync: service is not ready")
return@runCatching
}
Timber.i("Stop sync")
inner.stop()
}.onFailure {
Timber.d("Stop sync failed: $it")
} }
Timber.i("Stop sync")
innerSyncService.stop()
}.onFailure {
Timber.d("Stop sync failed: $it")
} }
suspend fun destroy() = withContext(NonCancellable) { suspend fun destroy() = withContext(NonCancellable) {
@ -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)
} }
} }