Skip to content

Commit

Permalink
Handle cancellation in withRetry
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Chelombitko committed Dec 20, 2024
1 parent 5bb61f1 commit c958f8a
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 23 deletions.
24 changes: 15 additions & 9 deletions core/src/main/kotlin/com/malinskiy/marathon/execution/Retry.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.malinskiy.marathon.execution

import kotlinx.coroutines.delay
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.isActive
import kotlinx.coroutines.time.delay
import java.time.Duration

suspend fun withRetry(maxAttempts: Int, retryDelay: Duration, block: suspend () -> Unit) {
check(maxAttempts >= 1) { "maxAttempts must be >= 1" }

@Suppress("TooGenericExceptionCaught")
suspend fun <T> withRetry(attempts: Int, delayTime: Long = 0, f: suspend () -> T): T {
var attempt = 1
while (true) {
while (currentCoroutineContext().isActive) {
try {
return f()
} catch (th: Throwable) {
if (attempt == attempts) {
throw th
return block()
} catch (@Suppress("TooGenericExceptionCaught") e: Throwable) {
currentCoroutineContext().ensureActive()
if (attempt == maxAttempts) {
throw e
} else {
delay(delayTime)
delay(retryDelay)
}
}
++attempt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import kotlinx.coroutines.CompletionHandler
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import java.time.Duration
import java.time.Instant
import kotlin.coroutines.CoroutineContext

Expand Down Expand Up @@ -157,18 +157,16 @@ class DeviceActor(

private fun initialize() {
logger.debug("[{}] Initializing", device.serialNumber)
job = scope.async {
job = scope.launch {
try {
withRetry(30, 10000) {
if (isActive) {
try {
device.prepare(configuration)
} catch (e: CancellationException) {
throw e
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
logger.debug("[{}] Initialization failed. Retrying", device.serialNumber, e)
throw e
}
withRetry(maxAttempts = 30, retryDelay = Duration.ofSeconds(10)) {
try {
device.prepare(configuration)
} catch (e: CancellationException) {
throw e
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
logger.debug("[{}] Initialization failed. Retrying", device.serialNumber, e)
throw e
}
}
state.transition(DeviceEvent.Complete)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.malinskiy.marathon.execution.withRetry
import com.malinskiy.marathon.io.FileHasher
import com.malinskiy.marathon.log.MarathonLogging
import java.io.File
import java.time.Duration
import java.time.Instant
import kotlin.system.measureTimeMillis

Expand Down Expand Up @@ -39,7 +40,7 @@ class AndroidAppInstaller(

@Suppress("TooGenericExceptionThrown")
private suspend fun ensureInstalled(device: AndroidDevice, appPackage: String, appApk: File) {
withRetry(attempts = MAX_RETIRES, delayTime = 1000) {
withRetry(maxAttempts = MAX_INSTALLATION_ATTEMPTS, retryDelay = INSTALLATION_RETRY_DELAY) {
try {
val checkStarted = Instant.now()
val fileHash = fileHasher.getHash(appApk)
Expand Down Expand Up @@ -138,7 +139,8 @@ class AndroidAppInstaller(
}

companion object {
private const val MAX_RETIRES = 3
private const val MAX_INSTALLATION_ATTEMPTS = 3
private val INSTALLATION_RETRY_DELAY = Duration.ofSeconds(1)
private const val MARSHMALLOW_VERSION_CODE = 23
private const val MD5_HASH_SIZE = 32
private const val INSTALLED_TEST_APPS_SCRIPT = "pm list packages -3 | grep -E '\\.test\$' | tr -d '\\r' | cut -d ':' -f 2"
Expand Down

0 comments on commit c958f8a

Please sign in to comment.