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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
|
/**
\page advanced Advanced FLTK
This chapter explains advanced programming and design topics
that will help you to get the most out of FLTK.
\section advanced_multithreading Multithreading
FLTK can be used to implement a GUI for a multithreaded application
but, as with multithreaded programming generally, there are some
concepts and caveats that must be kept in mind.
Key amongst these is that, for many of the target platforms on
which FLTK is supported, only the \p main() thread of the
process is permitted to handle system events, create or destroy windows
and open or close windows. Further, only the
\p main() thread of the process can safely write to the display.
To support this in a portable way, all FLTK \p draw() methods are
executed in the \p main() thread. A worker thread may update the
state of an existing widget, but it may not do any rendering directly,
nor create or destroy a window.
(\b NOTE: A special case exists for Fl_Gl_Window where it can, with
suitable precautions, be possible
to safely render to an existing GL context from a worker thread.)
<H3>Creating portable threads</H3>
We do not provide a threading interface as part of
the library. A simple example showing how threads can be implemented,
for all supported platforms, can be found in \p test/threads.h
and \p test/threads.cxx.
FLTK has been used with a variety of thread
interfaces, so if the simple example shown in \p test/threads.cxx
does not cover your needs, you might want to select a third-party
library that provides the features you require.
\section advanced_multithreading_lock FLTK multithread locking - Fl::lock() and Fl::unlock()
In a multithreaded
program, drawing of widgets (in the \p main() thread) happens
asynchronously to widgets being updated by worker threads, so
no drawing can occur safely whilst a widget is being modified
(and no widget should be modified whilst drawing is in progress).
FLTK supports multithreaded applications using a locking mechanism
internally. This allows a worker thread to lock the rendering context,
preventing any drawing from taking place,
whilst it changes the value of its widget.
\note
The converse is also true;
whilst a worker thread holds the lock, the \p main() thread may not
be able to process any drawing requests, nor service any events.
So a worker thread that holds the FLTK lock \b must contrive to do so
for the shortest time possible or it could impair operation
of the application.
The lock operates broadly as follows.
Using the FLTK library, the \p main() thread holds the lock
whenever it is processing events or redrawing the display.
It acquires (locks) and releases (unlocks) the FLTK lock
automatically and no "user intervention" is required.
Indeed, a function that runs in the context of the \p main()
thread ideally should \b not acquire / release the FLTK lock
explicitly. (Though note that the lock calls are recursive,
so calling Fl::lock() from a thread that already holds
the lock, including the \p main() thread, is benign.
The only constraint is that every call to Fl::lock()
\b must be balanced by a corresponding call to
Fl::unlock() to ensure the lock count is preserved.)
The \p main() thread \b must call Fl::lock() \b once
before any windows are shown, to enable the internal lock (it
is "off" by default since it is not useful in single-threaded
applications) but thereafter the \p main() thread lock is managed
by the library internally.
A worker thread, when it wants to alter the value of a widget,
can acquire the lock using Fl::lock(), update the widget, then
release the lock using Fl::unlock(). Acquiring the lock ensures
that the worker thread can update the widget, without any risk
that the \p main() thread will attempt to redraw the widget
whilst it is being updated.
Note that acquiring the lock
is a blocking action; the worker thread will stall for
as long as it takes to acquire the lock.
If the \p main() thread is engaged in some complex drawing operation
this may block the worker thread for a long time, effectively
serializing what ought to be parallel operations.
(This frequently comes as a surprise to coders less familiar
with multithreaded programming issues; see the discussion of
"lockless programming" later for strategies for managing this.)
To incorporate the locking mechanism in the library,
FLTK must be compiled with FLTK_USE_PTHREADS=On
set during the build process. This is the default since version 1.3.
IDE-based versions of FLTK are automatically compiled with
the locking mechanism incorporated if possible.
\section advanced_multithreading_lock_example Simple multithreaded examples using Fl::lock
In \p main(), call
Fl::lock() once before Fl::run() or Fl::wait() to enable the lock
and start the runtime multithreading support for your program.
All callbacks and derived functions like \p handle() and \p draw()
will now be properly locked.
This might look something like this:
\code
int main(int argc, char **argv) {
/* Create your windows and widgets here */
Fl::lock(); /* "start" the FLTK lock mechanism */
/* show your window */
main_win->show(argc, argv);
/* start your worker threads */
... start threads ...
/* Run the FLTK main loop */
int result = Fl::run();
/* terminate any pending worker threads */
... stop threads ...
return result;
}
\endcode
You can start as many threads as you like. From within
a thread (other than the \p main() thread) FLTK calls must be wrapped
with calls to Fl::lock() and Fl::unlock():
\code
void my_thread(void) {
while (thread_still_running) {
/* do thread work */
...
/* compute new values for widgets */
...
Fl::lock(); // acquire the lock
my_widget->update(values);
Fl::unlock(); // release the lock; allow other threads to access FLTK again
Fl::awake(); // use Fl::awake() to signal main thread to refresh the GUI
}
}
\endcode
\note
To trigger a refresh of the GUI from a worker thread, the
worker code should call Fl::awake()
<H3>Using Fl::awake callback messages</H3>
You can also request that the \p main() thread call a function on behalf of
the worker thread by using Fl::awake(Fl_Awake_Handler cb, void* userdata).
The \p main() thread will execute the callback "as soon as possible"
when next processing the pending events. This can be used by a worker
thread to perform operations (for example showing or hiding windows)
that are prohibited in a worker thread.
\code
void do_something_cb(void *userdata) {
// Will run in the context of the main thread
... do_stuff ...
}
// running in worker thread
void *data; // "data" is a pointer to your user data
Fl::awake(do_something_cb, data); // call to execute cb in main thread
\endcode
\note
The \p main() thread will execute the Fl_Awake_Handler
callback \p do_something_cb
asynchronously to the worker thread, at some short but indeterminate
time after the worker thread registers the request.
When it executes the Fl_Awake_Handler callback,
the \p main() thread will use the contents of
\p *userdata \b at \b the \b time \b of \b execution, not necessarily
the contents that \p *userdata had at the time that the worker thread
posted the callback request.
The worker thread should
therefore contrive \b not to alter the contents of \p *userdata once
it posts the callback, since the worker thread does not know when the
\p main() thread will consume that data.
It is often useful that \p userdata point to a struct, one member
of which the \p main() thread can modify to indicate that it has
consumed the data, thereby allowing the
worker thread to re-use or update \p userdata.
\warning
The Fl::awake(void* message) call has been deprecated because the API was not
sufficient to ensure the deliver of all message or the order of messages. The
cal still exists for back compatibility, but should be repleaced with a call
to Fl::awake(), Fl::awake(handler, user_data),
or Fl::awake_once(handler, user_data).
\section advanced_multithreading_lockless FLTK multithreaded "lockless programming"
The simple multithreaded examples shown above, using the FLTK lock,
work well for many cases where multiple threads are required.
However, when that model is extended to more complex programs,
it often produces results that the developer did not anticipate.
A typical case might go something like this.
A developer creates a program to process a huge data set.
The program has a \p main() thread and 7 worker threads and
is targeted to run on an 8-core computer.
When it runs, the program divides the data between the 7
worker threads, and as they process their share of the
data, each thread updates its portion of the GUI with the
results, locking and unlocking as they do so.
But when this program runs, it is much slower than expected
and the developer finds that only
one of the eight CPU cores seems to be utilised, despite
there being 8 threads in the program. What happened?
The threads in the program all run as expected, but they end up
being serialized (that is, not able to run in parallel) because
they all depend on the single FLTK lock.
Acquiring (and releasing) that lock has an associated cost, and
is a \b blocking action if the lock is already held by any other
worker thread or by the \p main() thread.
If the worker threads are acquiring the lock "too often", then the
lock will \b always be held \b somewhere and every attempt by any
other thread (even \p main()) to lock will cause that other
thread (including \p main()) to block. And blocking \p main() also
blocks event handling, display refresh...
As a result, only one thread will be running at any given time,
and the multithreaded program is effectively reduced to
being a (complicated and somewhat less efficient) single thread
program.
A "solution" is for the worker threads to lock "less often",
such that they do not block each other or the \p main()
thread. But judging what constitutes locking "too often"
for any given configuration,
and hence will block, is a very tricky question.
What works well on one machine, with a given graphics card
and CPU configuration may behave very differently
on another target machine.
There are "interesting" variations on this theme, too:
for example it is possible that a "faulty" multithreaded
program such as described above will work
adequately on a single-core machine (where all threads are
inherently serialized anyway and so are less likely to block
each other) but then stall or even deadlock in unexpected ways
on a multicore machine when the threads do interfere with each other.
(I have seen this - it really happens.)
The "better" solution is to avoid using the FLTK lock
so far as possible. Instead, the code should be designed so
that the worker threads do not update the GUI
themselves and therefore never need to acquire the FLTK lock.
This would be FLTK multithreaded "lockless programming".
There are a number of ways this can be achieved (or at
least approximated) in practice but the most
direct approach is for the worker threads to make use of the
Fl::awake(Fl_Awake_Handler cb, void* userdata) method so that
GUI updates can all run in the context of the \p main() thread,
alleviating the need for the worker thread to ever lock.
The onus is then on the worker threads to manage the \p userdata
so that it is delivered safely to the \p main() thread, but there
are many ways that can be done.
\note
Using Fl::awake is not, strictly speaking,
entirely "lockless" since the awake handler mechanism
incorporates resource locking internally to protect the
queue of pending awake messages.
These resource locks are held transiently and
generally do not trigger the pathological blocking
issues described here.
However, aside from using Fl::awake, there are many other
ways that a "lockless" design can be implemented, including
message passing, various forms of IPC, etc.
If you need high performing multithreaded programming,
then take some time to study the options and understand
the advantages and disadvantages of each; we can't even
begin to scratch the surface of this huge topic here!
And of course occasional, sparse, use of the FLTK lock from
worker threads will do no harm; it is "excessive"
locking (whatever that might be) that triggers the
failing behaviour.
It is always a Good Idea to update the GUI at the
lowest rate that is acceptable when processing bulk
data (or indeed, in all cases!)
Updating at a few frames per second is probably
adequate for providing feedback during a long calculation.
At the upper limit, anything faster than the frame rate
of your monitor and the updates
will never even be displayed; why waste CPU computing
pixels that you will never show?
\section advanced_multithreading_caveats FLTK multithreaded Constraints
FLTK supports multiple platforms, some of which allow only the
\p main() thread to handle system events and open or close windows.
The safe thing to do is to adhere to the following rules for
threads on all operating systems:
\li Don't \p show() or \p hide() anything
that contains Fl_Window based widgets from a
worker thread.
This includes any windows, dialogs, file choosers,
subwindows or widgets using Fl_Gl_Window.
Note that this constraint also applies to non-window
widgets that have tooltips, since the tooltip will
contain a Fl_Window object.
The safe and portable approach is \b never to
call \p show() or \p hide() on any widget from the
context of a worker thread.
Instead you can use the Fl_Awake_Handler
variant of Fl::awake() to request the \p main() thread
to create, destroy, show or hide the widget on behalf
of the worker thread.
\li Don't call Fl::run(), Fl::wait(), Fl::flush(), Fl::check() or any
related methods that will handle system messages from a worker thread
\li Don't intermix use of Fl::awake(Fl_Awake_Handler cb, void* userdata)
and Fl::awake(void* message) calls in the same program as they may
interact unpredictably on some platforms; choose one or other style
of Fl::awake(<thing>) mechanism and use that.
(Intermixing calls to Fl::awake() should be safe with either however.)
\li Starting with FLTK 1.4, it's possible to start (or cancel) a timer from a
worker thread under the condition that the call to Fl::add_timeout
(or Fl::remove_timeout) is wrapped in Fl::lock() and Fl::unlock().
\li Don't change window decorations or titles from a worker thread
\li The \p make_current() method will probably not work well for
regular windows, but should always work for a Fl_Gl_Window
to allow for high speed rendering on graphics cards with multiple
pipelines. Managing thread-safe access to the GL pipelines
is left as an exercise for the reader!
(And may be target specific...)
See also:
Fl::lock(),
Fl::unlock(),
Fl::awake(),
Fl::awake(Fl_Awake_Handler cb, void* userdata),
Fl::awake(void* message),
Fl::thread_message().
\htmlonly
<hr>
<table summary="navigation bar" width="100%" border="0">
<tr>
<td width="45%" align="LEFT">
<a class="el" href="fltk-options.html">
[Prev]
FLTK Runtime Options
</a>
</td>
<td width="10%" align="CENTER">
<a class="el" href="index.html">[Index]</a>
</td>
<td width="45%" align="RIGHT">
<a class="el" href="unicode.html">
Unicode and UTF-8 Support
[Next]
</a>
</td>
</tr>
</table>
\endhtmlonly
*/
|