File: omnithread.tex

package info (click to toggle)
omniorb-dfsg 4.3.3%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,172 kB
  • sloc: cpp: 115,843; python: 24,962; ansic: 13,414; sh: 2,665; makefile: 40
file content (217 lines) | stat: -rw-r--r-- 8,756 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
\documentclass[11pt,oneside,a4paper]{article}
\usepackage[T1]{fontenc}
\usepackage{textcomp}
\usepackage[scaled=0.9]{DejaVuSansMono}
\usepackage[scaled=0.9]{DejaVuSans}
\usepackage[scaled=0.9]{DejaVuSerif}
\linespread{1.05}

\addtolength{\oddsidemargin}{-0.2in}
\addtolength{\evensidemargin}{-0.6in}
\addtolength{\textwidth}{0.5in}

\pagestyle{headings}

% Discretionary break used to split long function names cleanly
%BEGIN LATEX
\newcommand{\dsc}{\discretionary{}{}}
%END LATEX
%HEVEA\newcommand{\dsc}{}

\title{The OMNI Thread Abstraction}

\date{Version 4.3}

\begin{document}

\maketitle

\section{Introduction}

The OMNI thread abstraction is designed to provide a common set of
thread operations for use in programs written in C++.  Programs
written using the abstraction should be much easier to port between
different architectures with different underlying threads primitives.

The programming interface is designed to be similar to the C language
interface to POSIX threads (IEEE draft standard 1003.1c --- previously
1003.4a, often known as ``pthreads'' \cite{pthreads}).

Much of the abstraction consists of simple C++ object wrappers around
pthread calls.  However for some features such as thread-specific
data, a better interface can be offered because of the use of C++.

Some of the more complex features of pthreads are not supported
because of the difficulty of ensuring the same features can be offered
on top of other thread systems.  Such features include thread
cancellation and complex scheduling control (though simple thread
priorities are supported).

See the \texttt{omnithread.h} header file for full details of the API.
The descriptions below assume you have some previous knowledge of
threads, mutexes, condition variables and semaphores.  Also refer to
other documentation (\cite{birrell}, \cite{pthreads}) for further
explanation of these ideas (particularly condition variables, the use
of which may not be particularly intuitive when first encountered).


\section{Synchronisation objects}

Synchronisation objects are used to synchronise threads within the
same process.  There is no inter-process synchronisation provided.
The synchronisation objects provided are mutexes, condition variables
and counting semaphores.

\subsection{Mutex}

An object of type \texttt{omni\_mutex} is used for mutual exclusion.
It provides two operations, \texttt{lock()} and \texttt{unlock()}.
The alternative names \texttt{acquire()} and \texttt{release()} can be
used if preferred.  Behaviour is undefined when a thread attempts to
lock the same mutex again or when a mutex is locked by one thread and
unlocked by a different thread.


\subsection{Condition Variable}

A condition variable is represented by an \texttt{omni\_condition} and
is used for signalling between threads.  A call to \texttt{wait()}
causes a thread to wait on the condition variable.  A call to
\texttt{signal()} wakes up at least one thread if any are waiting.  A
call to \texttt{broadcast()} wakes up all threads waiting on the
condition variable.

When constructed, a pointer to an \texttt{omni\_mutex} must be given.
A condition variable \texttt{wait()} has an implicit mutex
\texttt{unlock()} and \texttt{lock()} around it.  The link between
condition variable and mutex lasts for the lifetime of the condition
variable (unlike pthreads where the link is only for the duration of
the wait).  The same mutex may be used with several condition
variables.

A wait with a timeout can be achieved by calling
\texttt{timed\_wait()}.  This is given an absolute time to wait until.
The routine \texttt{omni\_thread::get\_time()} can be used to turn a
relative time into an absolute time.  \texttt{timed\_wait()} returns
\texttt{true} if the condition was signalled, \texttt{false} if the
time expired before the condition variable was signalled.


\subsection{Counting semaphores}

An \texttt{omni\_semaphore} is a counting semaphore.  When created it
is given an initial unsigned integer value.  When \texttt{wait()} is
called, the value is decremented if non-zero.  If the value is zero
then the thread blocks instead.  When \texttt{post()} is called, if
any threads are blocked in \texttt{wait()}, exactly one thread is
woken. If no threads were blocked then the value of the semaphore is
incremented.

