Using threads on MS Windows with fltk

84 views
Skip to first unread message

Greg Ercolano

unread,
Apr 17, 2021, 1:48:04 AM4/17/21
to fltkg...@googlegroups.com
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:

#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();
}


Ian MacArthur

unread,
Apr 17, 2021, 4:05:57 AM4/17/21
to fltkg...@googlegroups.com
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...



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.



Well, If you really want to balance them, you probably can, but no one ever does...
It would look like this:

int main (blah)
{
  // Initial app stuff

  Fl::lock(); // Acquire the fltk master lock
  
  // Create Windows etc.

  int res = Fl::run();

  // Wait for child threads to expire

  Fl::unlock(); // Can not release the master lock until we know all the child threads are done
  return res;
} // end of main

So the unlock() only occurs as the application terminates, it can not safely be done any sooner and so is not really value adding here...


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().


That specific MS Windows behaviour is because of the way the event loop is driving things on that platform...


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:


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.
Also, with a bit of creative casting you can *almost* make _beginthread() and pthread_create() be “the same” so it is easier to use portably across targets.

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.






Albrecht Schlosser

unread,
Apr 17, 2021, 7:49:33 AM4/17/21
to fltkg...@googlegroups.com
On 4/17/21 10:05 AM Ian MacArthur wrote:
>
> 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.
> Also, with a bit of creative casting you can *almost* make
> _beginthread() and pthread_create() be “the same” so it is easier to use
> portably across targets.

We have such an example in our test/threads.cxx demo and particularly
the simple threading helper test/threads.h

Greg Ercolano

unread,
Apr 17, 2021, 11:55:45 AM4/17/21
to fltkg...@googlegroups.com


On 4/17/21 1:05 AM, Ian MacArthur wrote:
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:

        Fl::lock();        // IMPORANT: enables locking in FLTK - no companion unlock here is not a bug!
        return Fl::run();
    }

    The problem is: I shouldn't have to do this; the code should read like it operates.
    I think many will not get that Fl::lock() might possibly be overloaded in purpose
    to also act as a flag to FLTK to 'enable locking'.

    I think many would think: who would overload the simple lock()/unlock() concept?

    Others have ran into this; it's not just me:
    https://stackoverflow.com/questions/8069177/how-to-use-flawake-in-the-fltk-library

    I remember reading bcdan's 2017 comment:

        Slightly unrelated, but let me add: It is extremely important to call Fl::lock()
        in the main function.
I was trying to write a program without that call, and it

        did not work due to contention.

    ..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;
}

    But see that still looks wrong; leaving the lock on entry to the app loop
    seems like its purpose is to lock the child threads from doing anything
    during the app loop.

    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.

    Mmm, thanks -- I saw that in the threads.h example and was wondering what that was all about.
    I thought "Damn, not another underbar function from microsoft!")

    I've actually had no trouble with CreateThread(); been using it for years in cross platform FLTK
    apps that get a lot of use, and never had trouble with it. It handles pipes for stdout/error from
    other processes and appends the text to a locked array that is read by a timer in the main FLTK thread.

    Perhaps the way I've been using it never tripped up on the C library stuff because I was using
    pure WIN32 within the thread.

    Focusing on that function, I see what you mean:
    https://social.msdn.microsoft.com/Forums/vstudio/en-US/c727ae29-5a7a-42b6-ad0b-f6b21c1180b2/createthread-vs-beginthreadex?forum=vclanguage

    ..there's a reference to a dead knowledgebase article (that's unfortunately 404 and is not even on archive.org), but on MSDN it apparently says:
 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.

    Weird though -- one of the few things in the WIN32 API I thought worked well and was simple
    to work with was CreateThread(). Of course it has complications.. ugh.




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...

    Right, I can see why. Seems CreateThread() is too low level to be compatible with thread safety
    in the C library.


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.

    Ya, I may change my design to not use it as this app I'm working on evolves. It's an "all windows" app,
    so I'm free to use pure WIN32 Api.  Using Fl::lock() is a good starting point for a new app, but yeah,
    I prefer messages and not doing ANY fltk stuff in child threads, which is probably the direction I'll be
    going once I get past the initial coding stage.

    Thanks for the leads!

    I still think we should make an Fl::lock_enable() function and get away from overloading Fl::lock()
    to also be 'a setting'.

Bill Spitzak

unread,
Apr 17, 2021, 2:12:55 PM4/17/21
to fltkg...@googlegroups.com
Yes it's more simple: you have to have the lock in order to call most fltk functions, including run().

There are two "hacks" in fltk:

1. You don't need to call unlock() before exiting the program (this is pretty common with mutexes)

2. It checks before calling unlock() internally so it does not crash if the program did not call lock().


--
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.
Reply all
Reply to author
Forward
0 new messages