fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_lock.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_lock.cxx 7903 2010-11-28 21:06:39Z matt $"
00003 //
00004 // Multi-threading support code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 
00029 #include <FL/Fl.H>
00030 #include <config.h>
00031 
00032 #include <stdlib.h>
00033 
00034 /*
00035    From Bill:
00036 
00037    I would prefer that FLTK contain the minimal amount of extra
00038    stuff for doing threads.  There are other portable thread
00039    wrapper libraries out there and FLTK should not be providing
00040    another.  This file is an attempt to make minimal additions
00041    and make them self-contained in this source file.
00042 
00043    From Mike:
00044 
00045    Starting with 1.1.8, we now have a callback so that you can
00046    process awake() messages as they come in.
00047 
00048 
00049    The API:
00050 
00051    Fl::lock() - recursive lock.  You must call this before the
00052    first call to Fl::wait()/run() to initialize the thread
00053    system. The lock is locked all the time except when
00054    Fl::wait() is waiting for events.
00055 
00056    Fl::unlock() - release the recursive lock.
00057 
00058    Fl::awake(void*) - Causes Fl::wait() to return (with the lock
00059    locked) even if there are no events ready.
00060 
00061    Fl::awake(void (*cb)(void *), void*) - Call a function
00062    in the main thread from within another thread of execution.
00063 
00064    Fl::thread_message() - returns an argument sent to an
00065    Fl::awake() call, or returns NULL if none.  WARNING: the
00066    current implementation only has a one-entry queue and only
00067    returns the most recent value!
00068 */
00069 
00070 #ifndef FL_DOXYGEN
00071 Fl_Awake_Handler *Fl::awake_ring_;
00072 void **Fl::awake_data_;
00073 int Fl::awake_ring_size_;
00074 int Fl::awake_ring_head_;
00075 int Fl::awake_ring_tail_;
00076 #endif
00077 
00078 static const int AWAKE_RING_SIZE = 1024;
00079 static void lock_ring();
00080 static void unlock_ring();
00081 
00082 
00084 int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
00085 {
00086   int ret = 0;
00087   lock_ring();
00088   if (!awake_ring_) {
00089     awake_ring_size_ = AWAKE_RING_SIZE;
00090     awake_ring_ = (Fl_Awake_Handler*)malloc(awake_ring_size_*sizeof(Fl_Awake_Handler));
00091     awake_data_ = (void**)malloc(awake_ring_size_*sizeof(void*));
00092   }
00093   if (awake_ring_head_==awake_ring_tail_-1 || awake_ring_head_+1==awake_ring_tail_) {
00094     // ring is full. Return -1 as an error indicator.
00095     ret = -1;
00096   } else {
00097     awake_ring_[awake_ring_head_] = func;
00098     awake_data_[awake_ring_head_] = data;
00099     ++awake_ring_head_;
00100     if (awake_ring_head_ == awake_ring_size_)
00101       awake_ring_head_ = 0;
00102   }
00103   unlock_ring();
00104   return ret;
00105 }
00107 int Fl::get_awake_handler_(Fl_Awake_Handler &func, void *&data)
00108 {
00109   int ret = 0;
00110   lock_ring();
00111   if (!awake_ring_ || awake_ring_head_ == awake_ring_tail_) {
00112     ret = -1;
00113   } else {
00114     func = awake_ring_[awake_ring_tail_];
00115     data = awake_data_[awake_ring_tail_];
00116     ++awake_ring_tail_;
00117     if (awake_ring_tail_ == awake_ring_size_)
00118       awake_ring_tail_ = 0;
00119   }
00120   unlock_ring();
00121   return ret;
00122 }
00123 
00124 //
00130 int Fl::awake(Fl_Awake_Handler func, void *data) {
00131   int ret = add_awake_handler_(func, data);
00132   Fl::awake();
00133   return ret;
00134 }
00135 
00137 // Windows threading...
00189 #ifdef WIN32
00190 #  include <windows.h>
00191 #  include <process.h>
00192 #  include <FL/x.H>
00193 
00194 // These pointers are in Fl_win32.cxx:
00195 extern void (*fl_lock_function)();
00196 extern void (*fl_unlock_function)();
00197 
00198 // The main thread's ID
00199 static DWORD main_thread;
00200 
00201 // Microsoft's version of a MUTEX...
00202 CRITICAL_SECTION cs;
00203 CRITICAL_SECTION *cs_ring;
00204 
00205 void unlock_ring() {
00206   LeaveCriticalSection(cs_ring);
00207 }
00208 
00209 void lock_ring() {
00210   if (!cs_ring) {
00211     cs_ring = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
00212     InitializeCriticalSection(cs_ring);
00213   }
00214   EnterCriticalSection(cs_ring);
00215 }
00216 
00217 //
00218 // 'unlock_function()' - Release the lock.
00219 //
00220 
00221 static void unlock_function() {
00222   LeaveCriticalSection(&cs);
00223 }
00224 
00225 //
00226 // 'lock_function()' - Get the lock.
00227 //
00228 
00229 static void lock_function() {
00230   EnterCriticalSection(&cs);
00231 }
00232 
00233 void Fl::lock() {
00234   if (!main_thread) InitializeCriticalSection(&cs);
00235 
00236   lock_function();
00237 
00238   if (!main_thread) {
00239     fl_lock_function   = lock_function;
00240     fl_unlock_function = unlock_function;
00241     main_thread        = GetCurrentThreadId();
00242   }
00243 }
00244 
00245 void Fl::unlock() {
00246   unlock_function();
00247 }
00248 
00249 void Fl::awake(void* msg) {
00250   PostThreadMessage( main_thread, fl_wake_msg, (WPARAM)msg, 0);
00251 }
00252 
00254 // POSIX threading...
00255 #elif HAVE_PTHREAD
00256 #  include <unistd.h>
00257 #  include <fcntl.h>
00258 #  include <pthread.h>
00259 
00260 // Pipe for thread messaging via Fl::awake()...
00261 static int thread_filedes[2];
00262 
00263 // Mutex and state information for Fl::lock() and Fl::unlock()...
00264 static pthread_mutex_t fltk_mutex;
00265 static pthread_t owner;
00266 static int counter;
00267 
00268 static void lock_function_init_std() {
00269   pthread_mutex_init(&fltk_mutex, NULL);
00270 }
00271 
00272 static void lock_function_std() {
00273   if (!counter || owner != pthread_self()) {
00274     pthread_mutex_lock(&fltk_mutex);
00275     owner = pthread_self();
00276   }
00277   counter++;
00278 }
00279 
00280 static void unlock_function_std() {
00281   if (!--counter) pthread_mutex_unlock(&fltk_mutex);
00282 }
00283 
00284 #  ifdef PTHREAD_MUTEX_RECURSIVE
00285 static bool lock_function_init_rec() {
00286   pthread_mutexattr_t attrib;
00287   pthread_mutexattr_init(&attrib);
00288   if (pthread_mutexattr_settype(&attrib, PTHREAD_MUTEX_RECURSIVE)) {
00289     pthread_mutexattr_destroy(&attrib);
00290     return true;
00291   }
00292 
00293   pthread_mutex_init(&fltk_mutex, &attrib);
00294   return false;
00295 }
00296 
00297 static void lock_function_rec() {
00298   pthread_mutex_lock(&fltk_mutex);
00299 }
00300 
00301 static void unlock_function_rec() {
00302   pthread_mutex_unlock(&fltk_mutex);
00303 }
00304 #  endif // PTHREAD_MUTEX_RECURSIVE
00305 
00306 void Fl::awake(void* msg) {
00307   if (write(thread_filedes[1], &msg, sizeof(void*))==0) { /* ignore */ }
00308 }
00309 
00310 static void* thread_message_;
00311 void* Fl::thread_message() {
00312   void* r = thread_message_;
00313   thread_message_ = 0;
00314   return r;
00315 }
00316 
00317 static void thread_awake_cb(int fd, void*) {
00318   if (read(fd, &thread_message_, sizeof(void*))==0) { 
00319     /* This should never happen */
00320   }
00321   Fl_Awake_Handler func;
00322   void *data;
00323   while (Fl::get_awake_handler_(func, data)==0) {
00324     (*func)(data);
00325   }
00326 }
00327 
00328 // These pointers are in Fl_x.cxx:
00329 extern void (*fl_lock_function)();
00330 extern void (*fl_unlock_function)();
00331 
00332 void Fl::lock() {
00333   if (!thread_filedes[1]) {
00334     // Initialize thread communication pipe to let threads awake FLTK
00335     // from Fl::wait()
00336     if (pipe(thread_filedes)==-1) {
00337       /* this should not happen */
00338     }
00339 
00340     // Make the write side of the pipe non-blocking to avoid deadlock
00341     // conditions (STR #1537)
00342     fcntl(thread_filedes[1], F_SETFL,
00343           fcntl(thread_filedes[1], F_GETFL) | O_NONBLOCK);
00344 
00345     // Monitor the read side of the pipe so that messages sent via
00346     // Fl::awake() from a thread will "wake up" the main thread in
00347     // Fl::wait().
00348     Fl::add_fd(thread_filedes[0], FL_READ, thread_awake_cb);
00349 
00350     // Set lock/unlock functions for this system, using a system-supplied
00351     // recursive mutex if supported...
00352 #  ifdef PTHREAD_MUTEX_RECURSIVE
00353     if (!lock_function_init_rec()) {
00354       fl_lock_function   = lock_function_rec;
00355       fl_unlock_function = unlock_function_rec;
00356     } else {
00357 #  endif // PTHREAD_MUTEX_RECURSIVE
00358       lock_function_init_std();
00359       fl_lock_function   = lock_function_std;
00360       fl_unlock_function = unlock_function_std;
00361 #  ifdef PTHREAD_MUTEX_RECURSIVE
00362     }
00363 #  endif // PTHREAD_MUTEX_RECURSIVE
00364   }
00365 
00366   fl_lock_function();
00367 }
00368 
00369 void Fl::unlock() {
00370   fl_unlock_function();
00371 }
00372 
00373 // Mutex code for the awake ring buffer
00374 static pthread_mutex_t *ring_mutex;
00375 
00376 void unlock_ring() {
00377   pthread_mutex_unlock(ring_mutex);
00378 }
00379 
00380 void lock_ring() {
00381   if (!ring_mutex) {
00382     ring_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
00383     pthread_mutex_init(ring_mutex, NULL);
00384   }
00385   pthread_mutex_lock(ring_mutex);
00386 }
00387 
00388 #else
00389 
00390 void unlock_ring() {
00391 }
00392 
00393 void lock_ring() {
00394 }
00395 
00396 void Fl::awake(void*) {
00397 }
00398 
00399 #endif // WIN32
00400 
00401 //
00402 // End of "$Id: Fl_lock.cxx 7903 2010-11-28 21:06:39Z matt $".
00403 //