You have to call Fl::lock() (without a corresponding
Fl::unlock())
just before calling Fl::run() to enable fltk locking.
As weird as it seems to have an Fl::lock() without a
corresponding Fl::unlock(),
in the context of the main() thread, this enables FLTK's app loop
our app uses locking.
I was chasing my tail cause the UI wasn't updating unless I moved
the mouse
over the window; apparently that's the weird behavior one sees (on
MS Windows
anyway) if you don't include that Fl::lock() before calling
Fl::run().
I'm probably going to add a code example that makes this perfectly
clear
in the docs for Fl::lock() and Fl::awake(), as apparently many
folks miss this.
The reason I missed it is I looked through the docs for Fl::lock()
and it didn't make
sense to call Fl::lock() without an unlock; it seemed like a
mistake. I went to the main
doc page and did a ^G search for "thread", didn't see any hits for
document on threading,
so thought "well I guess we don't have one", but because our
threading docs are "hidden"
in the side bar when closed, firefox's ^G doesn't find it. And
doxygen docs don't have a
"search" bar, so.. it went unnoticed.
Anyway, here's a pure Win32 example I just got working that
demonstrates a Win32 child
thread using WIN32's CreateThread() call; FLTK lock related calls
in green, WIN32 threading
related calls in magenta:
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <windows.h>
#include <string.h>
// Demonstrate use of threads with FLTK + WIN32 API -erco 4/16/21
Fl_Window *G_win = 0;
Fl_Box *G_box = 0;
DWORD WINAPI MyChildThread(void*) { // Child thread to changes box's label each sec
int i = 0;
while ( 1 ) { // FOREVER
i++; // lock FLTK calls from child thread
Fl::lock(); // lock FLTK calls from child thread
/*LOCK*/ switch ( i % 3 ) { // every second a different letter
/*LOCK*/ case 0: G_box->label("A"); break;
/*LOCK*/ case 1: G_box->label("B"); break;
/*LOCK*/ case 2: G_box->label("C"); break;
/*LOCK*/ }
/*LOCK*/ G_box->redraw();
Fl::unlock();
Fl::awake(); // tells fltk app loop we made a change
Sleep(1000); // WIN32 sleep for 1000 msecs (1sec)
}
}
int main(int argc, char **argv) {
G_win = new Fl_Window(200, 200, "Thread Test");
G_box = new Fl_Box(20,20,100,100,"-");
G_box->labelsize(24);
G_win->end();
G_win->show();
Fl::lock(); // <-- IMPORTANT: Enables FLTK's main thread to use locking.
// Looks weird there's no companion Fl::unlock(), but this
// tells FLTK to enable the use of locking. Expect weird behavior without it!
//
CreateThread(0,0,MyChildThread,0,0,0); // start child thread that change box's contents every second
return Fl::run();
}
First time I decided to use Fl::lock() instead of messaging, and hit a snare
I think many folks do when first using FLTK with threads and Fl::lock():You have to call Fl::lock() (without a corresponding Fl::unlock())
just before calling Fl::run() to enable fltk locking.
As weird as it seems to have an Fl::lock() without a corresponding Fl::unlock(),
in the context of the main() thread, this enables FLTK's app loop our app uses locking.
I was chasing my tail cause the UI wasn't updating unless I moved the mouse
over the window; apparently that's the weird behavior one sees (on MS Windows
anyway) if you don't include that Fl::lock() before calling Fl::run().
I'm probably going to add a code example that makes this perfectly clear
in the docs for Fl::lock() and Fl::awake(), as apparently many folks miss this.
The reason I missed it is I looked through the docs for Fl::lock() and it didn't make
sense to call Fl::lock() without an unlock; it seemed like a mistake. I went to the main
doc page and did a ^G search for "thread", didn't see any hits for document on threading,
so thought "well I guess we don't have one", but because our threading docs are "hidden"
in the side bar when closed, firefox's ^G doesn't find it. And doxygen docs don't have a
"search" bar, so.. it went unnoticed.
Anyway, here's a pure Win32 example I just got working that demonstrates a Win32 child
thread using WIN32's CreateThread() call; FLTK lock related calls in green, WIN32 threading
related calls in magenta:
On 17 Apr 2021, at 06:48, Greg Ercolano wrote:
First time I decided to use Fl::lock() instead of messaging, and hit a snare
I think many folks do when first using FLTK with threads and Fl::lock():
You have to call Fl::lock() (without a corresponding Fl::unlock())
just before calling Fl::run() to enable fltk locking.
Hi Greg,
This is the point where (for general users, anyway!) I’d ask whether they had checked the docs...
Ya, the trouble is (in my case) it just looked so wrong, I
honestly thought it a mistake in the docs.
I remember seeing this in the docs and just thinking "this has
got to be worded incorrectly,
you'd never leave a lock open while entering the application
loop. Locks are simple on/off
things and you never leave them on. A lock without
an unlock is an error".
It was just inconceivable to me (or I think anyone
coming at this for the first time)
that there's intentionally no unlock. That breaks a
lot of well-honed braincells to have
intentionally leave a lock open, esp. before entering app
loop.
From the app programmer's perspective, you're automatically
looking for that unlock..
"If we're locking this section of code, where do we unlock
it"?
It's just confusing.
which is why in fltkcoredev I made an RFC recommending we make
an Fl::enable_lock(),
so that apps can use that instead, so the code is self
commenting.
As it is now, my code looks like this, to warn others picking
up my work not to
fix what looks like a bug:
The problem is: I shouldn't have to do this; the code should read like it operates.Fl::lock(); // IMPORANT: enables locking in FLTK - no companion unlock here is not a bug!
return Fl::run();
}
Fl::lock() ..and thinking "What does he mean?
That can't be right."
Basically I thought the world must be wrong :^D
There's no way one should leave a lock open.
The brain cells are very strongly wired to reject what it
thinks is noise, even in the docs.
Well, If you really want to balance them, you probably can, but no one ever does...It would look like this:
Fl::lock(); // Acquire the fltk master lock
int res = Fl::run();
Fl::unlock();return res;}
That'd be unclear.
I’d *strongly* caution against using CreateThread(), it has some weirdnesses and does not always play well with the MS CRT, so if your code is “mostly posix like” (which mine is, and I’d guess yours too) then I’d advocate powerfully for using _beginthread() or _beginthreadex() instead.
A thread in an executable that calls the C run-time library (CRT) should use the _beginthread and _endthread functions for thread management rather than CreateThread and ExitThread; this requires the use of the multi-threaded version of the CRT.So I guess I'll switch to that.
Albrecht and I have poked at threading examples at various times, but thus far anyway, never committed one - there was an idea we might put something in the examples folder at some point, but...If we do, I’d *not* vote in favour of an example that uses CreateThread() FWIW...
And of course, as I have mentioned in the past, using the lock() in the child threads is a last resort anyway, as it inherently serializes the threads and thus prevents fully multithreaded operation... Fl::awake(with callback) is better for concurrent operation, in general.
--
You received this message because you are subscribed to the Google Groups "fltk.general" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkgeneral...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkgeneral/444f7f41-88ac-7cce-182f-0889f552300e%40seriss.com.