Say what the save exemption does not cover, rather than implying it is total
The note claimed `save` is left unguarded because nothing can overwrite what it writes. That half is true -- the only observation that could belongs to a job already in a terminal state. The other half was missing: a save whose copy is still in flight when the user taps Start over lands `Saved` on a screen they have just cleared. Guarding it would drop that write instead, which reports nothing for a file that may genuinely have reached the destination. That is a question about what the screen should offer during a save, and answering it in a race fix would be deciding it by accident. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -198,11 +198,17 @@ class ConversionViewModel @JvmOverloads constructor(
|
||||
* cancelling the superseded coroutine is not one.
|
||||
*
|
||||
* Every write below that lands after a suspension point is guarded by it: the two in
|
||||
* [onInputPicked] and the one in [observe]. [save] is the one deferred write that is not, and
|
||||
* deliberately: it is only reachable from [ConversionState.Converted] or a
|
||||
* [ConversionState.Failed] carrying its file, so the only observation that could overwrite
|
||||
* what it writes belongs to a job that has already reached a terminal state and will not emit
|
||||
* again.
|
||||
* [onInputPicked] and the one in [observe].
|
||||
*
|
||||
* [save] is the one left out, deliberately — and not because it is safe in both directions.
|
||||
* Nothing can overwrite what it writes: it is reachable only from [ConversionState.Converted]
|
||||
* or a [ConversionState.Failed] carrying its file, so the only observation that could belongs
|
||||
* to a job already in a terminal state, which will not emit again. What it can still do is
|
||||
* land on top of a [reset] taken while its copy was in flight, putting `Saved` on a screen the
|
||||
* user has just cleared. Guarding it would drop that write instead, reporting nothing for a
|
||||
* file that may genuinely have reached the user's destination. Which of those two is right is
|
||||
* a question about what the screen should offer during a save, not about this race, and it is
|
||||
* left open rather than answered in passing.
|
||||
*/
|
||||
private val ownership = ScreenOwnership()
|
||||
|
||||
|
||||
@@ -107,9 +107,10 @@ class JoinViewModel @JvmOverloads constructor(
|
||||
* The convert side had issue #49 reported against it four times in two days; this side has the
|
||||
* identical shape and was never reported, because nothing was watching. Every write below that
|
||||
* lands after a suspension point is guarded: the one in [onInputsPicked] and the one in
|
||||
* [observe]. [save] is the deferred write that is not, deliberately and for the reason
|
||||
* `ConversionViewModel` writes out -- it is reachable only from a job that has already reached
|
||||
* a terminal state and will not emit again.
|
||||
* [observe]. [save] is the one left out, deliberately and with the same limit its counterpart
|
||||
* in `ConversionViewModel` spells out: nothing can overwrite what it writes, but it can still
|
||||
* land on top of a [reset] taken while its copy was in flight, and which way that should go is
|
||||
* a question about the save screen rather than about this race.
|
||||
*/
|
||||
private val ownership = ScreenOwnership()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user