Module Thread_pool
A thread pool is a set of OCaml threads used to do work, where each piece of work is simply a thunk. One creates a thread pool, and then uses add_work to submit work to it. Work is done first-come-first-served by available threads in the pool. Any of the available threads in the pool could be used to do work submitted to the pool (except helper threads, see below).
A thread pool starts with no threads. As work is added, the thread pool creates new threads to do the work, up to the maximum number of allowed threads, max_num_threads, supplied to create. Thread-pool threads never die. They just get created up until max_num_threads is reached and then live forever, doing work. Each thread in the pool is in a loop, waiting for a piece of work, running the thunk, and then repeating. It may be that all the threads in the pool are not doing anything, but in this case, the threads still exist, and are simply blocked waiting for work.
Sometimes one wants work to run in a dedicated thread, e.g. some C libraries require this. To do this, use Helper_thread, see below.
All of the functions exposed by this module are thread safe; they synchronize using a mutex on the thread pool.
One must not call thread-pool functions from a GC finalizer, since a finalizer could run within a thread running a thread-pool function, which already holds the lock, and would therefore deadlock or error when attempting to re-acquire it. This is accomplished elsewhere by using Async finalizers, which are run from ordinary Async jobs, and thus do not hold the thread-pool lock.
One can control the priority and affinity of threads in the pool (priority in the sense of Linux_ext.setpriority). Work added to the pool can optionally be given a priority, and the pool will set the priority of the thread that runs it for the duration of the work. Helper threads can also be given a priority, which will be used for all work run by the helper thread, unless the work has an overriding priority. The thread pool has a "default" priority that will be used for all work and helper threads that have no specified priority. The default priority is the priority in effect when create is called.
Affinity, on the other hand, can only be specified when you create a pool. The default affinity is the affinity in effect when a new thread happens to be created (e.g. when you call add_work).
Behavior is unspecified if work calls setpriority or setaffinity directly.
module Cpu_affinity = Thread_pool_cpu_affinitymodule Priority : module type of Core.Linux_ext.Priority with type Priority.t = Core.Linux_ext.Priority.tval sexp_of_t : t -> Ppx_sexp_conv_lib.Sexp.t
val create : ?cpu_affinity:Cpu_affinity.t -> max_num_threads:int -> unit -> t Core.Or_error.tcreate ?cpuset ~max_num_threadsreturns a new thread pool. It is an error ifmax_num_threads < 1.If
cpusetis specified, then every thread will be affinitized to those CPU cores upon creation.If
cpusetis not specified, then every thread will inherit the affinitization of the thread (typically the main thread) that created it.
val cpu_affinity : t -> Cpu_affinity.tcpu_affinity treturns the CPU affinity thattwas created with. All threads created bytwill be created with this affinity.
val finished_with : t -> unitfinished_with tmakes it an error to subsequently calladd_work* torcreate_helper_thread t. And, once all current work intis finished, destroys all the threads int. It is OK to callfinished_withmultiple times on the samet; subsequent calls will have no effect.
val block_until_finished : t -> unitblock_until_finished tblocks the current thread until thread poolthas finished. One must previously have calledfinished_withto causetto start finishing.
val max_num_threads : t -> intmax_num_threads treturns the maximum number of threads thattis allowed to create.
val num_threads : t -> intnum_threads treturns the number of threads that the poolthas created.
val unfinished_work : t -> intunfinished_work treturns the number of jobs that have been submitted totbut haven't yet finished.
val default_priority : t -> Priority.tdefault_priority treturns the priority that will be used for work performed byt, unless that work is added with an overriding priority.
val add_work : ?priority:Priority.t -> ?name:string -> t -> (unit -> unit) -> unit Core.Or_error.tadd_work ?priority ?name t fenqueuesfto be done by some thread in the pool.Exceptions raised by
fare silently ignored.While the work is run, the name of the thread running the work will be set (via
Linux_ext.pr_set_name) tonameand the priority of the thread will be set topriority.It is an error to call
add_work tafterfinished_with t.
val num_work_completed : t -> intval has_unstarted_work : t -> boolhas_unstarted_work treturnstrueifthas work that it hasn't been assigned to start running in a thread.
module Helper_thread : sig ... endA helper thread is a thread with its own dedicated work queue. Work added for the helper thread is guaranteed to be run by that thread. The helper thread only runs work explicitly supplied to it. Helper threads count towards a thread pool's
max_num_threads.
val create_helper_thread : ?priority:Priority.t -> ?name:string -> t -> Helper_thread.t Core.Or_error.tcreate_helper_thread ?priority ?name ttakes an available thread from the thread pool and makes it a helper thread, raising if no threads are available or iffinished_with twas previously called. The new helper thread runs work withnameandpriority, except for work that is added with an overriding priority or name. The thread remains a helper thread untilfinished_with_helper_threadis called, if ever.
val become_helper_thread : ?priority:Priority.t -> ?name:string -> t -> Helper_thread.t Core.Or_error.tbecome_helper_thread ?priority ?name tshould be run from within work supplied toadd_work. Whenbecome_helper_threadruns, it transitions the current thread into a helper thread.Other than that,
become_helper_threadis likecreate_helper_thread, except it cannot fail because no threads are available.
val add_work_for_helper_thread : ?priority:Priority.t -> ?name:string -> t -> Helper_thread.t -> (unit -> unit) -> unit Core.Or_error.tadd_work_for_helper_thread ?priority ?name t helper_thread fenqueuesfonhelper_thread's work queue.Exceptions raised by
fare silently ignored.It is an error to call
add_work_for_helper_thread tafterfinished_with_helper_thread t.When the helper thread runs
f, it will be at the helper thread's name and priority, unless overriden bynameorpriority.
val finished_with_helper_thread : t -> Helper_thread.t -> unitfinished_with_helper_thread t helper_threadinforms thread pooltthat no future work will be added forhelper_thread, and makes it an error to in the future add work forhelper_thread. Furthermore, oncehelper_threadfinishes with its last piece of work, it will revert to a general thread-pool thread. It is OK to callfinished_with_helper_threadmultiple times on the samehelper_thread; subsequent calls will have no effect.
val last_thread_creation_failure : t -> Core.Sexp.t optionval thread_creation_failure_lockout : t -> Core.Time_ns.Span.tval debug : bool Core.ref