WiP: direct task sending

This commit is contained in:
Witold Kręcicki
2018-09-05 10:02:24 +02:00
parent b630c22f20
commit 39cc064238
3 changed files with 244 additions and 6 deletions
+2
View File
@@ -247,6 +247,8 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp);
* all resources used by the task will be freed.
*/
void
isc_task_sendorexecute(isc_task_t **taskp, isc_event_t **eventp, bool detach);
unsigned int
isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first,
+199
View File
@@ -521,6 +521,205 @@ isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) {
*taskp = NULL;
}
void
isc_task_sendorexecute(isc_task_t **taskp, isc_event_t **eventp, bool detach) {
bool idle1, idle2 = false;
isc__task_t *task;
isc__taskmgr_t *manager;
/*
* Send '*event' to '*taskp' and then detach '*taskp' from its
* task.
*/
REQUIRE(taskp != NULL);
task = (isc__task_t *)*taskp;
REQUIRE(VALID_TASK(task));
manager = task->manager;
REQUIRE(VALID_MANAGER(manager));
XTRACE("isc_task_sendanddetach");
LOCK(&task->lock);
idle1 = task_send(task, eventp);
if (detach) {
idle2 = task_detach(task);
}
UNLOCK(&task->lock);
/*
* If idle1, then idle2 shouldn't be true as well since we're holding
* the task lock, and thus the task cannot switch from ready back to
* idle.
*/
INSIST(!(idle1 && idle2));
if (idle1 || idle2) {
unsigned int dispatch_count = 0;
bool done = false;
bool requeue = false;
bool finished = false;
isc_event_t *event;
LOCK(&manager->lock);
manager->tasks_ready--;
manager->tasks_running++;
UNLOCK(&manager->lock);
LOCK(&task->lock);
INSIST(task->state == task_state_ready);
task->state = task_state_running;
XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
ISC_MSG_RUNNING, "running"));
TIME_NOW(&task->tnow);
task->now = isc_time_seconds(&task->tnow);
do {
if (!EMPTY(task->events)) {
event = HEAD(task->events);
DEQUEUE(task->events, event, ev_link);
task->nevents--;
/*
* Execute the event action.
*/
XTRACE(isc_msgcat_get(isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_EXECUTE,
"execute action"));
if (event->ev_action != NULL) {
UNLOCK(&task->lock);
(event->ev_action)(
(isc_task_t *)task,
event);
LOCK(&task->lock);
}
dispatch_count++;
}
if (task->references == 0 &&
EMPTY(task->events) &&
!TASK_SHUTTINGDOWN(task)) {
bool was_idle;
/*
* There are no references and no
* pending events for this task,
* which means it will not become
* runnable again via an external
* action (such as sending an event
* or detaching).
*
* We initiate shutdown to prevent
* it from becoming a zombie.
*
* We do this here instead of in
* the "if EMPTY(task->events)" block
* below because:
*
* If we post no shutdown events,
* we want the task to finish.
*
* If we did post shutdown events,
* will still want the task's
* quantum to be applied.
*/
was_idle = task_shutdown(task);
INSIST(!was_idle);
}
if (EMPTY(task->events)) {
/*
* Nothing else to do for this task
* right now.
*/
XTRACE(isc_msgcat_get(isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_EMPTY,
"empty"));
if (task->references == 0 &&
TASK_SHUTTINGDOWN(task)) {
/*
* The task is done.
*/
XTRACE(isc_msgcat_get(
isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_DONE,
"done"));
finished = true;
task->state = task_state_done;
} else
task->state = task_state_idle;
done = true;
} else if (dispatch_count >= task->quantum) {
/*
* Our quantum has expired, but
* there is more work to be done.
* We'll requeue it to the ready
* queue later.
*
* We don't check quantum until
* dispatching at least one event,
* so the minimum quantum is one.
*/
XTRACE(isc_msgcat_get(isc_msgcat,
ISC_MSGSET_TASK,
ISC_MSG_QUANTUM,
"quantum"));
task->state = task_state_ready;
requeue = true;
done = true;
}
} while (!done);
UNLOCK(&task->lock);
if (finished)
task_finished(task);
LOCK(&manager->lock);
manager->tasks_running--;
if (manager->exclusive_requested &&
manager->tasks_running == 1) {
SIGNAL(&manager->exclusive_granted);
} else if (manager->pause_requested &&
manager->tasks_running == 0) {
SIGNAL(&manager->paused);
}
if (requeue) {
/*
* We know we're awake, so we don't have
* to wakeup any sleeping threads if the
* ready queue is empty before we requeue.
*
* A possible optimization if the queue is
* empty is to 'goto' the 'if (task != NULL)'
* block, avoiding the ENQUEUE of the task
* and the subsequent immediate DEQUEUE
* (since it is the only executable task).
* We don't do this because then we'd be
* skipping the exit_requested check. The
* cost of ENQUEUE is low anyway, especially
* when you consider that we'd have to do
* an extra EMPTY check to see if we could
* do the optimization. If the ready queue
* were usually nonempty, the 'optimization'
* might even hurt rather than help.
*/
push_readyq(manager, task);
}
/*
* If we are in privileged execution mode and there are no
* tasks remaining on the current ready queue, then
* we're stuck. Automatically drop privileges at that
* point and continue with the regular ready queue.
*/
if (manager->tasks_running == 0 && empty_readyq(manager)) {
manager->mode = isc_taskmgrmode_normal;
if (!empty_readyq(manager))
BROADCAST(&manager->work_available);
}
UNLOCK(&manager->lock);
}
if (detach) {
*taskp = NULL;
}
}
#define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0)
static unsigned int
+43 -6
View File
@@ -2835,7 +2835,6 @@ isc_socket_close(isc_socket_t *sock0) {
LOCK(&sock->lock);
REQUIRE(sock->references == 1);
REQUIRE(sock->fd >= 0 && sock->fd < (int)sock->manager->maxsocks);
INSIST(!sock->connecting);
@@ -3217,7 +3216,10 @@ internal_recv(isc__socket_t *sock) {
* limits here, currently.
*/
while (dev != NULL) {
switch (doio_recv(sock, dev)) {
UNLOCK(&sock->lock);
int r = doio_recv(sock, dev);
LOCK(&sock->lock);
switch (r) {
case DOIO_SOFT:
goto poke;
@@ -3235,8 +3237,27 @@ internal_recv(isc__socket_t *sock) {
goto poke;
case DOIO_SUCCESS:
case DOIO_HARD:
send_recvdone_event(sock, &dev);
case DOIO_HARD: {
if (ISC_LINK_LINKED(dev, ev_link))
ISC_LIST_DEQUEUE(sock->recv_list, dev, ev_link);
bool empty = false; // ISC_LIST_EMPTY(sock->recv_list);
UNLOCK(&sock->lock);
isc_task_t *task;
task = dev->ev_sender;
dev->ev_sender = sock;
bool detach = dev->attributes & ISC_SOCKEVENTATTR_ATTACHED;
if (empty) {
isc_task_sendorexecute(&task, (isc_event_t**)&dev, detach);
} else {
if (detach) {
isc_task_sendanddetach(&task, (isc_event_t**)&dev);
}
else {
isc_task_send(task, (isc_event_t**)&dev);
}
}
}
LOCK(&sock->lock);
break;
}
@@ -3249,6 +3270,7 @@ internal_recv(isc__socket_t *sock) {
SELECT_POKE_READ);
UNLOCK(&sock->lock);
return;
}
static void
@@ -3278,7 +3300,11 @@ internal_send(isc__socket_t *sock) {
case DOIO_HARD:
case DOIO_SUCCESS:
send_senddone_event(sock, &dev);
if (ISC_LIST_EMPTY(sock->send_list)) {
goto launch_directly;
} else {
send_senddone_event(sock, &dev);
}
break;
}
@@ -3290,6 +3316,17 @@ internal_send(isc__socket_t *sock) {
unwatch_fd(sock->manager, sock->threadid, sock->fd, SELECT_POKE_WRITE);
UNLOCK(&sock->lock);
return;
launch_directly:
if (ISC_LINK_LINKED(dev, ev_link))
ISC_LIST_DEQUEUE(sock->send_list, dev, ev_link);
unwatch_fd(sock->manager, sock->threadid, sock->fd, SELECT_POKE_WRITE);
UNLOCK(&sock->lock);
isc_task_t *task;
task = dev->ev_sender;
dev->ev_sender = sock;
bool detach = dev->attributes & ISC_SOCKEVENTATTR_ATTACHED;
isc_task_sendorexecute(&task, (isc_event_t**)&dev, detach);
}
/*
@@ -3318,6 +3355,7 @@ process_fd(isc__socketmgr_t *manager, int threadid, int fd, bool readable,
}
sock = manager->fds[fd];
UNLOCK(&manager->fdlock[lockid]);
LOCK(&sock->lock);
sock->references++;
UNLOCK(&sock->lock);
@@ -3353,7 +3391,6 @@ check_write:
}
unlock_fd:
UNLOCK(&manager->fdlock[lockid]);
if (unwatch_read)
(void)unwatch_fd(manager, threadid, fd, SELECT_POKE_READ);
if (unwatch_write)