int API_FUNC qthread_disable_worker(const qthread_worker_id_t w) { /*{{{*/ assert(qthread_library_initialized); unsigned int shep = w % qlib->nshepherds; unsigned int worker = w / qlib->nshepherds; qassert_ret((shep < qlib->nshepherds), QTHREAD_BADARGS); qassert_ret((worker < qlib->nworkerspershep), QTHREAD_BADARGS); if ((worker == 0) & (shep == 0)) { /* currently, the "real mccoy" original thread cannot be migrated * (because I don't know what issues that could cause on all * architectures). For similar reasons, therefore, the original * shepherd cannot be disabled. One of the nice aspects of this is that * therefore it is impossible to disable ALL shepherds. * * ... it's entirely possible that I'm being overly cautious. This is a * policy based on gut feeling rather than specific issues. */ return QTHREAD_NOT_ALLOWED; } qthread_debug(SHEPHERD_CALLS, "began on worker(%i-%i)\n", shep, worker); (void)QT_CAS(qlib->shepherds[shep].workers[worker].active, 1, 0); qlib->nworkers_active--; // decrement active count if (worker == 0) { qthread_disable_shepherd(shep); } return QTHREAD_SUCCESS; } /*}}}*/
qlfqueue_t *qlfqueue_create(void) { /*{{{ */ qlfqueue_t *q; if (qlfqueue_node_pool == NULL) { switch ((uintptr_t)qthread_cas_ptr(&qlfqueue_node_pool, NULL, (void *)1)) { case 0: /* I won, I will allocate */ qlfqueue_node_pool = qpool_create_aligned(sizeof(qlfqueue_node_t), 0); break; case 1: while (qlfqueue_node_pool == (void *)1) { SPINLOCK_BODY(); } break; } } qassert_ret((qlfqueue_node_pool != NULL), NULL); q = MALLOC(sizeof(struct qlfqueue_s)); if (q != NULL) { q->head = (qlfqueue_node_t *)qpool_alloc(qlfqueue_node_pool); assert(q->head != NULL); if (q->head == NULL) { /* if we're not using asserts, fail nicely */ FREE(q, sizeof(struct qlfqueue_s)); return NULL; } q->tail = q->head; q->tail->next = NULL; } return q; } /*}}} */
int qlfqueue_empty(qlfqueue_t *q) { /*{{{ */ qlfqueue_node_t *head; qlfqueue_node_t *tail; qlfqueue_node_t *next; qassert_ret((q != NULL), QTHREAD_BADARGS); while (1) { head = q->head; tail = q->tail; next = head->next; MACHINE_FENCE; if (head == q->head) { /* are head, tail, and next consistent? */ if (head == tail) { /* is queue empty or tail falling behind? */ if (next == NULL) { /* queue is empty! */ return 1; } else { /* tail falling behind (queue NOT empty) */ return 0; } } else { /* queue is NOT empty and tail is NOT falling behind */ return 0; } } } } /*}}} */
void *qlfqueue_dequeue(qlfqueue_t *q) { /*{{{ */ void *p = NULL; qlfqueue_node_t *head; qlfqueue_node_t *tail; qlfqueue_node_t *next_ptr; qassert_ret((q != NULL), NULL); while (1) { head = q->head; hazardous_ptr(0, head); if (head != q->head) { continue; } tail = q->tail; next_ptr = head->next; hazardous_ptr(1, next_ptr); if (next_ptr == NULL) { return NULL; } /* queue is empty */ if (head == tail) { /* tail is falling behind! */ /* advance tail ptr... */ (void)qthread_cas_ptr((void **)&(q->tail), (void *)tail, next_ptr); continue; } /* read value before CAS, otherwise another dequeue might free the next node */ p = next_ptr->value; if (qthread_cas_ptr((void **)&(q->head), (void *)head, next_ptr) == head) { break; /* success! */ } } hazardous_release_node(qlfqueue_pool_free_wrapper, head); return p; } /*}}} */
int qlfqueue_destroy(qlfqueue_t *q) { /*{{{ */ qassert_ret((q != NULL), QTHREAD_BADARGS); while (q->head != q->tail) { qlfqueue_dequeue(q); COMPILER_FENCE; } COMPILER_FENCE; qpool_free(qlfqueue_node_pool, (void *)(q->head)); FREE(q, sizeof(struct qlfqueue_s)); return QTHREAD_SUCCESS; } /*}}} */
int qlfqueue_enqueue(qlfqueue_t *q, void *elem) { /*{{{ */ qlfqueue_node_t *tail; qlfqueue_node_t *next; qlfqueue_node_t *node; qassert_ret((elem != NULL), QTHREAD_BADARGS); qassert_ret((q != NULL), QTHREAD_BADARGS); node = (qlfqueue_node_t *)qpool_alloc(qlfqueue_node_pool); /* these asserts should be redundant */ qassert_ret((node != NULL), QTHREAD_MALLOC_ERROR); memset((void *)node, 0, sizeof(qlfqueue_node_t)); node->value = elem; while (1) { tail = q->tail; hazardous_ptr(0, tail); if (tail != q->tail) { continue; } next = tail->next; if (next != NULL) { /* tail not pointing to last node */ (void)qthread_cas_ptr((void **)&(q->tail), (void *)tail, next); continue; } /* tail must be pointing to the last node */ if (qthread_cas_ptr((void **)&(tail->next), (void *)next, node) == next) { break; /* success! */ } } (void)qthread_cas_ptr((void **)&(q->tail), (void *)tail, node); hazardous_ptr(0, NULL); // release the ptr (avoid hazardptr resource exhaustion) return QTHREAD_SUCCESS; } /*}}} */
qt_threadqueue_t INTERNAL *qt_threadqueue_new(void) { /*{{{ */ qt_threadqueue_t *q = ALLOC_THREADQUEUE(); qassert_ret(q != NULL, NULL); q->q.shadow_head = q->q.head = q->q.tail = NULL; q->advisory_queuelen = 0; q->q.nemesis_advisory_queuelen = 0; // redundant #ifdef QTHREAD_CONDWAIT_BLOCKING_QUEUE q->frustration = 0; QTHREAD_COND_INIT(q->trigger); #endif /* ifdef QTHREAD_CONDWAIT_BLOCKING_QUEUE */ return q; } /*}}} */