Merge lp:~mdavidsaver/epics-appdev/thread-pool into lp:~epics-documenters/epics-appdev/3.16

Proposed by Andrew Johnson
Status: Merged
Merged at revision: 54
Proposed branch: lp:~mdavidsaver/epics-appdev/thread-pool
Merge into: lp:~epics-documenters/epics-appdev/3.16
Diff against target: 303 lines (+293/-0)
1 file modified
tex/libCom.tex (+293/-0)
To merge this branch: bzr merge lp:~mdavidsaver/epics-appdev/thread-pool
Reviewer Review Type Date Requested Status
Andrew Johnson Approve
mdavidsaver Pending
Review via email: mp+228549@code.launchpad.net

This proposal supersedes a proposal from 2014-07-22.

Description of the change

Resubmitting proposal to remove prerequisite branch.

To post a comment you must log in.
Revision history for this message
Andrew Johnson (anj) wrote : Posted in a previous version of this proposal

This is also a review of the API to the thread-pool code, since it was easier to understand that by reading the documentation for it here.

Note that I'm trying out Launchpad's new "inline diff comments" feature, I have no idea how well that will work.

review: Needs Fixing
Revision history for this message
mdavidsaver (mdavidsaver) wrote : Posted in a previous version of this proposal

I think I have made the requested changes.

review: Needs Resubmitting
Revision history for this message
Andrew Johnson (anj) wrote :

