WIN32 specific fix for (or at least work around to) STR #3143.

In testing, this resolves the reported issue, but I'd be happy if we could find a
solution that resolved the underlying issue of us missing PostThreadMessage()
messages passed from the worker thread to the main thread, whilst the main window
is unresponsive (i.e. moving or dragging.)

This also puts in place an amendment to the way the awake callback ring-buffer indices
are tested, when the buffer is wrapping over or near to full. This was identified by 
Albrecht in STR #3223 (item #1 on that STR, though there are a few other issues 
identified there.)

In my testing, this appears to be correct and robust. Further testing would not go
amiss, however.


git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10714 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Ian MacArthur 2015-04-23 10:11:23 +00:00
parent 335927ab90
commit d0e5b00ea7
2 changed files with 55 additions and 14 deletions

View File

@ -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;
}
if (awake_ring_head_==awake_ring_tail_-1 || awake_ring_head_+1==awake_ring_tail_) {
// ring is full. Return -1 as an error indicator.
// 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;
}
// 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;

View File

@ -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: