Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[brucem] CRT thread fixups #771

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 59 additions & 38 deletions sources/lib/run-time/posix-threads.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -615,30 +618,41 @@ 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);
MSG0("thread-join-single: duplicate join error\n");
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;
Expand All @@ -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) {
Expand All @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space in if( here.

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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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);
}
Expand All @@ -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
Expand Down