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:
parent
335927ab90
commit
d0e5b00ea7
@ -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,34 +79,44 @@ 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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user