From 75f3bdfa55b8d3ed2a96cbedfff82515428f5388 Mon Sep 17 00:00:00 2001 From: Albrecht Schlosser Date: Mon, 14 Jul 2025 17:00:23 +0200 Subject: [PATCH] Improve test/threads demo program (backported from 1.5) - use Fl_Terminal instead of Fl_Browser for prime display - add Fl::lock()/Fl::unlock() at thread initialization - format "Max Prime" output as integer number - use a fixed font for better readability - close all windows when the user closes one of them - use standard background and text colors Note: This commit is a compromise. The version in FLTK 1.5 uses std::vector<> and can't be fully backported to 1.4. --- test/threads.cxx | 159 +++++++++++++++++++++++++++++------------------ 1 file changed, 98 insertions(+), 61 deletions(-) diff --git a/test/threads.cxx b/test/threads.cxx index f7349a668..67ec67a20 100644 --- a/test/threads.cxx +++ b/test/threads.cxx @@ -1,7 +1,7 @@ // // Threading example program for the Fast Light Tool Kit (FLTK). // -// Copyright 1998-2018 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 @@ -19,8 +19,8 @@ #if defined(HAVE_PTHREAD) || defined(_WIN32) # include # include -# include -# include +# include +# include # include # include "threads.h" # include @@ -28,34 +28,39 @@ Fl_Thread prime_thread; -Fl_Browser *browser1, *browser2; -Fl_Value_Output *value1, *value2; +Fl_Terminal *tty1, *tty2; +Fl_Output *value1, *value2; int start2 = 3; -void magic_number_cb(void *p) -{ - Fl_Value_Output *w = (Fl_Value_Output*)p; +void magic_number_cb(void *p) { + Fl_Output *w = (Fl_Output*)p; w->labelcolor(FL_RED); w->redraw_label(); + // if (w == value1) Fl::hide_all_windows(); // TEST: terminate early to measure time } -extern "C" void* prime_func(void* p) -{ - Fl_Browser* browser = (Fl_Browser*) p; - Fl_Value_Output *value; - int n; +extern "C" void* prime_func(void* p) { + Fl_Terminal* terminal = (Fl_Terminal*)p; + Fl_Output *value; + unsigned long n; + unsigned long max_value = 0; int step; char proud = 0; + char maxbuf[32]; - if (browser == browser2) { - n = start2; + // initialize thread variables + + if (terminal == tty2) { // multiple threads + Fl::lock(); // lock to prevent race condition on `start2` + n = start2; start2 += 2; - step = 12; - value = value2; - } else { - n = 3; - step = 2; - value = value1; + Fl::unlock(); + step = 12; + value = value2; + } else { // single thread + n = 3; + step = 2; + value = value1; } // very simple prime number calculator ! @@ -63,35 +68,45 @@ extern "C" void* prime_func(void* p) // The return at the end of this function can never be reached and thus // will generate a warning with some compilers, however we need to have // a return statement or other compilers will complain there is no return - // statement. To avoid warnings on all compilers, we fool the smart ones - // into beleiving that there is a chance that we reach the end by testing - // n>=0, knowing that logically, n will never be negative in this context. - if (n>=0) for (;;) { + // statement. To avoid warnings on all compilers, we fool the smart ones into + // believing that there is a chance that we reach the end by testing n > 0, + // knowing that logically, n will never be less than 3 in this context. + + if (n > 0) for (;;) { int pp; int hn = (int)sqrt((double)n); - for (pp=3; pp<=hn; pp+=2) if ( n%pp == 0 ) break; - if (pp >= hn) { - char s[128]; - snprintf(s, 128, "%d", n); + for (pp = 3; pp <= hn; pp += 2) { + if (n % pp == 0) + break; + } + + if (pp > hn) { // n is a prime + + // Obtain a lock before we access the terminal widget... + // Note: locking the GUI for each prime number is not recommended. + // A better (more efficient) solution has been implemented in FLTK 1.5 - // Obtain a lock before we access the browser widget... Fl::lock(); - browser->add(s); - browser->bottomline(browser->size()); - if (n > value->value()) value->value(n); - n += step; + terminal->printf("prime: %10u\n", n); + if (n > max_value) { + max_value = n; + snprintf(maxbuf, sizeof(maxbuf), "%9lu", max_value); + value->value(maxbuf); + } // Release the lock... 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); - if (n>10000 && !proud) { + // Send a message to the main thread, at which point it will process + // any pending redraws for our terminal widget. + + Fl::awake(); + + n += step; + + if (n > 2 * 1000 * 1000 && !proud) { proud = 1; Fl::awake(magic_number_cb, value); } @@ -107,42 +122,64 @@ extern "C" void* prime_func(void* p) return 0L; } +// close all windows when the user closes one of the windows + +void close_cb(Fl_Widget *w, void *v) { + Fl::hide_all_windows(); + printf("Max prime number with 1 thread : %s\n", value1->value()); + printf("Max prime number with 6 threads: %s\n", value2->value()); + return; +} + int main(int argc, char **argv) { + // First window: single thread Fl_Double_Window* w = new Fl_Double_Window(200, 200, "Single Thread"); - browser1 = new Fl_Browser(0, 0, 200, 175); - w->resizable(browser1); - value1 = new Fl_Value_Output(100, 175, 200, 25, "Max Prime:"); + tty1 = new Fl_Terminal(0, 0, 200, 175); + tty1->color(FL_BACKGROUND2_COLOR); + tty1->textcolor(FL_FOREGROUND_COLOR); + w->resizable(tty1); + value1 = new Fl_Output(100, 175, 98, 23, "Max Prime:"); + value1->textfont(FL_COURIER); + w->callback(close_cb); w->end(); w->show(argc, argv); + + // Second window: multiple threads w = new Fl_Double_Window(200, 200, "Six Threads"); - browser2 = new Fl_Browser(0, 0, 200, 175); - w->resizable(browser2); - value2 = new Fl_Value_Output(100, 175, 200, 25, "Max Prime:"); + tty2 = new Fl_Terminal(0, 0, 200, 175); + tty2->color(FL_BACKGROUND2_COLOR); + tty2->textcolor(FL_FOREGROUND_COLOR); + w->resizable(tty2); + value2 = new Fl_Output(100, 175, 98, 23, "Max Prime:"); + value2->textfont(FL_COURIER); + w->callback(close_cb); w->end(); w->show(); - browser1->add("Prime numbers:"); - browser2->add("Prime numbers:"); + tty1->printf("Prime numbers:\n"); + tty2->printf("Prime numbers:\n"); + + // Enable multi-thread support by locking from the main thread. + // Fl::wait() and Fl::run() call Fl::unlock() and Fl::lock() as needed + // to release control to the child threads when it is safe to do so... - // Enable multi-thread support by locking from the main - // thread. Fl::wait() and Fl::run() call Fl::unlock() and - // Fl::lock() as needed to release control to the child threads - // when it is safe to do so... Fl::lock(); // Start threads... - // One thread displaying in one browser - fl_create_thread(prime_thread, prime_func, browser1); + // One thread displaying in one terminal - // Several threads displaying in another browser - fl_create_thread(prime_thread, prime_func, browser2); - fl_create_thread(prime_thread, prime_func, browser2); - fl_create_thread(prime_thread, prime_func, browser2); - fl_create_thread(prime_thread, prime_func, browser2); - fl_create_thread(prime_thread, prime_func, browser2); - fl_create_thread(prime_thread, prime_func, browser2); + fl_create_thread(prime_thread, prime_func, tty1); + + // Six threads displaying in another terminal + + fl_create_thread(prime_thread, prime_func, tty2); + fl_create_thread(prime_thread, prime_func, tty2); + fl_create_thread(prime_thread, prime_func, tty2); + fl_create_thread(prime_thread, prime_func, tty2); + fl_create_thread(prime_thread, prime_func, tty2); + fl_create_thread(prime_thread, prime_func, tty2); Fl::run();