diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 42fa25d78b..706d5eb983 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -109,12 +109,13 @@ export const runBackendMobile = async () => { { logger: ['warn', 'error', 'log', 'debug', 'verbose'] } ) - rn_bridge.channel.on('close', async () => { + rn_bridge.channel.on('close', () => { const connectionsManager = app.get(ConnectionsManagerService) + connectionsManager.closeSocket() }) - rn_bridge.channel.on('open', async (msg: OpenServices) => { + rn_bridge.channel.on('open', (msg: OpenServices) => { const connectionsManager = app.get(ConnectionsManagerService) const torControl = app.get(TorControl) const proxyAgent = app.get<{ proxy: { port: string } }>(SOCKS_PROXY_AGENT) @@ -123,7 +124,11 @@ export const runBackendMobile = async () => { torControl.torControlParams.auth.value = msg.authCookie proxyAgent.proxy.port = msg.httpTunnelPort - await connectionsManager.openSocket() + // NOTE: There could be a race condition here. Nothing awaits + // these events and so it may be possible for the server to not be + // fully closed at this point which would result in `openSocket` + // failing to open the server. + connectionsManager.openSocket() }) } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 4763c66fef..a7095e9731 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -240,10 +240,8 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } } - public closeSocket() { - this.logger('Closing socket server') - // TODO: We should call this.socketService.close() instead - this.serverIoProvider.io.close() + public async closeSocket() { + await this.socketService.close() } // This method is only used on iOS through rn-bridge for reacting on lifecycle changes diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index 10b9797491..7e48861109 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -27,6 +27,8 @@ import { CONFIG_OPTIONS, SERVER_IO_PROVIDER } from '../const' import { ConfigOptions, ServerIoProviderTypes } from '../types' import { suspendableSocketEvents } from './suspendable.events' import Logger from '../common/logger' +import { sleep } from '../common/sleep' +import type net from 'node:net' @Injectable() export class SocketService extends EventEmitter implements OnModuleInit { @@ -34,6 +36,8 @@ export class SocketService extends EventEmitter implements OnModuleInit { public resolveReadyness: (value: void | PromiseLike) => void public readyness: Promise + private listening: boolean + private closeSockets: () => void constructor( @Inject(SERVER_IO_PROVIDER) public readonly serverIoProvider: ServerIoProviderTypes, @@ -44,12 +48,14 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.readyness = new Promise(resolve => { this.resolveReadyness = resolve }) + + this.listening = false + this.closeSockets = this.attachListeners() } async onModuleInit() { this.logger('init: Started') - this.attachListeners() await this.init() this.logger('init: Finished') @@ -71,7 +77,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.logger('init: Frontend connected') } - private readonly attachListeners = (): void => { + private readonly attachListeners = (): (() => void) => { // Attach listeners here this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { this.logger('Socket connection') @@ -195,25 +201,65 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.emit(SocketActionTypes.LOAD_MIGRATION_DATA, data) }) }) + + // Ensure the underlying connections get closed. See: + // https://github.com/socketio/socket.io/issues/1602 + // + // I also tried `this.serverIoProvider.io.disconnectSockets(true)` + // which didn't work for me. + const sockets = new Set() + + this.serverIoProvider.server.on('connection', conn => { + sockets.add(conn) + conn.on('close', () => { + sockets.delete(conn) + }) + }) + + return () => sockets.forEach(s => s.destroy()) + } + + public getConnections = (): Promise => { + return new Promise(resolve => { + this.serverIoProvider.server.getConnections((err, count) => { + if (err) throw new Error(err.message) + resolve(count) + }) + }) } public listen = async (port = this.configOptions.socketIOPort): Promise => { + this.logger(`Opening data server on port ${this.configOptions.socketIOPort}`) + + // Sometimes socket.io closes the HTTP server but doesn't close + // all underlying connections. So it doesn't appear that + // `this.serverIoProvider.server.listening` is sufficient. + if (this.listening) { + const numConnections = await this.getConnections() + this.logger('Failed to listen. Connections still open:', numConnections) + return + } + return await new Promise(resolve => { - if (this.serverIoProvider.server.listening) resolve() this.serverIoProvider.server.listen(this.configOptions.socketIOPort, '127.0.0.1', () => { this.logger(`Data server running on port ${this.configOptions.socketIOPort}`) + this.listening = true resolve() }) }) } - public close = async (): Promise => { - this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) - return await new Promise(resolve => { - this.serverIoProvider.server.close(err => { + public close = (): Promise => { + return new Promise(resolve => { + this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) + this.serverIoProvider.io.close(err => { if (err) throw new Error(err.message) + this.logger('Data server closed') + this.listening = false resolve() }) + this.logger('Disconnecting sockets') + this.closeSockets() }) } } diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index e9f9a220ae..42c4adb649 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -1,5 +1,17 @@ import { io } from 'socket.io-client' -import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply } from 'typed-redux-saga' +import { + select, + put, + call, + cancel, + fork, + take, + takeLeading, + takeEvery, + FixedTask, + delay, + apply, +} from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, Socket } from '@quiet/state-manager' import { encodeSecret } from '@quiet/common' @@ -36,17 +48,20 @@ export function* startConnectionSaga( }) yield* fork(handleSocketLifecycleActions, socket, action.payload) // Handle opening/restoring connection - yield* takeEvery(initActions.setWebsocketConnected, setConnectedSaga, socket) + yield* takeLeading(initActions.setWebsocketConnected, setConnectedSaga, socket) } function* setConnectedSaga(socket: Socket): Generator { + console.log('Frontend is ready. Forking state-manager sagas and starting backend...') + const task = yield* fork(stateManager.useIO, socket) - console.log('WEBSOCKET', 'Forking state-manager sagas', task) - // Handle suspending current connection - yield* takeEvery(initActions.suspendWebsocketConnection, cancelRootTaskSaga, task) - console.log('Frontend is ready. Starting backend...') + // @ts-ignore - Why is this broken? yield* apply(socket, socket.emit, [SocketActionTypes.START]) + + // Handle suspending current connection + const suspendAction = yield* take(initActions.suspendWebsocketConnection) + yield* call(cancelRootTaskSaga, task, suspendAction) } function* handleSocketLifecycleActions(socket: Socket, socketIOData: WebsocketConnectionPayload): Generator { diff --git a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts index 23ab6b8ba5..fba6757ce7 100644 --- a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts +++ b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts @@ -1,5 +1,5 @@ import { eventChannel } from 'redux-saga' -import { call, put, take } from 'typed-redux-saga' +import { call, put, take, cancelled } from 'typed-redux-saga' import { app, publicChannels, WEBSOCKET_CONNECTION_CHANNEL, INIT_CHECK_CHANNEL, network } from '@quiet/state-manager' import { initActions, InitCheckPayload, WebsocketConnectionPayload } from '../../init/init.slice' import { ScreenNames } from '../../../const/ScreenNames.enum' @@ -9,10 +9,18 @@ import { navigationActions } from '../../navigation/navigation.slice' import { nativeServicesActions } from '../nativeServices.slice' export function* nativeServicesCallbacksSaga(): Generator { - const channel = yield* call(deviceEvents) - while (true) { - const action = yield* take(channel) - yield put(action) + console.log('nativeServicesCallbacksSaga starting') + try { + const channel = yield* call(deviceEvents) + while (true) { + const action = yield* take(channel) + yield put(action) + } + } finally { + console.log('nativeServicesCallbacksSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesCallbacksSaga cancelled') + } } } diff --git a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts index 33df360946..47e2ff07e6 100644 --- a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts +++ b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts @@ -1,4 +1,4 @@ -import { select, call, put, takeLeading } from 'typed-redux-saga' +import { select, call, put } from 'typed-redux-saga' import { app } from '@quiet/state-manager' import { persistor } from '../../store' import { nativeServicesActions } from '../nativeServices.slice' @@ -9,11 +9,8 @@ import { ScreenNames } from '../../../../src/const/ScreenNames.enum' export function* leaveCommunitySaga(): Generator { console.log('Leaving community') - // Restart backend yield* put(app.actions.closeServices()) - - yield takeLeading(initActions.canceledRootTask.type, clearReduxStore) } export function* clearReduxStore(): Generator { diff --git a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts index 13f1fe441a..8544fbc563 100644 --- a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts +++ b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts @@ -1,13 +1,21 @@ -import { all, fork, takeEvery } from 'typed-redux-saga' +import { all, fork, takeEvery, cancelled } from 'typed-redux-saga' import { nativeServicesCallbacksSaga } from './events/nativeServicesCallbacks' import { leaveCommunitySaga } from './leaveCommunity/leaveCommunity.saga' import { flushPersistorSaga } from './flushPersistor/flushPersistor.saga' import { nativeServicesActions } from './nativeServices.slice' export function* nativeServicesMasterSaga(): Generator { - yield all([ - fork(nativeServicesCallbacksSaga), - takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), - takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), - ]) + console.log('nativeServicesMasterSaga starting') + try { + yield all([ + fork(nativeServicesCallbacksSaga), + takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), + takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), + ]) + } finally { + console.log('nativeServicesMasterSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesMasterSaga cancelled') + } + } } diff --git a/packages/mobile/src/store/root.saga.ts b/packages/mobile/src/store/root.saga.ts index b77b14dfb4..f1572f566e 100644 --- a/packages/mobile/src/store/root.saga.ts +++ b/packages/mobile/src/store/root.saga.ts @@ -1,4 +1,4 @@ -import { all, takeEvery, fork } from 'typed-redux-saga' +import { all, takeEvery, takeLeading, fork, cancelled } from 'typed-redux-saga' import { nativeServicesMasterSaga } from './nativeServices/nativeServices.master.saga' import { navigationMasterSaga } from './navigation/navigation.master.saga' import { initMasterSaga } from './init/init.master.saga' @@ -7,15 +7,25 @@ import { setupCryptoSaga } from './init/setupCrypto/setupCrypto.saga' import { publicChannels } from '@quiet/state-manager' import { showNotificationSaga } from './nativeServices/showNotification/showNotification.saga' import { restoreConnectionSaga } from './init/startConnection/restoreConnection/restoreConnection.saga' +import { clearReduxStore } from './nativeServices/leaveCommunity/leaveCommunity.saga' export function* rootSaga(): Generator { - yield all([ - takeEvery(initActions.setStoreReady.type, setupCryptoSaga), - takeEvery(initActions.setStoreReady.type, initMasterSaga), - takeEvery(initActions.setStoreReady.type, navigationMasterSaga), - takeEvery(initActions.setStoreReady.type, nativeServicesMasterSaga), - fork(restoreConnectionSaga), - // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one - takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), - ]) + console.log('rootSaga starting') + try { + yield all([ + fork(setupCryptoSaga), + fork(initMasterSaga), + fork(navigationMasterSaga), + fork(nativeServicesMasterSaga), + fork(restoreConnectionSaga), + // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one + takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), + takeLeading(initActions.canceledRootTask.type, clearReduxStore), + ]) + } finally { + console.log('rootSaga stopping') + if (yield cancelled()) { + console.log('rootSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/app/app.master.saga.ts b/packages/state-manager/src/sagas/app/app.master.saga.ts index 07dca44027..9775a83256 100644 --- a/packages/state-manager/src/sagas/app/app.master.saga.ts +++ b/packages/state-manager/src/sagas/app/app.master.saga.ts @@ -1,14 +1,22 @@ import { Socket } from '../../types' -import { all, takeEvery, takeLeading } from 'typed-redux-saga' +import { all, takeEvery, takeLeading, cancelled } from 'typed-redux-saga' import { appActions } from './app.slice' import { closeServicesSaga } from './closeServices.saga' import { stopBackendSaga } from './stopBackend/stopBackend.saga' import { loadMigrationDataSaga } from './loadMigrationData/loadMigrationData.saga' export function* appMasterSaga(socket: Socket): Generator { - yield* all([ - takeLeading(appActions.closeServices.type, closeServicesSaga, socket), - takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), - takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), - ]) + console.log('appMasterSaga starting') + try { + yield* all([ + takeLeading(appActions.closeServices.type, closeServicesSaga, socket), + takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), + takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), + ]) + } finally { + console.log('appMasterSaga stopping') + if (yield cancelled()) { + console.log('appMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts index bd2f054422..b0da74eb5b 100644 --- a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts +++ b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts @@ -1,6 +1,14 @@ -import { all, fork } from 'typed-redux-saga' +import { all, fork, cancelled } from 'typed-redux-saga' import { uptimeSaga } from './uptime/uptime.saga' export function* connectionMasterSaga(): Generator { - yield all([fork(uptimeSaga)]) + console.log('connectionMasterSaga starting') + try { + yield all([fork(uptimeSaga)]) + } finally { + console.log('connectionMasterSaga stopping') + if (yield cancelled()) { + console.log('connectionMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/communities/communities.master.saga.ts b/packages/state-manager/src/sagas/communities/communities.master.saga.ts index c5e1e1a275..a19dd3f20d 100644 --- a/packages/state-manager/src/sagas/communities/communities.master.saga.ts +++ b/packages/state-manager/src/sagas/communities/communities.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { communitiesActions } from './communities.slice' import { connectionActions } from '../appConnection/connection.slice' import { createCommunitySaga } from './createCommunity/createCommunity.saga' @@ -7,10 +7,18 @@ import { initCommunities, launchCommunitySaga } from './launchCommunity/launchCo import { createNetworkSaga } from './createNetwork/createNetwork.saga' export function* communitiesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), - takeEvery(connectionActions.torBootstrapped.type, initCommunities), - takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), - takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), - ]) + console.log('communitiesMasterSaga starting') + try { + yield all([ + takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), + takeEvery(connectionActions.torBootstrapped.type, initCommunities), + takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), + takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), + ]) + } finally { + console.log('communitiesMasterSaga stopping') + if (yield cancelled()) { + console.log('communitiesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/errors/errors.master.saga.ts b/packages/state-manager/src/sagas/errors/errors.master.saga.ts index 88b09d53b7..acaf09e588 100644 --- a/packages/state-manager/src/sagas/errors/errors.master.saga.ts +++ b/packages/state-manager/src/sagas/errors/errors.master.saga.ts @@ -1,7 +1,15 @@ -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { errorsActions } from './errors.slice' import { handleErrorsSaga } from './handleErrors/handleErrors.saga' export function* errorsMasterSaga(): Generator { - yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + console.log('errorsMasterSaga starting') + try { + yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + } finally { + console.log('errorsMasterSaga stopping') + if (yield cancelled()) { + console.log('errorsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/files/files.master.saga.ts b/packages/state-manager/src/sagas/files/files.master.saga.ts index 094e856eab..7b0a373084 100644 --- a/packages/state-manager/src/sagas/files/files.master.saga.ts +++ b/packages/state-manager/src/sagas/files/files.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { checkForMissingFilesSaga } from './checkForMissingFiles/checkForMissingFiles.saga' import { resetTransferSpeedSaga } from './resetTransferSpeed/resetTransferSpeed.saga' import { updateMessageMediaSaga } from './updateMessageMedia/updateMessageMedia' @@ -14,15 +14,23 @@ import { messagesActions } from '../messages/messages.slice' import { sendFileMessageSaga } from './uploadFile/sendFileMessage.saga' export function* filesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), - takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), - takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), - takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), - takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), - takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), - takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), - takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), - takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), - ]) + console.log('filesMasterSaga starting') + try { + yield all([ + takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), + takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), + takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), + takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), + takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), + takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), + takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), + takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), + takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), + ]) + } finally { + console.log('filesMasterSaga stopping') + if (yield cancelled()) { + console.log('filesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/identity/identity.master.saga.ts b/packages/state-manager/src/sagas/identity/identity.master.saga.ts index cf3c116db7..4a4ddbbd03 100644 --- a/packages/state-manager/src/sagas/identity/identity.master.saga.ts +++ b/packages/state-manager/src/sagas/identity/identity.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { identityActions } from './identity.slice' import { registerUsernameSaga } from './registerUsername/registerUsername.saga' import { verifyJoinTimestampSaga } from './verifyJoinTimestamp/verifyJoinTimestamp.saga' @@ -8,10 +8,18 @@ import { usersActions } from '../users/users.slice' import { updateCertificateSaga } from './updateCertificate/updateCertificate.saga' export function* identityMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), - takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), - takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), - takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), - ]) + console.log('identityMasterSaga starting') + try { + yield all([ + takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), + takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), + takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), + takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), + ]) + } finally { + console.log('identityMasterSaga stopping') + if (yield cancelled()) { + console.log('identityMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/messages/messages.master.saga.ts b/packages/state-manager/src/sagas/messages/messages.master.saga.ts index 6f7f7e70c6..2235f985de 100644 --- a/packages/state-manager/src/sagas/messages/messages.master.saga.ts +++ b/packages/state-manager/src/sagas/messages/messages.master.saga.ts @@ -1,5 +1,4 @@ -import { takeEvery } from 'redux-saga/effects' -import { all } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { type Socket } from '../../types' import { messagesActions } from './messages.slice' import { sendMessageSaga } from './sendMessage/sendMessage.saga' @@ -16,18 +15,26 @@ import { autoDownloadFilesSaga } from '../files/autoDownloadFiles/autoDownloadFi import { sendDeletionMessageSaga } from './sendDeletionMessage/sendDeletionMessage.saga' export function* messagesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), - takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), - takeEvery(messagesActions.addMessages.type, addMessagesSaga), - takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), - takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), - takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), - takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), - takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), - takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), - takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), - ]) + console.log('messagesMasterSaga starting') + try { + yield all([ + takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), + takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), + takeEvery(messagesActions.addMessages.type, addMessagesSaga), + takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), + takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), + takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), + takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), + takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), + takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), + takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), + ]) + } finally { + console.log('messagesMasterSaga stopping') + if (yield cancelled()) { + console.log('messagesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts index d97db92bf4..675146bcff 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { publicChannelsActions } from './publicChannels.slice' import { createChannelSaga } from './createChannel/createChannel.saga' import { deleteChannelSaga } from './deleteChannel/deleteChannel.saga' @@ -11,14 +11,22 @@ import { channelDeletionResponseSaga } from './channelDeletionResponse/channelDe import { sendIntroductionMessageSaga } from './sendIntroductionMessage/sendIntroductionMessage.saga' export function* publicChannelsMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), - takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), - takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), - takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), - takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), - takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), - takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), - takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), - ]) + console.log('publicChannelsMasterSaga starting') + try { + yield all([ + takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), + takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), + takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), + takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), + takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), + takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), + takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), + takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), + ]) + } finally { + console.log('publicChannelsMasterSaga stopping') + if (yield cancelled()) { + console.log('publicChannelsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index a64a65224c..17240cb4bd 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -1,6 +1,6 @@ import { eventChannel } from 'redux-saga' import { type Socket } from '../../../types' -import { all, call, fork, put, takeEvery } from 'typed-redux-saga' +import { all, call, fork, put, takeEvery, cancelled } from 'typed-redux-saga' import logger from '../../../utils/logger' import { appActions } from '../../app/app.slice' import { appMasterSaga } from '../../app/app.master.saga' @@ -176,23 +176,40 @@ export function subscribe(socket: Socket) { } export function* handleActions(socket: Socket): Generator { - const socketChannel = yield* call(subscribe, socket) - yield takeEvery(socketChannel, function* (action) { - yield put(action) - }) + console.log('handleActions starting') + try { + const socketChannel = yield* call(subscribe, socket) + yield takeEvery(socketChannel, function* (action) { + console.log('handleActions PUT', action) + yield put(action) + }) + } finally { + console.log('handleActions stopping') + if (yield cancelled()) { + console.log('handleActions cancelled') + } + } } export function* useIO(socket: Socket): Generator { - yield all([ - fork(handleActions, socket), - fork(publicChannelsMasterSaga, socket), - fork(messagesMasterSaga, socket), - fork(filesMasterSaga, socket), - fork(identityMasterSaga, socket), - fork(communitiesMasterSaga, socket), - fork(usersMasterSaga, socket), - fork(appMasterSaga, socket), - fork(connectionMasterSaga), - fork(errorsMasterSaga), - ]) + console.log('useIO starting') + try { + yield all([ + fork(handleActions, socket), + fork(publicChannelsMasterSaga, socket), + fork(messagesMasterSaga, socket), + fork(filesMasterSaga, socket), + fork(identityMasterSaga, socket), + fork(communitiesMasterSaga, socket), + fork(usersMasterSaga, socket), + fork(appMasterSaga, socket), + fork(connectionMasterSaga), + fork(errorsMasterSaga), + ]) + } finally { + console.log('useIO stopping') + if (yield cancelled()) { + console.log('useIO cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/users/users.master.saga.ts b/packages/state-manager/src/sagas/users/users.master.saga.ts index 72c7383ef3..947b95615a 100644 --- a/packages/state-manager/src/sagas/users/users.master.saga.ts +++ b/packages/state-manager/src/sagas/users/users.master.saga.ts @@ -1,9 +1,17 @@ -import { takeEvery } from 'redux-saga/effects' +import { takeEvery, cancelled } from 'redux-saga/effects' import { all } from 'typed-redux-saga' import { type Socket } from '../../types' import { usersActions } from './users.slice' import { saveUserProfileSaga } from './userProfile/saveUserProfile.saga' export function* usersMasterSaga(socket: Socket): Generator { - yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + console.log('usersMasterSaga starting') + try { + yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + } finally { + console.log('usersMasterSaga stopping') + if (yield cancelled()) { + console.log('usersMasterSaga cancelled') + } + } }