diff options
| -rw-r--r-- | src/Fl_lock.cxx | 27 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 42 |
2 files changed, 55 insertions, 14 deletions
diff --git a/src/Fl_lock.cxx b/src/Fl_lock.cxx index 918f24413..bef9d972d 100644 --- a/src/Fl_lock.cxx +++ b/src/Fl_lock.cxx @@ -3,7 +3,7 @@ // // Multi-threading support code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2015 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -70,7 +70,6 @@ static const int AWAKE_RING_SIZE = 1024; static void lock_ring(); static void unlock_ring(); - /** Adds an awake handler for use in awake(). */ int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data) { @@ -80,33 +79,43 @@ int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data) awake_ring_size_ = AWAKE_RING_SIZE; awake_ring_ = (Fl_Awake_Handler*)malloc(awake_ring_size_*sizeof(Fl_Awake_Handler)); awake_data_ = (void**)malloc(awake_ring_size_*sizeof(void*)); + // explicitly initialize the head and tail indices + awake_ring_head_= awake_ring_tail_ = 0; + } + // The next head index we will want (not the current index): + // We use this to check if the ring-buffer is full or not + // (and to update awake_ring_head_ if we do use the current index.) + int next_head = awake_ring_head_ + 1; + if (next_head >= awake_ring_size_) { + next_head = 0; } - if (awake_ring_head_==awake_ring_tail_-1 || awake_ring_head_+1==awake_ring_tail_) { - // ring is full. Return -1 as an error indicator. + // check that the ring buffer is not full, and that it exists + if ((!awake_ring_) || (next_head == awake_ring_tail_)) { + // ring is non-existent or full. Return -1 as an error indicator. ret = -1; } else { awake_ring_[awake_ring_head_] = func; awake_data_[awake_ring_head_] = data; - ++awake_ring_head_; - if (awake_ring_head_ == awake_ring_size_) - awake_ring_head_ = 0; + awake_ring_head_ = next_head; } unlock_ring(); return ret; } + /** Gets the last stored awake handler for use in awake(). */ int Fl::get_awake_handler_(Fl_Awake_Handler &func, void *&data) { int ret = 0; lock_ring(); - if (!awake_ring_ || awake_ring_head_ == awake_ring_tail_) { + if ((!awake_ring_) || (awake_ring_head_ == awake_ring_tail_)) { ret = -1; } else { func = awake_ring_[awake_ring_tail_]; data = awake_data_[awake_ring_tail_]; ++awake_ring_tail_; - if (awake_ring_tail_ == awake_ring_size_) + if (awake_ring_tail_ >= awake_ring_size_) { awake_ring_tail_ = 0; + } } unlock_ring(); return ret; diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx index 378ef168d..0bcd2e102 100644 --- a/src/Fl_win32.cxx +++ b/src/Fl_win32.cxx @@ -3,7 +3,7 @@ // // WIN32-specific code for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2012 by Bill Spitzak and others. +// Copyright 1998-2015 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this @@ -350,6 +350,16 @@ extern int fl_send_system_handlers(void *e); MSG fl_msg; +// A local helper function to flush any pending callback requests +// from the awake ring-buffer +static void process_awake_handler_requests(void) { + Fl_Awake_Handler func; + void *data; + while (Fl::get_awake_handler_(func, data) == 0) { + func(data); + } +} + // This is never called with time_to_wait < 0.0. // It *should* return negative on error, 0 if nothing happens before // timeout, and >0 if any callbacks were done. This version only @@ -423,15 +433,37 @@ int fl_wait(double time_to_wait) { if (fl_msg.message == fl_wake_msg) { // Used for awaking wait() from another thread thread_message_ = (void*)fl_msg.wParam; - Fl_Awake_Handler func; - void *data; - while (Fl::get_awake_handler_(func, data)==0) - func(data); + process_awake_handler_requests(); } TranslateMessage(&fl_msg); DispatchMessageW(&fl_msg); } + + // The following conditional test: + // (Fl::awake_ring_head_ != Fl::awake_ring_tail_) + // is a workaround / fix for STR #3143. This works, but a better solution + // would be to understand why the PostThreadMessage() messages are not + // seen by the main window if it is being dragged/ resized at the time. + // If a worker thread posts an awake callback to the ring buffer + // whilst the main window is unresponsive (if a drag or resize operation + // is in progress) we may miss the PostThreadMessage(). So here, we check if + // there is anything pending in the awake ring buffer and if so process + // it. This is not strictly thread safe (for speed it compares the head + // and tail indices without first locking the ring buffer) but is intended + // only as a fall-back recovery mechanism if the awake processing stalls. + // If the test erroneously returns true (may happen if we test the indices + // whilst they are being modified) we will call process_awake_handler_requests() + // unnecessarily, but this has no harmful consequences so is safe to do. + // Note also that if we miss the PostThreadMessage(), then thread_message_ + // will not be updated, so this is not a perfect solution, but it does + // recover and process any pending awake callbacks. + // Normally the ring buffer head and tail indices will match and this + // comparison will do nothing. Addresses STR #3143 + if (Fl::awake_ring_head_ != Fl::awake_ring_tail_) { + process_awake_handler_requests(); + } + Fl::flush(); // This should return 0 if only timer events were handled: |
