int dcethread_create(dcethread** _thread, dcethread_attr* attr, void *(*start_routine)(void *), void *arg) { dcethread_start_args *start_args; dcethread* thread; int detachstate; start_args = (dcethread_start_args *) malloc(sizeof(*start_args)); if (start_args == NULL) { return dcethread__set_errno(ENOMEM); } start_args->start = start_routine; start_args->data = arg; start_args->self = thread = dcethread__new(); /* Record if this thread was created joinably */ if (!attr || (pthread_attr_getdetachstate(attr, &detachstate), detachstate == PTHREAD_CREATE_JOINABLE)) { thread->flag.joinable = 1; } /* If thread is joinable, give it an extra reference */ if (thread->flag.joinable) { thread->refs++; } if (dcethread__set_errno(pthread_create((pthread_t*) &thread->pthread, attr, proxy_start, start_args))) { dcethread__delete(thread); free(start_args); return -1; } DCETHREAD_TRACE("Thread %p: created (pthread %lu)", thread, (unsigned long) thread->pthread); dcethread__lock(thread); while (thread->state == DCETHREAD_STATE_CREATED) { dcethread__wait(thread); } dcethread__unlock(thread); DCETHREAD_TRACE("Thread %p: started", thread); *_thread = thread; return dcethread__set_errno(0); }
void dcethread__lock(dcethread* thread) { if (pthread_mutex_lock((pthread_mutex_t*) &thread->lock)) DCETHREAD_ERROR("Thread %p: failed to lock mutex", thread); thread->flag.locked = 1; dcethread__sanity(thread); DCETHREAD_TRACE("Thread %p: locked", thread); }
void dcethread__change_state(dcethread* thread, int state) { DCETHREAD_TRACE("Thread %p: state transition %s -> %s", thread, state_name(thread->state), state_name(state)); thread->state = state; pthread_cond_broadcast((pthread_cond_t*) &thread->state_change); }
void dcethread__delete(dcethread* thread) { DCETHREAD_TRACE("Thread %p: deleted", thread); pthread_mutex_destroy((pthread_mutex_t*) &thread->lock); pthread_cond_destroy((pthread_cond_t*) &thread->state_change); if (thread->flag.joinable) pthread_detach(thread->pthread); free((void*) thread); }
int dcethread__interrupt_condwait(dcethread* thread, void* data) { condwait_info* info = (condwait_info*) data; if (pthread_equal(info->mutex->owner, pthread_self())) { DCETHREAD_TRACE("Thread %p: already owned mutex used for interrupt", thread); if (pthread_cond_broadcast(info->cond)) { DCETHREAD_ERROR("Thread %p: broadcast failed", thread); return 0; } else { DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread); return 1; } } else if (!pthread_mutex_trylock((pthread_mutex_t*) &info->mutex->mutex)) { info->mutex->owner = pthread_self(); if (pthread_cond_broadcast(info->cond)) { DCETHREAD_ERROR("Thread %p: broadcast failed", thread); info->mutex->owner = (pthread_t) -1; pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex); return 0; } else { DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread); info->mutex->owner = (pthread_t) -1; pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex); return 1; } } else { DCETHREAD_VERBOSE("Thread %p: could not acquire lock to interrupt condwait", thread); return 0; } }
void dcethread__interrupt(dcethread* thread) { int count = 0; int old_state = thread->state; if (old_state == DCETHREAD_STATE_INTERRUPT || old_state == DCETHREAD_STATE_DEAD) { /* Don't bother */ return; } DCETHREAD_TRACE("Thread %p: interrupt posted", thread); dcethread__change_state(thread, DCETHREAD_STATE_INTERRUPT); /* We need to poke the thread and wait for an acknowledgement of the interrupt if: */ if (thread != dcethread__self() && /* The interrupted thread is not us, and */ thread->flag.interruptible && /* The thread can be interrupted, and */ old_state == DCETHREAD_STATE_BLOCKED) /* The thread was blocked */ { /* FIXME: potential livelock here if another thread re-interrupts when the lock is released */ while (thread->state == DCETHREAD_STATE_INTERRUPT) { struct timespec waittime; if (count > 2) DCETHREAD_WARNING("Thread %p: still not interrupted after %i ms", thread, count * 100); if (thread->interrupt(thread, thread->interrupt_data)) { /* Interrupt is guaranteed to have succeeded, so leave state change wait loop */ break; } count++; my_clock_gettime(&waittime); waittime.tv_nsec += 100000000; if (waittime.tv_nsec > 1000000000) { waittime.tv_nsec -= 1000000000; waittime.tv_sec += 1; } /* Wait for state change */ dcethread__timedwait(thread, &waittime); } } }
void dcethread__release(dcethread* thread) { dcethread__sanity(thread); if (thread->refs <= 0) { DCETHREAD_ERROR("Thread %p: attempted to release freed thread", thread); } else { thread->refs--; DCETHREAD_TRACE("Thread %p: ref count decreased to %i", thread, thread->refs); } }
void dcethread__retain(dcethread* thread) { dcethread__sanity(thread); if (thread->refs == 0) { DCETHREAD_ERROR("Attempted to retain freed thread %p", thread); } else { thread->refs++; DCETHREAD_TRACE("Thread %p: ref count increased to %i", thread, thread->refs); } }
void dcethread__unlock(dcethread* thread) { unsigned int refs; dcethread__sanity(thread); /* Access reference count while thread is still locked in order to avoid race conditions */ refs = thread->refs; thread->flag.locked = 0; if (pthread_mutex_unlock((pthread_mutex_t*) &thread->lock)) DCETHREAD_ERROR("Thread %p: failed to unlock mutex", thread); DCETHREAD_TRACE("Thread %p: unlocked", thread); if (refs == 0) { dcethread__delete(thread); } }
void dcethread__dispatchinterrupt(dcethread* thread) { DCETHREAD_TRACE("Thread %p: interrupt acknowledged", thread); thread->handle_interrupt(thread, thread->handle_interrupt_data); }