Skip to content

Commit e000a28

Browse files
authored
Merge pull request #695 from code-payments/fix/give-transactor-dispose-race
fix(cash): race between give transactor dispose and start on quick bg/fg
2 parents 4e73d92 + 1a1b8d1 commit e000a28

2 files changed

Lines changed: 10 additions & 1 deletion

File tree

services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GiveBillTransactor.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ internal class GiveBillTransactor(
112112
* @return the confirmed [TransactionMetadata.SendPublicPayment] on success.
113113
*/
114114
suspend fun start(): Result<TransactionMetadata.SendPublicPayment> {
115+
if (!scope.isActive) {
116+
return logAndFail(GiveTransactorError.Other(message = "Transactor was disposed"))
117+
}
118+
115119
val ownerKey = owner
116120
?: return logAndFail(GiveTransactorError.Other(message = "No owner key. Did you call with() first?"))
117121
val desiredToken = token
@@ -212,13 +216,13 @@ internal class GiveBillTransactor(
212216

213217
/** Cancels the coroutine scope and clears all held state. */
214218
fun dispose() {
219+
scope.cancel()
215220
owner = null
216221
presentationData = BillPresentationData(emptyList(), emptyList())
217222
rendezvousKey = null
218223
receivingAccount = null
219224
token = null
220225
providedVerifiedState = null
221-
scope.cancel()
222226
}
223227

224228
sealed class GiveTransactorError(

services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers
2626
import kotlinx.coroutines.Job
2727
import kotlinx.coroutines.SupervisorJob
2828
import kotlinx.coroutines.cancel
29+
import kotlinx.coroutines.ensureActive
2930
import kotlinx.coroutines.launch
3031
import java.util.Timer
3132
import java.util.TimerTask
@@ -113,6 +114,10 @@ class BillTransactionManager @Inject constructor(
113114
present(transactor.presentationData)
114115
presentBillForGive(onTimeout)
115116

117+
// If cancelAwaitForGrab() fired between present() and here,
118+
// bail out before start() reads fields that dispose() may have nulled.
119+
ensureActive()
120+
116121
transactor.start()
117122
.onSuccess {
118123
childScope.cancel()

0 commit comments

Comments
 (0)