|
| 1 | +package com.devcycle.sdk.android.eventsource |
| 2 | + |
| 3 | +import com.devcycle.sdk.android.util.DevCycleLogger |
| 4 | +import com.launchdarkly.eventsource.EventSource |
| 5 | +import com.launchdarkly.eventsource.ReadyState |
| 6 | +import com.launchdarkly.eventsource.background.BackgroundEventSource |
| 7 | +import java.util.concurrent.RejectedExecutionException |
| 8 | + |
| 9 | +/** |
| 10 | + * A safe wrapper around BackgroundEventSource that handles RejectedExecutionException |
| 11 | + * during shutdown operations gracefully to prevent app crashes. |
| 12 | + * |
| 13 | + * This wrapper is necessary because when EventSource is closed, its internal executor |
| 14 | + * is shut down. If there are any pending tasks or race conditions during shutdown, |
| 15 | + * they would throw RejectedExecutionException which could crash the application. |
| 16 | + */ |
| 17 | +class SafeBackgroundEventSource( |
| 18 | + private val delegate: BackgroundEventSource |
| 19 | +) { |
| 20 | + /** |
| 21 | + * Access the underlying EventSource for state checks |
| 22 | + */ |
| 23 | + val eventSource: EventSource |
| 24 | + get() = delegate.eventSource |
| 25 | + |
| 26 | + /** |
| 27 | + * Start the background event source |
| 28 | + */ |
| 29 | + fun start() { |
| 30 | + try { |
| 31 | + delegate.start() |
| 32 | + } catch (e: RejectedExecutionException) { |
| 33 | + DevCycleLogger.d("EventSource start rejected (executor may be shut down): ${e.message}") |
| 34 | + } catch (e: Exception) { |
| 35 | + DevCycleLogger.w("Unexpected error starting EventSource: ${e.message}") |
| 36 | + throw e |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + /** |
| 41 | + * Close the background event source safely, catching any RejectedExecutionException |
| 42 | + * that may occur during shutdown due to race conditions with internal executor threads. |
| 43 | + */ |
| 44 | + fun close() { |
| 45 | + try { |
| 46 | + delegate.close() |
| 47 | + } catch (e: RejectedExecutionException) { |
| 48 | + // This is expected during shutdown when tasks are submitted to an already-terminated executor |
| 49 | + DevCycleLogger.d("EventSource close rejected during shutdown (expected): ${e.message}") |
| 50 | + } catch (e: Exception) { |
| 51 | + // Log unexpected exceptions but don't propagate them to prevent crashes |
| 52 | + DevCycleLogger.w("Unexpected error closing EventSource: ${e.message}") |
| 53 | + } |
| 54 | + } |
| 55 | +} |
| 56 | + |
0 commit comments