diff --git a/source/bulkdata/profile.c b/source/bulkdata/profile.c index 1b0cf593..f141c342 100644 --- a/source/bulkdata/profile.c +++ b/source/bulkdata/profile.c @@ -54,7 +54,7 @@ static bool initialized = false; static Vector *profileList; -static pthread_mutex_t plMutex; +static pthread_mutex_t profileListLock; // L0: protects profileList (rdlock for reads, wrlock for writes) static pthread_mutex_t reportLock; static pthread_mutex_t triggerConditionQueMutex = PTHREAD_MUTEX_INITIALIZER; @@ -302,19 +302,19 @@ T2ERROR profileWithNameExists(const char *profileName, bool *bProfileExists) *bProfileExists = false; return T2ERROR_FAILURE; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); for(; profileIndex < Vector_Size(profileList); profileIndex++) { tempProfile = (Profile *)Vector_At(profileList, profileIndex); if(strcmp(tempProfile->name, profileName) == 0) { *bProfileExists = true; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_SUCCESS; } } *bProfileExists = false; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Error("Profile with Name : %s not found\n", profileName); return T2ERROR_PROFILE_NOT_FOUND; } @@ -925,17 +925,17 @@ reportThreadEnd : void NotifyTimeout(const char* profileName, bool isClearSeekMap) { T2Debug("%s ++in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Profile *profile = NULL; if(T2ERROR_SUCCESS != getProfile(profileName, &profile)) { T2Error("Profile : %s not found\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return ; } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Info("%s: profile %s is in %s state\n", __FUNCTION__, profileName, profile->enable ? "Enabled" : "Disabled"); pthread_mutex_lock(&profile->reportInProgressMutex); if(profile->enable && !profile->reportInProgress) @@ -975,15 +975,15 @@ T2ERROR Profile_storeMarkerEvent(const char *profileName, T2Event *eventInfo) { T2Debug("%s ++in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Profile *profile = NULL; if(T2ERROR_SUCCESS != getProfile(profileName, &profile)) { T2Error("Profile : %s not found\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if(!profile->enable) { T2Warning("Profile : %s is disabled, ignoring the event\n", profileName); @@ -1138,10 +1138,10 @@ T2ERROR addProfile(Profile *profile) T2Error("profile list is not initialized yet, ignoring\n"); return T2ERROR_FAILURE; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Vector_PushBack(profileList, profile); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -1154,18 +1154,18 @@ T2ERROR enableProfile(const char *profileName) T2Error("profile list is not initialized yet, ignoring\n"); return T2ERROR_FAILURE; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Profile *profile = NULL; if(T2ERROR_SUCCESS != getProfile(profileName, &profile)) { T2Error("Profile : %s not found\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } if(profile->enable) { T2Info("Profile : %s is already enabled - ignoring duplicate request\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_SUCCESS; } else @@ -1174,19 +1174,19 @@ T2ERROR enableProfile(const char *profileName) if(pthread_mutex_init(&profile->triggerCondMutex, NULL) != 0) { T2Error(" %s Mutex init has failed\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } if(pthread_mutex_init(&profile->reportInProgressMutex, NULL) != 0) { T2Error(" %s Mutex init has failed\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } if(pthread_cond_init(&profile->reportInProgressCond, NULL) != 0) { T2Error(" %s Cond init has failed\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } @@ -1201,7 +1201,7 @@ T2ERROR enableProfile(const char *profileName) { profile->enable = false; T2Error("Unable to register profile : %s with Scheduler\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } T2ER_StartDispatchThread(); @@ -1209,7 +1209,7 @@ T2ERROR enableProfile(const char *profileName) T2Info("Successfully enabled profile : %s\n", profileName); } T2Debug("%s --out\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_SUCCESS; } @@ -1221,7 +1221,7 @@ void updateMarkerComponentMap() size_t profileIndex = 0; Profile *tempProfile = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); for(; profileIndex < Vector_Size(profileList); profileIndex++) { tempProfile = (Profile *)Vector_At(profileList, profileIndex); @@ -1237,7 +1237,7 @@ void updateMarkerComponentMap() } } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); } @@ -1251,12 +1251,12 @@ T2ERROR disableProfile(const char *profileName, bool *isDeleteRequired) return T2ERROR_FAILURE; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Profile *profile = NULL; if(T2ERROR_SUCCESS != getProfile(profileName, &profile)) { T2Error("Profile : %s not found\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } @@ -1272,7 +1272,7 @@ T2ERROR disableProfile(const char *profileName, bool *isDeleteRequired) removeProfileFromDisk(SEEKFOLDER, profile->name); #endif profile->isSchedulerstarted = false; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; @@ -1286,24 +1286,24 @@ T2ERROR deleteAllProfiles(bool delFromDisk) int profileIndex = 0; Profile *tempProfile = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); if(profileList == NULL) { T2Error("profile list is not initialized yet or profileList is empty, ignoring\n"); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } count = Vector_Size(profileList); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); for(; profileIndex < count; profileIndex++) { - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); tempProfile = (Profile *)Vector_At(profileList, profileIndex); tempProfile->enable = false; tempProfile->isSchedulerstarted = false; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if(T2ERROR_SUCCESS != unregisterProfileFromScheduler(tempProfile->name)) { @@ -1330,13 +1330,13 @@ T2ERROR deleteAllProfiles(bool delFromDisk) * after setting threadExists = false (see CollectAndReport cleanup). */ } - /* Re-acquire plMutex for profile cleanup */ - pthread_mutex_lock(&plMutex); + /* Re-acquire profileListLock for profile cleanup */ + pthread_mutex_lock(&profileListLock); if(tempProfile->grepSeekProfile) { freeGrepSeekProfile(tempProfile->grepSeekProfile); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if(delFromDisk == true) { removeProfileFromDisk(REPORTPROFILES_PERSISTENCE_PATH, tempProfile->name); @@ -1350,12 +1350,12 @@ T2ERROR deleteAllProfiles(bool delFromDisk) removeProfileFromDisk(REPORTPROFILES_PERSISTENCE_PATH, MSGPACK_REPORTPROFILES_PERSISTENT_FILE); } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); T2Debug("Deleting all profiles from the profileList\n"); Vector_Destroy(profileList, freeProfile); profileList = NULL; Vector_Create(&profileList); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); @@ -1366,17 +1366,17 @@ bool isProfileEnabled(const char *profileName) { bool is_profile_enable = false; Profile *get_profile = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); if(T2ERROR_SUCCESS != getProfile(profileName, &get_profile)) { T2Error("Profile : %s not found\n", profileName); T2Debug("%s --out\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return false; } is_profile_enable = get_profile->enable; T2Debug("is_profile_enable = %d \n", is_profile_enable); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return is_profile_enable; } @@ -1391,11 +1391,11 @@ T2ERROR deleteProfile(const char *profileName) } Profile *profile = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); if(T2ERROR_SUCCESS != getProfile(profileName, &profile)) { T2Error("Profile : %s not found\n", profileName); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return T2ERROR_FAILURE; } @@ -1407,14 +1407,14 @@ T2ERROR deleteProfile(const char *profileName) { profile->isSchedulerstarted = false; } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if(T2ERROR_SUCCESS != unregisterProfileFromScheduler(profileName)) { T2Info("Profile : %s already removed from scheduler\n", profileName); } T2Info("Waiting for CollectAndReport to be complete : %s\n", profileName); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); // Wait for any in-progress report to finish under reportInProgressMutex. // threadExists must NOT be read here — it is written under reuseThreadMutex @@ -1435,13 +1435,13 @@ T2ERROR deleteProfile(const char *profileName) bool threadStillExists = profile->threadExists; pthread_mutex_unlock(&profile->reuseThreadMutex); - /* Release plMutex before pthread_join to avoid deadlock. + /* Release profileListLock before pthread_join to avoid deadlock. * pthread_join can block indefinitely if the CollectAndReport thread - * is stuck (e.g., waiting on rbusMethodMutex). Holding plMutex during + * is stuck (e.g., waiting on rbusMethodMutex). Holding profileListLock during * pthread_join prevents other threads (timeout callbacks, other profile * operations) from making progress, creating a deadlock. */ - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if (threadStillExists) { @@ -1454,8 +1454,8 @@ T2ERROR deleteProfile(const char *profileName) * after setting threadExists = false (see CollectAndReport cleanup). */ } - /* Re-acquire plMutex for profile cleanup operations */ - pthread_mutex_lock(&plMutex); + /* Re-acquire profileListLock (wrlock) for profile cleanup operations */ + pthread_mutex_lock(&profileListLock); if(Vector_Size(profile->triggerConditionList) > 0) { @@ -1476,7 +1476,7 @@ T2ERROR deleteProfile(const char *profileName) #endif Vector_RemoveItem(profileList, profile, freeProfile); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -1487,7 +1487,7 @@ void sendLogUploadInterruptToScheduler() Profile *tempProfile = NULL; T2Debug("%s ++in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); for(; profileIndex < Vector_Size(profileList); profileIndex++) { tempProfile = (Profile *)Vector_At(profileList, profileIndex); @@ -1496,7 +1496,7 @@ void sendLogUploadInterruptToScheduler() SendInterruptToTimeoutThread(tempProfile->name); } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); } @@ -1640,20 +1640,22 @@ T2ERROR initProfileList(bool checkPreviousSeek) return T2ERROR_SUCCESS; } initialized = true; - if(pthread_mutex_init(&plMutex, NULL) != 0) + if(pthread_mutex_init(&profileListLock, NULL) != 0) { - T2Error("%s mutex init has failed\n", __FUNCTION__); + T2Error("%s rwlock init has failed\n", __FUNCTION__); return T2ERROR_FAILURE; } if(pthread_mutex_init(&reportLock, NULL) != 0 ) { T2Error("%s mutex init has failed\n", __FUNCTION__); + pthread_mutex_destroy(&profileListLock); + initialized = false; return T2ERROR_FAILURE; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); Vector_Create(&profileList); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); registerConditionalReportCallBack(&triggerReportOnCondtion); @@ -1672,9 +1674,9 @@ int getProfileCount() T2Info("profile list isn't initialized\n"); return count; } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); count = Vector_Size(profileList); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return count; @@ -1687,7 +1689,7 @@ hash_map_t *getProfileHashMap() Profile *tempProfile = NULL; T2Debug("%s ++in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); profileHashMap = hash_map_create(); for(; profileIndex < Vector_Size(profileList); profileIndex++) { @@ -1696,7 +1698,7 @@ hash_map_t *getProfileHashMap() char *profileHash = strdup(tempProfile->hash); hash_map_put(profileHashMap, profileName, profileHash, free); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return profileHashMap; @@ -1726,7 +1728,7 @@ T2ERROR uninitProfileList() } pthread_mutex_destroy(&reportLock); - pthread_mutex_destroy(&plMutex); + pthread_mutex_destroy(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; @@ -1746,7 +1748,7 @@ T2ERROR registerTriggerConditionConsumer() while(retry_count <= MAX_RETRY_COUNT) { - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); profileIndex = 0; for(; profileIndex < Vector_Size(profileList); profileIndex++) { @@ -1762,7 +1764,7 @@ T2ERROR registerTriggerConditionConsumer() } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); if(retry == 1) { if(retry_count >= MAX_RETRY_COUNT) @@ -1788,8 +1790,8 @@ void NotifySchedulerstart(char* profileName, bool isschedulerstarted) { size_t profileIndex = 0; Profile *tempProfile = NULL; - pthread_mutex_lock(&plMutex); - T2Debug("plMutex is locked %s\n", __FUNCTION__); + pthread_mutex_lock(&profileListLock); + T2Debug("profileListLock is rdlocked %s\n", __FUNCTION__); for(; profileIndex < Vector_Size(profileList); profileIndex++) { tempProfile = (Profile *)Vector_At(profileList, profileIndex); @@ -1801,8 +1803,8 @@ void NotifySchedulerstart(char* profileName, bool isschedulerstarted) } } } - pthread_mutex_unlock(&plMutex); - T2Debug("plMutex is unlocked %s\n", __FUNCTION__); + pthread_mutex_unlock(&profileListLock); + T2Debug("profileListLock is unlocked %s\n", __FUNCTION__); return; } @@ -1954,7 +1956,7 @@ T2ERROR triggerReportOnCondtion(const char *referenceName, const char *reference size_t j, profileIndex = 0; Profile *tempProfile = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); for(; profileIndex < Vector_Size(profileList); profileIndex++) { tempProfile = (Profile *)Vector_At(profileList, profileIndex); @@ -1976,9 +1978,9 @@ T2ERROR triggerReportOnCondtion(const char *referenceName, const char *reference char *tempProfilename = strdup(tempProfile->name); //RDKB-42640 - // plmutex should be unlocked before sending interrupt for report generation - T2Debug("%s : Release lock on &plMutex\n ", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + // profileListLock should be unlocked before sending interrupt for report generation + T2Debug("%s : Release lock on &profileListLock\n ", __FUNCTION__); + pthread_mutex_unlock(&profileListLock); T2Info("Triggering report on condition for %s with %s operator, %d threshold\n", triggerCondition->reference, triggerCondition->oprator, triggerCondition->threshold); if(tempProfile->isSchedulerstarted) @@ -1999,8 +2001,8 @@ T2ERROR triggerReportOnCondtion(const char *referenceName, const char *reference else { T2Info("Report generation will take place by popping up from the que by callback function \n"); - T2Debug("%s : Release lock on &plMutex\n ", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + T2Debug("%s : Release lock on &profileListLock\n ", __FUNCTION__); + pthread_mutex_unlock(&profileListLock); return T2ERROR_SUCCESS; } } @@ -2008,7 +2010,7 @@ T2ERROR triggerReportOnCondtion(const char *referenceName, const char *reference } } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -2018,18 +2020,18 @@ unsigned int getMinThresholdDuration(char *profileName) unsigned int minThresholdDuration = 0; Profile *get_profile = NULL; T2Debug("%s --in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&profileListLock); if(T2ERROR_SUCCESS != getProfile(profileName, &get_profile)) { T2Error("Profile : %s not found\n", profileName); T2Debug("%s --out\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); return 0; } minThresholdDuration = get_profile->minThresholdDuration; get_profile->minThresholdDuration = 0; // reinit the value T2Debug("minThresholdDuration = %u \n", minThresholdDuration); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&profileListLock); T2Debug("%s --out\n", __FUNCTION__); return minThresholdDuration; } diff --git a/source/bulkdata/profile.h b/source/bulkdata/profile.h index 21bd4de4..948b5782 100644 --- a/source/bulkdata/profile.h +++ b/source/bulkdata/profile.h @@ -39,6 +39,20 @@ typedef struct _JSONEncoding TimeStampFormat tsFormat; } JSONEncoding; +/** + * Lock Hierarchy (acquire in ascending order to prevent deadlocks): + * L0: profileListLock (module-level rwlock - protects profileList) + * L1: profileMutex (per-profile - protects profile state) + * L2: reuseThreadMutex (per-profile - protects thread lifecycle) + * L3: reportInProgressMutex (per-profile - protects reportInProgress flag) + * L4: triggerCondMutex / eventMutex / reportMutex (per-profile - leaf locks) + * + * Rules: + * - Always acquire L0 before L1, L1 before L2, etc. + * - Never hold a higher-numbered lock when acquiring a lower-numbered lock. + * - profileListLock uses rdlock for lookups/iterations, wrlock for add/remove. + * - Release profileListLock before long operations (pthread_join, HTTP send). + */ typedef struct _Profile { bool enable; @@ -94,6 +108,7 @@ typedef struct _Profile bool restartRequested; bool threadExists; GrepSeekProfile *grepSeekProfile; // To store GrepConfig + pthread_mutex_t profileMutex; // L1: per-profile lock for state protection } Profile; T2ERROR initProfileList(bool checkPreviousSeek); diff --git a/source/bulkdata/profilexconf.c b/source/bulkdata/profilexconf.c index 3a8e8424..db678534 100644 --- a/source/bulkdata/profilexconf.c +++ b/source/bulkdata/profilexconf.c @@ -45,7 +45,12 @@ static bool initialized = false; static ProfileXConf *singleProfile = NULL; -static pthread_mutex_t plMutex; /* TODO - we can remove plMutex most likely but firseck that CollectAndReport doesn't cause issue */ +/** + * xconfProfileLock: Protects singleProfile pointer and reportThreadExits flag. + * Kept as mutex (not rwlock) because pthread_cond_wait requires pthread_mutex_t. + * Lock hierarchy: L0 (same level as profileListLock in profile.c). + */ +static pthread_mutex_t xconfProfileLock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t reuseThread; static bool reportThreadExits = false; @@ -209,16 +214,16 @@ static void* CollectAndReportXconf(void* data) (void) data;// To fix compiler warning /* Set reportThreadExits flag under mutex to prevent data race with - * ProfileXConf_notifyTimeout which reads this flag under plMutex. + * ProfileXConf_notifyTimeout which reads this flag under xconfProfileLock. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); reportThreadExits = true; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); do { - /* CRITICAL SECTION START: Acquire plMutex to check/access singleProfile */ - pthread_mutex_lock(&plMutex); + /* CRITICAL SECTION START: Acquire xconfProfileLock to check/access singleProfile */ + pthread_mutex_lock(&xconfProfileLock); T2Info("%s while Loop -- START \n", __FUNCTION__); if(singleProfile == NULL) @@ -231,7 +236,7 @@ static void* CollectAndReportXconf(void* data) /* Set reportInProgress flag to prevent concurrent report generation * and profile deletion while we're working. This must be done under - * plMutex to prevent races with ProfileXConf_notifyTimeout() and + * xconfProfileLock to prevent races with ProfileXConf_notifyTimeout() and * ProfileXConf_delete(). */ profile->reportInProgress = true; @@ -257,21 +262,21 @@ static void* CollectAndReportXconf(void* data) T2Info("%s ++in profileName : %s\n", __FUNCTION__, profile->name); } - /* CRITICAL: Release plMutex before potentially blocking operations. + /* CRITICAL: Release xconfProfileLock before potentially blocking operations. * Report generation involves: * - * Holding plMutex during these operations blocks ALL other XCONF profile + * Holding xconfProfileLock during these operations blocks ALL other XCONF profile * operations (timeouts, updates, deletions, marker events) system-wide, * causing the telemetry system to hang. * - * We can safely release plMutex here because: + * We can safely release xconfProfileLock here because: * 1. We've already checked singleProfile is valid * 2. We use profile->reportInProgress to prevent concurrent reports * 3. Profile deletion waits for reportInProgress to be false - * 4. We'll re-acquire plMutex before updating shared state + * 4. We'll re-acquire xconfProfileLock before updating shared state */ - pthread_mutex_unlock(&plMutex); - /* CRITICAL SECTION END - plMutex released, other threads can now proceed */ + pthread_mutex_unlock(&xconfProfileLock); + /* CRITICAL SECTION END - xconfProfileLock released, other threads can now proceed */ int clockReturn = 0; clockReturn = clock_gettime(CLOCK_MONOTONIC, &startTime); @@ -280,11 +285,11 @@ static void* CollectAndReportXconf(void* data) if(T2ERROR_SUCCESS != initJSONReportXconf(&profile->jsonReportObj, &valArray)) { T2Error("Failed to initialize JSON Report\n"); - /* Re-acquire plMutex before updating profile state. - * CRITICAL: Keep plMutex locked before goto because reportXconfThreadEnd + /* Re-acquire xconfProfileLock before updating profile state. + * CRITICAL: Keep xconfProfileLock locked before goto because reportXconfThreadEnd * calls pthread_cond_wait which requires the mutex to be locked. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile == profile) { profile->reportInProgress = false; @@ -331,19 +336,19 @@ static void* CollectAndReportXconf(void* data) dcaFlagReportCompleation(); - /* CRITICAL: Re-acquire plMutex to safely access eMarkerList. + /* CRITICAL: Re-acquire xconfProfileLock to safely access eMarkerList. * External components can call t2_event_s() which modifies eMarkerList - * via ProfileXConf_storeMarkerEvent(). We must hold plMutex during + * via ProfileXConf_storeMarkerEvent(). We must hold xconfProfileLock during * event marker encoding to prevent race conditions. * This is safe because encoding is quick (~milliseconds), unlike HTTP * upload which can take 30+ seconds. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile == profile && profile->eMarkerList != NULL && Vector_Size(profile->eMarkerList) > 0) { encodeEventMarkersInJSON(valArray, profile->eMarkerList); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); profile->grepSeekProfile->execCounter += 1; T2Info("Xconf Profile Execution Count = %d\n", profile->grepSeekProfile->execCounter); @@ -364,11 +369,11 @@ static void* CollectAndReportXconf(void* data) if(ret != T2ERROR_SUCCESS) { T2Error("Unable to generate report for : %s\n", profile->name); - /* Re-acquire plMutex before updating profile state. - * CRITICAL: Keep plMutex locked before goto because reportXconfThreadEnd + /* Re-acquire xconfProfileLock before updating profile state. + * CRITICAL: Keep xconfProfileLock locked before goto because reportXconfThreadEnd * calls pthread_cond_wait which requires the mutex to be locked. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile == profile) { profile->reportInProgress = false; @@ -400,11 +405,11 @@ static void* CollectAndReportXconf(void* data) // Before caching the report, add "REPORT_TYPE": "CACHED" // tagReportAsCached(&jsonReport); Vector_PushBack(profile->cachedReportList, strdup(jsonReport)); - /* Re-acquire plMutex before updating profile state. - * CRITICAL: Keep plMutex locked before goto because reportXconfThreadEnd + /* Re-acquire xconfProfileLock before updating profile state. + * CRITICAL: Keep xconfProfileLock locked before goto because reportXconfThreadEnd * calls pthread_cond_wait which requires the mutex to be locked. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile == profile) { profile->reportInProgress = false; @@ -555,11 +560,11 @@ static void* CollectAndReportXconf(void* data) isAbortTriggered = false ; } - /* CRITICAL SECTION START: Re-acquire plMutex before updating profile state. - * pthread_cond_wait requires us to hold plMutex, so we acquire it here + /* CRITICAL SECTION START: Re-acquire xconfProfileLock before updating profile state. + * pthread_cond_wait requires us to hold xconfProfileLock, so we acquire it here * and hold it through the state update and into the cond_wait. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile == profile) { profile->reportInProgress = false; @@ -573,26 +578,26 @@ reportXconfThreadEnd : * Wait while: profile exists AND no timeout pending AND not shutting down. * Exit loop when: timeout arrives (reportInProgress=true) OR shutdown (initialized=false). * - * pthread_cond_wait atomically releases plMutex while waiting. - * When signaled or spuriously woken, it re-acquires plMutex before returning. + * pthread_cond_wait atomically releases xconfProfileLock while waiting. + * When signaled or spuriously woken, it re-acquires xconfProfileLock before returning. */ while(singleProfile && !singleProfile->reportInProgress && initialized) { - pthread_cond_wait(&reuseThread, &plMutex); + pthread_cond_wait(&reuseThread, &xconfProfileLock); } - /* After cond_wait loop exits, we hold plMutex again. Release it before + /* After cond_wait loop exits, we hold xconfProfileLock again. Release it before * the next loop iteration (which will re-acquire it). */ - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); } while(initialized); - /* Thread is exiting. We don't hold plMutex here, so acquire it to + /* Thread is exiting. We don't hold xconfProfileLock here, so acquire it to * update the reportThreadExits flag, then release it. */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); reportThreadExits = false; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Info("%s --out exiting the CollectAndReportXconf thread \n", __FUNCTION__); return NULL; @@ -608,18 +613,13 @@ T2ERROR ProfileXConf_init(bool checkPreviousSeek) Config *config = NULL; initialized = true; - if(pthread_mutex_init(&plMutex, NULL) != 0) - { - T2Error("%s Mutex init has failed\n", __FUNCTION__); - return T2ERROR_FAILURE; - } /* Initialize condition variable at module init to prevent race where * ProfileXConf_notifyTimeout signals before CollectAndReportXconf initializes it. */ if(pthread_cond_init(&reuseThread, NULL) != 0) { T2Error("%s Condition variable init has failed\n", __FUNCTION__); - pthread_mutex_destroy(&plMutex); + initialized = false; return T2ERROR_FAILURE; } Vector_Create(&configList); @@ -693,22 +693,22 @@ T2ERROR ProfileXConf_uninit() if(reportInProgress) { T2Debug("Waiting for final report before uninit\n"); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); pthread_cond_signal(&reuseThread); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); pthread_join(singleProfile->reportThread, NULL); pthread_mutex_lock(&singleProfile->reportInProgressMutex); singleProfile->reportInProgress = false ; pthread_mutex_unlock(&singleProfile->reportInProgressMutex); T2Info("Final report is completed, releasing profile memory\n"); } - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); freeProfileXConf(); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); /* Destroy condition variable at module uninit, after all threads are stopped */ pthread_cond_destroy(&reuseThread); - pthread_mutex_destroy(&plMutex); + pthread_mutex_destroy(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -719,7 +719,7 @@ T2ERROR ProfileXConf_set(ProfileXConf *profile) T2ERROR ret = T2ERROR_FAILURE; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(!singleProfile) { @@ -735,11 +735,11 @@ T2ERROR ProfileXConf_set(ProfileXConf *profile) addT2EventMarker(eMarker->markerName, eMarker->compName, singleProfile->name, eMarker->skipFreq); } - /* Release plMutex before calling scheduler API to avoid potential + /* Release xconfProfileLock before calling scheduler API to avoid potential * blocking while holding the mutex. Scheduler operations may involve * timer management and other operations that shouldn't block profile access. */ - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); if(registerProfileWithScheduler(singleProfile->name, singleProfile->reportingInterval, INFINITE_TIMEOUT, false, true, false, DEFAULT_FIRST_REPORT_INT, NULL) == T2ERROR_SUCCESS) { @@ -751,15 +751,15 @@ T2ERROR ProfileXConf_set(ProfileXConf *profile) T2Error("Unable to register profile : %s with Scheduler\n", singleProfile->name); } - /* Note: We already released plMutex above, so no need to unlock again */ - pthread_mutex_lock(&plMutex); + /* Note: We already released xconfProfileLock above, so no need to unlock again */ + pthread_mutex_lock(&xconfProfileLock); } else { T2Error("XConf profile already added, can't have more then 1 profile\n"); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); return ret; @@ -775,7 +775,7 @@ void ProfileXConf_updateMarkerComponentMap() } size_t emIndex = 0; EventMarker *eMarker = NULL; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile) { for(; emIndex < Vector_Size(singleProfile->eMarkerList); emIndex++) @@ -788,14 +788,14 @@ void ProfileXConf_updateMarkerComponentMap() { T2Error("Profile not found in %s\n", __FUNCTION__); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); } bool ProfileXConf_isNameEqual(char* profileName) { bool isName = false; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(initialized) { if(singleProfile && (singleProfile->name != NULL) && (profileName != NULL) && !strcmp(singleProfile->name, profileName)) //Adding NULL check to avoid strcmp crash @@ -805,7 +805,7 @@ bool ProfileXConf_isNameEqual(char* profileName) } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return isName; } @@ -820,23 +820,23 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) T2Debug("calling ProfileXConf_isNameEqual function form %s and line %d\n", __FUNCTION__, __LINE__); bool isNameEqual = ProfileXConf_isNameEqual(profile->name); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(!singleProfile) { T2Error("Profile not found in %s\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return T2ERROR_FAILURE; } /* Copy profile name before unlocking to prevent use-after-free if - * another thread deletes/frees singleProfile after we release plMutex. + * another thread deletes/frees singleProfile after we release xconfProfileLock. */ char profileNameCopy[256] = {0}; if(!isNameEqual && singleProfile && singleProfile->name) { strncpy(profileNameCopy, singleProfile->name, sizeof(profileNameCopy) - 1); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); if(isNameEqual) { @@ -849,7 +849,7 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) else { /* Use copied profile name instead of singleProfile->name to avoid - * use-after-free since we no longer hold plMutex. + * use-after-free since we no longer hold xconfProfileLock. */ if(profileNameCopy[0] != '\0') { @@ -865,7 +865,7 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) * causing use-after-free crash when CollectAndReportXconf() was still * accessing profile members during brief mutex holds (event encoding, etc). */ - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); unsigned int waitIterations = 0; const unsigned int LOG_INTERVAL = 3000; // Log every 3000 iterations (30 seconds at 10ms per iteration) while(singleProfile && singleProfile->reportInProgress) @@ -875,9 +875,9 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) T2Info("Waiting for CollectAndReportXconf to be complete : %s\n", singleProfile->name); } waitIterations++; - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); usleep(10000); // 10ms polling interval - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); } profile->reportThread = singleProfile->reportThread; @@ -989,7 +989,7 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) T2Info("removing profile : %s\n", singleProfile->name); freeProfileXConf(); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS; } @@ -997,7 +997,7 @@ T2ERROR ProfileXConf_delete(ProfileXConf *profile) bool ProfileXConf_isSet() { bool isSet = false; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(singleProfile != NULL) { @@ -1005,14 +1005,14 @@ bool ProfileXConf_isSet() isSet = true; } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return isSet; } char* ProfileXconf_getName() { char* profileName = NULL ; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(initialized) { if(singleProfile) @@ -1020,7 +1020,7 @@ char* ProfileXconf_getName() profileName = strdup(singleProfile->name); } } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return profileName; } @@ -1029,11 +1029,11 @@ void ProfileXConf_notifyTimeout(bool isClearSeekMap, bool isOnDemand) T2Debug("%s ++in\n", __FUNCTION__); int reportThreadStatus = 0 ; - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(!singleProfile) { T2Error("Profile not found in %s\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return ; } isOnDemandReport = isOnDemand; @@ -1063,7 +1063,7 @@ void ProfileXConf_notifyTimeout(bool isClearSeekMap, bool isOnDemand) T2Warning("Received profileTimeoutCb while previous callback is still in progress - ignoring the request\n"); } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); } @@ -1073,11 +1073,11 @@ T2ERROR ProfileXConf_storeMarkerEvent(T2Event *eventInfo) { T2Debug("%s ++in\n", __FUNCTION__); - pthread_mutex_lock(&plMutex); + pthread_mutex_lock(&xconfProfileLock); if(!singleProfile) { T2Error("Profile not found in %s\n", __FUNCTION__); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return T2ERROR_FAILURE; } @@ -1138,11 +1138,11 @@ T2ERROR ProfileXConf_storeMarkerEvent(T2Event *eventInfo) { T2Error("Event name : %s value : %s\n", eventInfo->name, eventInfo->value); T2Error("Event doens't match any marker information, shouldn't come here\n"); - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); return T2ERROR_FAILURE; } - pthread_mutex_unlock(&plMutex); + pthread_mutex_unlock(&xconfProfileLock); T2Debug("%s --out\n", __FUNCTION__); return T2ERROR_SUCCESS;