Tweak themes for more color consistency.
[ntk.git] / src / Fl_lock.cxx
blobe635a5973f6242b8a17c66c342f62f6ba4658dbe
1 //
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.
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.
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.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
29 #include <FL/Fl.H>
30 #include <config.h>
32 #include <stdlib.h>
35 From Bill:
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.
43 From Mike:
45 Starting with 1.1.8, we now have a callback so that you can
46 process awake() messages as they come in.
49 The API:
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.
56 Fl::unlock() - release the recursive lock.
58 Fl::awake(void*) - Causes Fl::wait() to return (with the lock
59 locked) even if there are no events ready.
61 Fl::awake(void (*cb)(void *), void*) - Call a function
62 in the main thread from within another thread of execution.
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!
70 #ifndef FL_DOXYGEN
71 Fl_Awake_Handler *Fl::awake_ring_;
72 void **Fl::awake_data_;
73 int Fl::awake_ring_size_;
74 int Fl::awake_ring_head_;
75 int Fl::awake_ring_tail_;
76 #endif
78 static const int AWAKE_RING_SIZE = 1024;
79 static void lock_ring();
80 static void unlock_ring();
83 /** Adds an awake handler for use in awake(). */
84 int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
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*));
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;
103 unlock_ring();
104 return ret;
106 /** Gets the last stored awake handler for use in awake(). */
107 int Fl::get_awake_handler_(Fl_Awake_Handler &func, void *&data)
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;
120 unlock_ring();
121 return ret;
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.
132 \see Fl::awake(void* message=0)
134 int Fl::awake(Fl_Awake_Handler func, void *data) {
135 int ret = add_awake_handler_(func, data);
136 Fl::awake();
137 return ret;
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.
150 Child threads must call unlock() when they are done
151 accessing FLTK.
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.
159 \return 0 if threading is available on the platform; non-zero
160 otherwise.
162 See also: \ref advanced_multithreading
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.
170 See also: \ref advanced_multithreading
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.
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.
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.
189 See also: \ref advanced_multithreading
191 #ifdef WIN32
192 # include <windows.h>
193 # include <process.h>
194 # include <FL/x.H>
196 // These pointers are in Fl_win32.cxx:
197 extern void (*fl_lock_function)();
198 extern void (*fl_unlock_function)();
200 // The main thread's ID
201 static DWORD main_thread;
203 // Microsoft's version of a MUTEX...
204 CRITICAL_SECTION cs;
205 CRITICAL_SECTION *cs_ring;
207 void unlock_ring() {
208 LeaveCriticalSection(cs_ring);
211 void lock_ring() {
212 if (!cs_ring) {
213 cs_ring = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
214 InitializeCriticalSection(cs_ring);
216 EnterCriticalSection(cs_ring);
220 // 'unlock_function()' - Release the lock.
223 static void unlock_function() {
224 LeaveCriticalSection(&cs);
228 // 'lock_function()' - Get the lock.
231 static void lock_function() {
232 EnterCriticalSection(&cs);
235 int Fl::lock() {
236 if (!main_thread) InitializeCriticalSection(&cs);
238 lock_function();
240 if (!main_thread) {
241 fl_lock_function = lock_function;
242 fl_unlock_function = unlock_function;
243 main_thread = GetCurrentThreadId();
245 return 0;
248 void Fl::unlock() {
249 unlock_function();
252 void Fl::awake(void* msg) {
253 PostThreadMessage( main_thread, fl_wake_msg, (WPARAM)msg, 0);
256 ////////////////////////////////////////////////////////////////
257 // POSIX threading...
258 #elif HAVE_PTHREAD
259 # include <unistd.h>
260 # include <fcntl.h>
261 # include <pthread.h>
263 // Pipe for thread messaging via Fl::awake()...
264 static int thread_filedes[2];
266 // Mutex and state information for Fl::lock() and Fl::unlock()...
267 static pthread_mutex_t fltk_mutex;
268 static pthread_t owner;
269 static int counter;
271 static void lock_function_init_std() {
272 pthread_mutex_init(&fltk_mutex, NULL);
275 static void lock_function_std() {
276 if (!counter || owner != pthread_self()) {
277 pthread_mutex_lock(&fltk_mutex);
278 owner = pthread_self();
280 counter++;
283 static void unlock_function_std() {
284 if (!--counter) pthread_mutex_unlock(&fltk_mutex);
287 # ifdef HAVE_PTHREAD_MUTEX_RECURSIVE
288 static 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;
296 pthread_mutex_init(&fltk_mutex, &attrib);
297 return false;
300 static void lock_function_rec() {
301 pthread_mutex_lock(&fltk_mutex);
304 static void unlock_function_rec() {
305 pthread_mutex_unlock(&fltk_mutex);
307 # endif // PTHREAD_MUTEX_RECURSIVE
309 void Fl::awake(void* msg) {
310 if (write(thread_filedes[1], &msg, sizeof(void*))==0) { /* ignore */ }
313 static void* thread_message_;
314 void* Fl::thread_message() {
315 void* r = thread_message_;
316 thread_message_ = 0;
317 return r;
320 static void thread_awake_cb(int fd, void*) {
321 if (read(fd, &thread_message_, sizeof(void*))==0) {
322 /* This should never happen */
324 Fl_Awake_Handler func;
325 void *data;
326 while (Fl::get_awake_handler_(func, data)==0) {
327 (*func)(data);
331 // These pointers are in Fl_x.cxx:
332 extern void (*fl_lock_function)();
333 extern void (*fl_unlock_function)();
335 int 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 */
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);
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);
353 // Set lock/unlock functions for this system, using a system-supplied
354 // recursive mutex if supported...
355 # ifdef HAVE_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 HAVE_PTHREAD_MUTEX_RECURSIVE
366 # endif // PTHREAD_MUTEX_RECURSIVE
369 fl_lock_function();
370 return 0;
373 void Fl::unlock() {
374 fl_unlock_function();
377 // Mutex code for the awake ring buffer
378 static pthread_mutex_t *ring_mutex;
380 void unlock_ring() {
381 pthread_mutex_unlock(ring_mutex);
384 void lock_ring() {
385 if (!ring_mutex) {
386 ring_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
387 pthread_mutex_init(ring_mutex, NULL);
389 pthread_mutex_lock(ring_mutex);
392 #else
394 void unlock_ring() {
397 void lock_ring() {
400 void Fl::awake(void*) {
403 int Fl::lock() {
404 return 1;
407 void Fl::unlock() {
410 void* Fl::thread_message() {
411 return NULL;
414 #endif // WIN32
417 // End of "$Id: Fl_lock.cxx 8393 2011-02-06 19:46:11Z manolo $".