The taskpool selectors measure load with ast_taskprocessor_size(), which
only reports queued tasks. A taskprocessor that is executing a long
running task has an empty queue, so it looks identical to an idle one and
the least full selector hands it more work while other executors sit free.
Add ast_taskprocessor_is_executing() and include the in-flight task in the
load the selectors compare.
This is not a new semantic. taskprocessor_push() already counts the
in-flight task as outstanding work when it notifies the listener:
/* The currently executing task counts as still in queue */
was_empty = tps->executing ? 0 : previous_size == 0;
The taskpool selectors are the one place that still measures only the
queue. This change applies the same rule there.
The accessor reads tps->executing without the object lock, the way the CLI
already does in main/taskprocessor.c, because taking the lock on every
push cost roughly six times the throughput: taskpool push efficiency went
from 324438 to 52966 tasks per second and the serializer variant from
217467 to 38313. Without the lock both are back to baseline. A stale read
only means one push is routed as if the taskprocessor had just changed
state, which the selector already tolerates.
Also add a unit test that holds one executor of a four executor pool
inside a long task and checks that a task pushed to a different
serializer runs on one of the three free executors. It fails on every run
without this change.
Fixes: #2074
UserNote: Taskpool executors that are running a long task are no longer
considered idle when work is distributed, so tasks are routed to free
executors instead of queueing behind a busy one.
pull/2080/head
Gian Diego Javes3 weeks agocommitted bygithub-actions[bot]