Would have been nice to have epicsThreadPoolReport() mentioned, maybe add later...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tex/libCom.tex'
2--- tex/libCom.tex 2014-04-22 19:15:31 +0000
3+++ tex/libCom.tex 2014-07-28 17:47:17 +0000
4@@ -1260,6 +1260,299 @@
5 \index{macInstallMacros}
6 NOTE: The directory \textless{}base\textgreater{}/src/libCom/macLib contains two files \verb|macLibNOTES| and \verb|macLibREADME| that explain this library.
7
8+\section{epicsThreadPool}
9+
10+\index{epicsThreadPool.h}
11+
12+\verb|epicsThreadPool.h| implements general purpose threaded work queue.
13+Pieces of work (jobs) are submitted to a queue which is shared by a group of worker threads.
14+After jobs are placed on the queue they may be executed in any order.
15+
16+The thread pool library will never implicitly destroy pools or jobs.
17+Such cleanup is always the responsibility of user code.
18+
19+\subsection{Configure a pool}
20+
21+An \verb|epicsThreadPool| instance can be obtained in two ways. The preferred
22+method is the use an existing shared thread pool. Alternately, a new
23+pool may be explicitly created. In both cases \verb|NULL| may be passed to use
24+the configuration, or a non-default configuration may be prepared in the following way.
25+
26+\begin{verbatim}
27+typedef struct {
28+ size_t initialThreads;
29+ size_t maxThreads;
30+ unsigned int workerStack;
31+ unsigned int workerPriority;
32+} epicsThreadPoolConfig;
33+
34+void epicsThreadPoolConfigDefaults(epicsThreadPoolConfig *);
35+\end{verbatim}
36+
37+\index{epicsThreadPoolConfig}
38+\index{epicsThreadPoolConfigDefaults}
39+
40+Pool configuration should always be initialized with \verb|epicsThreadPoolConfigDefault()|
41+before modification by user code. This will initialize configuration
42+parameters to sensible defaults, which can then be overwritten as
43+needed by user code.
44+
45+Note that no epicsThreadPool functions will ever retain a pointer
46+to the user provided \verb|epicsThreadPoolConfig| instance.
47+
48+\begin{description}
49+\item [{initialThreads}] A suggestion for the number of worker threads to start
50+immediately when the pool is created. When this number is less than
51+maxThreads additional workers may be started later as needed. Defaults to zero.
52+\item [{maxThreads}] Upper limit on the number of worker threads in this
53+pool. Defaults to number of CPU cores.
54+\item [{workerStack}] Worker thread stack size. Defaults to size of \verb|epicsThreadStackSmall|.
55+\item [{workerPriority}] Worker thread priority. Defaults to just above \verb|epicsThreadPriorityCAServerHigh|
56+\end{description}
57+
58+These defaults are choosen to be suitable for CPU intensive background work by drivers.
59+
60+\subsection{Create a shared pool}
61+
62+\begin{verbatim}
63+epicsThreadPool* epicsThreadPoolGetShared(epicsThreadPoolConfig *opts);
64+void epicsThreadPoolReleaseShared(epicsThreadPool *pool);
65+\end{verbatim}
66+
67+\index{epicsThreadPoolGetShared}
68+\index{epicsThreadPoolReleaseShared}
69+
70+A shared thread pool is obtained by calling \verb|epicsThreadPoolGetShared()|.
71+A global list of shared pools is examined. If an existing pool matches
72+the requested configuration, then it is returned. Otherwise a new
73+pool is created, added to the global list, then returned. \verb|epicsThreadPoolGetShared()|
74+may return NULL in situations of memory exhaustion.
75+
76+Note that \verb|NULL| may be passed to use the default configuration.
77+
78+As for example:
79+
80+\begin{verbatim}
81+void usercode() {
82+ epicsThreadPoolConfig myconf;
83+ epicsThreadPoolConfigDefault(&myconf);
84+ /* overwrite defaults if needed */
85+ myconf.workerPriority = epicsThreadPriorityLow;
86+ ... = epicsThreadPoolGetShared(&myconf);
87+
88+ /* or to use the defaults */
89+ ... = epicsThreadPoolGetShared(NULL);
90+}
91+\end{verbatim}
92+
93+The user provided configuration may be altered to ensure that the
94+maxThreads is greater than or equal to the number of threads the host
95+system can run in parallel. In addition, when a existing shared pool
96+is returned, the user supplied config is overwritten with the pool's
97+actual config.
98+
99+If a thread pool will not be used further it must be released, which
100+may cause it to be free'd when no other references exist.
101+It is advisable to ensure that all queued
102+jobs have completed as queued jobs may still run if the other references
103+to the queue remain.
104+
105+When matching a requested configuration against the configuration
106+of a existing shared pool, the following conditions must be meet
107+for an existing shared queue to be used.
108+\begin{itemize}
109+\item workerPriority must match exactly.
110+\item maxThreads and workerStack of the pool must be greater than or equal
111+to the corresponding parameters of the request.
112+\end{itemize}
113+
114+Note that the initialThreads option is ignored when requesting a shared pool.
115+
116+\subsection{Creating an exclusive pool}
117+
118+\begin{verbatim}
119+epicsThreadPool* epicsThreadPoolCreate(epicsThreadPoolConfig *opts);
120+void epicsThreadPoolDestroy(epicsThreadPool *);
121+\end{verbatim}
122+
123+
124+\index{epicsThreadPoolCreate}
125+\index{epicsThreadPoolDestory}
126+
127+Unshared thread pools are created/destroyed in a similar fashion.
128+
129+Note that \verb|NULL| may be passed to use the default configuration.
130+
131+When a pool is destroyed it will freeze the queue to prevent new jobs
132+from being added, then block until any previously queued jobs complete.
133+
134+\subsection{Basic jobs operations}
135+
136+\begin{verbatim}
137+/* job modes */
138+typedef enum {
139+ epicsJobModeRun,
140+ epicsJobModeCleanup
141+} epicsJobMode;
142+typedef void (*epicsJobFunction)(void* arg, epicsJobMode mode);
143+
144+#define EPICSJOB_SELF ...
145+epicsJob* epicsJobCreate(epicsThreadPool* pool,
146+ epicsJobFunction cb,
147+ void* user);
148+void epicsJobDestroy(epicsJob*);
149+int epicsJobQueue(epicsJob*);
150+int epicsJobUnqueue(epicsJob*);
151+\end{verbatim}
152+
153+
154+\index{epicsJobMode}
155+\index{epicsJobModeRun}
156+\index{epicsJobModeCleanup}
157+\index{EPICSJOB_SELF@EPICSJOB\_SELF}
158+\index{epicsJobFunction}
159+\index{epicsJobCreate}
160+\index{epicsJobDestroy}
161+\index{epicsJobQueue}
162+\index{epicsJobUnqueue}
163+
164+The basic lifecycle of a job is to be created, queued some number of
165+times, then destroyed. As with an \verb|epicsThreadPool*|, the lifecycle of
166+an \verb|epicsJob*| is completely controlled by user code. Jobs will never
167+be implicitly destroyed. When created, a pool, work function, and
168+user argument are specified. The special user argument \verb|EPICSJOB_SELF|
169+may be passed to set the user argument to the \verb|epicsJob*| returned
170+by \verb|epicsJobCreate()|.
171+
172+A job may be queued at any time. The queuing process can fail (return
173+non-zero) if:
174+
175+\begin{itemize}
176+\item The job is not currently associated with a pool.
177+\item The associated pool is not allowing jobs to be queued.
178+\end{itemize}
179+
180+A job may be unqueued with \verb|epicsJobUnqueue()|. This function will return
181+1 if the job was successful removed from the queue, or 0 if this is
182+not possible. A job can not be unqueued if it was not queued to begin
183+with, is running, or has completed.
184+
185+A job may also be destroyed at any time, including while its job function
186+is running. In this case destruction is deferred until the job function returns.
187+
188+If a thread pool is destroyed before all of its jobs are destroyed,
189+then each job function is called one final time with the mode \verb|epicsJobModeCleanup|
190+to provide an opportunity to call \verb|epicsJobDestroy|.
191+If this is not done, then the job is disassociated from the pool.
192+It is always the responsibility of user code to explicitly call \verb|epicsJobDestroy|.
193+
194+\subsection{Writing job functions}
195+
196+\begin{verbatim}
197+typedef struct {
198+ epicsJob* job;
199+ ...
200+} myWork;
201+
202+static
203+void myfunction(void* arg,
204+ epicsJobMode mode)
205+{
206+ myWork *priv=arg;
207+ if(mode==epicsJobModeCleanup) {
208+ epicsJobDestroy(priv->job);
209+ free(priv);
210+ return;
211+ }
212+ /* do normal work */
213+}
214+
215+static
216+void somewhere(...)
217+{
218+ epicsThreadPool *pool;
219+ myWork *priv = ...; /* allocate somehow */
220+ pool = epicsThreadPoolCreate(NULL);
221+ assert(pool!=NULL && priv!=NULL);
222+ priv->job = epicsJobCreate(pool, &myfunction, priv);
223+ assert(priv->job!=NULL);
224+ epicsJobQueue(priv->job);
225+ epicsThreadPoolDestroy(pool);
226+}
227+\end{verbatim}
228+
229+
230+Some restrictions apply to job functions. Only the following epicsThreadPool
231+functions may be called from a job function. When using a shared pool,
232+no modification should be made to the worker threads (eg. don't change
233+priority). If such modifications are needed, then an exclusively owned
234+pool should be created.
235+
236+\begin{itemize}
237+\item \verb|epicsJobQueue()|
238+\item \verb|epicsJobUnqueue()|
239+\item \verb|epicsJobCreate()|
240+\item \verb|epicsJobDestroy()|
241+\end{itemize}
242+
243+No internal locks are held while a job function runs.
244+So a job function may lock arbitrary mutexes without causing a deadlock.
245+When in a job function, care must be taken to only call those function explicitly
246+marked as safe to call from a running job function as these functions
247+are written to avoid corrupting the internal state of the pool.
248+
249+
250+\subsection{Moving jobs between pools}
251+
252+It may be desirable to move epicsJob instances between pools, or to
253+have jobs not associated with any pool. This is supported with the
254+caveat that the \verb|epicsJobMove()| function must not run concurrently
255+with any other epicsThreadPool functions operating on the same job.
256+In addition to functions operating explicitly on this job, this also includes
257+\verb|epicsThreadPoolDestroy()|
258+
259+A job may be created with no pool association by passing NULL to \verb|epicsJobCreate()|
260+instead of an \verb|epicsThreadPool*| pointer. The association can be changed
261+at runtime with the \verb|epicsJobMove()| function.
262+
263+
264+\subsection{Pool control}
265+
266+\begin{verbatim}
267+typedef enum {
268+ epicsThreadPoolQueueAdd,
269+ epicsThreadPoolQueueRun
270+} epicsThreadPoolOption;
271+void epicsThreadPoolControl(epicsThreadPool* pool,
272+ epicsThreadPoolQueueOption opt,
273+ unsigned int val);
274+int epicsThreadPoolWait(epicsThreadPool* pool, double timeout);
275+\end{verbatim}
276+
277+
278+\index{epicsThreadPoolQueueAdd}
279+\index{epicsThreadPoolQueueRun}
280+\index{epicsThreadPoolControl}
281+\index{epicsThreadPoolWait}
282+
283+It may be useful to manipulate the queue of a thread pool at runtime
284+(eg. unittests). Currently defined options are:
285+\begin{description}
286+\item [{epicsThreadPoolQueueAdd}] Set to 0 to prevent additional jobs from
287+being queued. Set to 1 to resume normal operation.
288+\item [{epicsThreadPoolQueueRun}] Set to 0 to prevents workers from taking
289+jobs from the queue. Set to 1 for normal operation.
290+\end{description}
291+These options may be combined with \verb|epicsThreadPoolWait()| to block
292+until the queue is empty.
293+
294+\verb|epicsThreadPoolWait()| accepts a timeout in seconds. A timeout value
295+less than 0.0 never times out, a value of exactly 0.0 will not block,
296+and values greater than 0.0 will block for the requested time at most.
297+
298+This function returns 0 if the queue was emptied and no jobs are running at any moment during the timeout period,
299+or ETIMEOUT if the timeout period ellapses and jobs remain in the queue or are running.
300+
301 \section{misc}
302
303 \subsection{aToIPAddr}

Subscribers

People subscribed via source and target branches