Skip to content

chore: [SDK-4213] remove background-threading FF, make async init default#2668

Open
abdulraqeeb33 wants to merge 10 commits into
mainfrom
ar/sdk-4213
Open

chore: [SDK-4213] remove background-threading FF, make async init default#2668
abdulraqeeb33 wants to merge 10 commits into
mainfrom
ar/sdk-4213

Conversation

@abdulraqeeb33

Copy link
Copy Markdown
Contributor

Summary

The sdk_background_threading remote feature flag is fully rolled out (100%), so this removes the flag entirely and makes the background/async init path the unconditional default. This eliminates the FF/cached-config read from the init decision path (the StrictMode bootstrap disk-read goal) and removes the legacy synchronous-blocking code path.

Resolves SDK-4213.

Production changes

  • FeatureFlag.kt — removed the SDK_BACKGROUND_THREADING enum entry. The generic FeatureActivationMode framework and SDK_IDENTITY_VERIFICATION remain.
  • ThreadingMode.kt — deleted (it only existed to hold useBackgroundThreading).
  • ThreadUtils.kt — collapsed every if (ThreadingMode.useBackgroundThreading) … else … branch to the OneSignalDispatchers path, dropping the legacy thread { runBlocking { … } } / GlobalScope.launch fallbacks. Renamed runOnSerialIOIfBackgroundThreadingrunOnSerialIO and updated all call sites.
  • OneSignalImp.kt — removed isBackgroundThreadingEnabled, the featureManager field, the injectable ioDispatcher constructor param, and the now-dead requireInitForOperation / warnIfBlockingOnMainThread. initWithContext always dispatches internalInit via suspendifyOnIO (never blocks the caller); login/logout/updateUserJwt/JWT-listener methods always use waitForInit; suspend accessors use OneSignalDispatchers.IO directly.
  • StartupService.kt — removed the IFeatureManager lookup; scheduleStart() always launches on OneSignalDispatchers.launchOnDefault.
  • OneSignalCrashUploaderWrapper.kt — removed the FF check in start(); always launches on IO (featureManager still injected for OTel telemetry).
  • FeatureManager.kt — removed the SDK_BACKGROUND_THREADING side-effect.

Test changes

  • Removed FF on/off matrix tests and ThreadingMode toggling across the affected suites.
  • Renamed ThreadUtilsFeatureFlagTestsThreadUtilsDispatchTests (tests the now-unconditional dispatch contracts).
  • Re-pointed the saturated-IO waitForInit SUCCESS fast-path regression tests (Flutter Remove OSNotificationPayload #1163) to the default async path; removed the obsolete warnIfBlockingOnMainThread test.

Net: +274 / −1004 across 26 files.

Test plan

  • compileDebugUnitTestKotlin green across all modules
  • Affected unit tests pass (SDKInitTests, OneSignalImpTests, FeatureManagerTests, FeatureFlagTests, ThreadUtilsDispatchTests, BackgroundManagerTests, StartupServiceTests, OneSignalCrashUploaderWrapperTest, SessionServiceTests, FeatureFlagsRefreshServiceTests, NotificationsManagerTests, NotificationPermissionControllerTests)
  • detekt passes for :core and :notifications
  • Full CI suite

Notes / follow-ups

  • The generic FeatureActivationMode.APP_STARTUP mode + latching logic in FeatureManager is retained but is now unused by any flag; could be removed in a follow-up if no APP_STARTUP flags are planned.
  • A few telemetry/config/parser tests still use the literal string "sdk_background_threading" as an arbitrary remote-flag fixture (no longer maps to any enum) — left as-is since they exercise generic plumbing.

Made with Cursor

AR Abdul Azeez and others added 9 commits June 24, 2026 00:52
…ault

The sdk_background_threading remote flag is fully rolled out, so the
background/async path is now the unconditional default and all FF gating
is removed:

- Drop SDK_BACKGROUND_THREADING from FeatureFlag and its FeatureManager
  side-effect; delete ThreadingMode.
- Collapse all ThreadUtils branches to the OneSignalDispatchers path and
  rename runOnSerialIOIfBackgroundThreading -> runOnSerialIO.
- OneSignalImp: remove isBackgroundThreadingEnabled, the featureManager
  field, the injectable ioDispatcher param, and the now-dead
  requireInitForOperation / warnIfBlockingOnMainThread. initWithContext
  always dispatches init asynchronously, eliminating the FF/cached-config
  read from the init decision path.
- StartupService / OneSignalCrashUploaderWrapper: always run async.
- Update tests to the default async behavior; rename
  ThreadUtilsFeatureFlagTests -> ThreadUtilsDispatchTests.

Co-authored-by: Cursor <cursoragent@cursor.com>
…#2653)

Co-authored-by: Nan <nan@onesignal.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: AR Abdul Azeez <abdul@onesignal.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Fadi George <fadii925@gmail.com>
)

