/* ===== tls_exiting_procs ======================================================== PUBLIC. ================================================================================ */ status_t tls_exiting_procs () { thread_id id = find_thread (NULL) ; _tls_thread_entry_t* e = NULL ; status_t status ; // Search for the thread entry. if ((status = tls_entries_mutex_p ()) == B_NO_ERROR) { status = tls_get_entry (id, &e) ; tls_entries_mutex_v () ; } // If the thread has no entry, then we should not call its exit procs. if (status == B_NAME_NOT_FOUND) return B_NO_ERROR ; // Other errors are... errors. if (status != B_NO_ERROR) return status ; // Free the entry structure (avoid monitoring thread to have to do this at // a bad time, in a bad thread). The entry deletion function will automatically // call exiting procs. if ((status = tls_entries_mutex_p ()) == B_NO_ERROR) { status = tls_delete_entry (e) ; tls_entries_mutex_v () ; } // Finish. return status ; }
/* ===== monitoring =============================================================== PRIVATE. Check in the thread entries list to detect ended threads. These threads see their entry deleted. ================================================================================ */ int32 monitoring (void* unused) { int i ; int numberDeleted ; thread_info info ; _tls_thread_entry_t* pEntry ; // We use semaphore time-outs to periodically wake up the monitoring thread, // with acquire_sem_etc returning B_TIMED_OUT. // When the term function is called, it set the semaphore and the thread // is immediatly resumed with acquire_sem_etc returning B_NO_ERROR. while (acquire_sem_etc (gm_signalExit, 1, B_TIMEOUT, 1000000LL) == B_TIMED_OUT) { // Must be reset at each iteration (ex-bug). numberDeleted = 0 ; tls_entries_mutex_p () ; { for (i=0 ; i<gm_entries_count ; i++) { pEntry = gm_sorted[i] ; // If not used, continue... if (pEntry->sign == SIGN_FREE) continue ; // Get thread info. If impossible... the thread should be down. if (B_NO_ERROR != get_thread_info (pEntry->ownerThread, &info)) { // As the entry is to be deleted, call the TLS exit procs. tls_call_entry_exit (pEntry->ownerThread, PROC_EXIT, pEntry) ; // Simply note it as free. pEntry->sign = SIGN_FREE ; numberDeleted++ ; } } // Update globals. if (numberDeleted != 0) { // Sort-it so deleted be moved at the end of the sorted array. assert (gm_sorted != NULL) ; assert (gm_entries_count >= 0) ; qsort (gm_sorted, gm_entries_count, sizeof (_tls_thread_entry_t*), qsort_comp_proc) ; // And decrement the number of elements. gm_entries_count -= numberDeleted ; } } tls_entries_mutex_v () ; } return 0 ; }
/* ===== tls_term ================================================================= PRIVATE. Terminate TLS management. Note: When its called, you should be sure that there is no more thread to call TLS services. ================================================================================ */ status_t tls_term () { status_t exit_value ; int i ; if (!gm_initOk) return B_NO_ERROR ; // Signal to monitoring thread that it must end, and waits for it. release_sem (gm_signalExit) ; wait_for_thread (gm_monitor, &exit_value) ; // Free monitor references. delete_sem (gm_signalExit) ; gm_signalExit = 0 ; gm_monitor = 0 ; // Remove entry and exit procs. while (gm_entry_proc_list && (B_NO_ERROR == tls_remove_entry_exit (PROC_ENTRY, gm_entry_proc_list->proc, gm_entry_proc_list->param))) ; while (gm_exit_proc_list && (B_NO_ERROR == tls_remove_entry_exit (PROC_EXIT, gm_exit_proc_list->proc, gm_exit_proc_list->param))) ; // Acquire resources to be released. if (gm_mutex_entries) tls_index_mutex_p () ; if (gm_mutex_indexs) tls_entries_mutex_p () ; if (gm_mutex_entry_exit) tls_entry_exit_mutex_p () ; // Free resources. delete_sem (gm_mutex_entries) ; gm_mutex_entries = 0 ; gm_mutex_entries_val = 1 ; for (i=0 ; i<THREAD_ENTRIES_RECENT_COUNT ; i++) gm_recent[i] = NULL ; delete_sem (gm_mutex_indexs) ; gm_mutex_indexs = 0 ; gm_mutex_indexs_val = 1 ; delete_sem (gm_mutex_entry_exit) ; gm_mutex_entry_exit = 0 ; gm_mutex_entry_exit_val = 1 ; // Note: memory is not freed, and counters are not reseted (can be reused as is). if (gm_indexs) memset (gm_indexs, 0, sizeof(bool)*gm_indexs_allocated_count) ; gm_initOk = false ; return B_NO_ERROR ; }
/* ===== tls_get ================================================================== PUBLIC. Get a TLS data at an index. ================================================================================ */ status_t tls_get (int index, tls_data_t* pData) { int indexOk ; _tls_thread_entry_t* e = NULL ; thread_id id ; status_t status ; // Check parameter. assert (pData != NULL) ; if (pData == NULL) return B_BAD_VALUE ; *pData = TLS_INVALID_DATA ; assert (index >= 0) ; if (index < 0) return B_BAD_VALUE ; // Check index value (verify index has been allocated). if ((status = tls_index_mutex_p ()) == B_NO_ERROR) { indexOk = tls_check_index (index) ; tls_index_mutex_v () ; } else return status ; if (!indexOk) return B_BAD_INDEX ; // Get the entry for the thread. id = find_thread (NULL) ; if ((status = tls_entries_mutex_p ()) == B_NO_ERROR) { if ((status = tls_get_entry (id, &e)) != B_NO_ERROR) status = tls_create_entry (id, &e) ; tls_entries_mutex_v () ; } // Get it using the get_inproc function. if (status == B_NO_ERROR) return tls_get_inproc (index, pData, e) ; else return status ; }
/* ===== tls_entering_procs ======================================================= PUBLIC. ================================================================================ */ status_t tls_entering_procs () { thread_id id = find_thread (NULL) ; _tls_thread_entry_t* e = NULL ; status_t status ; bool newlyCreated = false ; // Search for the thread entry. // If not founded, create it and dont call entry procs because tls_create_entry // automatically call them. // If founded, call entry procs. if ((status = tls_entries_mutex_p ()) == B_NO_ERROR) { if ((status = tls_get_entry (id, &e)) != B_NO_ERROR) status = tls_create_entry (id, &e) ; else tls_call_entry_exit (id, PROC_ENTRY, e) ; tls_entries_mutex_v () ; } if (status != B_NO_ERROR) return status ; // Finish return B_NO_ERROR ; }
/* ===== tls_init ================================================================= PRIVATE. Initialise TLS management. Called at dynamic module loading. Note: as we use Benaphore like mutexs, the semaphores initial count are 0. ================================================================================ */ status_t tls_init (int clientVersion) { int i ; status_t status ; int semCreatedCount = 0 ; // Currently, the test is based on the same primary version number. if ((clientVersion & 0xFF000000) < (TLS_VERSION &0xFF000000)) return B_ERROR ; // Create entries mutex. If the mutex already exist, the init phase is already // done (or is running). In this case, we wait for the mutex to be released // by its current owner, and then return doing nothing. if (gm_mutex_entries != 0) { if ((status=tls_entries_mutex_p ()) != B_NO_ERROR) // Wait for the mutex. goto ERROR_LABEL ; tls_entries_mutex_v (); // Release it. return B_NO_ERROR ; // Consider init already done. } else { // Note: When the semaphore is created, gm_mutex_entries_val is with // a value 1 (not 0). So during init phase other threads will block on // semaphore. status = gm_mutex_entries = create_sem (0, "tls-entries") ; if (gm_mutex_entries < B_NO_ERROR) goto ERROR_LABEL ; semCreatedCount = 1 ; } // Create indexs mutex. status = gm_mutex_indexs = create_sem (0, "tls-indexs") ; if (gm_mutex_indexs < B_NO_ERROR) goto ERROR_LABEL ; semCreatedCount = 2 ; // Create entry/exit mutex. status = gm_mutex_entry_exit = create_sem (0, "tls-entry-exit") ; if (gm_mutex_indexs < B_NO_ERROR) goto ERROR_LABEL ; semCreatedCount = 3 ; // Initialize array of last recently used entries. for (i=0 ; i<THREAD_ENTRIES_RECENT_COUNT ; i++) gm_recent[i] = NULL ; // Create tls ended thread management. status = gm_signalExit = create_sem (0, "tls-monitor-exit") ; if (gm_signalExit < B_NO_ERROR) goto ERROR_LABEL ; semCreatedCount = 4 ; status = gm_monitor = spawn_thread (monitoring, "tls_monitor", B_LOW_PRIORITY, NULL) ; if (gm_monitor != B_NO_MORE_THREADS && gm_monitor != B_NO_MEMORY) resume_thread (gm_monitor) ; else goto ERROR_LABEL ; // Finish. tls_entries_mutex_v () ; tls_index_mutex_v () ; tls_entry_exit_mutex_v () ; gm_initOk = true ; return B_NO_ERROR ; ERROR_LABEL: // Error processing goes here. Release the resources that may have been allocated. gm_monitor = 0 ; if (semCreatedCount >= 4) { delete_sem (gm_signalExit) ; gm_signalExit = 0 ; } if (semCreatedCount >= 3) { delete_sem (gm_mutex_entry_exit) ; gm_mutex_entry_exit = 0 ; gm_mutex_entry_exit_val = 1 ; } if (semCreatedCount >= 2) { delete_sem (gm_mutex_indexs) ; gm_mutex_indexs = 0 ; gm_mutex_indexs_val = 1 ; } if (semCreatedCount >= 1) { delete_sem (gm_mutex_entries) ; gm_mutex_entries = 0 ; gm_mutex_entries_val = 1 ; } // Return the status got when the error was detected. return status ; }