TSCONTSCHEDULEONPOOL(3ts)

NAME
TSContScheduleOnPool - TSContScheduleOnPool API function

SYNOPSIS

            #include <ts/ts.h>

    TSAction TSContScheduleOnPool(TSCont contp, TSHRTime timeout, TSThreadPool tp)

DESCRIPTION

Mostly the same as TSContSchedule(). Schedules contp on a random thread that belongs to tp. If thread type of the thread specified by thread affinity is the same as tp, the contp will be scheduled on the thread specified by thread affinity.

The continuation is scheduled for a particular thread selected from a group of similar threads, as indicated by tp.
Pool | Properties
TS_THREAD_POOL_NET | Transaction processing threads. Continuations on these threads must not block.
TS_THREAD_POOL_TASK | Background threads. Continuations can perform blocking operations.
TS_THREAD_POOL_DNS | DNS request processing. May not exist depending on configuration. Not recommended.
TS_THREAD_POOL_UDP | UDP processing.

In practice, any choice except TS_THREAD_POOL_NET or TS_THREAD_POOL_TASK is strongly not recommended. The TS_THREAD_POOL_NET threads are the same threads on which callback hooks are called and continuations that use them have the same restrictions. TS_THREAD_POOL_TASK threads are threads that exist to perform long or blocking actions, although sufficiently long operation can impact system performance by blocking other continuations on the threads.

Note that the TSContSchedule() family of API shall only be called from an ATS EThread. Calling it from raw non-EThreads can result in unpredictable behavior.

Note that some times if the TASK threads are not ready when you schedule a "contp" on the TS_THREAD_POOL_TASK then the "contp" could end up being executed in a ET_NET thread instead, more likely this is not what you want. To avoid this you can use the TS_LIFECYCLE_TASK_THREADS_READY_HOOK to make sure that the task threads have been started when you schedule a "contp". You can refer to TSLifecycleHookAdd() for more details.

EXAMPLE SCENARIOS

  Scenario 1 (no thread affinity info, different types of threads)

When thread affinity is not set, a plugin calls the API on thread "A" (which is an "ET_TASK" type), and wants to schedule on an "ET_NET" type thread provided in "tp", the system would see there is no thread affinity information stored in "contp."

In this situation, system sees there is no thread affinity information stored in "contp". It then checks whether the type of thread "A" is the same as provided in "tp", and sees that "A" is "ET_TASK", but "tp" says "ET_NET". So "contp" gets scheduled on the next available "ET_NET" thread provided by a round robin list, which we will call thread "B". Since "contp" doesn't have thread affinity information, thread "B" will be assigned as the affinity thread for it automatically.

The reason for doing this is most of the time people want to schedule the same things on the same type of thread, so logically it is better to default the first thread that it is scheduled on as the affinity thread.

  Scenario 2 (no thread affinity info, same types of threads)

Slight variation of scenario 1, instead of scheduling on a "ET_NET" thread, the plugin wants to schedule on a "ET_TASK" thread (i.e. "tp" contains "ET_TASK" now), all other conditions stays the same.

This time since the type of the desired thread for scheduling and thread "A" are the same, the system schedules "contp" on thread "A", and assigns thread "A" as the affinity thread for "contp".

The reason behind this choice is that we are trying to keep things simple such that lock contention problems happens less. And for the most part, there is no point of scheduling the same thing on several different threads of the same type, because there is no parallelism between them (a thread will have to wait for the previous thread to finish, either because locking or the nature of the job it's handling is serialized since its on the same continuation).

  Scenario 3 (has thread affinity info, different types of threads)

Slight variation of scenario 1, thread affinity is set for continuation "contp" to thread "A", all other conditions stays the same.

In this situation, the system sees that the "tp" has "ET_NET", but the type of thread "A" is "ET_TASK". So even though "contp" has an affinity thread, the system will not use that information since the type is different, instead it schedules "contp" on the next available "ET_NET" thread provided by a round robin list, which we will call thread "B". The difference with scenario 1 is that since thread "A" is set to be the affinity thread for "contp" already, the system will NOT overwrite that information with thread "B".

Most of the time, a continuation will be scheduled on one type of threads, and rarely gets scheduled on a different type. But when that happens, we want it to return to the thread it was previously on, so it won't have any lock contention problems. And that's also why "thread_affinity" is not a hashmap of thread types and thread pointers.

  Scenario 4 (has thread affinity info, same types of threads)

Slight variation of scenario 3, the only difference is "tp" now says "ET_TASK".

This is the easiest scenario since the type of thread "A" and "tp" are the same, so the system schedules "contp" on thread "A". And, as discussed, there is really no reason why one may want to schedule the same continuation on two different threads of the same type.

NOTE:
        In scenario 3 & 4, it doesn't matter which thread the plugin is calling the API from.

SEE ALSO

TSContSchedule TSContScheduleEvery TSContScheduleOnThread TSLifecycleHookAdd

COPYRIGHT
2026, dev@trafficserver.apache.org