Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.onesignal.core.internal.startup.IStartableService
import com.onesignal.core.internal.time.ITime
import com.onesignal.debug.LogLevel
import com.onesignal.debug.internal.logging.Logging
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.delay

internal class PreferencesService(
Expand Down Expand Up @@ -234,6 +235,9 @@ internal class PreferencesService(

// wait to be woken up for the next pass
waiter.waitForWake()
} catch (e: CancellationException) {
// Swallowing this turns the loop into a hot spin once the scope is gone.
throw e
} catch (e: Throwable) {
Logging.log(LogLevel.ERROR, "Error with Preference work loop", e)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.onesignal.core.internal.preferences
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import br.com.colman.kotest.android.extensions.robolectric.RobolectricTest
import com.onesignal.common.threading.OneSignalDispatchers
import com.onesignal.core.internal.preferences.impl.PreferencesService
import com.onesignal.debug.ILogListener
import com.onesignal.debug.LogLevel
import com.onesignal.debug.internal.logging.Logging
import com.onesignal.mocks.AndroidMockHelper
Expand All @@ -12,6 +14,7 @@ import io.kotest.assertions.throwables.shouldThrowUnit
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.delay
import java.util.concurrent.atomic.AtomicInteger

@RobolectricTest
class PreferencesServiceTests : FunSpec({
Expand Down Expand Up @@ -228,4 +231,23 @@ class PreferencesServiceTests : FunSpec({
store.contains(mockStringPrefStoreKey) shouldBe false
store.contains(mockStringSetPrefStoreKey) shouldBe false
}

test("the write loop stops when its scope is cancelled instead of spinning on the error path") {
val preferencesService = PreferencesService(AndroidMockHelper.applicationService(), MockHelper.time(1000))
val loopErrors = AtomicInteger()
val listener = ILogListener { if (it.entry.contains("Error with Preference work loop")) loopErrors.incrementAndGet() }
Logging.addListener(listener)
try {
preferencesService.start()
preferencesService.saveBool(mockPrefStoreName, mockBoolPrefStoreKey, true)
delay(300)

OneSignalDispatchers.resetForTest()
delay(500)

loopErrors.get() shouldBe 0
} finally {
Logging.removeListener(listener)
}
}
})
Loading