Update Fl::await() and friends API and documentation
This creates the base for #1263, but does not fix it yet.
This commit is contained in:
parent
3d13dfefa9
commit
eadea6a992
33
FL/Fl.H
33
FL/Fl.H
@ -346,12 +346,6 @@ public:
|
||||
static bool idle() { return (idle_ != nullptr); }
|
||||
|
||||
#ifndef FL_DOXYGEN
|
||||
private:
|
||||
static Fl_Awake_Handler *awake_ring_;
|
||||
static void **awake_data_;
|
||||
static int awake_ring_size_;
|
||||
static int awake_ring_head_;
|
||||
static int awake_ring_tail_;
|
||||
public:
|
||||
static const char* scheme_;
|
||||
static Fl_Image* scheme_bg_;
|
||||
@ -361,10 +355,6 @@ public:
|
||||
static int menu_linespacing_; // STR #2927
|
||||
#endif
|
||||
|
||||
|
||||
static int add_awake_handler_(Fl_Awake_Handler, void*);
|
||||
static int get_awake_handler_(Fl_Awake_Handler&, void*&);
|
||||
|
||||
public:
|
||||
|
||||
// API version number
|
||||
@ -1366,23 +1356,22 @@ public:
|
||||
cut/paste shortcut.
|
||||
*/
|
||||
static int dnd_text_ops() { return option(OPTION_DND_TEXT); }
|
||||
|
||||
// --- FLTK Multithreading support functions ---
|
||||
/** \defgroup fl_multithread Multithreading support functions
|
||||
fl multithreading support functions declared in <FL/Fl.H>
|
||||
@{ */
|
||||
|
||||
// Multithreading support:
|
||||
// Thread locking:
|
||||
static int lock();
|
||||
static void unlock();
|
||||
static void awake(void* message = 0);
|
||||
/** See void awake(void* message=0). */
|
||||
static int awake(Fl_Awake_Handler cb, void* message = 0);
|
||||
/**
|
||||
The thread_message() method returns the last message
|
||||
that was sent from a child by the awake() method.
|
||||
|
||||
See also: \ref advanced_multithreading
|
||||
*/
|
||||
static void* thread_message(); // platform dependent
|
||||
// Thread wakup and defered calls:
|
||||
static void awake();
|
||||
FL_DEPRECATED("since 1.5.0 - use Fl::awake() or Fl::awake(handler, user_data) instead",
|
||||
static void awake(void* message));
|
||||
static int awake(Fl_Awake_Handler handler, void* user_data=nullptr);
|
||||
static int awake_once(Fl_Awake_Handler handler, void* user_data=nullptr);
|
||||
FL_DEPRECATED("since 1.5.0 - use Fl::awake() or Fl::awake(handler, user_data) instead",
|
||||
static void* thread_message()); // platform dependent
|
||||
/** @} */
|
||||
|
||||
/** \defgroup fl_del_widget Safe widget deletion support functions
|
||||
|
||||
@ -161,50 +161,6 @@ with calls to Fl::lock() and Fl::unlock():
|
||||
To trigger a refresh of the GUI from a worker thread, the
|
||||
worker code should call Fl::awake()
|
||||
|
||||
<H3>Using Fl::awake thread messages</H3>
|
||||
You can send messages from worker threads to the \p main() thread
|
||||
using Fl::awake(void* message).
|
||||
If using this thread message interface, your \p main() might
|
||||
look like this:
|
||||
|
||||
\code
|
||||
int main(int argc, char **argv) {
|
||||
/* Create your windows and widgets here */
|
||||
|
||||
Fl::lock(); /* "start" the FLTK lock mechanism */
|
||||
|
||||
/* show your window */
|
||||
main_win->show(argc, argv);
|
||||
|
||||
/* start your worker threads */
|
||||
... start threads ...
|
||||
|
||||
/* Run the FLTK loop and process thread messages */
|
||||
while (Fl::wait() > 0) {
|
||||
if ((next_message = Fl::thread_message()) != NULL) {
|
||||
/* process your data, update widgets, etc. */
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
/* terminate any pending worker threads */
|
||||
... stop threads ...
|
||||
|
||||
return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
Your worker threads can send messages to the \p main() thread
|
||||
using Fl::awake(void* message):
|
||||
|
||||
\code
|
||||
void *msg; // "msg" is a pointer to your message
|
||||
Fl::awake(msg); // send "msg" to main thread
|
||||
\endcode
|
||||
|
||||
A message can be anything you like. The \p main() thread can retrieve
|
||||
the message by calling Fl::thread_message().
|
||||
|
||||
<H3>Using Fl::awake callback messages</H3>
|
||||
You can also request that the \p main() thread call a function on behalf of
|
||||
the worker thread by using Fl::awake(Fl_Awake_Handler cb, void* userdata).
|
||||
@ -245,19 +201,11 @@ consumed the data, thereby allowing the
|
||||
worker thread to re-use or update \p userdata.
|
||||
|
||||
\warning
|
||||
The mechanisms used to deliver Fl::awake(void* message)
|
||||
and Fl::awake(Fl_Awake_Handler cb, void* userdata) events to the
|
||||
\p main() thread can interact in unexpected ways on some platforms.
|
||||
Therefore, for reliable operation, it is advised that a program use
|
||||
either Fl::awake(Fl_Awake_Handler cb, void* userdata) or
|
||||
Fl::awake(void* message), but that they never be intermixed. Calling
|
||||
Fl::awake() with no parameters should be safe in either case.
|
||||
\par
|
||||
If you have to choose between using the Fl::awake(void* message)
|
||||
and Fl::awake(Fl_Awake_Handler cb, void* userdata) mechanisms and
|
||||
don't know which to choose, then try the
|
||||
Fl::awake(Fl_Awake_Handler cb, void* userdata) method first as it
|
||||
tends to be more powerful in general.
|
||||
The Fl::awake(void* message) call has been deprecated because the API was not
|
||||
sufficient to ensure the deliver of all message or the order of messages. The
|
||||
cal still exists for back compatibility, but should be repleaced with a call
|
||||
to Fl::awake(), Fl::awake(handler, user_data),
|
||||
or Fl::awake_once(handler, user_data).
|
||||
|
||||
\section advanced_multithreading_lockless FLTK multithreaded "lockless programming"
|
||||
|
||||
|
||||
@ -63,8 +63,20 @@ protected:
|
||||
// implement once for each platform
|
||||
static Fl_System_Driver *newSystemDriver();
|
||||
Fl_System_Driver();
|
||||
static bool awake_ring_empty();
|
||||
int filename_relative_(char *to, int tolen, const char *from, const char *base, bool case_sensitive);
|
||||
|
||||
// -- Awake handler stuff --
|
||||
public:
|
||||
static Fl_Awake_Handler *awake_ring_;
|
||||
static void **awake_data_;
|
||||
static int awake_ring_size_;
|
||||
static int awake_ring_head_;
|
||||
static int awake_ring_tail_;
|
||||
static bool awake_pending_;
|
||||
static int push_awake_handler(Fl_Awake_Handler, void*, bool once);
|
||||
static int pop_awake_handler(Fl_Awake_Handler&, void*&);
|
||||
static bool awake_ring_empty();
|
||||
|
||||
public:
|
||||
virtual ~Fl_System_Driver();
|
||||
static int command_key;
|
||||
|
||||
338
src/Fl_lock.cxx
338
src/Fl_lock.cxx
@ -1,7 +1,7 @@
|
||||
//
|
||||
// Multi-threading support code for the Fast Light Tool Kit (FLTK).
|
||||
//
|
||||
// Copyright 1998-2021 by Bill Spitzak and others.
|
||||
// Copyright 1998-2025 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
|
||||
@ -34,7 +34,6 @@
|
||||
Starting with 1.1.8, we now have a callback so that you can
|
||||
process awake() messages as they come in.
|
||||
|
||||
|
||||
The API:
|
||||
|
||||
Fl::lock() - recursive lock. You must call this before the
|
||||
@ -54,23 +53,47 @@
|
||||
Fl::awake() call, or returns NULL if none. WARNING: the
|
||||
current implementation only has a one-entry queue and only
|
||||
returns the most recent value!
|
||||
|
||||
From Matt:
|
||||
|
||||
25 years later, we have blazing fast CPUs with 24 cores and more.
|
||||
Fl::awake(void*) is no longer useful as "the last value" could have
|
||||
been overwritten by another thread before the main thread gets to it.
|
||||
Also, the ring buffer may potentially fill up much faster than expected,
|
||||
so I introduced Fl::awake_once(void (*cb)(void *), void*) which removes
|
||||
duplicate entries in the queue.
|
||||
*/
|
||||
|
||||
#ifndef FL_DOXYGEN
|
||||
Fl_Awake_Handler *Fl::awake_ring_;
|
||||
void **Fl::awake_data_;
|
||||
int Fl::awake_ring_size_;
|
||||
int Fl::awake_ring_head_;
|
||||
int Fl::awake_ring_tail_;
|
||||
|
||||
static constexpr int AWAKE_RING_SIZE = 1024;
|
||||
Fl_Awake_Handler *Fl_System_Driver::awake_ring_ = nullptr;
|
||||
void **Fl_System_Driver::awake_data_ = nullptr;
|
||||
int Fl_System_Driver::awake_ring_size_ = 0;
|
||||
int Fl_System_Driver::awake_ring_head_ = 0;
|
||||
int Fl_System_Driver::awake_ring_tail_ = 0;
|
||||
bool Fl_System_Driverawake_pending_ = false;
|
||||
|
||||
#endif
|
||||
|
||||
static const int AWAKE_RING_SIZE = 1024;
|
||||
|
||||
/** Adds an awake handler for use in awake(). */
|
||||
int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
|
||||
/**
|
||||
\brief Adds an awake handler for use in awake().
|
||||
|
||||
\internal Adds an awake handler for use in awake().
|
||||
|
||||
\param[in] func The function to call when the main thread is awake.
|
||||
\param[in] data The user data to pass to the function.
|
||||
\param[in] once If true, the handler will be added only once, removing any
|
||||
existing handler with the same function pointer and data pointer.
|
||||
\return 0 on success, -1 if the ring buffer is full.
|
||||
*/
|
||||
int Fl_System_Driver::push_awake_handler(Fl_Awake_Handler func, void *data, bool once)
|
||||
{
|
||||
int ret = 0;
|
||||
Fl::system_driver()->lock_ring();
|
||||
|
||||
// Allocate the ring buffers if we have not done so yet.
|
||||
if (!awake_ring_) {
|
||||
awake_ring_size_ = AWAKE_RING_SIZE;
|
||||
awake_ring_ = (Fl_Awake_Handler*)malloc(awake_ring_size_*sizeof(Fl_Awake_Handler));
|
||||
@ -78,6 +101,28 @@ int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
|
||||
// explicitly initialize the head and tail indices
|
||||
awake_ring_head_= awake_ring_tail_ = 0;
|
||||
}
|
||||
|
||||
// If we want to add the handler only once, go through the list of existing
|
||||
// handlers and remove any handler with the same function pointer
|
||||
// and data pointer.
|
||||
if (once) {
|
||||
int src = awake_ring_tail_;
|
||||
int dst = awake_ring_tail_;
|
||||
while (src != awake_ring_head_) {
|
||||
if ((awake_ring_[src] != func) || (awake_data_[src] != data)) {
|
||||
if (src != dst) {
|
||||
awake_ring_[dst] = awake_ring_[src];
|
||||
awake_data_[dst] = awake_data_[src];
|
||||
}
|
||||
dst++;
|
||||
if (dst >= awake_ring_size_) dst = 0; // wrap around
|
||||
}
|
||||
src++;
|
||||
if (src >= awake_ring_size_) src = 0; // wrap around
|
||||
}
|
||||
awake_ring_head_ = dst;
|
||||
}
|
||||
|
||||
// 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.)
|
||||
@ -85,21 +130,25 @@ int Fl::add_awake_handler_(Fl_Awake_Handler func, void *data)
|
||||
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.
|
||||
// check that the ring buffer is not full
|
||||
if (next_head == awake_ring_tail_) {
|
||||
// ring is 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_ = next_head;
|
||||
}
|
||||
|
||||
Fl::system_driver()->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)
|
||||
/**
|
||||
\brief Gets the last stored awake handler for use in awake().
|
||||
\internal Used in the main event loop when an Awake message is received.
|
||||
*/
|
||||
int Fl_System_Driver::pop_awake_handler(Fl_Awake_Handler &func, void *&data)
|
||||
{
|
||||
int ret = 0;
|
||||
Fl::system_driver()->lock_ring();
|
||||
@ -118,100 +167,177 @@ int Fl::get_awake_handler_(Fl_Awake_Handler &func, void *&data)
|
||||
}
|
||||
|
||||
/**
|
||||
Let the main thread know an update is pending and have it call a specific function.
|
||||
Registers a function that will be
|
||||
called by the main thread during the next message handling cycle.
|
||||
Returns 0 if the callback function was registered,
|
||||
and -1 if registration failed. Over a thousand awake callbacks can be
|
||||
registered simultaneously.
|
||||
|
||||
\see Fl::awake(void* message=0)
|
||||
*/
|
||||
int Fl::awake(Fl_Awake_Handler func, void *data) {
|
||||
int ret = add_awake_handler_(func, data);
|
||||
Fl::awake();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** \fn int Fl::lock()
|
||||
The lock() method blocks the current thread until it
|
||||
can safely access FLTK widgets and data. Child threads should
|
||||
call this method prior to updating any widgets or accessing
|
||||
data. The main thread must call lock() to initialize
|
||||
the threading support in FLTK. lock() will return non-zero
|
||||
if threading is not available on the platform.
|
||||
|
||||
Child threads must call unlock() when they are done
|
||||
accessing FLTK.
|
||||
|
||||
When the wait() method is waiting
|
||||
for input or timeouts, child threads are given access to FLTK.
|
||||
Similarly, when the main thread needs to do processing, it will
|
||||
wait until all child threads have called unlock() before processing
|
||||
additional data.
|
||||
|
||||
\return 0 if threading is available on the platform; non-zero
|
||||
otherwise.
|
||||
|
||||
See also: \ref advanced_multithreading
|
||||
*/
|
||||
/** \fn void Fl::unlock()
|
||||
The unlock() method releases the lock that was set
|
||||
using the lock() method. Child
|
||||
threads should call this method as soon as they are finished
|
||||
accessing FLTK.
|
||||
|
||||
See also: \ref advanced_multithreading
|
||||
*/
|
||||
/** \fn void Fl::awake(void* msg)
|
||||
Sends a message pointer to the main thread,
|
||||
causing any pending Fl::wait() call to
|
||||
terminate so that the main thread can retrieve the message and any pending
|
||||
redraws can be processed.
|
||||
|
||||
Multiple calls to Fl::awake() will queue multiple pointers
|
||||
for the main thread to process, up to a system-defined (typically several
|
||||
thousand) depth. The default message handler saves the last message which
|
||||
can be accessed using the
|
||||
Fl::thread_message() function.
|
||||
|
||||
In the context of a threaded application, a call to Fl::awake() with no
|
||||
argument will trigger event loop handling in the main thread. Since
|
||||
it is not possible to call Fl::flush() from a subsidiary thread,
|
||||
Fl::awake() is the best (and only, really) substitute.
|
||||
|
||||
It's \e not necessary to wrap calls to any form of Fl::awake() by Fl::lock() and Fl::unlock().
|
||||
Nevertheless, the early, single call to Fl::lock() used to initialize threading support is necessary.
|
||||
|
||||
Function Fl::awake() in all its forms is typically called by worker threads, but it can be used safely
|
||||
by the main thread too, as a means to break the event loop.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
*/
|
||||
|
||||
void Fl::awake(void *v) {
|
||||
Fl::system_driver()->awake(v);
|
||||
}
|
||||
|
||||
void* Fl::thread_message() {
|
||||
return Fl::system_driver()->thread_message();
|
||||
}
|
||||
|
||||
int Fl::lock() {
|
||||
return Fl::system_driver()->lock();
|
||||
}
|
||||
|
||||
void Fl::unlock() {
|
||||
Fl::system_driver()->unlock();
|
||||
}
|
||||
|
||||
#ifndef FL_DOXYGEN
|
||||
|
||||
\brief Checks if the awake ring buffer is empty.
|
||||
\internal Used in the main event loop when an Awake message is received.
|
||||
*/
|
||||
bool Fl_System_Driver::awake_ring_empty() {
|
||||
Fl::system_driver()->lock_ring();
|
||||
bool retval = (Fl::awake_ring_head_ == Fl::awake_ring_tail_);
|
||||
bool retval = (awake_ring_head_ == awake_ring_tail_);
|
||||
Fl::system_driver()->unlock_ring();
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif // FL_DOXYGEN
|
||||
/**
|
||||
\brief Notifies the main GUI thread from a worker thread.
|
||||
|
||||
In FLTK, worker threads can update the UI, but all UI changes must be wrapped
|
||||
between Fl::lock() and Fl::unlock(). After calling Fl::unlock(), the worker
|
||||
thread should call Fl::awake() to signal the main thread that
|
||||
updates are pending.
|
||||
|
||||
\note Worker threads must not create, show, or hide windows.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
\see Fl::awake(Fl_Awake_Handler, void*)
|
||||
\see Fl::awake_once(Fl_Awake_Handler, void*)
|
||||
*/
|
||||
void Fl::awake() {
|
||||
Fl::system_driver()->awake(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Awake the main GUI thread and leave a message pointer.
|
||||
|
||||
\deprecated Use Fl::awake() or Fl::awake(Fl_Awake_Handler, void*) instead.
|
||||
|
||||
This method is deprecated. The API can not ensure that Fl::thread_message()
|
||||
returns the messages sent by Fl::awake(void *v) complete and in the correct
|
||||
order.
|
||||
|
||||
Use Fl::awake() instead if you do not need to send a specific message.
|
||||
Use Fl::awake(Fl_Awake_Handler, void*) or Fl::awake_once(Fl_Awake_Handler, void*)
|
||||
if you need to send a message to the main thread and ensure that all messages
|
||||
are processed in the order they were sent.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
\see Fl::awake()
|
||||
\see Fl::awake(Fl_Awake_Handler, void*)
|
||||
\see Fl::awake_once(Fl_Awake_Handler, void*)
|
||||
*/
|
||||
void Fl::awake(void *v) {
|
||||
Fl::system_driver()->awake(v);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Schedules a callback to be executed by the main thread, then wakes up the main thread.
|
||||
|
||||
This function lets a worker thread request that a specific callback function
|
||||
be run by the main thread, passing optional user data. The callback will be
|
||||
executed during the main thread's next event handling cycle.
|
||||
|
||||
The queue holding the list of handlers is limited to 1024 entries.
|
||||
If the queue is full, the function will return -1 and the callback will not be
|
||||
scheduled. However the main thread will still be woken up to process any
|
||||
other pending events.
|
||||
|
||||
\note If user_data points to dynamically allocated memory, it is the
|
||||
responsibility of the caller to ensure that the memory is valid until the
|
||||
callback is executed. The callback will be executed during the main thread's
|
||||
next event handling cycle, but depending on the sytems load, this may take
|
||||
several seconds.
|
||||
|
||||
\return 0 if the callback was successfully scheduled
|
||||
\return -1 if the queue is full.
|
||||
|
||||
\see Fl::awake()
|
||||
\see Fl::awake_once(Fl_Awake_Handler, void*)
|
||||
\see \ref advanced_multithreading
|
||||
*/
|
||||
int Fl::awake(Fl_Awake_Handler handler, void *user_data) {
|
||||
int ret = Fl_System_Driver::push_awake_handler(handler, user_data, false);
|
||||
Fl::awake();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Schedules a callback to be executed once by the main thread, then wakes up the main thread.
|
||||
|
||||
This function lets a worker thread request that a specific callback function
|
||||
be run by the main thread, passing optional user data. If a callback with the
|
||||
same user_data is already scheduled, the previous entry will be removed and
|
||||
the new entry will be appended to the list.
|
||||
|
||||
\return 0 if the callback was successfully scheduled
|
||||
\return -1 if the queue is full.
|
||||
|
||||
\see Fl::awake()
|
||||
\see Fl::awake(Fl_Awake_Handler, void*)
|
||||
\see \ref advanced_multithreading
|
||||
*/
|
||||
int Fl::awake_once(Fl_Awake_Handler handler, void *user_data) {
|
||||
// TODO: remove any previous entry with the same handler and user_data
|
||||
int ret = Fl_System_Driver::push_awake_handler(handler, user_data, true);
|
||||
Fl::awake();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the last message sent by a child thread.
|
||||
|
||||
\deprecated Use Fl::awake(Fl_Awake_Handler, void*) or
|
||||
Fl::awake_once(Fl_Awake_Handler, void*) instead.
|
||||
|
||||
The thread_message() method returns the last message
|
||||
that was sent from a child by the Fl::awake(void*) method.
|
||||
|
||||
This method is deprecated. The API can not ensure that Fl::thread_message()
|
||||
returns the messages sent by Fl::awake(void *v) complete and in the correct
|
||||
order.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
\see Fl::awake()
|
||||
\see Fl::awake(Fl_Awake_Handler, void*)
|
||||
\see Fl::awake_once(Fl_Awake_Handler, void*)
|
||||
*/
|
||||
void* Fl::thread_message() {
|
||||
return Fl::system_driver()->thread_message();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
\brief Acquire the global UI lock for FLTK.
|
||||
|
||||
The lock() method blocks the current thread until it
|
||||
can safely access FLTK widgets and data. Child threads should
|
||||
call this method prior to updating any widgets or accessing
|
||||
data. The main thread must call Fl::lock() once before any windows are shown
|
||||
to initialize the threading support in FLTK. The initial Fl::lock() call
|
||||
will return non-zero if threading is not available on the platform.
|
||||
|
||||
Child threads enclose calls to FLTK functions between Fl::lock() and
|
||||
Fl::unlock() accessing FLTK. When a child thread has finshed accessing FLTK
|
||||
and wants the main thread to update the UI, it should call Fl::awake().
|
||||
|
||||
Child threads can never create, show, or hide windows.
|
||||
|
||||
When the wait() method is waiting
|
||||
for input or timeouts, child threads are given access to FLTK.
|
||||
Similarly, when the main thread needs to do processing, it will
|
||||
wait until all child threads have called unlock() before processing
|
||||
additional data.
|
||||
|
||||
\return 0 if threading is available on the platform; non-zero
|
||||
otherwise.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
\see Fl::lock()
|
||||
\see Fl::awake()
|
||||
*/
|
||||
int Fl::lock() {
|
||||
return Fl::system_driver()->lock();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Release the global UI lock set by Fl::lock().
|
||||
|
||||
The unlock() method releases the lock that was set using the lock() method.
|
||||
Child threads should call this method as soon as they are finished
|
||||
accessing FLTK.
|
||||
|
||||
\see \ref advanced_multithreading
|
||||
\see Fl::lock()
|
||||
*/
|
||||
void Fl::unlock() {
|
||||
Fl::system_driver()->unlock();
|
||||
}
|
||||
|
||||
|
||||
@ -350,7 +350,7 @@ MSG fl_msg;
|
||||
static void process_awake_handler_requests(void) {
|
||||
Fl_Awake_Handler func;
|
||||
void *data;
|
||||
while (Fl::get_awake_handler_(func, data) == 0) {
|
||||
while (Fl::pop_awake_handler(func, data) == 0) {
|
||||
func(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,7 +387,7 @@ static void thread_awake_cb(int fd, void*) {
|
||||
}
|
||||
Fl_Awake_Handler func;
|
||||
void *data;
|
||||
while (Fl::get_awake_handler_(func, data)==0) {
|
||||
while (Fl_System_Driver::pop_awake_handler(func, data)==0) {
|
||||
(*func)(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,10 +87,8 @@ extern "C" void* prime_func(void* p)
|
||||
Fl::unlock();
|
||||
|
||||
// Send a message to the main thread, at which point it will
|
||||
// process any pending redraws for our browser widget. The
|
||||
// message we pass here isn't used for anything, so we could also
|
||||
// just pass NULL.
|
||||
Fl::awake(p);
|
||||
// process any pending redraws for our browser widget.
|
||||
Fl::awake();
|
||||
if (n>10000 && !proud) {
|
||||
proud = 1;
|
||||
Fl::awake(magic_number_cb, value);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user