Repository navigation
Refuse processing thread pushes after stop() and after a task threw - #285
Merged
Merged
Conversation
…art() - The flag is set and checked under taskMutex_, so a push that passed the check no longer lands after the drain - stop() runs the drained tasks outside the lock, pushes meanwhile are refused instead of waiting for the drain
…en promise - UnfinishedTaskResult makes it: nothing for void, false for bool, the error of a std::expected (SftpError and OperationError define one); other results pass their own - A finalized strand completes its promise tasks the same way instead of with a runtime_error - awaitCycle reads the value, workQueue fails the first strand operation when its batch did not run
…l queued - Queued tasks are dropped there, so their promise tasks complete as unfinished instead of waiting for stop() - Strand final tasks are cleanup tasks: they still run when the loop fails, and after stop() on the calling thread, so SFTP sessions and channels close and leave their owner - stop() drains as the processing thread; cleanup tasks outside the loop run one at a time under cleanupMutex_
- From another thread during a permanent cycle, removePermanentTask returned whether the deferred removal ran, so a second removal reported success (CanSafelyDoubleDeletePermanentTask flaked, 9 in 50000 TSan runs)
- A refused or dropped push completes the future with an error instead of a broken promise
…ot run - A refused, unpushed or dropped chunk completes the future with TaskPushFailed, a closed file with FileNull
… barriers - A loop that throws while stop() drains leaves the queue and intake to the drain, which resets the thread id and closes intake together, so no cleanup task runs on its caller beside stop() - The drain and cleanup tasks run on a caller contain task exceptions like the loop does - A promise task pushed while stop() drains is queued refused and completes when the drain reaches it, so Operation::waitForStrandTasks still waits for the tasks queued before it
- A refused, unpushed or dropped read completes the future with TaskPushFailed instead of a broken promise
5cript
enabled auto-merge (rebase)
October 28, 2026 23:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #260.
ProcessingThreadno longer accepts tasks afterstop()(until the nextstart()) or after a task threw, and a refused or dropped promise task completes its future with an error value instead of a broken promise. Before, such tasks ran later in the destructor on the destroying thread, or never ran while their callers waited on them.stop()drains outside it, so a push racingstop()is either run by the drain or refused. A promise task refused during the drain completes once the drain reached it, so it still works as a barrier for the tasks queued before it.pushPromiseTasktakes an optional unfinished result.UnfinishedTaskResultprovides it forvoid(nothing),bool(false) andstd::expectedofSftpError(TaskPushFailed) orOperationError(ChannelClosed). A finalized strand answers the same way instead of with aruntime_error.workQueuethen fails the operations that need the strand, one per cycle, instead of blocking inget().stop()on the calling thread, which acts as the processing thread meanwhile, soSftpSession::closeandChannel::closekeep removing themselves. The drain and these caller-side runs contain task exceptions like the loop does.removePermanentTaskfrom another thread during a permanent cycle returns whether it removed the task, not whether the deferred removal ran.Session::createPtyChannelandcreateSftpSessiongo throughpushPromiseTask, andFileStream::readAlland a chunkedFileStream::writecomplete withTaskPushFailedwhen a read or chunk cannot run (FileNullon a closed file).