@@ -13,13 +13,15 @@ import com.datadog.android.flags.StateObservable
1313import com.datadog.android.flags.model.FlagsClientState
1414import com.datadog.android.internal.utils.DDCoreSubscription
1515import java.util.concurrent.ExecutorService
16+ import java.util.concurrent.locks.ReentrantLock
17+ import kotlin.concurrent.withLock
1618
1719/* *
1820 * Manages state transitions and notifications for a [com.datadog.android.flags.FlagsClient].
1921 *
2022 * This class handles state change notifications to registered listeners. All notification
2123 * methods are thread-safe and guarantee ordered delivery to listeners by using a
22- * single-threaded executor service.
24+ * single-threaded executor service and a fair ReentrantLock .
2325 *
2426 * The current state is stored and emitted to new listeners immediately upon registration,
2527 * ensuring every listener receives the current state.
@@ -33,19 +35,26 @@ internal class FlagsStateManager(
3335 private val executorService : ExecutorService ,
3436 private val internalLogger : InternalLogger
3537) : StateObservable {
38+ /* *
39+ * Fair lock to ensure FIFO ordering of state mutations and listener operations.
40+ * The fair parameter ensures that threads acquire the lock in the order they requested it.
41+ */
42+ private val stateLock = ReentrantLock (true )
43+
3644 /* *
3745 * The current state of the client.
38- * Thread-safe: uses volatile for visibility across threads .
46+ * Thread-safe: access is protected by stateLock .
3947 */
40- @Volatile
4148 private var currentState: FlagsClientState = FlagsClientState .NotReady
4249
4350 /* *
4451 * Returns the current state synchronously.
4552 *
4653 * @return The current [FlagsClientState].
4754 */
48- override fun getCurrentState (): FlagsClientState = currentState
55+ override fun getCurrentState (): FlagsClientState = stateLock.withLock {
56+ currentState
57+ }
4958
5059 /* *
5160 * Updates the state and notifies all listeners.
@@ -56,8 +65,8 @@ internal class FlagsStateManager(
5665 * @param newState The new state to transition to.
5766 */
5867 internal fun updateState (newState : FlagsClientState ) {
59- // lock the currentState until listener notifications are queued to prevent new listeners during state change.
60- synchronized(currentState) {
68+ // Lock to ensure atomic state update and listener notification queueing
69+ stateLock.withLock {
6170 currentState = newState
6271 subscription.notifyListeners {
6372 executorService.executeSafe(
@@ -71,7 +80,7 @@ internal class FlagsStateManager(
7180 }
7281
7382 override fun addListener (listener : FlagsStateListener ) {
74- synchronized(currentState) {
83+ stateLock.withLock {
7584 subscription.addListener(listener)
7685
7786 val stateToEmit = currentState
@@ -85,7 +94,7 @@ internal class FlagsStateManager(
8594 }
8695
8796 override fun removeListener (listener : FlagsStateListener ) {
88- synchronized(currentState) {
97+ stateLock.withLock {
8998 subscription.removeListener(listener)
9099 }
91100 }
0 commit comments