blocking_pipe_header * receive_blocking_req_internal( blocking_child * c ) { blocking_pipe_header * req; int rc; /* * Child blocks here when idle. SysV semaphores maintain a * count and release from sem_wait() only when it reaches 0. */ do { rc = wait_for_sem(c->blocking_req_ready, NULL); } while (-1 == rc && EINTR == errno); INSIST(0 == rc); req = c->workitems[c->next_workeritem]; INSIST(NULL != req); c->workitems[c->next_workeritem] = NULL; c->next_workeritem = (1 + c->next_workeritem) % c->workitems_alloc; if (CHILD_EXIT_REQ == req) { /* idled out */ send_blocking_resp_internal(c, CHILD_GONE_RESP); req = NULL; } return req; }
int queue_blocking_response( blocking_child * c, blocking_pipe_header * resp, size_t respsize, const blocking_pipe_header * req ) { resp->octets = respsize; resp->magic_sig = BLOCKING_RESP_MAGIC; resp->rtype = req->rtype; resp->context = req->context; resp->done_func = req->done_func; return send_blocking_resp_internal(c, resp); }
blocking_pipe_header * receive_blocking_req_internal( blocking_child * c ) { blocking_pipe_header * req; int rc; /* * Child blocks here when idle. SysV semaphores maintain a * count and release from sem_wait() only when it reaches 0. * Windows auto-reset events are simpler, and multiple SetEvent * calls before any thread waits result in a single wakeup. * On Windows, the child drains all workitems each wakeup, while * with SysV semaphores wait_sem() is used before each item. */ #ifdef SYS_WINNT while (NULL == c->workitems[c->next_workeritem]) { /* !!!! SetEvent(c->child_is_blocking); */ rc = wait_for_sem(c->blocking_req_ready, NULL); INSIST(0 == rc); /* !!!! ResetEvent(c->child_is_blocking); */ } #else do { rc = wait_for_sem(c->blocking_req_ready, NULL); } while (-1 == rc && EINTR == errno); INSIST(0 == rc); #endif req = c->workitems[c->next_workeritem]; INSIST(NULL != req); c->workitems[c->next_workeritem] = NULL; c->next_workeritem = (1 + c->next_workeritem) % c->workitems_alloc; if (CHILD_EXIT_REQ == req) { /* idled out */ send_blocking_resp_internal(c, CHILD_GONE_RESP); req = NULL; } return req; }
/* -------------------------------------------------------------------- * Wait for the 'incoming queue no longer empty' signal, lock the shared * structure and dequeue an item. */ blocking_pipe_header * receive_blocking_req_internal( blocking_child * c ) { blocking_pipe_header * req; size_t qhead, qtail; req = NULL; do { /* wait for tickle from the producer side */ wait_for_sem(c->workitems_pending, NULL); /* >>>> ACCESS LOCKING STARTS >>>> */ wait_for_sem(c->accesslock, NULL); qhead = c->head_workitem; do { qtail = c->tail_workitem; if (qhead == qtail) break; c->tail_workitem = qtail + 1; qtail %= c->workitems_alloc; req = c->workitems[qtail]; c->workitems[qtail] = NULL; } while (NULL == req); tickle_sem(c->accesslock); /* <<<< ACCESS LOCKING ENDS <<<< */ } while (NULL == req); INSIST(NULL != req); if (CHILD_EXIT_REQ == req) { /* idled out */ send_blocking_resp_internal(c, CHILD_GONE_RESP); req = NULL; } return req; }