blob: e95bceb97a33719dd315a5e5a9a2fe4f04c762e4 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: Fl_lock.cxx 8393 2011-02-06 19:46:11Z manolo $"
3//
4// Multi-threading support code for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28
29#include <FL/Fl.H>
30#include <config.h>
31
32#include <stdlib.h>
33
34/*
35 From Bill:
36
37 I would prefer that FLTK contain the minimal amount of extra
38 stuff for doing threads. There are other portable thread
39 wrapper libraries out there and FLTK should not be providing
40 another. This file is an attempt to make minimal additions
41 and make them self-contained in this source file.
42
43 From Mike:
44
45 Starting with 1.1.8, we now have a callback so that you can
46 process awake() messages as they come in.
47
48
49 The API:
50
51 Fl::lock() - recursive lock. You must call this before the
52 first call to Fl::wait()/run() to initialize the thread
53 system. The lock is locked all the time except when
54 Fl::wait() is waiting for events.
55
56 Fl::unlock() - release the recursive lock.
57
58 Fl::awake(void*) - Causes Fl::wait() to return (with the lock
59 locked) even if there are no events ready.
60
61 Fl::awake(void (*cb)(void *), void*) - Call a function
62 in the main thread from within another thread of execution.
63
64 Fl::thread_message() - returns an argument sent to an
65 Fl::awake() call, or returns NULL if none. WARNING: the
66 current implementation only has a one-entry queue and only
67 returns the most recent value!
68*/
69
70#ifndef FL_DOXYGEN
71Fl_Awake_Handler *Fl::awake_ring_;
72void **Fl::awake_data_;
73int Fl::awake_ring_size_;
74int Fl::awake_ring_head_;
75int Fl::awake_ring_tail_;
76#endif
77
78static const int AWAKE_RING_SIZE = 1024;
79static void lock_ring();
80static void unlock_ring();
81
82
83/** Adds an awake handler for use in awake(). */
84int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
85{
86 int ret = 0;
87 lock_ring();
88 if (!awake_ring_) {
89 awake_ring_size_ = AWAKE_RING_SIZE;
90 awake_ring_ = (Fl_Awake_Handler*)malloc(awake_ring_size_*sizeof(Fl_Awake_Handler));
91 awake_data_ = (void**)malloc(awake_ring_size_*sizeof(void*));
92 }
93 if (awake_ring_head_==awake_ring_tail_-1 || awake_ring_head_+1==awake_ring_tail_) {
94 // ring is full. Return -1 as an error indicator.
95 ret = -1;
96 } else {
97 awake_ring_[awake_ring_head_] = func;
98 awake_data_[awake_ring_head_] = data;
99 ++awake_ring_head_;
100 if (awake_ring_head_ == awake_ring_size_)
101 awake_ring_head_ = 0;
102 }
103 unlock_ring();
104 return ret;
105}
106/** Gets the last stored awake handler for use in awake(). */
107int Fl::get_awake_handler_(Fl_Awake_Handler &func, void *&data)
108{
109 int ret = 0;
110 lock_ring();
111 if (!awake_ring_ || awake_ring_head_ == awake_ring_tail_) {
112 ret = -1;
113 } else {
114 func = awake_ring_[awake_ring_tail_];
115 data = awake_data_[awake_ring_tail_];
116 ++awake_ring_tail_;
117 if (awake_ring_tail_ == awake_ring_size_)
118 awake_ring_tail_ = 0;
119 }
120 unlock_ring();
121 return ret;
122}
123
124/**
125 Let the main thread know an update is pending and have it call a specific function.
126 Registers a function that will be
127 called by the main thread during the next message handling cycle.
128 Returns 0 if the callback function was registered,
129 and -1 if registration failed. Over a thousand awake callbacks can be
130 registered simultaneously.
131
132 \see Fl::awake(void* message=0)
133*/
134int Fl::awake(Fl_Awake_Handler func, void *data) {
135 int ret = add_awake_handler_(func, data);
136 Fl::awake();
137 return ret;
138}
139
140////////////////////////////////////////////////////////////////
141// Windows threading...
142/** \fn int Fl::lock()
143 The lock() method blocks the current thread until it
144 can safely access FLTK widgets and data. Child threads should
145 call this method prior to updating any widgets or accessing
146 data. The main thread must call lock() to initialize
147 the threading support in FLTK. lock() will return non-zero
148 if threading is not available on the platform.
149
150 Child threads must call unlock() when they are done
151 accessing FLTK.
152
153 When the wait() method is waiting
154 for input or timeouts, child threads are given access to FLTK.
155 Similarly, when the main thread needs to do processing, it will
156 wait until all child threads have called unlock() before processing
157 additional data.
158
159 \return 0 if threading is available on the platform; non-zero
160 otherwise.
161
162 See also: \ref advanced_multithreading
163*/
164/** \fn void Fl::unlock()
165 The unlock() method releases the lock that was set
166 using the lock() method. Child
167 threads should call this method as soon as they are finished
168 accessing FLTK.
169
170 See also: \ref advanced_multithreading
171*/
172/** \fn void Fl::awake(void* msg)
173 Sends a message pointer to the main thread,
174 causing any pending Fl::wait() call to
175 terminate so that the main thread can retrieve the message and any pending
176 redraws can be processed.
177
178 Multiple calls to Fl::awake() will queue multiple pointers
179 for the main thread to process, up to a system-defined (typically several
180 thousand) depth. The default message handler saves the last message which
181 can be accessed using the
182 Fl::thread_message() function.
183
184 In the context of a threaded application, a call to Fl::awake() with no
185 argument will trigger event loop handling in the main thread. Since
186 it is not possible to call Fl::flush() from a subsidiary thread,
187 Fl::awake() is the best (and only, really) substitute.
188
189 See also: \ref advanced_multithreading
190*/
191#ifdef WIN32
192# include <windows.h>
193# include <process.h>
194# include <FL/x.H>
195
196// These pointers are in Fl_win32.cxx:
197extern void (*fl_lock_function)();
198extern void (*fl_unlock_function)();
199
200// The main thread's ID
201static DWORD main_thread;
202
203// Microsoft's version of a MUTEX...
204CRITICAL_SECTION cs;
205CRITICAL_SECTION *cs_ring;
206
207void unlock_ring() {
208 LeaveCriticalSection(cs_ring);
209}
210
211void lock_ring() {
212 if (!cs_ring) {
213 cs_ring = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
214 InitializeCriticalSection(cs_ring);
215 }
216 EnterCriticalSection(cs_ring);
217}
218
219//
220// 'unlock_function()' - Release the lock.
221//
222
223static void unlock_function() {
224 LeaveCriticalSection(&cs);
225}
226
227//
228// 'lock_function()' - Get the lock.
229//
230
231static void lock_function() {
232 EnterCriticalSection(&cs);
233}
234
235int Fl::lock() {
236 if (!main_thread) InitializeCriticalSection(&cs);
237
238 lock_function();
239
240 if (!main_thread) {
241 fl_lock_function = lock_function;
242 fl_unlock_function = unlock_function;
243 main_thread = GetCurrentThreadId();
244 }
245 return 0;
246}
247
248void Fl::unlock() {
249 unlock_function();
250}
251
252void Fl::awake(void* msg) {
253 PostThreadMessage( main_thread, fl_wake_msg, (WPARAM)msg, 0);
254}
255
256////////////////////////////////////////////////////////////////
257// POSIX threading...
258#elif HAVE_PTHREAD
259# include <unistd.h>
260# include <fcntl.h>
261# include <pthread.h>
262
263// Pipe for thread messaging via Fl::awake()...
264static int thread_filedes[2];
265
266// Mutex and state information for Fl::lock() and Fl::unlock()...
267static pthread_mutex_t fltk_mutex;
268static pthread_t owner;
269static int counter;
270
271static void lock_function_init_std() {
272 pthread_mutex_init(&fltk_mutex, NULL);
273}
274
275static void lock_function_std() {
276 if (!counter || owner != pthread_self()) {
277 pthread_mutex_lock(&fltk_mutex);
278 owner = pthread_self();
279 }
280 counter++;
281}
282
283static void unlock_function_std() {
284 if (!--counter) pthread_mutex_unlock(&fltk_mutex);
285}
286
287# ifdef PTHREAD_MUTEX_RECURSIVE
288static bool lock_function_init_rec() {
289 pthread_mutexattr_t attrib;
290 pthread_mutexattr_init(&attrib);
291 if (pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE)) {
292 pthread_mutexattr_destroy(&attrib);
293 return true;
294 }
295
296 pthread_mutex_init(&fltk_mutex, &attrib);
297 return false;
298}
299
300static void lock_function_rec() {
301 pthread_mutex_lock(&fltk_mutex);
302}
303
304static void unlock_function_rec() {
305 pthread_mutex_unlock(&fltk_mutex);
306}
307# endif // PTHREAD_MUTEX_RECURSIVE
308
309void Fl::awake(void* msg) {
310 if (write(thread_filedes[1], &msg, sizeof(void*))==0) { /* ignore */ }
311}
312
313static void* thread_message_;
314void* Fl::thread_message() {
315 void* r = thread_message_;
316 thread_message_ = 0;
317 return r;
318}
319
320static void thread_awake_cb(int fd, void*) {
321 if (read(fd, &thread_message_, sizeof(void*))==0) {
322 /* This should never happen */
323 }
324 Fl_Awake_Handler func;
325 void *data;
326 while (Fl::get_awake_handler_(func, data)==0) {
327 (*func)(data);
328 }
329}
330
331// These pointers are in Fl_x.cxx:
332extern void (*fl_lock_function)();
333extern void (*fl_unlock_function)();
334
335int Fl::lock() {
336 if (!thread_filedes[1]) {
337 // Initialize thread communication pipe to let threads awake FLTK
338 // from Fl::wait()
339 if (pipe(thread_filedes)==-1) {
340 /* this should not happen */
341 }
342
343 // Make the write side of the pipe non-blocking to avoid deadlock
344 // conditions (STR #1537)
345 fcntl(thread_filedes[1], F_SETFL,
346 fcntl(thread_filedes[1], F_GETFL) | O_NONBLOCK);
347
348 // Monitor the read side of the pipe so that messages sent via
349 // Fl::awake() from a thread will "wake up" the main thread in
350 // Fl::wait().
351 Fl::add_fd(thread_filedes[0], FL_READ, thread_awake_cb);
352
353 // Set lock/unlock functions for this system, using a system-supplied
354 // recursive mutex if supported...
355# ifdef PTHREAD_MUTEX_RECURSIVE
356 if (!lock_function_init_rec()) {
357 fl_lock_function = lock_function_rec;
358 fl_unlock_function = unlock_function_rec;
359 } else {
360# endif // PTHREAD_MUTEX_RECURSIVE
361 lock_function_init_std();
362 fl_lock_function = lock_function_std;
363 fl_unlock_function = unlock_function_std;
364# ifdef PTHREAD_MUTEX_RECURSIVE
365 }
366# endif // PTHREAD_MUTEX_RECURSIVE
367 }
368
369 fl_lock_function();
370 return 0;
371}
372
373void Fl::unlock() {
374 fl_unlock_function();
375}
376
377// Mutex code for the awake ring buffer
378static pthread_mutex_t *ring_mutex;
379
380void unlock_ring() {
381 pthread_mutex_unlock(ring_mutex);
382}
383
384void lock_ring() {
385 if (!ring_mutex) {
386 ring_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
387 pthread_mutex_init(ring_mutex, NULL);
388 }
389 pthread_mutex_lock(ring_mutex);
390}
391
392#else
393
394void unlock_ring() {
395}
396
397void lock_ring() {
398}
399
400void Fl::awake(void*) {
401}
402
403int Fl::lock() {
404 return 1;
405}
406
407void Fl::unlock() {
408}
409
410void* Fl::thread_message() {
411 return NULL;
412}
413
414#endif // WIN32
415
416//
417// End of "$Id: Fl_lock.cxx 8393 2011-02-06 19:46:11Z manolo $".
418//