diff --git a/sources/lib/run-time/posix-threads.c b/sources/lib/run-time/posix-threads.c index 2f93c78e8e..53e88a3581 100644 --- a/sources/lib/run-time/posix-threads.c +++ b/sources/lib/run-time/posix-threads.c @@ -319,7 +319,10 @@ static void grow_all_tlv_vectors(size_t newsize) list = list->next; } - // Let writes proceed again + // Let writes proceed again, but only at a moment + // where TLV_GROW is undisturbed. This ensures + // that we are not between an increment and a + // decrement on the TLV writer side. while (atomic_cas(&tlv_writer_counter, 0, TLV_GROW) != TLV_GROW); } @@ -615,11 +618,13 @@ D primitive_thread_join_single(D t) assert(thread != NULL); + /* Acquire the join lock */ if (pthread_mutex_lock(&thread_join_lock) != 0) { MSG0("thread-join-single: error obtaining thread join lock\n"); return GENERAL_ERROR; } + /* Make sure the thread isn't already part of a join operation */ state = (uintptr_t)thread->handle1; if (state & MARKED || state & JOINED) { pthread_mutex_unlock(&thread_join_lock); @@ -627,18 +632,27 @@ D primitive_thread_join_single(D t) return GENERAL_ERROR; } + /* Mark the thread as joining */ + state = (uintptr_t)thread->handle1; thread->handle1 = (void *)(state | MARKED); - completed = state & COMPLETED; - while (!completed) { - if (pthread_cond_wait(&thread_exit_event, &thread_join_lock)) { - MSG0("thread-join-single: error waiting for thread exit event\n"); - return GENERAL_ERROR; + + /* Spin until the thread is joined */ + do { + state = (uintptr_t)thread->handle1; + completed = state & COMPLETED; + if(!completed) { + if (pthread_cond_wait(&thread_exit_event, &thread_join_lock)) { + MSG0("thread-join-single: error waiting for thread exit event\n"); + return GENERAL_ERROR; + } } - completed = (uintptr_t)thread->handle1 & COMPLETED; - } + } while (!completed); - thread->handle1 = (void *)((uintptr_t)thread->handle1 ^ (JOINED | MARKED)); + /* Mark the thread as joined and remove the join mark */ + state = (uintptr_t)thread->handle1; + thread->handle1 = (void *)((state | JOINED) & ~MARKED); + /* Release the join lock */ if (pthread_mutex_unlock(&thread_join_lock) != 0) { MSG0("thread-join-single: error releasing thread join lock\n"); return GENERAL_ERROR; @@ -663,14 +677,13 @@ D primitive_thread_join_multiple(D v) size = ((uintptr_t)(thread_vector->size)) >> 2; threads = (volatile DTHREAD **)(thread_vector->data); + /* Acquire the join lock */ if (pthread_mutex_lock(&thread_join_lock)) { MSG0("thread-join-multiple: error obtaining thread join lock\n"); return GENERAL_ERROR; } - /* Make sure none of the threads is already - * part of a join operation - */ + /* Ensure none of the threads are already joining or joined */ for (i = 0; i < size; i++) { state = (uintptr_t)threads[i]->handle1; if (state & MARKED || state & JOINED) { @@ -679,42 +692,42 @@ D primitive_thread_join_multiple(D v) } } - /* Now mark the threads as being part of a join - */ + /* Now mark the threads as joining */ for (i = 0; i < size; i++) { state = (uintptr_t)threads[i]->handle1; threads[i]->handle1 = (void *)(state | MARKED); } - for (i = 0; i < size; i++) { - state = (uintptr_t)threads[i]->handle1; - if (state & COMPLETED) { - joined_thread = threads[i]; - break; - } - } - - while (joined_thread == NULL) { - if (pthread_cond_wait(&thread_exit_event, &thread_join_lock)) { - MSG0("thread-join-multiple: error waiting for thread exit event\n"); - return GENERAL_ERROR; - } + /* Spin until a thread is joined */ + do { + /* Check thread states */ for (i = 0; i < size; i++) { - if ((uintptr_t)threads[i]->handle1 & COMPLETED) { + state = (uintptr_t)threads[i]->handle1; + if (state & COMPLETED) { joined_thread = threads[i]; break; } } - } + /* Wait for join condition unless we already found a thread */ + if(joined_thread == NULL) { + if (pthread_cond_wait(&thread_exit_event, &thread_join_lock)) { + MSG0("thread-join-multiple: error waiting for thread exit event\n"); + return GENERAL_ERROR; + } + } + } while (joined_thread == NULL); + /* Mark the joined thread as such */ state = (uintptr_t)joined_thread->handle1; joined_thread->handle1 = (void *)(state | JOINED); + /* Remove join mark on all the threads so we can retry */ for (i = 0; i < size; i++) { state = (uintptr_t)threads[i]->handle1; - threads[i]->handle1 = (void *)(state ^ MARKED); + threads[i]->handle1 = (void *)(state & ~MARKED); } + /* Release the join lock */ if (pthread_mutex_unlock(&thread_join_lock) != 0) { MSG0("thread-join-multiple: error releasing thread join lock\n"); return GENERAL_ERROR; @@ -726,12 +739,9 @@ D primitive_thread_join_multiple(D v) /* 4.5 */ void primitive_detach_thread(D t) { - DTHREAD* thread = t; - THREAD* rthread; - assert(thread != NULL); - rthread = (THREAD*)(thread->handle2); + DTHREAD *thread = (DTHREAD *)t; - pthread_detach(rthread->tid); + assert(thread != NULL); } /* 5 */ @@ -1569,10 +1579,19 @@ D primitive_read_thread_variable(D h) /* 35 */ -static void primitive_write_thread_variable_internal(void) +static void primitive_write_thread_variable_blocked(void) { + // Spin until we see normal writer counts again do { + // This decrement will undo the increment done before + // entering this function or on the previous loop iteration. + // This will disturb the writer count even if we do not + // have a lock, but only if the count is negative. + // Since TLV_GROW is large we will never change the sign. if (atomic_decrement(&tlv_writer_counter) < 0) { + // Synchronize with TLV growing by waiting + // on the TLV list lock whenever we encounter + // a negative writer count. pthread_mutex_lock(&tlv_vector_list_lock); pthread_mutex_unlock(&tlv_vector_list_lock); } @@ -1584,9 +1603,11 @@ D primitive_write_thread_variable(D h, D nv) TLV_VECTOR tlv_vector; uintptr_t offset; - // Wait until there are no other writers + // Acquire write lock by incrementing if (atomic_increment(&tlv_writer_counter) < 0) { - primitive_write_thread_variable_internal(); + // If the count is negative then another + // thread is growing TLVs. Wait for it. + primitive_write_thread_variable_blocked(); } // The variable handle is the byte offset where the variable's value is