Imported Upstream version 3.0
[debian/gnuradio] / gnuradio-core / doc / other / omnithread.html
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
2             "http://www.w3.org/TR/REC-html40/loose.dtd">
3 <HTML>
4 <HEAD><TITLE>The OMNI Thread Abstraction</TITLE>
5
6 <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
7 <META name="GENERATOR" content="hevea 1.06">
8 </HEAD>
9 <BODY >
10 <!--HEVEA command line is: /usr/local/bin/hevea omnithread -->
11 <!--HTMLHEAD-->
12 <!--ENDHTML-->
13 <!--PREFIX <ARG ></ARG>-->
14 <!--CUT DEF section 1 -->
15
16
17 <H1 ALIGN=center>The OMNI Thread Abstraction</H1>
18
19 <H3 ALIGN=center>Tristan Richardson<BR>
20 AT&amp;T Laboratories Cambridge<BR>
21 </H3>
22
23 <H3 ALIGN=center><I>Revised</I> November 2001</H3>
24 <!--TOC section Introduction-->
25
26 <H2><A NAME="htoc1">1</A>&nbsp;&nbsp;Introduction</H2><!--SEC END -->
27
28 The OMNI thread abstraction is designed to provide a common set of
29 thread operations for use in programs written in C++. Programs
30 written using the abstraction should be much easier to port between
31 different architectures with different underlying threads primitives.<BR>
32 <BR>
33 The programming interface is designed to be similar to the C language
34 interface to POSIX threads (IEEE draft standard 1003.1c --- previously
35 1003.4a, often known as ``pthreads'' [<A HREF="#pthreads"><CITE>POSIX94</CITE></A>]).<BR>
36 <BR>
37 Much of the abstraction consists of simple C++ object wrappers around
38 pthread calls. However for some features such as thread-specific
39 data, a better interface can be offered because of the use of C++.<BR>
40 <BR>
41 Some of the more complex features of pthreads are not supported
42 because of the difficulty of ensuring the same features can be offered
43 on top of other thread systems. Such features include thread
44 cancellation and complex scheduling control (though simple thread
45 priorities are supported).<BR>
46 <BR>
47 The abstraction layer is currently implemented for the following
48 architectures / thread systems:
49 <UL><LI>Solaris 2.x using pthreads draft 10
50 <LI>Solaris 2.x using solaris threads (but pthreads version is now standard)
51 <LI>Alpha OSF1 using pthreads draft 4
52 <LI>Windows NT using NT threads
53 <LI>Linux 2.x using Linuxthread 0.5 (which is based on pthreads draft 10)
54 <LI>Linux 2.x using MIT pthreads (which is based on draft 8)
55 <LI>ATMos using pthreads draft 6 (but not Virata ATMos)</UL>
56 See the <TT>omnithread.h</TT> header file for full details of the API.
57 The descriptions below assume you have some previous knowledge of
58 threads, mutexes, condition variables and semaphores. Also refer to
59 other documentation ([<A HREF="#birrell"><CITE>Birrell89</CITE></A>], [<A HREF="#pthreads"><CITE>POSIX94</CITE></A>]) for further
60 explanation of these ideas (particularly condition variables, the use
61 of which may not be particularly intuitive when first encountered).<BR>
62 <BR>
63 <!--TOC section Synchronisation objects-->
64
65 <H2><A NAME="htoc2">2</A>&nbsp;&nbsp;Synchronisation objects</H2><!--SEC END -->
66
67 Synchronisation objects are used to synchronise threads within the
68 same process. There is no inter-process synchronisation provided.
69 The synchronisation objects provided are mutexes, condition variables
70 and counting semaphores.<BR>
71 <BR>
72 <!--TOC subsection Mutex-->
73
74 <H3><A NAME="htoc3">2.1</A>&nbsp;&nbsp;Mutex</H3><!--SEC END -->
75
76 An object of type <TT>omni_mutex</TT> is used for mutual exclusion.
77 It provides two operations, <TT>lock()</TT> and <TT>unlock()</TT>.
78 The alternative names <TT>acquire()</TT> and <TT>release()</TT> can be
79 used if preferred. Behaviour is undefined when a thread attempts to
80 lock the same mutex again or when a mutex is locked by one thread and
81 unlocked by a different thread.<BR>
82 <BR>
83 <!--TOC subsection Condition Variable-->
84
85 <H3><A NAME="htoc4">2.2</A>&nbsp;&nbsp;Condition Variable</H3><!--SEC END -->
86
87 A condition variable is represented by an <TT>omni_condition</TT> and
88 is used for signalling between threads. A call to <TT>wait()</TT>
89 causes a thread to wait on the condition variable. A call to
90 <TT>signal()</TT> wakes up at least one thread if any are waiting. A
91 call to <TT>broadcast()</TT> wakes up all threads waiting on the
92 condition variable.<BR>
93 <BR>
94 When constructed, a pointer to an <TT>omni_mutex</TT> must be given.
95 A condition variable <TT>wait()</TT> has an implicit mutex
96 <TT>unlock()</TT> and <TT>lock()</TT> around it. The link between
97 condition variable and mutex lasts for the lifetime of the condition
98 variable (unlike pthreads where the link is only for the duration of
99 the wait). The same mutex may be used with several condition
100 variables.<BR>
101 <BR>
102 A wait with a timeout can be achieved by calling
103 <TT>timed_wait()</TT>. This is given an absolute time to wait until.
104 The routine <TT>omni_thread::get_time()</TT> can be used to turn a
105 relative time into an absolute time. <TT>timed_wait()</TT> returns
106 <TT>true</TT> if the condition was signalled, <TT>false</TT> if the
107 time expired before the condition variable was signalled.<BR>
108 <BR>
109 <!--TOC subsection Counting semaphores-->
110
111 <H3><A NAME="htoc5">2.3</A>&nbsp;&nbsp;Counting semaphores</H3><!--SEC END -->
112
113 An <TT>omni_semaphore</TT> is a counting semaphore. When created it
114 is given an initial unsigned integer value. When <TT>wait()</TT> is
115 called, the value is decremented if non-zero. If the value is zero
116 then the thread blocks instead. When <TT>post()</TT> is called, if
117 any threads are blocked in <TT>wait()</TT>, exactly one thread is
118 woken. If no threads were blocked then the value of the semaphore is
119 incremented.<BR>
120 <BR>
121 If a thread calls <TT>try_wait()</TT>, then the thread won't block if
122 the semaphore's value is 0, returning <TT>false</TT> instead.<BR>
123 <BR>
124 There is no way of querying the value of the semaphore.<BR>
125 <BR>
126 <!--TOC section Thread object-->
127
128 <H2><A NAME="htoc6">3</A>&nbsp;&nbsp;Thread object</H2><!--SEC END -->
129
130 A thread is represented by an <TT>omni_thread</TT> object. There are
131 broadly two different ways in which it can be used.<BR>
132 <BR>
133 The first way is simply to create an <TT>omni_thread</TT> object,
134 giving a particular function which the thread should execute. This is
135 like the POSIX (or any other) C language interface.<BR>
136 <BR>
137 The second method of use is to create a new class which inherits from
138 <TT>omni_thread</TT>. In this case the thread will execute the
139 <TT>run()</TT> member function of the new class. One advantage of
140 this scheme is that thread-specific data can be implemented simply by
141 having data members of the new class.<BR>
142 <BR>
143 When constructed a thread is in the "new" state and has not actually
144 started. A call to <TT>start()</TT> causes the thread to begin
145 executing. A static member function <TT>create()</TT> is provided to
146 construct and start a thread in a single call. A thread exits by
147 calling <TT>exit()</TT> or by returning from the thread function.<BR>
148 <BR>
149 Threads can be either detached or undetached. Detached threads are
150 threads for which all state will be lost upon exit. Other threads
151 cannot determine when a detached thread will disappear, and therefore
152 should not attempt to access the thread object unless some explicit
153 synchronisation with the detached thread guarantees that it still
154 exists.<BR>
155 <BR>
156 Undetached threads are threads for which storage is not reclaimed
157 until another thread waits for its termination by calling
158 <TT>join()</TT>. An exit value can be passed from an undetached
159 thread to the thread which joins it.<BR>
160 <BR>
161 Detached / undetached threads are distinguished on creation by the
162 type of function they execute. Undetached threads execute a function
163 which has a <TT>void*</TT> return type, whereas detached threads
164 execute a function which has a <TT>void</TT> return type.
165 Unfortunately C++ member functions are not allowed to be distinguished
166 simply by their return type. Thus in the case of a derived class of
167 <TT>omni_thread</TT> which needs an undetached thread, the member
168 function executed by the thread is called <TT>run_undetached()</TT>
169 rather than <TT>run()</TT>, and it is started by calling
170 <TT>start_undetached()</TT> instead of <TT>start()</TT>.<BR>
171 <BR>
172 The abstraction currently supports three priorities of thread, but no
173 guarantee is made of how this will affect underlying thread
174 scheduling. The three priorities are <TT>PRIORITY_LOW</TT>,
175 <TT>PRIORITY_NORMAL</TT> and <TT>PRIORITY_HIGH</TT>. By default all
176 threads run at <TT>PRIORITY_NORMAL</TT>. A different priority can be
177 specified on thread creation, or while the thread is running using
178 <TT>set_priority().</TT> A thread's current priority is returned by
179 <TT>priority()</TT>.<BR>
180 <BR>
181 Other functions provided are <TT>self()</TT> which returns the calling
182 thread's <TT>omni_thread</TT> object, <TT>yield()</TT> which
183 requests that other threads be allowed to run, <TT>id()</TT> which
184 returns an integer id for the thread for use in debugging,
185 <TT>state()</TT>, <TT>sleep()</TT> and <TT>get_time()</TT>.<BR>
186 <BR>
187 <!--TOC section Per-thread data-->
188
189 <H2><A NAME="htoc7">4</A>&nbsp;&nbsp;Per-thread data</H2><!--SEC END -->
190
191 omnithread supports per-thread data, via member functions of the
192 <TT>omni_thread</TT> object.<BR>
193 <BR>
194 First, you must allocate a key for with the
195 <TT>omni_thread::allocate_key()</TT> function. Then, any object
196 whose class is derived from <TT>omni_thread::value_t</TT> can be
197 stored using the <TT>set_value()</TT> function. Values are retrieved
198 or removed with <TT>get_value()</TT> and <TT>remove_value()</TT>
199 respectively.<BR>
200 <BR>
201 When the thread exits, all per-thread data is deleted (hence the base
202 class with virtual destructor).<BR>
203 <BR>
204 Note that the per-thread data functions are <B>not</B> thread safe,
205 so although you can access one thread's storage from another thread,
206 there is no concurrency control. Unless you really know what you are
207 doing, it is best to only access per-thread data from the thread it is
208 attached to.<BR>
209 <BR>
210 <!--TOC section Using OMNI threads in your program-->
211
212 <H2><A NAME="htoc8">5</A>&nbsp;&nbsp;Using OMNI threads in your program</H2><!--SEC END -->
213
214 Obviously you need to include the <TT>omnithread.h</TT> header file in
215 your source code, and link in the omnithread library with your
216 executable. Because there is a single <TT>omnithread.h</TT> for all
217 platforms, certain preprocessor defines must be given as compiler
218 options. The easiest way to do this is to study the makefiles given
219 in the examples provided with this distribution. If you are to
220 include OMNI threads in your own development environment, these are
221 the necessary preprocessor defines:<BR>
222 <TABLE BORDER=1 CELLSPACING=0 CELLPADDING=1>
223 <TR><TD ALIGN=left NOWRAP>Platform</TD>
224 <TD ALIGN=left NOWRAP>Preprocessor Defines</TD>
225 </TR>
226 <TR><TD ALIGN=left NOWRAP>Sun Solaris 2.x</TD>
227 <TD ALIGN=left NOWRAP><CODE>-D__sunos__ -D__sparc__ -D__OSVERSION__=5</CODE></TD>
228 </TR>
229 <TR><TD ALIGN=left NOWRAP>&nbsp;</TD>
230 <TD ALIGN=left NOWRAP><CODE>-DSVR4 -DUsePthread -D_REENTRANT</CODE></TD>
231 </TR>
232 <TR><TD ALIGN=left NOWRAP>x86 Linux 2.0</TD>
233 <TD ALIGN=left NOWRAP><CODE>-D__linux__ -D__i86__ -D__OSVERSION__=2</CODE></TD>
234 </TR>
235 <TR><TD ALIGN=left NOWRAP>with linuxthreads 0.5</TD>
236 <TD ALIGN=left NOWRAP><CODE>-D_REENTRANT</CODE></TD>
237 </TR>
238 <TR><TD ALIGN=left NOWRAP>Digital Unix 3.2</TD>
239 <TD ALIGN=left NOWRAP><CODE>-D__osf1__ -D__alpha__ -D__OSVERSION__=3</CODE></TD>
240 </TR>
241 <TR><TD ALIGN=left NOWRAP>&nbsp;</TD>
242 <TD ALIGN=left NOWRAP><CODE>-D_REENTRANT</CODE></TD>
243 </TR>
244 <TR><TD ALIGN=left NOWRAP>Windows NT</TD>
245 <TD ALIGN=left NOWRAP><CODE>-D__NT__ -MD</CODE></TD>
246 </TR></TABLE><BR>
247 <!--TOC section Threaded I/O shutdown for Unix-->
248
249 <H2><A NAME="htoc9">6</A>&nbsp;&nbsp;Threaded I/O shutdown for Unix</H2><!--SEC END -->
250
251 or, how one thread should tell another thread to shut down when it
252 might be doing a blocking call on a socket.<BR>
253 <BR>
254 <B>If you are using omniORB, you don't need to worry about all
255 this, since omniORB does it for you.</B> This section is only relevant
256 if you are using omnithread in your own socket-based programming. It
257 is also seriously out of date.<BR>
258 <BR>
259 Unfortunately there doesn't seem to be a standard way of doing this
260 which works across all Unix systems. I have investigated the
261 behaviour of Solaris 2.5 and Digital Unix 3.2. On Digital Unix
262 everything is fine, as the obvious method using shutdown() seems to
263 work OK. Unfortunately on Solaris shutdown can only be used on a
264 connected socket, so we need devious means to get around this
265 limitation. The details are summarised below:<BR>
266 <BR>
267 <!--TOC subsection read()-->
268
269 <H3><A NAME="htoc10">6.1</A>&nbsp;&nbsp;read()</H3><!--SEC END -->
270
271 Thread A is in a loop, doing <CODE>read(sock)</CODE>, processing the data,
272 then going back into the read.<BR>
273 <BR>
274 Thread B comes along and wants to shut it down --- it can't cancel
275 thread A since (i) working out how to clean up according to where A is
276 in its loop is a nightmare, and (ii) this isn't available in
277 omnithread anyway.<BR>
278 <BR>
279 On Solaris 2.5 and Digital Unix 3.2 the following strategy works:<BR>
280 <BR>
281 Thread B does <CODE>shutdown(sock,2)</CODE>.<BR>
282 <BR>
283 At this point thread A is either blocked inside <CODE>read(sock)</CODE>, or
284 is elsewhere in the loop. If the former then read will return 0,
285 indicating that the socket is closed. If the latter then eventually
286 thread A will call <CODE>read(sock)</CODE> and then this will return 0.
287 Thread A should <CODE>close(sock)</CODE>, do any other tidying up, and exit.<BR>
288 <BR>
289 If there is another point in the loop that thread A can block then
290 obviously thread B needs to be aware of this and be able to wake it up
291 in the appropriate way from that point.<BR>
292 <BR>
293 <!--TOC subsection accept()-->
294
295 <H3><A NAME="htoc11">6.2</A>&nbsp;&nbsp;accept()</H3><!--SEC END -->
296
297 Again thread A is in a loop, this time doing an accept on listenSock,
298 dealing with a new connection and going back into accept. Thread B
299 wants to cancel it.<BR>
300 <BR>
301 On Digital Unix 3.2 the strategy is identical to that for read:<BR>
302 <BR>
303 Thread B does <CODE>shutdown(listenSock,2)</CODE>. Wherever thread A is in
304 the loop, eventually it will return <CODE>ECONNABORTED</CODE> from the
305 accept call. It should <CODE>close(listenSock)</CODE>, tidy up as necessary
306 and exit.<BR>
307 <BR>
308 On Solaris 2.5 thread B can't do <CODE>shutdown(listenSock,2)</CODE> ---
309 this returns <CODE>ENOTCONN</CODE>. Instead the following strategy can be
310 used:<BR>
311 <BR>
312 First thread B sets some sort of "shutdown flag" associated with
313 listenSock. Then it does <CODE>getsockaddr(listenSock)</CODE> to find out
314 which port listenSock is on (or knows already), sets up a socket
315 dummySock, does <CODE>connect(dummySock,</CODE> <CODE>this host, port)</CODE> and
316 finally does <CODE>close(dummySock)</CODE>.<BR>
317 <BR>
318 Wherever thread A is in the loop, eventually it will call
319 <CODE>accept(listenSock)</CODE>. This will return successfully with a new
320 socket, say connSock. Thread A then checks to see if the "shutdown
321 flag" is set. If not, then it's a normal connection. If it is set,
322 then thread A closes listenSock and connSock, tidies up and exits.<BR>
323 <BR>
324 <!--TOC subsection write()-->
325
326 <H3><A NAME="htoc12">6.3</A>&nbsp;&nbsp;write()</H3><!--SEC END -->
327
328 Thread A may be blocked in write, or about to go in to a
329 potentially-blocking write. Thread B wants to shut it down.<BR>
330 <BR>
331 On Solaris 2.5:<BR>
332 <BR>
333 Thread B does <CODE>shutdown(sock,2)</CODE>.<BR>
334 <BR>
335 If thread A is already in <CODE>write(sock)</CODE> then it will return with
336 <CODE>ENXIO</CODE>. If thread A calls write after thread B calls shutdown
337 this will return <CODE>EIO</CODE>.<BR>
338 <BR>
339 On Digital Unix 3.2:<BR>
340 <BR>
341 Thread B does <CODE>shutdown(sock,2)</CODE>.<BR>
342 <BR>
343 If thread A is already in <CODE>write(sock)</CODE> then it will return the
344 number of bytes written before it became blocked. A subsequent call
345 to write will then generate <CODE>SIGPIPE</CODE> (or <CODE>EPIPE</CODE> will be
346 returned if <CODE>SIGPIPE</CODE> is ignored by the thread).<BR>
347 <BR>
348 <!--TOC subsection connect()-->
349
350 <H3><A NAME="htoc13">6.4</A>&nbsp;&nbsp;connect()</H3><!--SEC END -->
351
352 Thread A may be blocked in connect, or about to go in to a
353 potentially-blocking connect. Thread B wants to shut it down.<BR>
354 <BR>
355 On Digital Unix 3.2:<BR>
356 <BR>
357 Thread B does <CODE>shutdown(sock,2)</CODE>.<BR>
358 <BR>
359 If thread A is already in <CODE>connect(sock)</CODE> then it will return a
360 successful connection. Subsequent reading or writing will show that
361 the socket has been shut down (i.e. read returns 0, write generates
362 <CODE>SIGPIPE</CODE> or returns <CODE>EPIPE</CODE>). If thread A calls connect
363 after thread B calls shutdown this will return <CODE>EINVAL</CODE>.<BR>
364 <BR>
365 On Solaris 2.5:<BR>
366 <BR>
367 There is no way to wake up a thread which is blocked in connect.
368 Instead Solaris forces us through a ridiculous procedure whichever way
369 we try it. One way is this:<BR>
370 <BR>
371 First thread A creates a pipe in addition to the socket. Instead of
372 shutting down the socket, thread B simply writes a byte to the pipe.<BR>
373 <BR>
374 Thread A meanwhile sets the socket to non-blocking mode using
375 <CODE>fcntl(sock,</CODE> <CODE>F_SETFL, O_NONBLOCK)</CODE>. Then it calls connect
376 on the socket --- this will return <CODE>EINPROGRESS</CODE>. Then it must
377 call <CODE>select()</CODE>, waiting for either sock to become writable or
378 for the pipe to become readable. If select returns that just sock is
379 writable then the connection has succeeded. It then needs to set the
380 socket back to blocking mode using <CODE>fcntl(sock, F_SETFL, 0)</CODE>. If
381 instead select returns that the pipe is readable, thread A closes the
382 socket, tidies up and exits.<BR>
383 <BR>
384 An alternative method is similar but to use polling instead of the
385 pipe. Thread B justs sets a flag and thread A calls select with a
386 timeout, periodically waking up to see if the flag has been set.<BR>
387 <BR>
388 <!--TOC section References-->
389
390 <H2>References</H2><!--SEC END -->
391 <DL COMPACT=compact><DT><A NAME="pthreads"><FONT COLOR=purple>[POSIX94]</FONT></A><DD>
392 <EM>Portable Operating System Interface (POSIX) Threads Extension</EM>,
393 P1003.1c Draft 10,
394 IEEE,
395 September 1994.<BR>
396 <BR>
397 <DT><A NAME="birrell"><FONT COLOR=purple>[Birrell89]</FONT></A><DD>
398 <EM>An Introduction to Programming with Threads</EM>,
399 Research Report 35,
400 DEC Systems Research Center,
401 Palo Alto, CA,
402 January 1989.</DL>
403 <!--HTMLFOOT-->
404 <!--ENDHTML-->
405 <!--FOOTER-->
406 <HR SIZE=2>
407 <BLOCKQUOTE><EM>This document was translated from L<sup>A</sup>T<sub>E</sub>X by
408 </EM><A HREF="http://pauillac.inria.fr/~maranget/hevea/index.html"><EM>H<FONT SIZE=2><sup>E</sup></FONT>V<FONT SIZE=2><sup>E</sup></FONT>A</EM></A><EM>.
409 </EM></BLOCKQUOTE>
410 </BODY>
411 </HTML>