Co-authored-by: Cursor <cursoragent@cursor.com>
…SDK-4803] (#2667)

Co-authored-by: AR Abdul Azeez <abdul@onesignal.com>
Co-authored-by: github-actions[bot] <noreply@onesignal.com>
origin/main already modernized ThreadUtils/OneSignalDispatchers and removed
ThreadingMode; fix the remaining stale ThreadingMode doc reference in
OneSignalDispatchers.prewarm now that background dispatch is unconditional.

Co-authored-by: Cursor <cursoragent@cursor.com>
With background threading now unconditional, all dispatch routes through the
shared OneSignalDispatchers pool. Fix the resulting unit-test fallout:

- OneSignalDispatchers: add a test-only resetForTest() backed by a swappable
  Pools holder so each spec starts with a clean pool (shutdownNow + recreate
  executors/dispatchers/scopes), preventing cross-spec pool saturation.
- OneSignalTestProjectConfig: new Kotest AbstractProjectConfig that calls
  resetForTest() before/after every spec for process-wide isolation.
- FeatureFlagsRefreshServiceTests: wrap the launchOnIO no-op stub in try/finally
  so the mock no longer leaks into HttpClientTests/OperationRepoTests.
- SessionServiceTests: register IOMockHelper so runOnSerialIO dispatch is inline
  and the focus/duration assertions are deterministic.
- SDKInitTests: mark test-spawned threads as daemon so a stray blocked init
  thread can never keep the JVM alive (fixes the 6h CI hang). Production
  waitForInit semantics unchanged.
- NotificationGenerationProcessorTests: dispatch launchOnIO on a real
  Dispatchers.IO-backed scope (mirroring the prior GlobalScope path) so the
  withTimeout-guarded external callbacks can be cancelled instead of hanging
  inside IOMockHelper's inline runBlocking.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/threading/OneSignalDispatchers.kt
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

📊 Diff Coverage Report

Diff Coverage Report (Changed Lines Only)

Gate: aggregate coverage on changed executable lines must be ≥ 80% (JaCoCo line data for lines touched in the diff).

Changed Files Coverage

  • OneSignalDispatchers.kt: 92/109 touched executable lines (84.4%) (174 touched lines in diff)
  • ThreadUtils.kt: 15/20 touched executable lines (75.0%) (26 touched lines in diff)
    • 5 uncovered touched lines in this file
  • ⚠️ ThreadingMode.kt: Not in coverage report (may not be compiled/tested)
  • BackgroundManager.kt: 2/2 touched executable lines (100.0%) (4 touched lines in diff)
  • FeatureFlagsRefreshService.kt: 2/2 touched executable lines (100.0%) (3 touched lines in diff)
  • FeatureFlag.kt: 9/9 touched executable lines (100.0%) (all lines — could not resolve diff)
  • StartupService.kt: 4/6 touched executable lines (66.7%) (6 touched lines in diff)
    • 2 uncovered touched lines in this file
  • OneSignalCrashUploaderWrapper.kt: 0/3 touched executable lines (0.0%) (3 touched lines in diff)
    • 3 uncovered touched lines in this file
  • OneSignalImp.kt: 1/27 touched executable lines (3.7%) (58 touched lines in diff)
    • 26 uncovered touched lines in this file
  • SessionService.kt: 2/2 touched executable lines (100.0%) (5 touched lines in diff)
  • NotificationsManager.kt: 0/1 touched executable lines (0.0%) (2 touched lines in diff)
    • 1 uncovered touched lines in this file
  • NotificationPermissionController.kt: 0/2 touched executable lines (0.0%) (3 touched lines in diff)
    • 2 uncovered touched lines in this file
  • ⚠️ IOMockHelper.kt: Not in coverage report (may not be compiled/tested)

Overall (aggregate gate)

127/183 touched executable lines covered (69.4% — requires ≥ 80%)

Per-file detail (informational; gate is aggregate above):

  • ThreadUtils.kt: 75.0% (5 uncovered touched lines)

  • StartupService.kt: 66.7% (2 uncovered touched lines)

  • OneSignalCrashUploaderWrapper.kt: 0.0% (3 uncovered touched lines)

  • OneSignalImp.kt: 3.7% (26 uncovered touched lines)

  • NotificationsManager.kt: 0.0% (1 uncovered touched lines)

  • NotificationPermissionController.kt: 0.0% (2 uncovered touched lines)

❌ Coverage Check Failed

Aggregate coverage on touched lines is 69.4% (minimum 80%).

📥 View workflow run

… comments

Make OneSignalImp.ioDispatcher a get() accessor instead of an eagerly
captured val. The eager field forced the lazy pools.IO executor/dispatcher
to construct during OneSignalImp construction (on the main thread, before
initWithContext's prewarm() runs), partially defeating SDK-4507/4794, and
pinned the instance to a single pool generation so a reused instance could
dispatch onto a shutdownNow()'d executor after resetForTest() swaps pools.

Also remove stale feature-flag comments left over from the background-
threading FF removal: BackgroundManager.onFocus's "FF gates the rollout"
note and internalInit's catch block describing the removed two-arm
FF-on/FF-off design. Document that the synchronous initWithContext always
returns true and does not reflect init success.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants