tpool(n)

πŸ“¦
version 8.6.17 Β· 2 versions
, in alpine 3.22, alpine 3.23, alpine 3.24, arch rolling, epel 9, fedora 43, fedora 44, opensuse-leap 16.0

πŸŽ€ NAME πŸŽ€

tpool β€” it's part of the Tcl threading extension, and it's the whole vibe for pools of worker threads. 🧚✨ (yes bestie, this is tpool(n), welcome to the function)


🌈 SYNOPSIS 🌈

package require Tcl  8.4

package require Thread  ?2.8?

tpool::create ?options?

tpool::names

tpool::post ?-detached? ?-nowait? tpool script

tpool::wait tpool joblist ?varname?

tpool::cancel tpool joblist ?varname?

tpool::get tpool job

tpool::preserve tpool

tpool::release tpool

tpool::suspend tpool

tpool::resume tpool

πŸ’– DESCRIPTION πŸ’–

Okay so, this package creates and manages pools of worker threads. It lets you post jobs to worker threads and wait for them to be done. Slay. 🫢

And here's the cute part β€” the threadpool implementation is Tcl event-loop aware. What that means in practice: any time a caller has to wait for an event (a job finishing, or a worker thread going idle, or a worker thread getting initialized), the implementation will enter the event loop and let other pending file events, timer events, or any other supported events get serviced while it's waiting. No blocked vibes. πŸ’…


πŸ‘‘ COMMANDS πŸ‘‘

tpool::create ?options?

This command creates a new threadpool. It takes several options as key-value pairs, and those options tune some threadpool parameters. It returns the ID of the newly created threadpool. ✨

Here are the options, bestie β€” all of them, no skips:

  • -minworkers number β€” the minimum number of worker threads this threadpool instance needs. When the threadpool gets created, the implementation will create that many worker threads upfront, and it keeps at least that many alive for the whole lifetime of the threadpool instance. Default value is 0 (zero), which means a freshly made threadpool starts with no worker threads initially β€” every worker thread gets started on demand by callers running the tpool::post command and posting jobs into the job queue. 🐣
  • -maxworkers number β€” the maximum number of worker threads allowed for this threadpool instance. If a new job is pending and there are no idle worker threads available, the implementation tries to create a new worker thread. If the number of available worker threads is lower than the given number, a new worker thread will start, and the caller automatically enters the event loop and waits until that worker thread has initialized. If, however, the number of available worker threads is equal to the given number, the caller enters the event loop and waits for the first worker thread to become idle β€” i.e. ready to run the job. Default value is 4 (four), meaning the threadpool instance allows a max of 4 worker threads running jobs or sitting idle waiting for new jobs to be posted to the job queue. πŸ“
  • -idletime seconds β€” the time in seconds an idle worker thread waits for a job to get posted to the job queue. If no job shows up during that interval and the time expires, the worker thread checks how many worker threads are currently available, and if that number is higher than the number set by the minthreads option, it exits. And if an exitscript has been defined, the exiting worker thread first runs that script and then exits. Errors from the exit script, if any, are ignored.

Heads up: the idle worker thread is not servicing the event loop. If you put the worker thread into the event loop by evaluating vwait or other related Tcl commands, the worker thread will not be in the idle state, so the idle timer will not be taken into account. Default value for this option is unspecified. ⏳

  • -initcmd script β€” sets a Tcl script used to initialize a new worker thread. This is usually for loading packages and commands in the worker, setting default variables, creating namespaces, and such. If the script you pass runs into a Tcl error, the worker will not be created, and the initiating command (either tpool::create or tpool::post) will throw an error. Default value for this option is unspecified, hence the Tcl interpreter of the worker thread will contain just the initial set of Tcl commands. 🧡
  • -exitcmd script β€” sets a Tcl script run when an idle worker thread exits. Normally this is for cleaning up the state of the worker thread, releasing reserved resources, cleaning up memory and such. Default value for this option is unspecified, so no Tcl script runs on worker thread exit. πŸšͺ

tpool::names

Returns a list of IDs of threadpools created with the tpool::create command. If no threadpools were found, the command returns an empty list. Short and sweet, she's an icon. ✨


tpool::post ?-detached? ?-nowait? tpool script

This command sends a script to the target tpool threadpool for execution. The script runs in the first available idle worker thread. If there are no idle worker threads available, the command creates a new one, enters the event loop, and services events until the newly created thread is initialized. If the current number of worker threads is equal to the maximum number of worker threads (as defined when the threadpool was created), the command enters the event loop and services events while waiting for one of the worker threads to become idle.

If the optional ?-nowait? argument is given, the command will not wait for an idle worker. It just drops the job into the pool's job queue and returns immediately. πŸƒβ™€οΈπŸ’¨

The command returns the ID of the posted job. That ID is what you use with tpool::wait, tpool::get and tpool::cancel to wait for and retrieve the result of the posted script, or to cancel the posted job. If the optional ?-detached? argument is specified, the command posts a detached job β€” a detached job can't be cancelled or waited upon, and it is not identified by the job ID. πŸ™ˆ

