Fix Thread-safety race condition in AbstractRedisReactiveCommands.getScheduler() #3272
Labels
status: feedback-reminder
We've sent a reminder that we need additional information before we can continue
status: waiting-for-feedback
We need additional information before we can continue
Feature Request
(Self-check: A quick search for "getScheduler thread safety" or similar terms in Lettuce issues didn't reveal an identical report, but thorough checking by maintainers is advised.)
Is your feature request related to a problem? Please describe
Yes, the current implementation of the
private EventExecutorGroup getScheduler()
method withinio.lettuce.core.AbstractRedisReactiveCommands
, while using avolatile
field, is still not fully thread-safe regarding its lazy initialization logic.The
scheduler
field is correctly marked asvolatile
, ensuring visibility across threads. However, the method employs a "check-then-act" pattern (if (scheduler == null) { /* initialize and assign */ }
) which is not atomic.In a multi-threaded environment:
volatile scheduler
field and seenull
.if (scheduler == null)
check before any thread performs the assignment.schedulerToUse
, potentially callingconnection.getResources().eventExecutorGroup()
) can execute multiple times across different threads.this.scheduler = schedulerToUse
), leading to redundant work and potentially overwriting each other's results.While
volatile
guarantees that writes become visible, it does not provide the mutual exclusion needed to make the entire check-initialize-assign sequence atomic. This can lead to inefficient resource usage or subtle concurrency issues.Describe the solution you'd like
To ensure the initialization logic runs exactly once and the assignment is performed atomically with the check, the Double-Checked Locking (DCL) pattern should be fully implemented using a
synchronized
block. Thevolatile
keyword (already present) is a prerequisite for DCL's correctness.This approach ensures:
synchronized
block guarantees that the critical section (the second check and the initialization/assignment) is executed by only one thread at a time, ensuring the initialization logic runs at most once.volatile
field and the memory semantics of thesynchronized
block ensures correct publication and visibility of the initializedscheduler
across threads.Considered Drawbacks: Minimal performance impact compared to the current partially thread-safe version, significantly outweighed by the correctness guarantee.
Describe alternatives you've considered
java.util.concurrent.atomic.AtomicReference<EventExecutorGroup>
:compareAndSet
loop pattern might still execute the scheduler determination logic multiple times concurrently before one succeeds. Ifconnection.getResources().eventExecutorGroup()
creates a new resource each time, it could lead to temporary resource creation that needs cleanup. DCL avoids this by ensuring the creation logic runs only once within the lock.private synchronized EventExecutorGroup getScheduler() { ... }
Given the need to ensure the initialization logic runs only once, DCL appears to be the most suitable pattern here, correctly utilizing the existing
volatile
field with the necessary synchronization.Teachability, Documentation, Adoption, Migration Strategy
This is primarily an internal implementation fix to improve robustness and thread safety.
synchronized
block (despitevolatile
) would be beneficial.The text was updated successfully, but these errors were encountered: