RDKEMW-15927: Addressing the occurring race condition while deleting all report profiles#308
RDKEMW-15927: Addressing the occurring race condition while deleting all report profiles#308yogeswaransky wants to merge 1 commit intosupport/1.8.5from
Conversation
…ort profiles Signed-off-by: Yogeswaran K <yogeswaransky@gmail.com>
There was a problem hiding this comment.
Pull request overview
Addresses a reported race condition when deleting all report profiles (notably during rapid PrivacyMode toggling), aiming to prevent concurrent deletion paths from corrupting profile state.
Changes:
- Serialize PrivacyMode callback handling in
rbusInterface.cusing a process-wide mutex. - Add a
deleteInProgressguard indeleteAllProfiles()to prevent concurrent re-entry.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
source/ccspinterface/rbusInterface.c |
Adds a global mutex to serialize privacy callback worker execution to avoid concurrent profile-deletion actions. |
source/bulkdata/profile.c |
Adds an in-progress flag to avoid re-entrant deleteAllProfiles() executions. |
| if(deleteInProgress) | ||
| { | ||
| T2Info("deleteAllProfiles already in progress, skipping\n"); | ||
| pthread_mutex_unlock(&plMutex); | ||
| return T2ERROR_SUCCESS; |
There was a problem hiding this comment.
The new deleteInProgress early-return changes the function’s semantics from “delete completes before return” to “may return while another thread is still deleting”. This is dangerous for callers like uninitProfileList() (calls deleteAllProfiles(false) and then destroys plMutex), because a concurrent in-flight delete can continue using plMutex after it has been destroyed, leading to undefined behavior/crash.
Instead of returning success when deleteInProgress is true, either (a) wait for the active deletion to complete (e.g., pthread_cond_wait under plMutex and pthread_cond_broadcast when clearing the flag), or (b) return an error code and update all callers to handle it safely (including uninitProfileList).
| if(deleteInProgress) | |
| { | |
| T2Info("deleteAllProfiles already in progress, skipping\n"); | |
| pthread_mutex_unlock(&plMutex); | |
| return T2ERROR_SUCCESS; | |
| if (deleteInProgress) | |
| { | |
| T2Info("deleteAllProfiles already in progress, waiting for completion\n"); | |
| /* Wait until the ongoing delete operation completes. | |
| * This restores the guarantee that deleteAllProfiles does not | |
| * return while another thread is still deleting and using plMutex. | |
| */ | |
| while (deleteInProgress) | |
| { | |
| pthread_mutex_unlock(&plMutex); | |
| /* Sleep briefly to avoid busy-waiting while allowing the | |
| * active deleter to make progress. | |
| */ | |
| usleep(1000); | |
| pthread_mutex_lock(&plMutex); | |
| } |
| static bool deleteInProgress = false; | ||
|
|
||
| pthread_mutex_lock(&plMutex); | ||
| if(deleteInProgress) | ||
| { | ||
| T2Info("deleteAllProfiles already in progress, skipping\n"); | ||
| pthread_mutex_unlock(&plMutex); | ||
| return T2ERROR_SUCCESS; |
There was a problem hiding this comment.
deleteInProgress is set/reset without any test coverage to verify the new behavior (re-entrant calls, waiting/skipping behavior, and interaction with teardown paths like uninitProfileList). Since this change is specifically addressing a race condition, please add unit tests that exercise concurrent/overlapping deleteAllProfiles calls (e.g., block the first call mid-way via mocks and verify the second call’s behavior).
| return NULL; | ||
| } | ||
| T2Debug("%s ++in mode=%s\n", __FUNCTION__, mode); | ||
| pthread_mutex_lock(&privacyWorkerMutex); |
There was a problem hiding this comment.
Serializing the entire worker with a global mutex can cause unbounded accumulation of detached threads if PrivacyMode is toggled rapidly: each event still spawns a new thread, and additional threads will block on privacyWorkerMutex while holding full thread stacks. Consider coalescing events (single long-lived worker thread + queue), or using pthread_mutex_trylock/an atomic “worker running” flag to drop or merge redundant invocations rather than creating another thread that will just wait.
| pthread_mutex_lock(&privacyWorkerMutex); | |
| /* Use trylock to avoid blocking multiple detached threads on the | |
| * global mutex. If another worker is already running, drop this | |
| * redundant invocation to prevent unbounded accumulation of waiting | |
| * threads (each holding a full stack). | |
| */ | |
| if(pthread_mutex_trylock(&privacyWorkerMutex) != 0) | |
| { | |
| T2Debug("%s: privacyWorkerMutex busy, skipping redundant privacyModeCallbackWorker for mode=%s\n", | |
| __FUNCTION__, mode); | |
| free(mode); | |
| return NULL; | |
| } |
No description provided.