source: git/Singular/dyn_modules/systhreads/doc/threadpools.md @ f0f0003

spielwiese
Last change on this file since f0f0003 was f0f0003, checked in by Reimer Behrends <behrends@…>, 5 years ago
Libthread implementation.
  • Property mode set to 100644
File size: 9.7 KB
Line 
1# Userspace Threading
2
3Note: priorities are not yet fully supported.
4
5# Threadpools
6
7The function `createThreadPool()` is used to create a new threadpool:
8
9    threadpool pool = createThreadPool(int nthreads);
10
11The `nthreads` arguments defines how many worker threads the threadpool
12should have (and thus the maximum level of parallelism that the threadpool
13allows for). The value zero for `nthreads` is special; it will not actually
14create any separate threads, but create a pseudo threadpool that is
15subordinate to the current thread. It is useful for testing and debugging
16and can also be used to emulate user-space threads in single-threaded
17Singular.
18
19A threadpool can be shut down with the `closeThreadPool()` function:
20
21    int remaining = closeThreadPool(threadpool pool[, int wait]);
22
23The `pool` argument is the threadpool that is to be shutdown. The pool
24will be shutdown immediately after all threads finish executing their
25current jobs. If the optional argument `wait` is non-zero,
26`closeThreadPool()` will wait with shutting down the pool until there
27are no remaining jobs that can be executed anymore, either because there
28are none left or because all remaining jobs are not ready.
29
30It returns an integer that describes the remaining number of jobs
31remaining in the threadpool.
32
33The current threadpool can be set and queried via the
34`setCurrentThreadPool()` and `currentThreadPool()` functions:
35
36    setCurrentThreadPool(threadpool pool);
37    threadpool pool = currentThreadPool();
38
39Setting the current threadpool allows the threadpool argument for
40the `startJob()` and `scheduleJob()` functions to be omitted (see
41below). While executing a job, the current threadpool is automatically
42set without requiring external action.
43
44# Threadpool Initialization
45
46Threadpools can be initialized with any of the following functions that
47requests them to (respectively) load a library, execute a quoted
48expression, execute the contents of a file, execute the contents of a
49string, or call a function:
50
51    threadPoolsLoadLib(threadpool pool, string lib);
52    threadPoolExec(threadpool pool, def quote_expr);
53    threadPoolExecFile(threadpool pool, string file);
54    threadPoolExecString(threadpool pool, string str);
55    threadPoolExecFunc(threadpool pool, string funcname);
56
57For a threadpool, this initialization request is passed on to all
58threads in the pool. Initialization happens after any currently running
59jobs have been processed. At program startup, this occurs immediately.
60
61# Creating Jobs
62
63A job is a descriptor for a piece of work that a thread is meant to
64perform. The basic job is
65
66    job j = createJob(string func[, def arg1, ..., def argn]);
67
68Here, `func` is the name of the function to be executed. This name
69can include a package name and `"Current"` for the current package.
70Examples are `"foo"`, `"Somepackage::foo"`, or `"Current::foo"`.
71
72The arguments `arg1` through `argn` are any arguments to be passed
73along. The argument list *can* be partial; one does not have to supply
74any arguments at job creation, but the missing arguments have to be
75supplied at startup (see below).
76
77Examples:
78
79    proc add(int x, int y) { return (x+y); }
80
81    job adder = createJob("add");
82    job increment = createJob("add", 1);
83    job add2and2 = createJob("add", 2, 2);
84
85In this example, three different jobs are created. The first one,
86`adder`, will still require two arguments in order to be run. The
87second one, `increment`, requires one additional argument. And the
88`add2and2` job does not require any more arguments.
89
90Jobs can also be created from other jobs by pasing in a job argument
91instead of a function name as the first argument:
92
93    job j2 = createJob(job j[, def arg1, ..., def argn]);
94
95Any arguments are appended to the jobs existing argument list. Note that
96the original job will not be modified: the call to `createJob` will
97first create a copy, then add the arguments to the copy.
98
99Finally, one can also supply a quoted expression to `createJob()`:
100
101    createJob(def quote_expr);
102
103Example:
104
105    job j = createJob(quote(factorial(1000)));
106
107Within the kernel, additionally C/C++ jobs can be created by supplying
108a function pointer (and optional arguments). These jobs can be returned
109
110For debugging purposes, a job can be given a descriptive name with the
111`nameJob()` function:
112
113    nameJob(job j, string name);
114
115This has no effect on execution, but can be useful for debugging. The
116name can be queried with the `getJobName()` function:
117
118    string name = getJobName(job j);
119
120If no name is explicitly assigned to a job, the name defaults to the
121function name or (for quoted expression) to the fixed string
122`"quote(...)"`.
123
124# Running jobs
125
126The simplest way to execute a job is with the `startJob()` function:
127
128    job j2 = startJob([threadpool pool, [int prio,]]
129        job|string j[, def arg1, ..., def argn]);
130
131The first argument is the optional threadpool on which to execute the
132job; if absent, it defaults to the current threadpool. It is followed
133(optionally) by a priority argument, which defaults to zero. Higher
134numbers indicate a higher priority. High priority jobs will preempt
135low priority jobs; note that this can lead to low priority jobs being
136starved if there is a constant influx of high priority jobs.
137
138The next argument is the actual job. For simple jobs, a string
139describing a function name can be supplied instead, which follows
140the same conventions as the first argument for `createJob()`. It
141allows bypassing the `createJob()` step for simple jobs.
142
143Finally, the remaining arguments are taken to be the remaining
144arguments for the job. They are appended to any arguments provided
145during `createJob()`.
146
147For convenience, `startJob()` will also return the job it was passed
148in (or the job it created if a function name was supplied).
149
150A job scheduled by `startJob()` will be executed as soon as a worker
151thread becomes available to run it.
152
153Example:
154
155    proc add(int x, int y) { return (x+y); }
156
157    threadpool pool = createThreadPool(4);
158    job inc = createJob("add", 1);
159    startJob(pool, inc, 1);
160
161The result of a job can be queried with `waitJob()`:
162
163    def result = waitJob(job j);
164
165Note that a job need not return a result; if the underlying function
166does not return a value, the job will not have a result. One can still
167call `waitJob()` on the job, but it likewise will not return a value.
168
169Example:
170
171    proc add(int x, int y) { return (x+y); }
172
173    threadpool pool = createThreadPool(4);
174    job inc = createJob("add", 1);
175    startJob(pool, inc, 1);
176    int result = waitJob(inc);
177
178A job's execution can be cancelled with `cancelJob()`:
179
180    cancelJob(job j);
181
182If the job has not been executed by a worker thread yet, it is removed
183from the scheduler. If it has been executed already, the function will
184not do anything. If it is being executed, nothing will be done, either.
185However, a flag will be set for the job and the job can use the function
186`jobCancelled()` to find out if cancellation has been requested and
187abort early.
188
189Example:
190
191    if (jobCancelled()) { return (); }
192
193More interestingly, the execution of jobs can be made contingent on
194the completion of other jobs or on triggers.
195
196    scheduleJobs([threadpool pool, [int prio,]]
197      list|job|string jobs[,
198      list|job|trigger dep1, ..., list|job|trigger depn]);
199
200The first two arguments (`pool` and `prio`) are the same as for
201`startJob()` and are again both optional. The third argument is either a
202job, a function name (with the same semantics as for `startJob()`) or a
203list of jobs.
204
205The remaining arguments list dependencies. The jobs will not be executed
206until all dependencies are met, i.e. the underlying tasks are completed
207or the triggers have been fully activated.
208
209If any of the jobs or triggers `dep1` through `depn` return results,
210then those will be appended as arguments to the scheduled jobs.
211
212Example:
213
214    proc add(int x, int y) { return (x+y); }
215
216    threadpool pool = createThreadPool(4);
217    job j1 = startJob("add", 1, 2);
218    job j2 = startJob("add", 3, 4);
219    job total = createJob("add");
220    scheduleJob(total, j1, j2);
221    int sum = waitJob(total);
222
223# Triggers
224
225Triggers allow the programmer to create more complex interactions
226between jobs.
227
228    trigger trig = createTrigger([threadpool pool,]
229      string type[, def arg1, ..., argn]);
230
231We currently support three types of triggers, accumulators, counters, and
232sets.
233
234    trigger trig = createTrigger("acc", int n);
235    trigger trig = createTrigger("count", int n);
236    trigger trig = createTrigger("set", int n);
237
238An accumulator trigger will accumulate a set of results. It will fire
239when `n` results have been added; any accumulated results will be passed
240as an additional list argument to triggered jobs.
241
242A counter trigger will simply count up and fire when it has been activated `n`
243times. A set trigger represents the set `{1..n}` and will fire when all
244elements in the set have been added at least once. Neither of the last
245two will pass any additional arguments to triggered jobs.
246
247Adding to a trigger can be done with `updateTrigger()`.
248
249    updateTrigger(trigger trig[, def arg]);
250
251Triggers can also be activated when a job finishes or another trigger
252fires via the `chainTrigger()` function:
253
254    chainTrigger(trigger trig, job|trigger dep);
255
256The state of a trigger can be tested explicitly with `testTrigger()`,
257which returns 1 or 0, depending on whether it has fired or not.
258
259    int success = testTrigger(trigger trig);
260
261Because it is often useful to discard any remaining jobs when a trigger
262fires, the `cancelOnTrigger()` function allows to automate this.
263
264    cancelOnTrigger(trigger trig, job|list arg1, ..., job|list argn);
265
266The function takes an arbitrary number of jobs or lists of jobs in addition
267to a trigger argument. When the trigger fires, all those jobs will be
268cancelled if they haven't finished yet.
269
270A simple use of triggers is to wait for the first job in a list to be
271triggered.
Note: See TracBrowser for help on using the repository browser.