/* -------------------------------------------------------------------- * Push a response into the return queue and eventually tickle the * receiver. */ int send_blocking_resp_internal( blocking_child * c, blocking_pipe_header * resp ) { size_t qhead; int empty; /* >>>> ACCESS LOCKING STARTS >>>> */ wait_for_sem(c->accesslock, NULL); empty = ensure_workresp_empty_slot(c); qhead = c->head_response; c->responses[qhead % c->responses_alloc] = resp; c->head_response = 1 + qhead; tickle_sem(c->accesslock); /* <<<< ACCESS LOCKING ENDS <<<< */ /* queue consumer wake-up notification */ if (empty) { # ifdef WORK_PIPE write(c->resp_write_pipe, "", 1); # else tickle_sem(c->responses_pending); # endif } return 0; }
/* -------------------------------------------------------------------- * sleep for a given time or until the wakup semaphore is tickled. */ int worker_sleep( blocking_child * c, time_t seconds ) { struct timespec until; int rc; # ifdef HAVE_CLOCK_GETTIME if (0 != clock_gettime(CLOCK_REALTIME, &until)) { msyslog(LOG_ERR, "worker_sleep: clock_gettime() failed: %m"); return -1; } # else if (0 != getclock(TIMEOFDAY, &until)) { msyslog(LOG_ERR, "worker_sleep: getclock() failed: %m"); return -1; } # endif until.tv_sec += seconds; rc = wait_for_sem(c->wake_scheduled_sleep, &until); if (0 == rc) return -1; if (-1 == rc && ETIMEDOUT == errno) return 0; msyslog(LOG_ERR, "worker_sleep: sem_timedwait: %m"); return -1; }
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; }
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; }
void *thread_trigger(void *arg) { int trigger_no = *(int *) arg; while (!shutdown) { if (wait_for_sem(&trigger_update_sems[trigger_no]) == -1) { continue; } update_trigger(trigger_no); sem_post(&trigger_updated_sems[trigger_no]); notify_actions(); } exit_thread("trigger", trigger_no, triggers[trigger_no].name); }
/* -------------------------------------------------------------------- * Fetch the next response from the return queue. In case of signalling * via pipe, make sure the pipe is flushed, too. */ blocking_pipe_header * receive_blocking_resp_internal( blocking_child * c ) { blocking_pipe_header * removed; size_t qhead, qtail, slot; #ifdef WORK_PIPE int rc; char scratch[32]; do rc = read(c->resp_read_pipe, scratch, sizeof(scratch)); while (-1 == rc && EINTR == errno); #endif /* >>>> ACCESS LOCKING STARTS >>>> */ wait_for_sem(c->accesslock, NULL); qhead = c->head_response; qtail = c->tail_response; for (removed = NULL; !removed && (qhead != qtail); ++qtail) { slot = qtail % c->responses_alloc; removed = c->responses[slot]; c->responses[slot] = NULL; } c->tail_response = qtail; tickle_sem(c->accesslock); /* <<<< ACCESS LOCKING ENDS <<<< */ if (NULL != removed) { DEBUG_ENSURE(CHILD_GONE_RESP == removed || BLOCKING_RESP_MAGIC == removed->magic_sig); } if (CHILD_GONE_RESP == removed) { cleanup_after_child(c); removed = NULL; } return removed; }
/* -------------------------------------------------------------------- * queue_req_pointer() - append a work item or idle exit request to * blocking_workitems[]. Employ proper locking. */ static int queue_req_pointer( blocking_child * c, blocking_pipe_header * hdr ) { size_t qhead; /* >>>> ACCESS LOCKING STARTS >>>> */ wait_for_sem(c->accesslock, NULL); ensure_workitems_empty_slot(c); qhead = c->head_workitem; c->workitems[qhead % c->workitems_alloc] = hdr; c->head_workitem = 1 + qhead; tickle_sem(c->accesslock); /* <<<< ACCESS LOCKING ENDS <<<< */ /* queue consumer wake-up notification */ tickle_sem(c->workitems_pending); return 0; }
int worker_sleep( blocking_child * c, time_t seconds ) { struct timespec until; int rc; if (0 != clock_gettime(CLOCK_REALTIME, &until)) { msyslog(LOG_ERR, "worker_sleep: clock_gettime() failed: %m"); return -1; } until.tv_sec += seconds; do { rc = wait_for_sem(c->wake_scheduled_sleep, &until); } while (-1 == rc && EINTR == errno); if (0 == rc) return -1; if (-1 == rc && ETIMEDOUT == errno) return 0; msyslog(LOG_ERR, "worker_sleep: sem_timedwait: %m"); return -1; }