If the threadpool tpool is not found in the list of active thread pools, the command will throw an error. The error will also be triggered if the newly created worker thread fails to initialize. πŸ’€


tpool::wait tpool joblist ?varname?

This command waits for one or many jobs β€” the ones whose job IDs are given in the joblist β€” to get processed by the worker thread(s). If none of the specified jobs are ready, the command enters the event loop, services events, and waits for the first job to get ready. 🫢

It returns the list of completed job IDs. If the optional variable ?varname? is given, it gets set to the list of jobs in joblist that are still pending. If the threadpool tpool is not found in the list of active thread pools, the command will throw an error. 🚨


tpool::cancel tpool joblist ?varname?

This command cancels the previously posted jobs given by joblist on the pool tpool. Cancellation succeeds only for jobs still waiting to be processed. If a job is already being executed by one of the worker threads, it will not be cancelled. 😭

The command returns the list of cancelled job IDs. If the optional variable ?varname? is given, it gets set to the list of jobs in joblist which were not cancelled. If the threadpool tpool is not found in the list of active thread pools, the command will throw an error. πŸ’…


tpool::get tpool job

This command retrieves the result of a previously posted job. Only results of jobs waited upon with the tpool::wait command can be retrieved. πŸ‘‘

If the execution of the script resulted in an error, the command will throw the error and update the errorInfo and errorCode variables correspondingly. If the pool tpool is not found in the list of threadpools, the command will throw an error. And if the job job is not ready for retrieval because it's currently being executed by the worker thread, the command will throw an error too. No sneaky peeking at unfinished work, bestie. πŸ™…β™€οΈ


tpool::preserve tpool

Each call to this command increments the reference counter of the threadpool tpool by one (1). The command returns the value of the reference counter after the increment. By incrementing the reference counter, the caller signalizes that they wish to use the resource for a longer period of time. ✨


tpool::release tpool

Each call to this command decrements the reference counter of the threadpool tpool by one (1). The command returns the value of the reference counter after the decrement.

Warning bestie, this one's serious: when the reference counter reaches zero (0), the threadpool tpool is marked for termination. You should not reference the threadpool after the tpool::release command returns zero. The tpool handle goes out of scope and should not be used any more. Any following reference to the same threadpool handle will result in a Tcl error. 🚫

tpool::suspend tpool

Suspends processing work on this queue. All pool workers are paused, but additional work can still be added to the pool. Note that adding additional work will not increase the number of workers dynamically, since pool processing is suspended β€” the number of workers is maintained at the count found prior to suspending worker activity. If you need to assure a certain number of worker threads, use the minworkers option of the tpool::create command. 😴


tpool::resume tpool

Resumes processing work on this queue. All paused (suspended) workers are free to get work from the pool. Note that resuming pool operation will just let already created workers proceed β€” it will not create additional worker threads to handle the work posted to the pool's work queue. β˜€οΈ


πŸ’­ DISCUSSION (aka why this whole thing slaps) πŸ’­

Okay so β€” threadpool is literally one of the most common threading paradigms out there when it comes to server applications that are handling a large number of relatively small tasks. It's giving main character energy in the server world, fr fr.

Picture the very simplistic model for building a server application: you just create a new thread every single time a request arrives, and service the request inside that shiny new thread. Cute in theory! πŸ’… But bestie… one of the disadvantages of this approach is that the overhead of creating a new thread for every request is significant. A server that created a new thread for each request would spend more time β€” and burn more system resources β€” creating and destroying threads than it would spend processing actual user requests. 😭 Like, imagine doing a full skincare routine for every single customer who walks in. Exhausting.

And it's not just the creating-and-destroying overhead either. Active threads consume system resources, periodt. Creating too many threads can cause the system to run out of memory, or to thrash due to excessive memory consumption. πŸ’€ Not the vibe.


A thread pool offers a solution to both the problem of thread life-cycle overhead and the problem of resource thrashing. ✨ By reusing threads for multiple tasks, the thread-creation overhead gets spread over many tasks instead of being paid fresh every time. And as a lil bonus πŸŽ€ β€” because the thread already exists when a request arrives, the delay introduced by thread creation is eliminated. So the request can be serviced immediately. Iconic behaviour, honestly.

Furthermore, by properly tuning the number of threads in the thread pool, resource thrashing may also be eliminated by forcing any request to wait until a thread is available to process it. Yes bestie, the queue is a feature, not a bug. πŸ¦‹


πŸ” SEE ALSO πŸ”

Go read these next, they're part of the extended universe:

  • thread
  • tsv
  • ttrace

🏷️ KEYWORDS 🏷️

  • thread
  • threadpool

And that's the whole page, bestie!! 🫢 This was the Tcl Threading package, version 2.8, and you were reading the tpool(n) manual page. Go forth and pool those threads, you absolute queen. πŸ‘‘πŸ’–