If a thread calls \texttt{try\_wait()}, then the thread won't block if
the semaphore's value is 0, returning \texttt{false} instead.

There is no way of querying the value of the semaphore.



\section{Thread object}

A thread is represented by an \texttt{omni\_thread} object.  There are
broadly two different ways in which it can be used.

The first way is simply to create an \texttt{omni\_thread} object,
giving a particular function which the thread should execute.  This is
like the POSIX (or any other) C language interface.

The second method of use is to create a new class which inherits from
\texttt{omni\_\dsc{}thread}.  In this case the thread will execute the
\texttt{run()} member function of the new class.  One advantage of
this scheme is that thread-specific data can be implemented simply by
having data members of the new class.

When constructed a thread is in the "new" state and has not actually
started.  A call to \texttt{start()} causes the thread to begin
executing.  A static member function \texttt{create()} is provided to
construct and start a thread in a single call.  A thread exits by
calling \texttt{exit()} or by returning from the thread function.

Threads can be either detached or undetached.  Detached threads are
threads for which all state will be lost upon exit.  Other threads
cannot determine when a detached thread will disappear, and therefore
should not attempt to access the thread object unless some explicit
synchronisation with the detached thread guarantees that it still
exists.

Undetached threads are threads for which storage is not reclaimed
until another thread waits for its termination by calling
\texttt{join()}.  An exit value can be passed from an undetached
thread to the thread which joins it.

Detached / undetached threads are distinguished on creation by the
type of function they execute.  Undetached threads execute a function
which has a \texttt{void*} return type, whereas detached threads
execute a function which has a \texttt{void} return type.
Unfortunately C++ member functions are not allowed to be distinguished
simply by their return type.  Thus in the case of a derived class of
\texttt{omni\_thread} which needs an undetached thread, the member
function executed by the thread is called \texttt{run\_undetached()}
rather than \texttt{run()}, and it is started by calling
\texttt{start\_\dsc{}undetached()} instead of \texttt{start()}.

The abstraction currently supports three priorities of thread, but no
guarantee is made of how this will affect underlying thread
scheduling.  The three priorities are \texttt{PRIORITY\_LOW},
\texttt{PRIORITY\_NORMAL} and \texttt{PRIORITY\_HIGH}.  By default all
threads run at \texttt{PRIORITY\_NORMAL}.  A different priority can be
specified on thread creation, or while the thread is running using
\texttt{set\_priority().}  A thread's current priority is returned by
\texttt{priority()}.

Other functions provided are \texttt{self()} which returns the calling
thread's \texttt{omni\_\dsc{}thread} object, \texttt{yield()} which
requests that other threads be allowed to run, \texttt{id()} which
returns an integer id for the thread for use in debugging,
\texttt{state()}, \texttt{sleep()} and \texttt{get\_time()}.



\section{Per-thread data}

omnithread supports per-thread data, via member functions of the
\texttt{omni\_thread} object.

First, you must allocate a key for with the
\texttt{omni\_thread::allocate\_key()} function. Then, any object
whose class is derived from \texttt{omni\_thread::value\_t} can be
stored using the \texttt{set\_value()} function. Values are retrieved
or removed with \texttt{get\_value()} and \texttt{remove\_value()}
respectively.

When the thread exits, all per-thread data is deleted (hence the base
class with virtual destructor).

Note that the per-thread data functions are \textbf{not} thread safe,
so although you can access one thread's storage from another thread,
there is no concurrency control. Unless you really know what you are
doing, it is best to only access per-thread data from the thread it is
attached to.


\begin{thebibliography}{lo}

\bibitem[POSIX94]{pthreads}
\emph{Portable Operating System Interface (POSIX) Threads Extension},
P1003.1c Draft 10,
IEEE,
September 1994.

\bibitem[Birrell89]{birrell}
\emph{An Introduction to Programming with Threads},
Research Report 35,
DEC Systems Research Center,
Palo Alto, CA,
January 1989.

\end{thebibliography}

\end{document}