How to do realtime data display in FLTK

3.104 Aufrufe
Direkt zur ersten ungelesenen Nachricht

Albrecht Schlosser

ungelesen,
29.05.2013, 09:24:4729.05.13
an fltkg...@googlegroups.com
I'm going to try to write down some design thoughts about realtime
display and how to design the GUI with thread support to solve problems
seen in the priidash2 application (see thread "fl_create_offscreen
compiler error: 'Fl' has not been declared ?").

The problem is how to integrate processing data from a serial line
("realtime data") in the GUI and still have a responsive GUI and display
realtime data in a predictable and feasible manner.

Note that I don't want to define "realtime" in any way. Here in this
context it should mean: we get processing data in an unpredictable way
(timing) from an external data source like a serial line. The main
problem is that reading from the serial line with blocking reads shall
not block the GUI, but updating the GUI with the realtime data is a slow
operation and shall not block reading from the serial line either.
Although reading the serial data can be slow (you don't "know" when the
next byte will arrive), it is time-critical, since data reception can be
very fast, and (hardware) buffering can be limited. In the priidash2
context, it was mentioned that the serial I/O bit rate should be 1.5 or
2 MBit/s. This is pretty fast, and even with a hardware FIFO reading
from the serial line should never be blocked by anything else, so this
must be done in an independent thread.

FLTK requires that modifying widgets from a thread other than the main
GUI thread that calls Fl::run() must be calling Fl::lock() to acquire
the FLTK lock, but this can block the thread. So this can't be done from
the serial data reading thread - otherwise data loss would be possible.
The solution would be another thread that processes the data read from
the serial line thread. These two threads would have to be synchronized
as well, if they were to access shared data, but these locks could be
really short, so that this wouldn't matter much. Note that there may be
thread-safe ring buffer concepts that don't need to lock the shared data
(one writer thread "owns" the write pointer, one reader thread "owns"
the read pointer).

So we may end up having three thread levels:

- level A = time-critical, but maybe slow, blocking realtime data reading

- level B = not time-critical data processing, feeding the FLTK
widgets with data

- level C = GUI (the main thread).

FLTK "internals" that need to be known:

In a multi-threaded application FLTK holds a lock all the time while
processing system messages (events) and drawing data. FLTK does this in
its main loop (Fl::run()), as long as messages (events) are in the
system message queue. Only when there are no more messages, FLTK draws
all widgets that need to be drawn (by calling Fl::flush() internally),
and then it releases the lock and waits for more events. This is done
w/o polling, i.e. waiting for some system event that is triggered when a
message is put into the message queue, a timer event or something else.

A thread that called Fl::lock() would have to wait until FLTK is ready
processing events and drawing, before it can acquire the lock. Calling
Fl::awake() sends a message to the FLTK message queue (the
implementation is different on different platforms, but that's what is
done essentially). Now FLTK wakes up, calls Fl::lock() internally, and
message processing and drawing starts. Note that one thread or multiple
threads can send many Fl::awake() messages while they are holding the
lock, but FLTK can only start over after the thread holding the lock
releases it. However, threads can call Fl::awake() also w/o holding the
FLTK lock.

GUI event processing, callbacks, and threads:

The nature of FLTK event processing is that all events are serialized.
The system puts events (mouse move, click, keyboard, elapsed timers)
into the message queue, and FLTK handles the events. Eventually a
widget's handle() method does this for a widget, and if requested, a
callback is called for the event. This callback should be handled in the
callback function very quickly, and the callback should return as soon
as possible, since it is done during event processing and locks out all
other events until "this event" has been processed. That precludes
long-running functions like reading data from a serial line, but also
from a file for replay or simulation of real work. For a limited time
(maybe a few seconds) it would usually be feasible to call event
processing functions *during* the callback by calling Fl::check() every
now and then, but it may be difficult to find the best points when to do
this. One example are modal popup windows that do user interaction like
fl_ask(), fl_message() or similar, but this is OT here. Also, reading a
file and writing to another file may be okay - isn't there a function to
convert a log file to csv in priidash2? This could maybe be done in a
callback, if Fl::check() was called regularly. By calling Fl::check(),
buttons would be responsive, so that a cancel button could be effective.

GUI and thread management:

Long-running tasks like realtime data display or data simulation could
better be done by starting one or more threads from a callback ("start
button"). When done so, the callback would be responsible to change the
GUI to reflect the new state ("running..."), enable and disable buttons
(enable the stop button, disable other buttons), create/show/hide
windows, and then start the worker thread(s). After that the callback
returns, and normal event processing continues.

The mentioned "stop button" should then do a callback that shuts the
worker thread down gracefully. This can usually done by setting a stop
flag in global data and waiting for the thread to stop, or by killing
the threads. This can be accompanied by a popup window that says
something like "waiting for simulation to stop" (not covered here, how
this can be done).

Then, after the worker thread(s) stopped, the callback would restore the
windows and buttons to "normal" mode and return.

Threads feeding FLTK widgets with data:

This is the main problem, because it must be done "right" - otherwise
the display of data and behavior of the program may be wrong (skipping
data, slow, program appears to hang, user interaction impossible, ...).

We should distinguish two different situations: I may call them "slow"
and "realtime" applications. They may need to be handled differently to
get the best results.

Let's talk about "slow" applications first. This can be something like a
process temperature display or similar. Whenever one temperature
changes, the display shall show this. Temperatures usually change slow,
we won't have a display rate of more than a few per second. This is the
easy case. The worker thread would do something like:

if (value_changed) {
Fl::lock();
widget->value(new_value);
widget->redraw();
Fl::awake();
Fl::unlock();
}

That's simple and works pretty well in the "slow" update case. For each
changed value the widget gets its new value, redraw() requests a display
update (that is done later, see below), Fl::awake() tells FLTK that it
has something to do, but only after Fl::unlock() can FLTK start its
activities and eventually call draw() for the widget that requested its
drawing by calling redraw().

However, what if we had several hundred updates per second with such a
structure? This would introduce unnecessary overhead (lock, unlock,
context switching), and it would maybe draw some widgets more often than
a user could recognize. Last, but not least, the CPU would be flooded
with many events and unnecessary drawing, and then there could be a
point where the system would become slow and unresponsive. If the CPU
load became too high, even the worker threads would probably be
effected, and we would lose data (from the serial line).

To overcome this, it is best to reduce the display rate, but process
data as it comes in in a reliable way. Since we can't know the data rate
in advance (at least we should not assume anything but a reasonable
range and speed), the best is to skip data that can't be seen anyway,
after processing, but before displaying it. Now a timer comes into play.
We can always assume a reasonable display rate for a given problem.
Humans can recognize up to about 25 frames per sec (fps) in a running
display (video), but this would be much too high for a display showing
numbers like a digital speed or rpm display.

First step: limit the display rate to n (~15-20) fps with a timer. This
would be done in the worker thread (level B) like this:

if (value_changed) {
Fl::lock();
widget->value(new_value);
widget->redraw();
Fl::unlock();
}

Note that we don't call Fl::awake() any more. This will be done by a
timer. The timer will be started from the main thread, either before
Fl::run() is called, or from the callback starting the simulation or
display thread. The timer function can be empty, since it only serves
one purpose: to wake up FLTK's event processing.

#define FPS 15 // frames per second: change, as needed

void timer_cb(void *v) {
Fl::repeat_timeout(double(1.0)/FPS,timer_cb);
} // timer_cb()

and in main before Fl::run() or in the callback:

Fl::add_timeout(0.1,timer_cb); // start "immediately" (0.1 sec. delay)

Now, whenever FLTK wakes up by processing the timer event, it will also
call Fl::flush() to draw all widgets that called redraw() before.

(Note that this is not tested, but it *should* work this way, IMHO).

That's good, but it has some drawbacks. For instance, "wiggling the
mouse" would also result in event processing and update the display as
soon as something changed, resulting in too may updates for a human to grok.

What to do now? Second step: limit the update rate of all widgets, so
that they are displayed only in the defined display rate:

We need a global array of pointers to all widgets in question or another
way to get all display widgets (called gauges here, with widget type
Fl_Gauge). I assume that we have an array like this one:

Fl_Gauge *gauges[NGAUGES]; // properly initialized when the window is set up

When we change a widget's value, we don't call redraw() anymore.
Instead, we set a "dirty" flag. The timer loop needs to check this flag
and call redraw() if needed. We assume that we have simple methods that
do what we need. Changing a widget's value in the worker thread becomes now:

if (value_changed) {
Fl::lock();
widget->value(new_value); // redraw() NOT called !
widget->set_dirty(1); // mark widget as dirty: needs draw()
Fl::unlock();
}

The timer function becomes now:

void timer_cb(void *v) {
for (int i=0; i<NGAUGES; i++) {
Fl_Gauge *gauge = gauges[i];
if (gauge->dirty()) {
gauge->redraw(); // request draw(), drawing will be done later
}
}
Fl::repeat_timeout(double(1.0)/FPS,timer_cb);
} // timer_cb()

Note that the widget's draw() method needs to reset the dirty flag.
This is not shown here.

That's much better. Now mouse wiggling and other events don't trigger
unwanted display updates. However, we can still improve it. There may be
some widgets (particularly those displaying numbers) that are not well
suited to be updated with 15 or more fps.

The easy thing to do is to add some kind of time stamp to the widget's
data that is stored whenever the widget is drawn. The timer callback
would then be something like:

void timer_cb(void *v) {
for (int i=0; i<NGAUGES; i++) {
Fl_Gauge *gauge = gauges[i];
if (gauge->dirty() && gauge->valid_to_redraw()) {
gauge->redraw();
}
}
Fl::repeat_timeout(double(1.0)/FPS,timer_cb,g);
} // timer_cb()

The method valid_to_redraw() would have to check the elapsed time since
the last draw(), compare it to the redraw rate for that particular
widget, and return true, if okay to redraw, but false otherwise. The
draw() method would set the new timestamp, and that's it.

This way the worker thread can update the widget's values as often as it
gets new data, w/o impacting performance. Also, other calls to draw()
would not be affected (resizing window etc.).

Note that the worker thread(s) in "level B" still have to call
Fl::lock() and Fl::unlock() for each value update they are processing.
This could be optimized, if the data for updates could be combined and
stored in "messages" independent of FLTK's widget structure. Then one
level B thread could do all the work combined for many FLTK widgets in
one lock/unlock block to avoid locking overhead. That's probably more
difficult, since it needs more data management and it will only be
effective, if a significant number of widget changes could be combined.

Another interesting point is to run all the widget updates in the
context of the main FLTK thread by calling Fl::awake(callback,message)
to let FLTK call a callback that can do all the work. This can be useful if

(a) many FLTK updates can be combined, but the processing is fast
(saves locking overhead)
or
(b) you need to create/show/hide windows
or
(c) you need user interaction, for instance fl_message() etc.

The "message" argument in Fl::awake() is simply a "void *" and can be NULL.


This is all done with a clear code and data flow in mind. For instance,
data logging to an external file would be done in the level B thread
that reads the data from the serial data thread (level A). More than one
level A thread can read data, but then access to the internal data queue
must be synchronized. The internal data queue would usually be something
like a ring buffer.

Another level B thread could be started to run the simulation. Reading a
file (or generating simulation data) is not a time-critical task.
However, to do a good simulation, the simulation thread itself must
decide how fast it shall feed the data to the FLTK GUI. If it is
replaying recorded data from a file, it should read included time stamps
and replay the data accordingly, maybe in slow motion or even faster
than original. Skipping unnecessary data and so on would be this
thread/function's task. The FLTK loop including the timer callback would
be the same, whether realtime data is read, simulation, or replay of
files is done.

I hope this was helpful, and if, then maybe parts of it could be
included in the docs or in an article. Comments and corrections welcome...

Albrecht

Team Prii

ungelesen,
29.05.2013, 15:04:4329.05.13
an fltkg...@googlegroups.com
Thank you very much!

For the non-threaded approach that calls Fl::check() periodically in a long callback function, why does it have to be limited to a few seconds? Why can't it be running for hours? If it could be running for hours then this non-threaded approach seems to be the simplest and most efficient (no locking overhead) solution for my particular problem (PriiDash). In practice the car data comes in regularly (no burst of high data rate or long waits) and the 2D drawing of the gauges is very fast (as far as I can tell) so there shouldn't be any blocking problem for this particular application.

For the first step in the threaded approach:

============ start quote ======================


First step: limit the display rate to n (~15-20) fps with a timer. This
would be done in the worker thread (level B) like this:

if (value_changed) {
   Fl::lock();
   widget->value(new_value);
   widget->redraw();
   Fl::unlock();
}

Note that we don't call Fl::awake() any more. This will be done by a
timer. The timer will be started from the main thread, either before
Fl::run() is called, or from the callback starting the simulation or
display thread. The timer function can be empty, since it only serves
one purpose: to wake up FLTK's event processing.

#define FPS 15 // frames per second: change, as needed

void timer_cb(void *v) {
   Fl::repeat_timeout(double(1.
0)/FPS,timer_cb);
} // timer_cb()

and in main before Fl::run() or in the callback:

   Fl::add_timeout(0.1,timer_cb); // start "immediately" (0.1 sec. delay)

Now, whenever FLTK wakes up by processing the timer event, it will also
call Fl::flush() to draw all widgets that called redraw() before.

(Note that this is not tested, but it *should* work this way, IMHO).

============== end quote ===========================

If a widget calls redraw() multiple times during the 1/15 second timeout period, then when the main thread processes the timer event would it also redraw the same widget multiple times or would it be smart enough to know to just redraw it once? I am hoping for the latter so I don't need to go to steps 2, 3, 4 etc. which seem like a lot of additional work.

To answer an earlier question from the original thread: if VC, VC_WIN, and SDH are not #defined then it's the Cygwin version. I will check again but I believe the Cygwin version did not have unpaired lock() and unlock(), nor any lock() and unlock() pair that's separated by a long distance. (except the single lock() in the main() of course).

Thanks again!

Team Prii

ungelesen,
29.05.2013, 15:55:4929.05.13
an fltkg...@googlegroups.com
Quick update: I tried the non-threaded approach with one Fl::check() per redraw() (maybe too frequent but just for quick test). It almost worked. The update speed is the same as my old, wrong approach (starting the Gauges window from the worker thread, every redraw() is accompanied by Fl::lock(), unlock(), and flush()). The difference is that the new, non-threaded approach leaves some drawing un-rendered (blank) for some reason.


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Team Prii

ungelesen,
29.05.2013, 15:58:5729.05.13
an fltkg...@googlegroups.com
Forgot to mention that the new approach updates the Go/Stop button correctly every time now. The old approach sometimes needs a mouse wiggle to update the Go/Stop button.

Ian MacArthur

ungelesen,
29.05.2013, 16:00:1529.05.13
an fltkg...@googlegroups.com

On 29 May 2013, at 20:04, Team Prii wrote:

> For the non-threaded approach that calls Fl::check() periodically in a long callback function, why does it have to be limited to a few seconds? Why can't it be running for hours?


Because if you are effectively inside the callback context "for ever" then processing of external events may be messed up, since the normal fltk event loop processing only gets a chance to run very rarely, if at all.

So, you know, don't do that...

> If it could be running for hours then this non-threaded approach seems to be the simplest and most efficient (no locking overhead) solution for my particular problem (PriiDash). In practice the car data comes in regularly (no burst of high data rate or long waits) and the 2D drawing of the gauges is very fast (as far as I can tell) so there shouldn't be any blocking problem for this particular application.

If you have a "two thread" approach, as Albrecht suggests, where one thread is "all fltk" and the worker thread is all about handling the sensors, then you can easily make that lock-free.




Ian MacArthur

ungelesen,
29.05.2013, 16:24:1829.05.13
an fltkg...@googlegroups.com
On 29 May 2013, at 14:24, Albrecht Schlosser wrote:

<lots of really good stuff about display the data in "real time">


I imagine that in this case (Team Prii's dash application) it might be feasible to have just two threads - the main() thread running the GUI, and a worker thread managing the sensors.

The interface between these threads could be something quite crude - it might not need to be even as sophisticated as a queue or a ring buffer or anything. Rather, I think it might be enough just to have a shared structure, or even just some global memory, that simply holds the most recent value read from each sensor...

This probably doesn't even need a mutex to protect it.
If the structure holds a lot of largely independent values (as I imagine, from looking at the extant code, that it would) then we can *assume* that any given parameter will *probably* update atomically.

If it is a single CPU that is context switching between threads, then this is almost certainly true anyway; if it is a multicore CPU then the memory coherency between CPU's will generally work at the cache line level, so we can probably treat any data element smaller than a cache line as updating atomically.
Mind you, I wouldn't want to run a nuclear reactor on that assumption, but for a dash display I suspect it is good enough...
Even if you get a duff reading, it will be rare, and corrected in the next display refresh anyway. So no one will ever know!

So, here's how I see it working:

- The worker thread services the sensors, and for each it writes a value into the global store. And repeat...

- The main GUI thread fires periodically, say off a timer at 50ms intervals. It grabs a snapshot of the global store. It then compares each value against the previous copy, and for any that are changed, it updates the relevant widget. Then triggers a redraw. Sleep and repeat...

And that's just about the whole story. Two concurrent threads, with no locks. Should be fine!




Albrecht Schlosser

ungelesen,
29.05.2013, 16:41:5929.05.13
an fltkg...@googlegroups.com
On 29.05.2013 21:04, Team Prii wrote:
> Thank you very much!

You're welcome.

> For the non-threaded approach that calls Fl::check() periodically in a
> long callback function, why does it have to be limited to a few seconds?
> Why can't it be running for hours? If it could be running for hours then
> this non-threaded approach seems to be the simplest and most efficient
> (no locking overhead) solution for my particular problem (PriiDash).

Well, it could. But usually you would want to return from the callback
as soon as possible. Calling Fl::check() is similar to calling Fl::run()
from a callback, but Fl::check() would return when there are no more
events, whereas Fl::run() would wait. Anyway: this is something like
recursion. In principle the user could press any (active) button when
you call Fl::check(), and you could start another long-running callback,
and so on... Although it would work, the stack would grow and grow..

> In
> practice the car data comes in regularly (no burst of high data rate or
> long waits) and the 2D drawing of the gauges is very fast (as far as I
> can tell) so there shouldn't be any blocking problem for this particular
> application.

I wouldn't want to do serial line i/o in a callback. There are too many
possibilities of failure (lost bytes, hanging i/o, ...). The latter
would make the entire application hang.

However, simulation or playback of recorded data (reliable input) would
probably work.


> For the first step in the threaded approach:
>
> ============ start quote ======================
...
> ============== end quote ===========================
>
> If a widget calls redraw() multiple times during the 1/15 second timeout
> period, then when the main thread processes the timer event would it
> also redraw the same widget multiple times or would it be smart enough
> to know to just redraw it once?

Yes, redraw() marks the widget as damaged, so the draw() method will be
called only once.

> I am hoping for the latter so I don't
> need to go to steps 2, 3, 4 etc. which seem like a lot of additional work.

The problem with step 1 is that you don't have control over the real
drawing of widgets. Whenever you call redraw() on a widget, this widget
*will* be drawn whenever FLTK (the main loop or Fl::check() or Fl::run()
) gets a chance. Whilst this is usually what you want, this may not be
wanted with realtime input (multiple widget changes during this 1/15 s.
interval would potentially lead to multiple draw() calls of the same
widget). As I tried to point out, this would be the case if the user
moved the mouse. So mouse movement (which induces lots of CPU activity
by itself) would also lead to more draw operations than necessary.
Additionally it would make reading of numeric output impossible, because
it would flicker. You can see this in the demo program I posted.
Remember what Ian wrote about airplane displays: a slower update rate is
a MUST for readability.

I really can't tell what will work for you in the end. Maybe a
compromise is good enough. I wanted to point out what would happen, and
how to work with it, so that FLTK and your app do what you (or anybody
else who is reading this) want. It is best to understand *why* something
happens, so you can improve it. However, what you do with this
information is up to you.

PS: I'll be very busy and/or offline during the next days, but I can
probably try more tests next Sunday, if you can post a new version until
then. I'd like to have a version running, compiled with MinGW (but I
don't have Boost installed and wouldn't want to with MinGW, although I
don't know if this would be complicated).

PPS: how do you build your app? I didn't see a Makefile. Do you use an
IDE? Which one? Is it realistic to build the app, run it, and start the
simulation, or would I need more ?

Albrecht Schlosser

ungelesen,
29.05.2013, 16:43:2829.05.13
an fltkg...@googlegroups.com
On 29.05.2013 21:58, Team Prii wrote:
> Forgot to mention that the new approach updates the Go/Stop button
> correctly every time now. The old approach sometimes needs a mouse
> wiggle to update the Go/Stop button.

Good, that's a step forward. :-)

Evan Laforge

ungelesen,
29.05.2013, 16:45:2729.05.13
an fltkg...@googlegroups.com
> And that's just about the whole story. Two concurrent threads, with no locks. Should be fine!

I think you need either a lock, or an atomic swap (e.g.
__sync_bool_compare_and_swap). Otherwise one CPU is not guaranteed to
ever see updates from the other CPU.

Ian MacArthur

ungelesen,
29.05.2013, 16:55:4229.05.13
an fltkg...@googlegroups.com
In practice? I really don't think so - across a bundle of OS (Windows, Linux, OSX, VxWorks, um, probably others...) running on multi-core chips (with and without hyper-threading) and on both X86 and PPC architectures (well, not PPC for Windows...) I've never actually seen the cache-coherency fail to make the shared memory visible to all the threads.

Indeed, since I'm not asserting the CPU affinity for the threads (and can you even do that under Windows? Hmm, I don't know...) there's no way the OS can work unless the system ensures the memory is "properly" shared, since threads pop around from CPU to CPU all the time in a desktop system.

So, I'm *very* confident my hack method of lockless inter-thread comms would actually work just fine! Indeed, I've done pretty much this in the past, and it works well.




Albrecht Schlosser

ungelesen,
29.05.2013, 16:58:0429.05.13
an fltkg...@googlegroups.com
On 29.05.2013 22:24, Ian MacArthur wrote:
> On 29 May 2013, at 14:24, Albrecht Schlosser wrote:
>
> <lots of really good stuff about display the data in "real time">

Thanks, you're welcome.

> I imagine that in this case (Team Prii's dash application) it might be feasible to have just two threads - the main() thread running the GUI, and a worker thread managing the sensors.
>
> The interface between these threads could be something quite crude - it might not need to be even as sophisticated as a queue or a ring buffer or anything. Rather, I think it might be enough just to have a shared structure, or even just some global memory, that simply holds the most recent value read from each sensor...

Oh, that's a good idea. I see you have experience with embedded systems.
Easy to implement, but really good.

> This probably doesn't even need a mutex to protect it.
> If the structure holds a lot of largely independent values (as I imagine, from looking at the extant code, that it would) then we can *assume* that any given parameter will *probably* update atomically.
>
> If it is a single CPU that is context switching between threads, then this is almost certainly true anyway; if it is a multicore CPU then the memory coherency between CPU's will generally work at the cache line level, so we can probably treat any data element smaller than a cache line as updating atomically.
> Mind you, I wouldn't want to run a nuclear reactor on that assumption,

:-)

> but for a dash display I suspect it is good enough...
> Even if you get a duff reading, it will be rare, and corrected in the next display refresh anyway. So no one will ever know!

Yep.

> So, here's how I see it working:
>
> - The worker thread services the sensors, and for each it writes a value into the global store. And repeat...
>
> - The main GUI thread fires periodically, say off a timer at 50ms intervals. It grabs a snapshot of the global store. It then compares each value against the previous copy, and for any that are changed, it updates the relevant widget. Then triggers a redraw.

Easy.

> Sleep and repeat...

(Nitpicking:) No exlicit sleep needed, just restart the timer.

Really good idea. I like it, and AFAICT it could work easily.

> And that's just about the whole story. Two concurrent threads, with no locks. Should be fine!

Bingo! This makes all problems with multiple drawings of widgets
superfluous.

And simulation would work in exactly the same way. The simulation thread
would need its own timer (simple sleep function), update the global
store, watch a global stop flag, sleep and repeat. Done. The GUI thread
would just do the same.

Albrecht

Team Prii

ungelesen,
29.05.2013, 22:43:2629.05.13
an fltkg...@googlegroups.com
Thanks so much guys! I am trying the easiest route and getting close:
 
In the worker thread when a widget is updated I have
 
Fl::lock(); redraw(); Fl::unlock();
 
and in the main thread I just have an empty timeout function as suggested:
 
const double FPS = 30; // frame per second

void timer_cb(void *v) {
   Fl::repeat_timeout(double(1.0)/FPS,timer_cb);
} // timer_cb()
 
Running in simulation mode the screen updates much faster than before (either the non-threaded approach with FL::check() or the old wrong approach with illegal flush() in the worker thread).
 
I again saw some drawings seemingly un-rendered, but after closer look I found it was actually some custom defined color constants that somehow seem to be rendered as black. I have, for example,
 
const Fl_Color my_fl_gray=fl_color_cube(128*(FL_NUM_RED-1)/255, 128*(FL_NUM_GREEN-1)/255, 128*(FL_NUM_BLUE-1)/255);
 
textcolor(my_fl_gray); now seem to give black, not gray, and
 
textcolor(fl_rgb_color(160); works.
 
In the old wrong approach with illegal flush(), all the custom color constants worked fine. Now they all seem to become black.
 
What's going on?
 
I am attaching two zip files. One has the source code and the makefiles generated by the NetBeans IDE that I use. The other has the Cygwin version of executable and associated files that can all be put in the same directory and run from a Cygwin X term.
 
Thanks again!


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to fltkgeneral+unsubscribe@googlegroups.com.
20130529.zip
PriiDash3_20130529.zip

Greg Ercolano

ungelesen,
30.05.2013, 01:29:1030.05.13
an fltkg...@googlegroups.com
On 05/29/13 19:43, Team Prii wrote:
> textcolor(my_fl_gray); now seem to give black, not gray, and
> textcolor(fl_rgb_color(160); works.
>
> In the old wrong approach with illegal flush(), all the custom
> color constants worked fine. Now they all seem to become black.
>
> What's going on?

Perhaps the code order changed in such that fl_color_cube()
is somehow being called before FLTK has initialized the color map?

You might want to try printing the value that my_fl_gray is set to,
and while you're at it, us fl_get_color(my_fl_gray,r,g,b) to determine
what the RGB values of that color is.

Just curious: does it work fine if you use absolute RGB colors
(instead of the colormap), eg:

const Fl_Color my_fl_gray = 0x80808000; // flat gray (80 hex for R/G/B)


Bill Spitzak

ungelesen,
30.05.2013, 02:37:3330.05.13
an fltkg...@googlegroups.com
On 05/29/2013 01:55 PM, Ian MacArthur wrote:
>
> On 29 May 2013, at 21:45, Evan Laforge wrote:
>
>>> And that's just about the whole story. Two concurrent threads, with no locks. Should be fine!
>>
>> I think you need either a lock, or an atomic swap (e.g.
>> __sync_bool_compare_and_swap). Otherwise one CPU is not guaranteed to
>> ever see updates from the other CPU.
>
>
> In practice? I really don't think so - across a bundle of OS (Windows, Linux, OSX, VxWorks, um, probably others...) running on multi-core chips (with and without hyper-threading) and on both X86 and PPC architectures (well, not PPC for Windows...) I've never actually seen the cache-coherency fail to make the shared memory visible to all the threads.

I have found this to work if the task does any locking at all, which the
main thread did when fl_run called fl_lock.

All the multithreading primitives like pthread mutex will cause a memory
cache flush when they lock and unlock on the popular modern processors.

I agree Ian's design is probably the best one for this, as the reading
thread does not do any locking.

If the data comes in sporadically you might add an extra "something
changed" flag that the main thread timeout can check quickly and do
nothing on. Main thread must turn this off before looking at values, and
reading thread must turn it on after writing the values.

If the data stops coming in it may be desirable for both threads to go
to sleep. This gets trickier. The main thread on seeing changes would do
the timeout once. If there are no changes then it does not do the
timeout. The reading thread would, on a change, detect that the main
thread is not doing a timeout and awaken it. I think this requires using
sync primitives.

MacArthur, Ian (Selex ES, UK)

ungelesen,
30.05.2013, 05:50:2430.05.13
an fltkg...@googlegroups.com
> > The interface between these threads could be something quite crude -
> it might not need to be even as sophisticated as a queue or a ring
> buffer or anything. Rather, I think it might be enough just to have a
> shared structure, or even just some global memory, that simply holds
> the most recent value read from each sensor...
>
> Oh, that's a good idea.

Thanks!

I like it as an approach - though Evan is uneasy that the mechanism is not robust, I do find that in practice it works OK.
Technically, there's a lot of merit in his concern, but in for this sort os use case; I'm sure that it will be fine!


> I see you have experience with embedded systems.

I'm so old. I've been doing embedded for 30 years...

<OT>
I do think symmetric multi-processing is such a good thing, and multi-core chips are just brilliant!
In the previous cycle we had systems with dozens of individual CPU's, all interacting, and each running its own OS in its own address space.
Now we have only one or two OS instances, and all the CPU's are running in the shared address space. It's made things so much easier!
</OT>


> > So, here's how I see it working:
> >
> > - The worker thread services the sensors, and for each it writes a
> value into the global store. And repeat...
> >
> > - The main GUI thread fires periodically, say off a timer at 50ms
> intervals. It grabs a snapshot of the global store. It then compares
> each value against the previous copy, and for any that are changed, it
> updates the relevant widget. Then triggers a redraw.
>
> Easy.
>
> > Sleep and repeat...
>
> (Nitpicking:) No exlicit sleep needed, just restart the timer.

Yes, sorry.
I was being a bit glib there; I really only meant that the main() thread would just be waiting until the timer fired again, so not *actually* sleeping as such, but just idling away servicing incoming events and so on in the "usual" way. But not chewing up much in the way of resources, or so on.


> And simulation would work in exactly the same way. The simulation
> thread
> would need its own timer (simple sleep function), update the global
> store, watch a global stop flag, sleep and repeat. Done. The GUI thread
> would just do the same.

Yes; the replay mechanism for simulation is just the "same thing" flipped around, it just has to push data into the global store on timer, replacing the behaviour of the sensor thread...



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

MacArthur, Ian (Selex ES, UK)

ungelesen,
30.05.2013, 05:58:2130.05.13
an fltkg...@googlegroups.com
> I agree Ian's design is probably the best one for this, as the reading
> thread does not do any locking.

Thanks Bill, appreciate the comments.
I think "design" might be stretching things a bit though!


> If the data comes in sporadically you might add an extra "something
> changed" flag that the main thread timeout can check quickly and do
> nothing on. Main thread must turn this off before looking at values,
> and
> reading thread must turn it on after writing the values.

Yes - though the quantity of data isn't so great, just looking at them all probably wouldn't be an issue. If the main() GUI thread only runs every 50ms or so anyway, it's not going to grab that much CPU time anyway!


> If the data stops coming in it may be desirable for both threads to go
> to sleep. This gets trickier. The main thread on seeing changes would
> do
> the timeout once. If there are no changes then it does not do the
> timeout. The reading thread would, on a change, detect that the main
> thread is not doing a timeout and awaken it. I think this requires
> using
> sync primitives.

Hmm, I was just going to let the timer fire in the main() thread every 50ms, whether there was new data or not; I figure it is cheaper to just take a look, see that nothing has changed, and carry on.
The alternative, as you say, would require some synchronisation to "restart" the GUI timer when new data became available, and in this case I really doubt it is worth the hassle.

But, you know, there are many paths to enlightenment...

Team Prii

ungelesen,
30.05.2013, 09:22:5230.05.13
an fltkg...@googlegroups.com
I changed the custom colors to hex constants, i.e., for example,

const Fl_Color my_fl_gray =            (Fl_Color)0x80808000;

as suggested and ran a simulation. It worked! I will test in the car soon. Thanks!


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe?hl=en-US.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.

Greg Ercolano

ungelesen,
30.05.2013, 12:02:5130.05.13
an fltkg...@googlegroups.com
On 05/30/13 06:22, Team Prii wrote:
I changed the custom colors to hex constants, i.e., for example,

const Fl_Color my_fl_gray =            (Fl_Color)0x80808000;

as suggested and ran a simulation. It worked! I will test in the car soon. Thanks!


    Good -- note that FLTK colors can either be color map colors, or 24 bit RGB colors depending on how the bytes in the color are specified. See the 'Colors' section of the 'Drawing Things In FLTK' section of the manual, specifically how colors are encoded:


As the Fl_Color encoding maps to a 32-bit unsigned integer representing RGBI, it is also possible to specify a color using a hex constant as a color map index:
// COLOR MAP INDEX
color(0x000000II)
        ------ |
           |   |
           |   Color map index (8 bits)
           Must be zero
button->color(0x000000ff);                  // colormap index #255 (FL_WHITE)

or specify a color using a hex constant for the RGB components:

// RGB COLOR ASSIGNMENTS
color(0xRRGGBB00)
         | | | |
         | | | Must be zero
         | | Blue (8 bits)
         | Green (8 bits)
         Red (8 bits)
button->color(0xff000000);                  // RGB: red
button->color(0x00ff0000);                  // RGB: green
button->color(0x0000ff00);                  // RGB: blue
button->color(0xffffff00);                  // RGB: white

Team Prii

ungelesen,
30.05.2013, 15:53:5630.05.13
an fltkg...@googlegroups.com
In preparation to fix the Windows version, someone mentioned that these lines of code

        Fl::lock();
        wait(0.1);
        MSG wMsg;
        BOOL bRet;
        bRet = GetMessage( &wMsg, NULL, 0, 0 );
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            TranslateMessage(&wMsg);
            DispatchMessage(&wMsg);
        }
        Fl::unlock();

can be replaced by a single FL function but I forgot what it is (awake() maybe?) and can't seem to find it in both threads of e-mails. Could someone remind me? Thanks!


--

Ian MacArthur

ungelesen,
30.05.2013, 19:16:4130.05.13
an fltkg...@googlegroups.com
Yes, I'd suggest a call to Fl::awake() in there, see how it goes. Certainly, you don't want to be pumping the Win32 message loop like that in your fltk code, ever.

If removing that code stops things working, then that is a sure sign that something is wrong elsewhere!

We'll get this sorted yet!
--
Ian
Sent, much to my surprise, from my Palm Pre3


On 30 May 2013 20:53, Team Prii <team...@gmail.com> wrote:

Team Prii

ungelesen,
31.05.2013, 15:52:0031.05.13
an fltkg...@googlegroups.com
Getting close... I had a few core dumps in the car but that was before I searched and found some more redraw() or value() or activate() in the worker thread that wasn't locked. After fixing them the log to csv conversion finally worked as intended for the first time! I used to get a blank Gauge window and only after the conversion was done the window would be drawn. Now I get an animated Gauge window that replays (at much higher speed) the drive data saved in the log. I am going to test in the car soon. If everything works fine I will tackle the Windows version next.

MinGW looks both promising and scary. The installation instruction seems so complicated. Also is there an IDE to go with it? I use NetBeans with Cygwin and the combination works pretty well.

Team Prii

ungelesen,
01.06.2013, 10:34:2401.06.13
an fltkg...@googlegroups.com
Attached is the latest version. I tested it in the car and so far so good! Interestingly the old, wrong version (where I had flush() in the worker thread) would work in the car for many weeks before a very rare core dump happens. So it will take some time to thoroughly test this new version.
 
I plan to add this line to the list of acknowledgement for PriiDash:
Ian MacArthur, Greg Ercolano, and Albrecht Schlosser from the FLTK general discussion group, who helped with the correct usage of FLTK with threads.
Please let me know if there is any change or objection. Thanks!
20130531.zip

Ian MacArthur

ungelesen,
01.06.2013, 16:59:1901.06.13
an fltkg...@googlegroups.com

On 1 Jun 2013, at 15:34, Team Prii wrote:

> Attached is the latest version. I tested it in the car and so far so good! Interestingly the old, wrong version (where I had flush() in the worker thread) would work in the car for many weeks before a very rare core dump happens. So it will take some time to thoroughly test this new version.


Indeed, bugs with threads are often like that...

To be honest, having taken a brief look at your code, I suspect you are still going to have issues: you haven't yet really fixed all the things that look problematic to me, I'm afraid to say...



> I plan to add this line to the list of acknowledgement for PriiDash:
> Ian MacArthur, Greg Ercolano, and Albrecht Schlosser from the FLTK general discussion group, who helped with the correct usage of FLTK with threads.
> Please let me know if there is any change or objection. Thanks!


I doubt there's any objection to a namecheck, though I don't think any of us in the fltk group are in this for the fame or adulation...


I would caution against using the phrase "the correct usage of FLTK" at this stage, as I honestly believe the code is still wrong, based on what was in the zip you posted.

I'd like to help more, but I have not the time to hack into it properly right now...





Team Prii

ungelesen,
01.06.2013, 22:25:4001.06.13
an fltkg...@googlegroups.com
Thanks! Agreed the Windows version is still a mess but I haven't done much there. I tried to clean up the Cygwin version but maybe I missed something. Please let me know what you saw wrong. Basically right now I have lock() and unlock() pairs surrounding value(), redraw() and activate() etc. in the worker thread. In the main thread there is timer function to update the screen. All windows and widgets are created from the main thread and updated/changed from the main thread.


Albrecht Schlosser

ungelesen,
02.06.2013, 12:37:3902.06.13
an fltkg...@googlegroups.com
On 31.05.2013 21:52, Team Prii wrote:

> Getting close... I had a few core dumps in the car but that was before I
> searched and found some more redraw() or value() or activate() in the
> worker thread that wasn't locked. After fixing them the log to csv
> conversion finally worked as intended for the first time! I used to get
> a blank Gauge window and only after the conversion was done the window
> would be drawn. Now I get an animated Gauge window that replays (at much
> higher speed) the drive data saved in the log.

That's fine.

> I am going to test in the
> car soon. If everything works fine I will tackle the Windows version next.
>
> MinGW looks both promising and scary. The installation instruction seems
> so complicated.

In fact it's *really* easy to do. You don't need to do a "Manual
Installation", there is also a GUI installer - docs are here:

http://www.mingw.org/wiki/Getting_Started

You can get the latest installer using this link:
http://sourceforge.net/projects/mingw/files/latest/download?source=files

As I wrote elsewhere, run the downloaded installer and follow the
instructions. It is good to use the "MinTTY" terminal program to start
the MinGW bash, but there are also other options. I can't tell exactly
how to select this, but you'd probably see when you try to install.

When it comes to selecting components, select
- C Compiler
- C++ Compiler
- MinGW Developer ToolKit (Includes MSYS Basic System).

That should install everything you need. After installation you may want
to add other packages for development, but that's not important now.

> Also is there an IDE to go with it? I use NetBeans with
> Cygwin and the combination works pretty well.

There's no IDE _included_ with MinGW, but you're free to use several
IDE's with MinGW - you need to configure the *IDE* to use the correct
compiler and linker commands to run the MinGW (gcc/g++) compiler and linker.

I recently started evaluating and using an IDE for MinGW (Windows) and
other platforms (UNIX/Linux and Mac OS X), and I'm using CodeLite now. I
imported your project files in CodeLite, and I compiled the demo program
I posted earlier using the Makefile attached to that posting for Linux
and Windows. There may be other and "better" IDE's (this depends on your
usage and your personal view), and you can probably also use NetBeans,
if you like.

<slightly OT>
From my experience with CodeLite: There are three download options for
Windows:
http://codelite.org/LiteEditor/Download#toc3

I used the "IDE only" option, since I had MinGW installed already, but
for your case the 2nd option "with MinGW suite" might be the better
choice, but I don't know what (potentially modified) version of MinGW
that download includes.

I tried CodeLite with my installed MinGW, and I configured my priidash2
demo project as "Custom Build" with an external Makefile. It's just a
matter of setting the correct environment and path variables to make
this work. Until now I failed when I tried to use the built-in rules to
compile with my separately installed MinGW/Msys compilers, but this will
probably be easier if you installed the combined download.

The Makefile (Custom Build) option is a good selection if you also want
to be able to compile your project from the command line, and this is
easy to configure for different platforms (see my demo Makefile for
Windows and Unix/Linux).

CodeLite helped me a lot to analyze your code. There are good search
functions including "entire IDE" which means that you can search all
files for a string or a "resource", which can be a function, variable,
or whatever.
</slightly OT>

HTH
Albrecht

Albrecht Schlosser

ungelesen,
02.06.2013, 12:46:4402.06.13
an fltkg...@googlegroups.com
On 01.06.2013 22:59, Ian MacArthur wrote:

>> I plan to add this line to the list of acknowledgement for PriiDash:
>> Ian MacArthur, Greg Ercolano, and Albrecht Schlosser from the FLTK general discussion group, who helped with the correct usage of FLTK with threads.
>> Please let me know if there is any change or objection. Thanks!

WRT my person, there's no need to do this, but if you like to do it, I'd
suggest to use the wording "who helped to improve usage of FLTK with
threads". You never know if it's _correct_. ;-)

Albrecht

Ian MacArthur

ungelesen,
02.06.2013, 17:18:3602.06.13
an fltkg...@googlegroups.com

On 2 Jun 2013, at 03:25, Team Prii wrote:

> Thanks! Agreed the Windows version is still a mess but I haven't done much there. I tried to clean up the Cygwin version but maybe I missed something.


Well, hard to say without a proper look, but as an example there's still locking from within the Fl_Gauge widget; generally that's considered a bad idea, as there's maybe a chance that the widget will be redrawn from within the main() thread, so causing a potential double lock...

Also, the VC_WIN code still pumps the Windows message loop directly, which is a bad thing.

There may be other things, of course!


> Please let me know what you saw wrong. Basically right now I have lock() and unlock() pairs surrounding value(), redraw() and activate() etc. in the worker thread. In the main thread there is timer function to update the screen. All windows and widgets are created from the main thread and updated/changed from the main thread.


Yes, I see that, but I really think that's still not the optimal model for your needs. Really, I think you need to separate the GUI thread from the sensor thread so there is a clear distinction.

I believe you can make this code lock-free, and that will give you the best (and most stable!) operation.

But... I acknowledge that is more of a change for your baseline...




Team Prii

ungelesen,
02.06.2013, 21:01:4802.06.13
an fltkg...@googlegroups.com
Thanks for the good comments. I will change the wording as suggested. Here is my plan: I will first tackle the Windows version. Then come back to try to make it lock-free since that will need more changes.


Bill Spitzak

ungelesen,
11.06.2013, 00:20:1611.06.13
an fltkg...@googlegroups.com
On 06/02/2013 02:18 PM, Ian MacArthur wrote:

> Well, hard to say without a proper look, but as an example there's still locking from within the Fl_Gauge widget; generally that's considered a bad idea, as there's maybe a chance that the widget will be redrawn from within the main() thread, so causing a potential double lock...

If this is the Fl::lock() this is ok, as it is purposely a recursive
lock to allow this.


MacArthur, Ian (Selex ES, UK)

ungelesen,
11.06.2013, 04:30:0211.06.13
an fltkg...@googlegroups.com

> > Well, hard to say without a proper look, but as an example there's
> still locking from within the Fl_Gauge widget; generally that's
> considered a bad idea, as there's maybe a chance that the widget will
> be redrawn from within the main() thread, so causing a potential double
> lock...
>
> If this is the Fl::lock() this is ok, as it is purposely a recursive
> lock to allow this.

Oh yes, that's true - I kinda forgot that...

Though, as a rule of thumb, I still think it is better to avoid the possibility of double-locking if possible. You know, Just In Case, cos threads is hard...

(Of course, what I really think is that this particular chunk of code can easily be lock-free anyway, which would be the better option overall, but will entail some work to separate the existing threads!)

Team Prii

ungelesen,
11.06.2013, 11:08:3311.06.13
an fltkg...@googlegroups.com
Right now the code structure is that the main thread does all the window opening/closing, button pressing call backs, and the empty timer call back (that executes the actual redraws if I understand it correctly). All the data collection and computation is done in the worker thread in the runUI() function. So are all the calls to this Fl_Gauge widget function:
void Fl_Gauge::myredraw(){
    Fl::lock(); redraw(); ++num_redraws; Fl::unlock();
}
It is never called from the main thread. So it should be safe from double-locking.

For the lock-free approach I am quoting Ian's suggestion here:


"
- The worker thread services the sensors, and for each it writes a value into the global store. And repeat...

- The main GUI thread fires periodically, say off a timer at 50ms intervals. It grabs a snapshot of the global store. It then compares each value against the previous copy, and for any that are changed, it updates the relevant widget. Then triggers a redraw. Sleep and repeat...
"

I need to think of an easy way to do something like this. I am trying to avoid having to maintain a structure (the global store) that tells the worker thread which widget should put its value where and tells the main thread which value corresponds to which widget. If I add more widgets then that structure also needs to be updated. I have a vague idea of passing the "this" pointer to the main thread to tell it that this widget needs to be redrawn but may be that is equivalent to the current situation where lock is required?





Team Prii

ungelesen,
12.06.2013, 16:18:3912.06.13
an fltkg...@googlegroups.com
Going back to the original post by Albrecht, in this piece of code:

if (value_changed) {
   Fl::lock();
   widget->value(new_value);  // redraw() NOT called !
   widget->set_dirty(1);      // mark widget as dirty: needs draw()
   Fl::unlock();
}

he said that Fl::lock() and unlock() pair is still needed for the widget->value() call. Is it still true for the Fl_Gauge class which overloads the value() method? I am attaching a set of files that can be compiled with either Cygwin or MinGW just for testing the drawing and updating. Inside the Fl_Gauge.cxx file you can see that the value() function does nothing but change a bunch of Fl_Gauge class variables such as Value and TextColor, except inside myredraw() function there is a call to redraw() that is surrounded by Fl::lock() and unlock() pair. In other words the Fl_Gauge::value() function stores the value in a separately defined class variable Value, and does not rely on the original Fl widget value() function to store the value. (The program needs to run from the command line with an argument either 0 or 1 to tell it to call redraw() from the main thread without locking or from the worker thread with locking.) So in this case do I still need to surround the overloaded value() method with Fl::lock() and unlock() pair? Thanks!

By the way the Cygwin graphics looks a lot smoother than the MinGW version. The latter has many visible jagged edges in curves. Also the MinGW display update seems jumpier. Is there a way to measure how frequent the re-drawing of a widget is actually executed? Thanks!
source.zip

Greg Ercolano

ungelesen,
12.06.2013, 17:01:3712.06.13
an fltkg...@googlegroups.com
On 06/12/13 13:18, Team Prii wrote:
> Is there a way to measure how frequent the re-drawing of a widget is actually executed? Thanks!

Derive a class from the widget you want to measure, and use that in place of
the fltk one.

Then your widget can overload draw() so that you can insert code to keep track
of the actual draw() calls that draw the widget. So for instance, to simply
count each redraw:

class YourWidget : public Fl_Whatever_Widget {
int drawcount;
public:
YourWidget(int X,int Y,int W,int H,const char *L=0) : Fl_Whatever_Widget(X,Y,W,H,L) {
drawcount = 0;
}
void draw() {
printf("This is draw() #%d\n", ++drawcount); // <-- add code here to see how frequently draw() is called
Fl_Whatever_Widget::draw(); // <-- DONT FORGET TO CALL THE BASE CLASS'S draw() METHOD!
}
};

If you want to /time/ how long it takes between calling redraw()
and the actual draw() code, you can start your timer when redraw()
is called, and stop it in the above draw() code. Just add an overload
for redraw() in the above class, start the timer, and call the base class's redraw().
e.g.

void redraw() {
StartYourTimer();
Fl_Whatever_Widget::redraw();
}
void draw() {
StopYourTimer();
Fl_Whatever_Widget::draw();
}

MacArthur, Ian (Selex ES, UK)

ungelesen,
13.06.2013, 05:22:2913.06.13
an fltkg...@googlegroups.com

 

he said that Fl::lock() and unlock() pair is still needed for the widget->value() call. Is it still true for the Fl_Gauge class which overloads the value() method? I am attaching a set of files that can be compiled with either Cygwin or MinGW just for testing the drawing and updating. Inside the Fl_Gauge.cxx file you can see that the value() function does nothing but change a bunch of Fl_Gauge class variables such as Value and TextColor, except inside myredraw() function there is a call to redraw() that is surrounded by Fl::lock() and unlock() pair. In other words the Fl_Gauge::value() function stores the value in a separately defined class variable Value, and does not rely on the original Fl widget value() function to store the value. (The program needs to run from the command line with an argument either 0 or 1 to tell it to call redraw() from the main thread without locking or from the worker thread with locking.) So in this case do I still need to surround the overloaded value() method with Fl::lock() and unlock() pair? Thanks!

 

Short answer; it depends. (Threads is hard...)

 

If you are accessing the GUI context, then you need the locks. Though here, as we have said before, I would *not* “conceal” the locks in the class methods, I would make them explicit in the calling context.

 

If you are *not* accessing the GUI context, then you may not need the locks, if you don’t mind the code being a bit hacky.

If you are simply writing class member data that will be read by the other thread “later”, then it may (in practice) work fine to do that without locking.

 

Conceptually, the threads are fully asynchronous, so the reader can read whilst the writer is writing. In which case the reader will likely get the wrong value, every now and then. Or indeed the writer value might be marked invalid by the cache coherency logic even though it was just written. Weird stuff happens.

 

Then you need to decide:

 

- Does the occasional bad-read matter? If the display is refreshing “often”, displaying one bad value every now and then might not even be visible to the user. Do you care?

 

- If your system only has a single CPU, that is simulating multi-threading by multi-tasking, then there is no actual simultaneous access in practice, so code will often work fine on a single-core CPU (then behave really weirdly on a modern multi-core CPU!)

 

- If you have a single writer, and one or more readers, you can *often* get away with this with no locking; the writer value will *probably* be written atomically when the cache line is flushed to ensure cache-coherency. (Most current multi-core systems have cache-snooping in h/w to ensure cache coherency across CPU cores.)

 

- If you have multiple writers, you probably do need locks to allow the writers to arbitrate access to the store. Two threads writing to the same location will *not* go well.

BUT these do not need to be fltk locks, since you are not accessing the GUI context. So you can create your own mutex just to protect the access to the member variables. Using this mutex will be much cheaper and faster than locking the flk context, since it will not need to pause until the GUI context is available; it can usually acquire the mutex straight away.

 

- Similarly, if you are worried about your reader getting duff values, you should use a non-fltk lock (i.e. a mutex) to protect the data. This will allow you to ensure that “good” values are written, and read, without having to delay for the GUI context to synchronize. So it is cheap to do.

 

 

By the way the Cygwin graphics looks a lot smoother than the MinGW version. The latter has many visible jagged edges in curves. Also the MinGW display update seems jumpier. Is there a way to measure how frequent the re-drawing of a widget is actually executed? Thanks!

 

Greg’s already posted some suggestions, so you get the idea; basically, subclass your draw method, count how many times it is called, and every now and then, check the elapsed time and work out the average frame rate...

 

 

 

 

 

 

Team Prii

ungelesen,
13.06.2013, 10:05:5313.06.13
an fltkg...@googlegroups.com
Thank you guys for the detailed answers! Just to make sure I understand correctly, a writer is a member function that changes a member variable, say Value, and a reader is any member function that uses the variable Value, right?

I implemented a counter in the draw() function as suggested and sure enough the Cygwin version draws 3 times as frequently as the MinGW version does. That's really puzzling. The Cygwin version is supposed to be going through some extra X11 stuff (slower) and the MinGW is supposed to be native Windows (faster). But we just see the opposite and a huge difference. The two versions were built from exactly the same source code (attached). Here are the screen shots of the numbers:

Cygwin version:
Timing: simulate 4000 iterations =    711.0 (ms), current time=1371130398.395182, num redraws=105057, num draws=  2830
Timing: simulate 4000 iterations =    776.0 (ms), current time=1371130399.171182, num redraws=104212, num draws=  3331
Timing: simulate 4000 iterations =    698.0 (ms), current time=1371130399.869182, num redraws=104280, num draws=  3055
Timing: simulate 4000 iterations =    773.0 (ms), current time=1371130400.642182, num redraws=104367, num draws=  3233
Timing: simulate 4000 iterations =    730.0 (ms), current time=1371130401.372182, num redraws=104269, num draws=  3127
Timing: simulate 4000 iterations =    754.0 (ms), current time=1371130402.126182, num redraws=104213, num draws=  3164
Timing: simulate 4000 iterations =    749.0 (ms), current time=1371130402.875182, num redraws=104252, num draws=  3160
Timing: simulate 4000 iterations =    704.0 (ms), current time=1371130403.579182, num redraws=104282, num draws=  3029
Timing: simulate 4000 iterations =    775.0 (ms), current time=1371130404.354182, num redraws=104218, num draws=  3322

MinGW version:
Timing: simulate 4000 iterations =    725.0 (ms), current time=1371130416.873182, num redraws=105057, num draws=   557
Timing: simulate 4000 iterations =    459.0 (ms), current time=1371130417.332182, num redraws=104212, num draws=   526
Timing: simulate 4000 iterations =    476.0 (ms), current time=1371130417.808182, num redraws=104280, num draws=   624
Timing: simulate 4000 iterations =    470.0 (ms), current time=1371130418.278182, num redraws=104367, num draws=   592
Timing: simulate 4000 iterations =    481.0 (ms), current time=1371130418.759182, num redraws=104269, num draws=   591
Timing: simulate 4000 iterations =    470.0 (ms), current time=1371130419.229182, num redraws=104213, num draws=   591
Timing: simulate 4000 iterations =    471.0 (ms), current time=1371130419.700182, num redraws=104252, num draws=   583
Timing: simulate 4000 iterations =    462.0 (ms), current time=1371130420.162182, num redraws=104282, num draws=   559
Timing: simulate 4000 iterations =    492.0 (ms), current time=1371130420.654182, num redraws=104218, num draws=   623
Timing: simulate 4000 iterations =    463.0 (ms), current time=1371130421.117182, num redraws=104226, num draws=   592
Timing: simulate 4000 iterations =    464.0 (ms), current time=1371130421.581182, num redraws=104328, num draws=   592
Timing: simulate 4000 iterations =    476.0 (ms), current time=1371130422.057182, num redraws=104362, num draws=   591
Timing: simulate 4000 iterations =    452.0 (ms), current time=1371130422.509182, num redraws=104234, num draws=   558
Timing: simulate 4000 iterations =    476.0 (ms), current time=1371130422.985182, num redraws=104210, num draws=   559
Timing: simulate 4000 iterations =    490.0 (ms), current time=1371130423.475182, num redraws=104269, num draws=   653
Timing: simulate 4000 iterations =    476.0 (ms), current time=1371130423.951182, num redraws=104267, num draws=   590


This is done by calling redraw() from the worker thread (with locks) and the main thread has a timer call back function every 1/60 second (FPS=60). There are 32 widgets being simulated. So we should have 60 * 32 = 1920 draws per second. But in the MinGW case we only got 1200 draws per second, and in the Cygwin case we got 4200 draws per second, more than what we asked for. No wonder the Cygwin version motion looked smoother and the MinGW jumpier. What's happening? Maybe I should try higher frame rate?





--
source20130613num_draws.zip

MacArthur, Ian (Selex ES, UK)

ungelesen,
13.06.2013, 12:05:3013.06.13
an fltkg...@googlegroups.com
> Just to make sure I understand correctly, a writer
> is a member function that changes a member variable,
> say Value, and a reader is any member function that
> uses the variable Value, right?

Yes, that'll pretty much cover it.

And, for completeness a mutex is, well, a mutex; which surprisingly even MS call a mutex...

On Windows you want CreateMutex() to create one and WaitForSingleObject(mtx, INFINITE) to lock it, then RelaseMutex(mtx) to release it again.

(Posix equivalents are pthread_mutex_init(), pthread_mutex_lock() and pthread_mutex_unlock()...)


> I implemented a counter in the draw() function

I have not looked at your code yet, but it is not really clear to me where you have implemented this counter.
I can't comprehend why the returned value should be any bigger than, say, 60, since redrawing the display any faster than that is just wasting cycles... (indeed anything over 20 is probably a waste!)

> as suggested and sure enough the Cygwin version
> draws 3 times as frequently as the MinGW version
> does. That's really puzzling. The Cygwin version
> is supposed to be going through some extra X11 stuff
> (slower) and the MinGW is supposed to be native
> Windows (faster). But we just see the opposite and a
> huge difference.

I think the issue is that you are not thinking clearly about what is being done here, and what you are actually measuring.

I'll outline what I think is going on, see if that helps...

Consider the Windows GDI case first; that's your "MinGW" version.

Every time it executes a lock, it forces the threads to sync, and the main thread has to wait on the GUI context before it can do that. So the sensor thread also has to wait...
This will be slow.

Now consider the X11 case; here, the lock is synchronising the threads, as before, but now the GUI context is (I assume) only locking whatever framebuffer the X11 server is using to composite its windows before delivering them to the underlying GDI layer to be drawn. It is not locking the actual physical GDI layer.
So in this case, your code does not have to wait so long to acquire the lock, and it runs "faster".

This illustrates why I keep saying you need to make this code lockless; there is absolutely no reason at all for your sensor thread to be waiting around to sync with the GUI thread, and doing so is always going to impact performance, even in cases where the GUI thread is "buffered" from the underlying hardware implementation.



> The two versions were built from exactly
> the same source code (attached). Here are the screen shots
> of the numbers:

I'm not at all clear on what is being measured here; I still can not comprehend why the number of draws has to be so high...

Greg Ercolano

ungelesen,
13.06.2013, 12:27:0113.06.13
an fltkg...@googlegroups.com
On 06/13/13 07:05, Team Prii wrote:
> So we should have 60 * 32 = 1920 draws per second. But in the MinGW case
> we only got 1200 draws per second, and in the Cygwin case we got 4200 draws
> per second, more than what we asked for. No wonder the Cygwin version motion
> looked smoother and the MinGW jumpier. What's happening? Maybe I should try higher frame rate?

Have you tried timing the amount of time it takes to draw each widget?
This should help you determine if it's the graphics drawing that's slower,
or if the timer/apploop overhead is slower.

If it's the graphics, then you can probably narrow it down to a specific
graphics call that's slower on one than the other.

Or, replace the draw() code with something that just draws a small
rectangle, and see if your render times go down. That too would indicate
it's the graphics drawing code, as opposed to the overhead of FLTK, or
the timers not triggering right, or some such.

Also: have you thought about using opengl in your fltk app
to draw these realtime widgets? Opengl can sometimes take advantage
of hardware optimizations that the native graphics can't, and would
perhaps bypass any issues with mingw vs. cygwin's interfaces to the
native win32 graphics libraries.

When doing realtime apps, I usually start with small simple test programs
at first, and once I've got that running at a decent speed, I scale that
code up into the full app, watching to see if somewhere along the development
cycle things slow down, so I can tell what I did to screw things up.

Perhaps you're already doing this, but I can't tell because the app
you posted is huge. (By 'simple test', I mean something a page or two
long, including main() and no separate .H files)

Team Prii

ungelesen,
13.06.2013, 14:15:0913.06.13
an fltkg...@googlegroups.com
Thank you for the quick reply! The posted code was cut out from the full program with all the gauges intact; just the main runUI() loop was much simplified. So sorry it's still huge. I can try timing the drawing but am suspecting I won't have enough time resolution. gettimeofday() on my laptop seems to have only 1 ms resolution. I will definitely keep the suggestion in mind for future projects. Thanks!


erco

ungelesen,
13.06.2013, 14:30:4513.06.13
an fltkg...@googlegroups.com
On 06/13/13 11:15, Team Prii wrote:
> Thank you for the quick reply! The posted code was cut out from the full program
> with all the gauges intact; just the main runUI() loop was much simplified.
> So sorry it's still huge. I can try timing the drawing but am suspecting I
> won't have enough time resolution. gettimeofday() on my laptop seems to have
> only 1 ms resolution.

You could have your draw() function call the real widget's draw() function 100x
per draw, just to lengthen the amount of time so that your gettimeofday() can
time a longer time sample. You'll probably get more accurate info that way.

Also, did you try having your draw() code simply return (not draw anything),
just to see if it's the drawing code at all.. perhaps the timers aren't triggering
fast enough, or the FLTK event loop is eating up the time.

Might try doing a test that way on both compilers, just to see if something
unrelated to drawing is also causing issues. (Might be a combo of both)

BTW, looking through this thread, seems you're using mingw and cygwin.
Have you tried building with Microsoft's own compiler, VS Express? It's free,
and for sure, it's "native"..

Team Prii

ungelesen,
13.06.2013, 15:17:1213.06.13
an fltkg...@googlegroups.com
Thanks! I will work on them.

I used to use VS Express but decided to switch to MinGW because of potential distribution issue. It wasn't clear to me how strict the "personal use" restriction was. Does anyone know whether it is allowed to distribute the compiled exe file as part of an open source software distribution?


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.

Team Prii

ungelesen,
13.06.2013, 15:45:4413.06.13
an fltkg...@googlegroups.com
Sorry for some reason I missed Ian's message until now. The explanation of why X11 (in the locking case) actually work faster makes a lot of sense. Thank you!

The reason why there were a lot more than 60 draws per second is that each time a draw() is called the counter is advanced by one. (The counter is a static member.) There are 32 counters being redrawn each time. So that's why I was expecting 60 * 32 draws per second.

Team Prii

ungelesen,
13.06.2013, 16:09:2113.06.13
an fltkg...@googlegroups.com
Regarding Ian's earlier comment:

========================= quote ======================================

Conceptually, the threads are fully asynchronous, so the reader can read whilst the writer is writing. In which case the reader will likely get the wrong value, every now and then. Or indeed the writer value might be marked invalid by the cache coherency logic even though it was just written. Weird stuff happens.

 

Then you need to decide:

 

- Does the occasional bad-read matter? If the display is refreshing “often”, displaying one bad value every now and then might not even be visible to the user. Do you care?

 

- If your system only has a single CPU, that is simulating multi-threading by multi-tasking, then there is no actual simultaneous access in practice, so code will often work fine on a single-core CPU (then behave really weirdly on a modern multi-core CPU!)

 

- If you have a single writer, and one or more readers, you can *often* get away with this with no locking; the writer value will *probably* be written atomically when the cache line is flushed to ensure cache-coherency. (Most current multi-core systems have cache-snooping in h/w to ensure cache coherency across CPU cores.)

 

- If you have multiple writers, you probably do need locks to allow the writers to arbitrate access to the store. Two threads writing to the same location will *not* go well.

BUT these do not need to be fltk locks, since you are not accessing the GUI context. So you can create your own mutex just to protect the access to the member variables. Using this mutex will be much cheaper and faster than locking the flk context, since it will not need to pause until the GUI context is available; it can usually acquire the mutex straight away.

 

- Similarly, if you are worried about your reader getting duff values, you should use a non-fltk lock (i.e. a mutex) to protect the data. This will allow you to ensure that “good” values are written, and read, without having to delay for the GUI context to synchronize. So it is cheap to do.

====== end of quote ================

What's the read value when a bad read happens? Say the writer writes 3.2, then 5.6, and then 4.8. Does a bad read mean that the reader reads back something like 3.2, 3.2, 4.8 which just misses some values and/or having some values delayed/duplicated. Or is it like this 3.2, 65535.34875, 4.8 which has completely corrupted wrong data?

Is there a way to tell the system to run the program
with only one CPU core?

Yes I only have one worker thread (writer) and one main thread (reader).

I am leaning toward using a mutex to be on the safe side if it's not too complicated. Unfortunately I have been using Boost::thread and the examples I found is clear but no detailed explanation. Like this one:


class BankAccount {
    boost::mutex mtx_;
    int balance_;
public:
    void Deposit(int amount) {
        mtx_.lock();
        balance_ += amount;
        mtx_.unlock();
    }
    void Withdraw(int amount) {
        mtx_.lock();
        balance_ -= amount;
        mtx_.unlock();
    }
    int GetBalance() {
        mtx_.lock();
        int b = balance_;
        mtx_.unlock();
        return balance_;
    }
};

It's not clear what mtx_ locks when I have (a lot) more members than just a single integer balance_. Presumably locking just one variable would be better than locking all members in the class. It's not clear from this example how to do that.
 

Greg Ercolano

ungelesen,
13.06.2013, 16:10:0013.06.13
an fltkg...@googlegroups.com
On 06/13/13 12:17, Team Prii wrote:
> It wasn't clear to me how strict the "personal use" restriction was.
> Does anyone know whether it is allowed to distribute the compiled exe
> file as part of an open source software distribution?

1. http://social.msdn.microsoft.com/Forums/en-US/Vsexpressinstall/thread/1aaf1efc-04df-40b9-9289-f3db0420c206
2. http://social.msdn.microsoft.com/Forums/en-US/Vsexpressinstall/thread/e471aff6-90fd-432b-8388-0d9a65a394b0
3. http://forums.asp.net/t/1204510.aspx/1

In link #1, Vicky Song works for MSFT, and is an MSDN mod, and she said:

"""
As far as I know that, yes, you can use VS 2012 Express Edition for commercial purpose. "
"""

In link #2, Andrew Wu is an MSDN moderator, and says the same.
He also mentions a 1-800 number you can call for a "Microsoft licensing specialist".
Quoting his response, in case the link goes stale:

"""
As far as I know, a quick answer to your question should be yes, you can use it for commercial use.
However, I still recommand you to check the EULA in the VS Express installer. And for legal question,
I suggest you contact Microsoft Legal and Corporate Affairs Group directly to get an offical response.

You can also call 1-800-426-9400, Monday through Friday, 6:00 A.M. to 6:00 P.M. (Pacific Time)
to speak directly to a Microsoft licensing specialist, and you can get more detail information
from there. You can also visit the following site for more information and support on
licensing issues: http://www.microsoft.com/licensing/mplahome.mspx
"""

In link #3, Mikhail Arkhipov's response (works at Microsoft) also replied:

"""
There are no restrictions. You can use Express editions for commercial development.
"""

Many of these threads link to a MS FAQ that is now gone and redirects to a product page:
http://www.microsoft.com/express/support/support-faq.aspx

Although that link is now stale, archive.org has a copy of it:
http://web.archive.org/web/20110217145540/http://www.microsoft.com/express/support/support-faq.aspx

Quoting that page, as others have done in year's past when the page was up:

"""
Can I use Express Editions for commercial use?
Yes, there are no licensing restrictions for applications built using Visual Studio Express Editions.
"""

The EULA would of course be the final word, which seems to say nothing about limiting the
executables or "distributables" (ie. MS's C library DLLs), which I think are referred to
in the MS SDK docs.

But if you /really/ wanna be sure, I'd say call that 1-800 number in Andrew Wu's message.

Greg Ercolano

ungelesen,
13.06.2013, 16:41:3913.06.13
an fltkg...@googlegroups.com
> class BankAccount {
> boost::mutex mtx_;
> int balance_;
> public:
> void Deposit(int amount) {
> mtx_.lock();
> balance_ += amount;
> mtx_.unlock();
> }
> void Withdraw(int amount) {
> mtx_.lock();
> balance_ -= amount;
> mtx_.unlock();
> }
> int GetBalance() {
> mtx_.lock();
> int b = balance_;
> mtx_.unlock();
> return balance_;
> }
> };
>
>
> It's not clear what mtx_ locks when I have (a lot) more members than just a single integer balance_.

mtx doesn't lock your members, it just locks itself.

It's up to you to decide on which members the lock should affect
by how you write your code to test and manipulate the lock
before reading/writing the variables.

It's like a bool in that it's like a light switch, either on (locked) or off (unlocked).
The only difference is when you lock() or unlock() it, it guarantees there's no race
on the value between threads.

Normally a mutex will have read and write options; a 'read' is one kind of lock
that allows other threads to read at the same time but prevents writes.
A write lock is another kind of lock that prevents other reads and writes.

This way a read lock will allow other threads to read, without blocking them,
increasing the possibility of increased parallel performance.

I don't use boost, I either use pthreads or the OS's own native locking interface.

Anything you do using just normal variables without mutexes or locks
across threads will have the potential for a race. This naive code to make a lock is buggy
in the context of threads:

bool lock = 0;

int lock_buggy() {
if ( !lock ) {
// <-- BUG: another thread could sneak in right here and change 'lock'.
// Result, both threads think they have the lock (which is BAD)
lock = 1;
return 1; // we locked it
} else {
return -1; // someone else has the lock
}
}

A mutex guarantees this won't happen by making sure other threads aren't running
while the mutex code adjusts the lock. This is something the OS has to provide,
as the OS controls thread execution.

> Presumably locking just one variable would be better than locking all members in the class.
> It's not clear from this example how to do that.

It's up to you.. you can have several mutex values if you want, one for each
variable.

But you shouldn't get too complicated; the reverse problem of a 'race'
is a 'deadlock', which can come up in strange cases if you try to get "too clever".

As long as locks are turned on and off quickly, that will prevent deadlocks.
But if you enable a lock, then call some other code that calls some other code,
that deeper circuitous code might come back around and try to lock the same lock,
causing a deadlock. Watch out for that.

Greg Ercolano

ungelesen,
13.06.2013, 16:49:2913.06.13
an fltkg...@googlegroups.com
On 06/13/13 13:41, Greg Ercolano wrote:

> Normally a mutex will have read and write options; a 'read' is one kind of lock
> that allows other threads to read at the same time but prevents writes.
> A write lock is another kind of lock that prevents other reads and writes.
>
> This way a read lock will allow other threads to read, without blocking them,
> increasing the possibility of increased parallel performance.
>
> I don't use boost, I either use pthreads or the OS's own native locking interface.

Oh, and after that last comment about not using boost, I was going to say:
So I'm not sure what the syntax is for using read/write locks; the example
you pasted doesn't show that use, but I'm sure boost supports it. (certainly
pthreads and MS's native locks do)

Ian MacArthur

ungelesen,
13.06.2013, 17:11:5613.06.13
an fltkg...@googlegroups.com
On 13 Jun 2013, at 19:30, erco wrote:

> BTW, looking through this thread, seems you're using mingw and cygwin.
> Have you tried building with Microsoft's own compiler, VS Express? It's free,
> and for sure, it's "native"..

Though in this case, I don't think the choice of toolchain is the issue: rather, I think the interactions between the threads and the fltk locks are.

I'd expect that the code, built with VS, would exhibit essentially the same behaviour as the MinGW version does.


Ian MacArthur

ungelesen,
13.06.2013, 18:04:3413.06.13
an fltkg...@googlegroups.com
On 13 Jun 2013, at 21:09, Team Prii wrote:

Regarding Ian's earlier comment:


What's the read value when a bad read happens? Say the writer writes 3.2, then 5.6, and then 4.8. Does a bad read mean that the reader reads back something like 3.2, 3.2, 4.8 which just misses some values and/or having some values delayed/duplicated. Or is it like this 3.2, 65535.34875, 4.8 which has completely corrupted wrong data?


It depends:

With any recent system, it is overwhelmingly likely that the CPU cores are reading/writing to cache, not directly to RAM, and that the cache is written through to RAM in cache-line sized bursts. (A cache line is likely to be 8 or maybe 16 words. Words are "never" declared to straddle a line boundary, so are in essence written atomically.)

So if one CPU writes a value, and the other CPU reads it before the caches have been sync'd, the second CPU will read the previous value again. (Assuming the CPU's have separate cache's at all; many multi-core chips have unified cache's for all the cores anyway, so the second CPU may well see the "new" value directly in the cache...)

In either case, it is not likely that you would read an incomplete write, or a corrupted value.

That *used* to happen in the "olden days", before the cache snooping logic etc. caught up, but I don't think I've seen that in a long time, not on any standard desktop-class CPU at any rate.
I'm old though...


Is there a way to tell the system to run the program with only one CPU core?

Sure. But you don't want to.
You can set the "cpu affinity" in most OS, in this case to tell the system you want the code to run all its threads on a specific core. But it is seldom useful, unless you are doing some sort of low-level thing where the code really has to run on a specific CPU, for whatever reason.
It's not a useful thing to do for general applications like this.


Yes I only have one worker thread (writer) and one main thread (reader).


And with a little work, we can make them interact without locking the fltk GUI context, and you code will be swifter...


I am leaning toward using a mutex to be on the safe side if it's not too complicated. Unfortunately I have been using Boost::thread and the examples I found is clear but no detailed explanation.


I can't comment on Boosts implementation, I never use it. The native mutex API's are very simple to use, so that's the way I'd go.

(Note, also that you do want a mutex for this, not a semaphore, nor any of the other variants.)

It works something like this:

You create a mutex in global scope, where both threads can see it, e.g. 

   HANDLE mtx = CreateMutex(NULL, FALSE, NULL);


As the writer thread runs, it makes up a local copy of all the data it wants to change, and when it is ready, it acquires the mutex with:

  DWORD err = WaitForSingleObject(mtx, INFINITE);

(now check err to see if you got the mutex or not, if you are being thorough!)

Now, write your local copy of the data into the shared store.

Then release the mutex:

  ReleaseMutex(mtx);

And carry on.

Note: It is imperative that you spend as little time as possible with the mutex acquired. 
Both the reader and the writer want to hold the lock for the very minimum time possible, or they may interfere with each others normal operation.
Acquiring the lock is a potentially blocking action - if one thread already holds the mutex, the other will block in the WaitForSingleObject() call until the mutex is available again.


The reader thread does the same actions, in that when it wants to update its local copy of the data, it first acquires the mutex with WaitForSingleObject(), then it copies the global data into its local store, then it calls ReleaseMutex().

After that, it can process its updated local copy of the data "in its own time."

Observe that the mutex does not "lock" the data in any actual sense, in fact it only locks itself. 
But if you follow the discipline of always strictly acquiring (and subsequently releasing) the mutex before you do anything with the global shared data store, then that will allow you to be sure the shared data is consistent and safe.

So in your case, the sensor thread might have each sensor updating a local copy of the data store freely, safe in the knowledge that no other thread can access that data. No locking is needed for that.

Every "now and then", maybe every few hundred ms, or when a "complete" set of data is ready, the sensor thread can acquire the mutex, update the global shared data store, release the mutex and continue.

For its part, the reader thread can check the global store (maybe triggered by a timer every 20Hz or so), acquire the mutex, copy the data, release the mutex, then process the data for display and trigger the redraw.

The sensor thread never has to acquire the fltk lock at all, and the data mutex is acquired as rarely as possible and for as little time as possible.
Not quite lockless, but nearly.
To make it lockless, we'd need to look at how you get the data from one thread to the next, and this mutex around a simple shared data store is probably simpler...!



Albrecht Schlosser

ungelesen,
13.06.2013, 19:45:5413.06.13
an fltkg...@googlegroups.com
On 13.06.2013 16:05, Team Prii wrote:

> I implemented a counter in the draw() function as suggested and sure
> enough the Cygwin version draws 3 times as frequently as the MinGW
> version does. That's really puzzling. The Cygwin version is supposed to
> be going through some extra X11 stuff (slower) and the MinGW is supposed
> to be native Windows (faster). But we just see the opposite and a huge
> difference. The two versions were built from exactly the same source
> code (attached). Here are the screen shots of the numbers:
>
> Cygwin version:
> Timing: simulate 4000 iterations = 711.0 (ms), current
> time=1371130398.395182, num redraws=105057, num draws= 2830
> Timing: simulate 4000 iterations = 776.0 (ms), current
> time=1371130399.171182, num redraws=104212, num draws= 3331
> Timing: simulate 4000 iterations = 698.0 (ms), current
> time=1371130399.869182, num redraws=104280, num draws= 3055
...

> MinGW version:
> Timing: simulate 4000 iterations = 725.0 (ms), current
> time=1371130416.873182, num redraws=105057, num draws= 557
> Timing: simulate 4000 iterations = 459.0 (ms), current
> time=1371130417.332182, num redraws=104212, num draws= 526
> Timing: simulate 4000 iterations = 476.0 (ms), current
> time=1371130417.808182, num redraws=104280, num draws= 624
...

> This is done by calling redraw() from the worker thread (with locks) and
> the main thread has a timer call back function every 1/60 second
> (FPS=60). There are 32 widgets being simulated. So we should have 60 *
> 32 = 1920 draws per second. But in the MinGW case we only got 1200 draws
> per second, and in the Cygwin case we got 4200 draws per second, more
> than what we asked for. No wonder the Cygwin version motion looked
> smoother and the MinGW jumpier. What's happening? Maybe I should try
> higher frame rate?

No, please try a lower frame rate instead! Surprising? Really, you seem
to have lost your real goal: make a good display of real time data. Now
you're trying to push through as many updates as possible. This is
nothing that can lead you to a good result. Note that I've read the
other posts up to now, and I agree with Ian that you should try to get
your threads lock-free, but anyway, *trying* to explain what's happening
(but I don't really know exactly, and I didn't look at your code yet):

First of all, if I interpret your numbers correctly, the MinGW version
runs *faster*, because it takes only about 470 ms for 4000 iterations,
whereas the Cygwin version takes about 750 ms for the same 4000
iterations. That's a factor of about 1.6 slower. Note that this is or
should be the limit of the worker threads, taking the GUI thread aside.
You are calling redraw ~104.200 times in 4.000 iterations, that's about
26 widget changes per iteration. Is this a plausible value? How often
(in what rate) would the data be delivered in the car when driving? I
don't think that you could query the serial line so fast that you could
get about 150.000 (Cygwin) to 210.000 (MinGW) single results per second,
and I remember that you wrote that your simulation runs faster than in
real time. Taking your figures, the Cygwin version does about 150.000
FLTK locks per second, and the MinGW version does about 200.000 FLTK
locks per second (one per redraw() call). Phew, that's a lot.

The Cygwin version does the FLTK locks with pthreads, whereas the MinGW
version will use native Windows locks - independent of your use of
Boost::threads and its implementation. That's how Fl::lock() is
implemented. I don't know which one would perform better, but since
Cygwin's pthreads implementation must and WILL eventually call Windows
system services, I do strongly assume that the Cygwin implementation
must be slower. And that's what the figures show, too (IMHO).

But there's another problem. Between all the FLTK locks your threads are
calling while FLTK is sitting and waiting for events, FLTK will
sometimes have to service the events (e.g. the timer event), and will
eventually call draw() for all widgets that have called redraw() before
exactly ONCE. This will block the worker threads even more.

Now remember what I wrote in the first message of this thread: if you
call redraw a lot of times between two timer events, you don't have
control over what happens any more. Any OTHER event can trigger another
draw cycle between timer events. See "Second step" in my mentioned post.
Only marking the widget as "dirty" and calling redraw when the timer
kicks in would prevent any overfluous draw() calls from happening, since
FLTK wouldn't know that one widget needs an update until redraw is
called. I explained this with the example that "wiggling the mouse"
could cause unwanted redraws. I don't have an exact explanation why the
Cygwin version calls draw more often than you expect, but I assume that
this is to do with the messages the X11 library (Xlib) sends to the X
server and especially those it receives from the X server. Each message
wakes the FLTK thread up, and FLTK will call Fl::flush() eventually and
draw all widgets marked as damaged (redraw()). Note that the Xlib
protocol also buffers data and sends it in big blocks, if data comes in
quickly. Maybe the X server processes multiple draw calls more
efficiently, because it really draws only after it processes multiple
draw calls. But that's kinda speculation.

The effects and differences you can see with the MinGW and the Cygwin
version do probably result from too much competition for CPU resources
(thread context switching, maybe). Once a certain saturation exists in
such a situation, the system will not act smoothly and predictably any
more, but will instead act somehow randomly (from the view of a human,
at least). Think of what happens when the system memory is used up. New
memory requests make the system swap memory contents to disk, and this
makes the system slow and unpredictable.

In your case this means that you should either avoid feeding too much
data into the worker thread (make it sleep one ms after each 10
iterations or so), or make it lock-free, as Ian suggested, to avoid the
useless overhead of several 100.000 locks per second.

Whatever you do to reduce the data or draw frequency will make the app
run smoother, since it can spend its time more predictable on tasks it
needs to do. Please do really try *slower* frame rates (maybe down to
15-20 FPS) to see what happens with the MinGW version. I assume that it
will run much smoother and you wouldn't see "jumpy" behavior. But don't
confuse jumpy behavior of the gauges with display updates. If you run
the simulation faster than in real time, then the gauges will not move
as smooth as in real time in the car.

Note that Ian's suggestion will not only avoid unnecessary locks, but
will also by design avoid unnecessary redraws, if you let the timer
thread fetch the new values from the global store, update the widget,
and call redraw after this. This will not display those value updates
that happen between timer callbacks, but you will see as much as you can
handle with the least CPU activities and bottle necks.

HTH
Albrecht

Greg Ercolano

ungelesen,
13.06.2013, 22:34:1513.06.13
an fltkg...@googlegroups.com
On 06/13/13 14:11, Ian MacArthur wrote:
> Though in this case, I don't think the choice of toolchain is the issue:
> rather, I think the interactions between the threads and the fltk locks are.

I see -- yes, I've only been skim-reading this thread.. it's a lulu..!


Evan Laforge

ungelesen,
14.06.2013, 00:03:4214.06.13
an fltkg...@googlegroups.com
I've only been skimming too, but I must say I'm impressed how helpful
you guys are. In fact this is a very pleasant little community in general.

Team Prii

ungelesen,
14.06.2013, 09:41:4514.06.13
an fltkg...@googlegroups.com
Albrecht you are right on that I was mistakenly flooding the system with locks and wasn't testing the graphics properly. I added 10 ms wait per iteration (so the rate of redraw request is at 100 FPS) and ran the tests again. Now I have much less frequent redraws (locks) and I also checked the CPU wasn't railing. For 100 FPS timer call back I got

Cygwin:
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215298.279502 num redraws=   4032 num draws=   2680
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215299.279472 num redraws=   4034 num draws=   2686
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215300.279442 num redraws=   4033 num draws=   2698
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215301.279412 num redraws=   4033 num draws=   2770
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215302.279382 num redraws=   4033 num draws=   2707
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215303.279352 num redraws=   4033 num draws=   2639
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215304.279322 num redraws=   4033 num draws=   2670
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215305.279292 num redraws=   4033 num draws=   2569
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215306.279262 num redraws=   4033 num draws=   2642
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215307.279232 num redraws=   4033 num draws=   2643

MinGW:
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215336.588353 num redraws=   4034 num draws=   2008
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215337.588323 num redraws=   4032 num draws=   2046
Timing: simulate 100 iterations   1010.0 (ms)  current time= 1371215338.598293 num redraws=   4034 num draws=   1889
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215339.598263 num redraws=   4032 num draws=   1981
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215340.598233 num redraws=   4034 num draws=   1917
Timing: simulate 100 iterations   1010.0 (ms)  current time= 1371215341.608202 num redraws=   4033 num draws=   1985
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215342.608172 num redraws=   4033 num draws=   2010
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215343.608142 num redraws=   4033 num draws=   1947
Timing: simulate 100 iterations   1010.0 (ms)  current time= 1371215344.618112 num redraws=   4033 num draws=   1957
Timing: simulate 100 iterations   1010.0 (ms)  current time= 1371215345.628082 num redraws=   4033 num draws=   1962

and for 20 FPS I got

Cygwin:
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215970.350760 num redraws=   3882 num draws=   2715
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215971.350740 num redraws=   3925 num draws=   2645
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215972.350720 num redraws=   4033 num draws=   2460
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215973.350700 num redraws=   4033 num draws=   2801
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215974.350680 num redraws=   4033 num draws=   2455
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215975.350660 num redraws=   4033 num draws=   2567
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215976.350640 num redraws=   4034 num draws=   2609
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215977.350620 num redraws=   4032 num draws=   2438
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215978.350600 num redraws=   4034 num draws=   2995
Timing: simulate 100 iterations   1000.0 (ms)  current time= 1371215979.350580 num redraws=   4032 num draws=   2905

MinGW:
Timing: simulate 100 iterations   1230.0 (ms)  current time= 1371216125.258149 num redraws=   3925 num draws=    660
Timing: simulate 100 iterations   1300.0 (ms)  current time= 1371216126.558136 num redraws=   4033 num draws=    692
Timing: simulate 100 iterations   1230.0 (ms)  current time= 1371216127.788124 num redraws=   4033 num draws=    627
Timing: simulate 100 iterations   1280.0 (ms)  current time= 1371216129.068111 num redraws=   4033 num draws=    693
Timing: simulate 100 iterations   1220.0 (ms)  current time= 1371216130.288099 num redraws=   4033 num draws=    627
Timing: simulate 100 iterations   1180.0 (ms)  current time= 1371216131.468087 num redraws=   4034 num draws=    627
Timing: simulate 100 iterations   1350.0 (ms)  current time= 1371216132.818074 num redraws=   4032 num draws=    726
Timing: simulate 100 iterations   1300.0 (ms)  current time= 1371216134.118061 num redraws=   4034 num draws=    693
Timing: simulate 100 iterations   1270.0 (ms)  current time= 1371216135.388048 num redraws=   4032 num draws=    660
Timing: simulate 100 iterations   1300.0 (ms)  current time= 1371216136.688035 num redraws=   4034 num draws=    693

So it looks like the MinGW version is trying to follow the FPS timer but the Cygwin version draws more frequently regardless of FPS. Neither actually draws at 100 FPS when asked - Cygwin does 60 ~ 70 FPS and MinGW does ~ 50 FPS. It's strange that the MinGW version had 2 ~ 3 ms of extra delay per iteration in the 20 FPS case but not in the 100 FPS case. At 20 FPS the MinGW version display looks very choppy and at 100 FPS it looks a lot more smooth/continuous. I will add the same counting code in the main program and test in the car to see what kind of redraw and draw rate we get in real life. Stay tuned...



--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.

Team Prii

ungelesen,
14.06.2013, 14:22:0514.06.13
an fltkg...@googlegroups.com
Some more data: I removed most of the drawing commands in the draw() function except
fl_push_clip()
fl_pop_clip()
draw_label()
Now for some reason the system time tick interval seems to go back to the old PC standard 16 ms. So the 10 ms wait caused a total of 16 ms delay. The PC was restarted due to Windows update. For 20 FPS I got

Cygwin:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232539.220511 num redraws=   4034 num draws=   2612
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232540.780421 num redraws=   4032 num draws=   2612
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232542.340331 num redraws=   4034 num draws=   2726
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232543.900241 num redraws=   4032 num draws=   2379
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232545.460151 num redraws=   4034 num draws=   2524
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232547.020061 num redraws=   4033 num draws=   2724
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232548.579971 num redraws=   4033 num draws=   2498
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232550.139881 num redraws=   4033 num draws=   2500
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232551.699791 num redraws=   4033 num draws=   2447
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232553.259701 num redraws=   4033 num draws=   2708

MinGW:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232580.261743 num redraws=   4033 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232581.821653 num redraws=   4033 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232583.381563 num redraws=   4033 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232584.941473 num redraws=   4033 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232586.501383 num redraws=   4034 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232588.061293 num redraws=   4032 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232589.621203 num redraws=   4034 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232591.181113 num redraws=   4032 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232592.741023 num redraws=   4034 num draws=    825
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232594.300933 num redraws=   4033 num draws=    825

For 100 FPS I got:

Cygwin:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232648.913383 num redraws=   4033 num draws=   3092
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232650.473293 num redraws=   4033 num draws=   3176
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232652.033203 num redraws=   4033 num draws=   3066
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232653.593113 num redraws=   4033 num draws=   3224
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232655.153023 num redraws=   4034 num draws=   3192
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232656.712933 num redraws=   4032 num draws=   3190
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232658.272843 num redraws=   4034 num draws=   3150
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232659.832753 num redraws=   4032 num draws=   2897
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232661.392663 num redraws=   4034 num draws=   3135
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232662.952573 num redraws=   4033 num draws=   3101

MinGW:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232684.307740 num redraws=   4033 num draws=   2875
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232685.867650 num redraws=   4033 num draws=   2997
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232687.427560 num redraws=   4033 num draws=   3152
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232688.987470 num redraws=   4034 num draws=   2962
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232690.547380 num redraws=   4032 num draws=   2975
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232692.107290 num redraws=   4034 num draws=   3016
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232693.667200 num redraws=   4032 num draws=   3133
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232695.227110 num redraws=   4034 num draws=   2969
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232696.787020 num redraws=   4033 num draws=   2998
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232698.346930 num redraws=   4033 num draws=   2977

Putting all the drawing commands back to the draw() function for 100 FPS I got:

Cygwin:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232754.020118 num redraws=   3925 num draws=   2098
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232755.580028 num redraws=   4033 num draws=   2236
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232757.139938 num redraws=   4033 num draws=   1988
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232758.699848 num redraws=   4033 num draws=   2249
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232760.259758 num redraws=   4033 num draws=   1954
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232761.819668 num redraws=   4034 num draws=   2032
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232763.379578 num redraws=   4032 num draws=   1960
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232764.939488 num redraws=   4034 num draws=   2075
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232766.499398 num redraws=   4032 num draws=   2261
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232768.059308 num redraws=   4034 num draws=   1993

MinGW:
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232799.943869 num redraws=   3925 num draws=   2733
Timing: simulate 100 iterations   1575.5 (ms)  current time= 1371232801.519378 num redraws=   4033 num draws=   2875
Timing: simulate 100 iterations   1575.5 (ms)  current time= 1371232803.094887 num redraws=   4033 num draws=   2795
Timing: simulate 100 iterations   1575.5 (ms)  current time= 1371232804.670396 num redraws=   4033 num draws=   2775
Timing: simulate 100 iterations   1559.9 (ms)  current time= 1371232806.230343 num redraws=   4033 num draws=   2900
Timing: simulate 100 iterations   1560.0 (ms)  current time= 1371232807.790303 num redraws=   4034 num draws=   2702
Timing: simulate 100 iterations   1560.0 (ms)  current time= 1371232809.350263 num redraws=   4032 num draws=   2933
Timing: simulate 100 iterations   1591.2 (ms)  current time= 1371232810.941422 num redraws=   4034 num draws=   2884
Timing: simulate 100 iterations   1575.6 (ms)  current time= 1371232812.516982 num redraws=   4032 num draws=   2433
Timing: simulate 100 iterations   1560.0 (ms)  current time= 1371232814.076942 num redraws=   4034 num draws=   2365

Now at 100 FPS with all the drawing commands running the number of draws per second for Cygwin is lower than MinGW - opposite from what I saw earlier in the previous message. Weird. Removing most of the drawing commands does not affect the MinGW version much but does boost the drawing frequency for Cygwin significantly.

Albrecht Schlosser

ungelesen,
15.06.2013, 07:11:0315.06.13
an fltkg...@googlegroups.com
On 14.06.2013 20:22, Team Prii wrote:
> Some more data: I removed most of the drawing commands in the draw()
> function except
> fl_push_clip()
> fl_pop_clip()
> draw_label()
> Now for some reason the system time tick interval seems to go back to
> the old PC standard 16 ms. So the 10 ms wait caused a total of 16 ms
> delay. The PC was restarted due to Windows update.

You can never rely on exact timing, but you probably know that.
Additionally there's IMHO a platform dependency that would make
Fl::repeat_timeout() more exact on Linux (including Cygwin) than on
Windows (MinGW), but that's not important here.

> For 20 FPS I got

... [data removed]

This shows that the MinGW version does exactly (more or less) what is to
be expected, but the Cygwin version does many more drawings. Indeed,
this confirms my assumption that it is to do with Xlib's messages
flowing around. This also explains why your older (first) Cygwin version
seemed to work better (w/o mouse wiggling) than the MinGW version and
that you (or someone else) had to add Windows-specific code to "pump"
the message queue. Fortunately this is no more necessary.

But: keep in mind that the Cygwin version does *extraneous* draws that
you really don't want to have, and that this version of the program has
no control over this fact (see the test described below). This confirms
what I wrote in my earlier post: you must go to "step 2" to improve
this. I'll post more about this and a *simple* proposal (maybe a patch)
I have in mind later (maybe tonight, but I can't tell yet, when exactly
this will be).

> For 100 FPS I got:

... [data removed]

> Putting all the drawing commands back to the draw() function for 100 FPS
> I got:

... [data removed]

> Now at 100 FPS with all the drawing commands running the number of draws
> per second for Cygwin is lower than MinGW - opposite from what I saw
> earlier in the previous message. Weird. Removing most of the drawing
> commands does not affect the MinGW version much but does boost the
> drawing frequency for Cygwin significantly.

Yep, see also above. Since Cygwin sends more messages to the X server,
it keeps itself going and reaches a limit somewhere, whereas the MinGW
version works more as expected (or as it should do).

To verify this, please do another test with different FPS rates, and see
(and report) how this works. For each test do:

- run 5 loops of 100 iterations as before
- run another 5 loops of 100 iterations with mouse wiggling in
different speeds.

See what happens.

My bet: the MinGW version will do many more draws, maybe even more than
the Cygwin version.

Another assumption (not as certain as the "bet" above): the Cygwin
version will either do slightly more draws or even less draws than w/o
mouse wiggling. That depends...

If you find out that one or both assumptions are true, then you can
clearly see that your program doesn't do what it should, and that it
should be improved.

My goal is to make it display exactly in the given FPS rate, no matter
how fast data arrives, but you can make the FPS rate adjustable if you
like. Maybe 10 FPS should be more than enough in a driving car, but 50
FPS would be good for high-speed simulation or replay of data, and 25
FPS would be the best choice for replaying data in slow motion. Does
this sound feasible?

Stay tuned ...

Albrecht

Ian MacArthur

ungelesen,
15.06.2013, 15:22:5815.06.13
an fltkg...@googlegroups.com

On 14 Jun 2013, at 14:41, Team Prii wrote:

> Albrecht you are right on that I was mistakenly flooding the system with locks


If we are talking about Fl::lock() here, then you absolutely *must* have as few locks as possible, as doing an Fl::lock() might be very time consuming (depending on what the graphics system is doing when you try to lock).

Every time you do an Fl::lock(), both threads will block until the graphics context is ready. That is not a Good Thing.




Ian MacArthur

ungelesen,
15.06.2013, 16:40:1515.06.13
an fltkg...@googlegroups.com
On 15 Jun 2013, at 12:11, Albrecht Schlosser wrote:

> You can never rely on exact timing, but you probably know that. Additionally there's IMHO a platform dependency that would make Fl::repeat_timeout() more exact on Linux (including Cygwin) than on Windows (MinGW), but that's not important here.

There may be something else with the Windows timing, at least if the Fl::lock() is involved...

<Random anecdote warning>

A while back, I was working on some code that was rendering into a GL window, using a timer to set the frame rate, much as we are discussing here, and under Windows the refresh rate was slightly lower than expected. Other platforms (indeed the same h/w but running other OS) gave much more consistent results.

But, the target frame rate was not all that high; certainly the h/w was more than capable of rendering the simple scene involved at vastly higher frame rates.

So, what was going on?

Step one; remove the timer and redraw the scene as fast as possible - this shows that a frame rate of 10 times the desired rate is easily attained.

Step two: Add a slider that allows me to dynamically alter the desired frame rate.

This is much more interesting, in that it shows that the attained frame rate is heavily quantised. I can't remember actual values, but it went something like this:

If I ask for a rate between 20Hz and 30 Hz, I got a pretty consistent value of (say) 18.2Hz. Then, at some threshold, say I asked for 32Hz, I suddenly got 28Hz, and it stayed at 28 until the demand got to 45Hz or so when the achieved rate suddenly jumped to 42Hz... and so on.

So, in my experience, there is some sort of weird quantisation of refresh rates on Windows platforms (at least when GL was involved, I assume it affects non-GL rendering too, however) where you seem to be "allowed" certain refresh rates, but not others, and these "allowed" refresh rates are quantised in some way that I don't understand...

I imagine that the quantisation is something to do with syncing to the monitor refresh, or the system timers, or some property of the driver layer, the phase of the moon, the current dollar exchange rate, or some other factor...



> My goal is to make it display exactly in the given FPS rate, no matter how fast data arrives, but you can make the FPS rate adjustable if you like.

Yes, I agree; though see above. Maybe we need to decide what value is "fast enough" then see what actual value Windows is comfortable with that lies in that ballpark...


> Maybe 10 FPS should be more than enough in a driving car, but 50 FPS would be good for high-speed simulation or replay of data, and 25 FPS would be the best choice for replaying data in slow motion.


Though I would (again) like to say that, in the past, for aircraft display systems, we have had customers explicitly specify that the refresh rate *not* be high. These are requirements flowing from customers who have invested *a lot* of time and money in understanding the "human factors" involved in delivering information...

In a high-workload environment (flying a 'plane, driving a car, etc.) it is *much* easier for an operator to actually read a dial that "pauses" at each update, rather than smoothly varying. It may not look as nice, but a display you can actually read is a lot more useful in practice than one that looks all swoopy in a cool demo.

So, think carefully about what you actually want here!




Team Prii

ungelesen,
16.06.2013, 11:29:0016.06.13
an fltkg...@googlegroups.com
OK I did the mouse wiggling exercise (no wiggles in the first few iterations). Strange enough the time tick went back to 10 ms this time.

Cygwin:
Timing: simulate 100 iterations   1047.1 (ms)  current time= 1371393503.023576 num redraws=   3855 num draws=   3079
Timing: simulate 100 iterations   1024.1 (ms)  current time= 1371393504.047634 num redraws=   3882 num draws=   3136
Timing: simulate 100 iterations   1006.1 (ms)  current time= 1371393505.053692 num redraws=   3925 num draws=   3166
Timing: simulate 100 iterations   1005.1 (ms)  current time= 1371393506.058749 num redraws=   4033 num draws=   3243
Timing: simulate 100 iterations   1001.1 (ms)  current time= 1371393507.059807 num redraws=   4033 num draws=   3248
Timing: simulate 100 iterations   1006.1 (ms)  current time= 1371393508.065864 num redraws=   4033 num draws=   3243
Timing: simulate 100 iterations   1011.1 (ms)  current time= 1371393509.076922 num redraws=   4033 num draws=   3239
Timing: simulate 100 iterations   1004.1 (ms)  current time= 1371393510.080979 num redraws=   4034 num draws=   3241
Timing: simulate 100 iterations   1017.1 (ms)  current time= 1371393511.098038 num redraws=   4032 num draws=   3209
Timing: simulate 100 iterations   1013.1 (ms)  current time= 1371393512.111095 num redraws=   4034 num draws=   3238

MinGW
Timing: simulate 100 iterations   1142.1 (ms)  current time= 1371393708.466326 num redraws=   4033 num draws=   2084
Timing: simulate 100 iterations   1167.1 (ms)  current time= 1371393709.633393 num redraws=   4033 num draws=   2162
Timing: simulate 100 iterations   1248.1 (ms)  current time= 1371393710.881465 num redraws=   4033 num draws=   2303
Timing: simulate 100 iterations   1280.1 (ms)  current time= 1371393712.161538 num redraws=   4034 num draws=   2199
Timing: simulate 100 iterations   1203.1 (ms)  current time= 1371393713.364607 num redraws=   4032 num draws=   2810
Timing: simulate 100 iterations   1257.1 (ms)  current time= 1371393714.621678 num redraws=   4034 num draws=   3073
Timing: simulate 100 iterations   1281.1 (ms)  current time= 1371393715.902752 num redraws=   4032 num draws=   3105
Timing: simulate 100 iterations   1230.1 (ms)  current time= 1371393717.132822 num redraws=   4034 num draws=   2754
Timing: simulate 100 iterations   1317.1 (ms)  current time= 1371393718.449897 num redraws=   4033 num draws=   2629
Timing: simulate 100 iterations   1222.1 (ms)  current time= 1371393719.671967 num redraws=   4033 num draws=   2803

It does look like the Cygwin version is not affected by mouse wiggles much and MinGW is.

In the car the redraw and draw rates are both just a few hundred per second.


Team Prii

ungelesen,
22.06.2013, 09:05:4122.06.13
an fltkg...@googlegroups.com
OK I cleaned up the code which still has Fl::lock(); redraw(); unlock() in the worker thread and an empty timer call back function in the main thread. Now the native Windows version (MinGW) no longer has extra "message pumping" and works fine so far. The only difference between the MinGW and the CygWin versions are the way window icons are generated, serial port stuff, and file path (f: vs. /cygdrive/f).

Now to remove the Fl::lock(); redraw(); unlock() in the worker thread, since it is unlikely that the reader will read a corrupted value in the absence of a locked mutex as Ian commented below, and a missed new value in an occasional display frame is no big deal, I am thinking this approach:
1. Currently all gauges (widgets) are contained in a single window (designed with fluid), and they are all members of the class Gauges (code auto generated by fluid):
class Gauges {
public:
  Gauges();
  Fl_Double_Window *gauges_window;
  Fl_Gauge *iceT_G;
  Fl_Gauge *wT_G;
  Fl_Gauge *speed_G;
...
2. I will add a vector member in the class Gauges, each element in the vector contains a pointer to Fl_Gauge and a pair of value variables "old" and "new".
3. In the value(double v) function of the Fl_Gauge, in addition to assigning the value v to the member Value, if it is called for the first time, then add an element to the vector, remember its index, assign the "this" pointer to the pointer in the vector element, and set the "old" and "new" value variable in the vector element to v also. If it is called for the 2nd time and later, then just set the "new" value variable in the vector element to v. These are all done in the worker thread.
4. In the main thread timer call back function, iterate through the vector, compare "old" and "new" values, if different then redraw(), and copy the "new" value to "old".

Does this makes sense? Thanks!



Ian MacArthur

ungelesen,
22.06.2013, 10:45:0522.06.13
an fltkg...@googlegroups.com
On 22 Jun 2013, at 14:05, Team Prii wrote:

> Now to remove the Fl::lock(); redraw(); unlock() in the worker thread, since it is unlikely that the reader will read a corrupted value in the absence of a locked mutex as Ian commented below, and a missed new value in an occasional display frame is no big deal, I am thinking this approach:
> 1. Currently all gauges (widgets) are contained in a single window (designed with fluid), and they are all members of the class Gauges (code auto generated by fluid):
> class Gauges {
> public:
> Gauges();
> Fl_Double_Window *gauges_window;
> Fl_Gauge *iceT_G;
> Fl_Gauge *wT_G;
> Fl_Gauge *speed_G;
> ...
> 2. I will add a vector member in the class Gauges, each element in the vector contains a pointer to Fl_Gauge and a pair of value variables "old" and "new".
> 3. In the value(double v) function of the Fl_Gauge, in addition to assigning the value v to the member Value, if it is called for the first time, then add an element to the vector, remember its index, assign the "this" pointer to the pointer in the vector element, and set the "old" and "new" value variable in the vector element to v also. If it is called for the 2nd time and later, then just set the "new" value variable in the vector element to v. These are all done in the worker thread.
> 4. In the main thread timer call back function, iterate through the vector, compare "old" and "new" values, if different then redraw(), and copy the "new" value to "old".
>
> Does this makes sense? Thanks!

Hmm, well... That'll probably work, but I'd do things differently, I think .

Specifically:

- I'm wary of storing the old and new values in the same vector, since they may be updated asynchronously (the sensor thread may write new values whilst the display thread is processing data) and being sure that works as desired may not be trivial. Instead, I'd have separate vectors (maybe even just arrays, which may have lower overhead to store and manipulate.)
Indeed, I'd probably have *several* vectors...

- I be shy of calling the new element "new" anyway, for fear of confusing folks, it being a language keyword and all. Maybe "updated" and "previous" or such...

- In this sort of model, I'd probably use a mutex to secure access to the data vectors, just to be safe. (Not fltk lock though, just a mutex.) To do fully lockless we'd need to think about data structures that can be safely accessed concurrently - the simple arrays or vectors we are looking at here may to too "simplistic" for that.

So; here's my model for this:-

- The sensor thread has an array/vector in which it stores "update" values whenever it wants to. Only the sensor thread ever access this store.

- There is a "global" vector, protected by a mutex. Every now and then (say every 100ms or so) the sensor thread grabs the mutex, copies it's data vector into the global vector, then releases the mutex. It does not care whether values are changed or not, it just dumps the lot into the global vector.

- The (main) display thread has a timer, set at the desired FPS. When this fires, the timer callback (running in the main thread) acquires the mutex, copies the "global" vector into *its* local copy of the data vector, then releases the mutex.

- The timer callback then walks along this local copy of the data vector, looking for changes (this may entail the display thread holding two copies of the data, of course...) and updating any widgets as required. This is done with the mutex released of course, so does not impact the sensor thread in any way.

- Once all the widgets that need updated, have been updated, then trigger the redraw().

- And repeat...

There are a few things I'd flag up:

- It looks like there are an awful lot of duplicate copies of the data in this model, but I'd suggest that unless you have an awful lot of gauges on the display, it's not going to kill you in terms of memory usage! (And the widgets themselves will still dominate the per-guage RAM usage I imagine...)

- This still uses a mutex to lock the data, but holds it for the shortest time possible, reducing the implicit synchronisation between the threads to more manageable proportions.

- It *might* not be necessary to check if the values change or not in the display thread; it may be enough just to update them all anyway, whether they change or not, then just redraw the whole lot. That's more work for the renderer, but less work for you, and it may work out just fine!







Albrecht Schlosser

ungelesen,
22.06.2013, 11:12:1822.06.13
an fltkg...@googlegroups.com
Yes, it does. Although I would perhaps do it a little different, because
it seems easier to manage (for me). See below.

Well, I just saw that Ian posted his suggestion already, with lots of
good information. I'll post mine anyway - take the best of both for your
implementation. ;-)

BTW: sorry for the long delay. I wanted to post a code patch, but it
turned out to be more complicated than I thought. The main problem was
that your value() method(s) do some sophisticated things, and that there
are three of 'em, or maybe four. What I did so far was to make the code
compile under MinGW w/o Boost::threads, using the FLTK header
test/threads.h. This way I could make your demo program run w/o boost.
If someone is interested (Ian, maybe?), I can post the code or patch...

Back to topic: I would build the vector when the Fl_Gauge widgets are
created by adding one vector element for each Fl_Gauge widget. The
vector elements could point to a structure of whatever you need, for
instance like this:

struct {
Fl_Gauge *gauge; // points back to the Widget
double value[4]; // if you need a max. of 4 values
char value_set[4]; // set to 1 if value[n] is set
}

You can store a pointer to this struct or its index in the widget itself
to access in the worker thread.

This struct would represent the "global store" Ian suggested, or
something like this. Note that there is not need for an "old" value,
since the old value is always stored in the widget itself: value().

Whenever your worker thread finds a new value it doesn't use the
value(doulble or whatever) method directly, but stores the value in the
external struct.

Then, as you wrote, iterate through the vector in the timer thread and
call value() or value1() or whatever, if the corresponding value in the
global store is set (value_set[n]) and reset the value_set[n] flag.

Alternative method: use the vector only for the pointer to all Fl_Gauge
widgets and store the new values in the widget itself in additional
member variables, similar to those above. You could use a new_value() or
set_value() method to do this.

In both cases, do not call redraw() from the thread - call it only from
the timer callback if any value has changed. Since this would be a
general function of your special class, I'd add a method called
update_display() or such. The timer callback would only call
update_display() for each Fl_Gauge widget, and this method would decide
everything [1], update the values, and call redraw() if needed.

Either way it could be made (Fl::)lock-less, since the worker thread
wouldn't do any FLTK operations except storing values in internal
variables. There could, however, still be race conditions, but these
could be neglected. If you're really paranoid, then you can protect the
internal values by using a mutex (one for all would suffice) [as Ian
suggested as well].

So far to these changes.

[1] as Ian mentioned elsewhere, widgets should maybe be displayed with
individual (slower) refresh rates. If you decouple the data source
(worker thread) and the display (update_display() method), then it
should not be a big deal to set a minimum time delta for each Fl_Gauge
widget so that you can delay updates that are too fast. You can safely
ignore the changed ("new") value until the time delta has passed, so
that the update is not too fast, but if the value has been changed, you
wouldn't lose it - unless it has been overwritten by another (newer) value.

One more note to your usage of threads. It seems to me that your
demo/simulation program starts one thread (the UI thread) right at
startup time, and this does different things throughout the life time of
the program: either run the simulation, or do some idle display. In your
real program, there might be even more things to do: real time data
acquisition or replay of stored data from a file. So I would probably
change this to start a thread with a single function from the button
callback and let it run until it is said to stop, like this:

(1) enable all buttons that can be used to start a function:
(a) simulate
(b) run (car mode)
(c) replay

(2) in each button's callback(!):
- disable all *other* buttons
- change the button's label to "Stop" or whatever
- set status to "running"
- start the thread for this function

(3) when the button is clicked again ("Stop"), then in its callback:
- disable this button (yes!)
- set the button's label to "stopping..."
- set status to "stop" to notify the thread that it has to finish

(4) in the thread:
- if status is "stop", terminate activities
- send Fl::awake(... simulation_stopped_cb ...)
- terminate the thread
This tells FLTK that the simulation has stopped.

(5) in simulation_stopped_cb():
- set all button labels to "Start" again
- enable all buttons

This way you can do all FLTK widget changes in the callback(s) of the
button or in the "stopped" callback, triggered by the thread. There's no
need to use Fl::lock() in the thread to change the buttons or
enable/disable them.

Maybe this looks more complicated, though, and your design is "easier"
to implement, but maybe mine is more general for your different running
modes.

Albrecht

Team Prii

ungelesen,
23.06.2013, 17:14:5623.06.13
an fltkg...@googlegroups.com
Thank you Ian and Albrecht for the detailed suggestions! It will take me some time to digest them. Just some quick comments: I wasn't thinking naming the variable "new", which I didn't know it's even possible. The comment on thread is great. I will work on that. The runU() function has also grown to be an eye sore. There are many different run modes and each one share some common code but also has individual different code. So I end up with many switch statements mixed with the common part, making it hard to read. I am thinking of separating each run mode into its own function but don't want to have multiple copies of the common part. One of the common part is that every second the clock display is updated and a set of values is saved to a csv file. Maybe that part should be running in another thread?


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral+unsubscribe@googlegroups.com.

Albrecht Schlosser

ungelesen,
23.06.2013, 17:57:0323.06.13
an fltkg...@googlegroups.com
On 23.06.2013 23:14, Team Prii wrote:

> ... The runU() function has
> also grown to be an eye sore. There are many different run modes and
> each one share some common code but also has individual different code.
> So I end up with many switch statements mixed with the common part,
> making it hard to read. I am thinking of separating each run mode into
> its own function but don't want to have multiple copies of the common
> part. One of the common part is that every second the clock display is
> updated and a set of values is saved to a csv file. Maybe that part
> should be running in another thread?

Well, it depends. In my model there is always only one active worker
thread. Whether this thread needs to save data to a csv file may be
different (simulation and "replay mode" wouldn't, but "car mode" would).
So this could be a subroutine (function) that each worker thread could
call, if applicable.

Changing the clock display every second would, surprisingly ;-), be a
task for Fl::add_timeout/Fl::repeat_timeout. You can start as many
timers as you like, and they will run independently. Since they run as
callbacks in the main FLTK thread, there's no locking necessary, and
such a clock timer could be started in the initialization of your
program, just as the (still empty ?) timer you're using now, and it
would run "forever". No need to stop, synchronize, or whatever.

... and while we're at it: your license or copyright display wouldn't
need a thread as well. Usually you would use a modal window that blocks
all other windows until it is closed. ... I just looked at some old
code. AFAICT there's no thread in it, but in the version I saw there is
an own message loop involved, see showLic() in ini.cpp.

A modal window would simplify this significantly:

in main, right after showing the main window, insert the following code
(simplified here, you may need something more):

main_window->show(argc, argv); // insert after this line

about_window = make_about_window(); // should return the window
about_window->set_modal(); // make it modal
about_window->show(); // "blocks" the main window until closed

// now continue as before:

return Fl::run();

Simple, isn't it?


Albrecht

Team Prii

ungelesen,
27.06.2013, 10:34:3327.06.13
an fltkg...@googlegroups.com
OK I just moved the updates of the clock and some other things (specifically the units of fuel and distance) into a 2nd timer call back function in the main thread. It's working, even without redraw(). So does the built in label() and value() call redraw() automatically?





Albrecht

Albrecht Schlosser

ungelesen,
02.07.2013, 12:56:2902.07.13
an fltkg...@googlegroups.com
On 27.06.2013 16:34, Team Prii wrote:
> OK I just moved the updates of the clock and some other things
> (specifically the units of fuel and distance) into a 2nd timer call back
> function in the main thread. It's working, even without redraw(). So
> does the built in label() and value() call redraw() automatically?

IIRC value() does, whereas label() usually does not call redraw(),
although this may depend on the particular widget - I don't know for
sure. If you really want to know, you could try to look at the docs, but
if it's not documented (that may well be so), then only the source can
give you an answer.

As has been mentioned before (probably by Greg) FLTK is designed with
speed in mind, so usually simple setter methods like color, box,
x/y-coordinates, size (w,h) etc. do not call redraw to let the
programmer do it when really needed, but there may be exceptions (like
value).

Greg Ercolano

ungelesen,
02.07.2013, 13:18:0802.07.13
an fltkg...@googlegroups.com
On 07/02/13 09:56, Albrecht Schlosser wrote:
> As has been mentioned before (probably by Greg) FLTK is designed with
> speed in mind, so usually simple setter methods like color, box,
> x/y-coordinates, size (w,h) etc. do not call redraw to let the
> programmer do it when really needed, but there may be exceptions (like
> value).

It's wise to call redraw() on the widget you think might need
to be redrawn just before returning to the app loop.

Depending on the context, a call to redraw() can end up looping
through the widget hierarchy, so FLTK thinks it's best in many
contexts to leave redraw() logic up to the app programmer.

For instance, if the app decides it needs to change 1000 things
in a single operation, it may be better to have the user call
redraw() once on the parent widget, instead of having to call
redraw() 1000 times, once for each change.

I think the design philosophy is, if the widget's method to change
a value (e.g. label()) can be very low latency, such as just changing
a char*, then not calling redraw() can make the call efficient during
a mass change. But if the method is already slow (eg. Fl_Input::value()),
then it may do the user a favor and call redraw() just because it thinks
it can do it more efficiently (just redraw the text changed, not the
entire widget), then it may do the redraw() logic itself.

Team Prii

ungelesen,
08.07.2013, 14:53:3708.07.13
an fltkg...@googlegroups.com
I am trying to implement the thread usage suggested by Albrecht in a previous message:

============== quote =================


One more note to your usage of threads. It seems to me that your demo/simulation program starts one thread (the UI thread) right at startup time, and this does different things throughout the life time of the program: either run the simulation, or do some idle display. In your real program, there might be even more things to do: real time data acquisition or replay of stored data from a file. So I would probably change this to start a thread with a single function from the button callback and let it run until it is said to stop, like this:

(1) enable all buttons that can be used to start a function:
  (a) simulate
  (b) run (car mode)
  (c) replay

(2) in each button's callback(!):
  - disable all *other* buttons
  - change the button's label to "Stop" or whatever
  - set status to "running"
  - start the thread for this function

(3) when the button is clicked again ("Stop"), then in its callback:
  - disable this button (yes!)
  - set the button's label to "stopping..."
  - set status to "stop" to notify the thread that it has to finish

(4) in the thread:
  - if status is "stop", terminate activities
  - send Fl::awake(... simulation_stopped_cb ...)
  - terminate the thread
  This tells FLTK that the simulation has stopped.

(5) in simulation_stopped_cb():
  - set all button labels to "Start" again
  - enable all buttons

This way you can do all FLTK widget changes in the callback(s) of the button or in the "stopped" callback, triggered by the thread. There's no need to use Fl::lock() in the thread to change the buttons or enable/disable them.

============= end quote ===============

I really like the approach but am having some difficulty. I narrowed down the problem in the function setUIstate() below. It is called by the callback function for the Go/Stop button that starts/stops a simulation or actual data collection. A worker thread is started in the case Running. In the case Stopped, I would like to wait for the worker thread to end before re-activating the button. But the program hangs right at the workerThread.join() statement. If I comment out the statement like what's shown below, then the program runs fine (but the button is re-activated too soon; before the worker thread ends).

Any suggestions? Thanks!

void setUIstate(UI_state_Type UIst) { // Called by go_button_CB call back function in the main thread
   
static boost::thread workerThread; //thread for runUI()
   
    switch (UIst) {
        case Stopped:{ // "Stop" button has just been clicked to stop running
            go_button->deactivate();
            go_button->copy_label("Stopping"); go_button->redraw(); Fl::check();
            printf("UI stopping\n");fflush(stdout);
           
            //wait for UI thread to end
//            printf("waiting for runUI() thread to end ... ");fflush(stdout);
//            workerThread.join();
//            printf("thread ended. \n");fflush(stdout);

           
                go_button->copy_label("Go");
                go_button->activate();
                go_button->redraw();//Fl::flush();//Fl::unlock();
                g_gs.go_button->copy_label("Go");               
                settings_group->activate();
                run_type_choice->activate();
            //runUI();
        }
            break;
        case Running:{ // "Go" button has just been clicked to start running
            settings_group->deactivate();
            run_type_choice->deactivate();
            go_button->deactivate();
            go_button->copy_label("Starting"); go_button->redraw(); Fl::check();
            printf("UI starting\n");fflush(stdout);

           
    printf("runUI() thread starting ... ");    fflush(stdout);
    workerThread = boost::thread(runUI);
    printf("runUI() thread started\n");    fflush(stdout);
           
            init_filenames();
            //runUI();
        }
            break;
    }
}



--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.

Team Prii

ungelesen,
08.07.2013, 15:42:2208.07.13
an fltkg...@googlegroups.com
OK I think I know what went wrong. The worker thread is not lock free yet. If the callback function prevents the worker thread from getting Fl:lock(), then the worker thread can't end. Then the callback function is stuck at waiting for the worker thread to end. Thus the program hangs. Does this make sense?

Now I understand better the need of step (4) and (5) in Albrecht's message. Thanks!

Ian MacArthur

ungelesen,
08.07.2013, 16:45:3008.07.13
an fltkg...@googlegroups.com
On 8 Jul 2013, at 19:53, Team Prii wrote:

I really like the approach but am having some difficulty. I narrowed down the problem in the function setUIstate() below. It is called by the callback function for the Go/Stop button that starts/stops a simulation or actual data collection. A worker thread is started in the case Running. In the case Stopped, I would like to wait for the worker thread to end before re-activating the button. But the program hangs right at the workerThread.join() statement.


Yes, that's what I'd expect a thread join function to do.
I'm not familiar with the boost implementation, but here's a quote from the man page for pthread_join, which is typical of the type...

"The pthread_join() function suspends execution of the calling thread until the target thread terminates, unless the target thread has already terminated."

Which is what you are seeing. You spawn your worker thread, then you block on it when you execute the join - thereby somewhat negating the use of spawning the worker in the first place...

The "normal" use for thread join would be if you have one master thread that starts several workers, and wants to wait until they all finish before continuing. It's not really appropriate in your use here, though.

What you really want to do is have your worker thread call an awake_callback just before it expires, and have the awake_callback function set the button back to the "active" state.

I think that is what Albrecht was suggesting in his example, too...




Ian MacArthur

ungelesen,
08.07.2013, 16:50:0108.07.13
an fltkg...@googlegroups.com
On 8 Jul 2013, at 20:42, Team Prii wrote:

> OK I think I know what went wrong. The worker thread is not lock free yet. If the callback function prevents the worker thread from getting Fl:lock(), then the worker thread can't end. Then the callback function is stuck at waiting for the worker thread to end. Thus the program hangs. Does this make sense?


Ermmm, no, not really - it's not Fl::lock() that is causing your code to block, it is joining the worker thread that causes the block.

Calling join on a worker thread, from the main thread, will always block the main thread (and hence the fltk GUI) until the worker expires, so you really do not want to do that.

Just let the main thread continue as normal, but have the worker send a message to set the button state back to active just before it expires.





Team Prii

ungelesen,
09.07.2013, 08:13:0009.07.13
an fltkg...@googlegroups.com
Thanks! But if it's not Fl::lock() then I don't see why the worker thread gets stuck - it no longer updates the graphics - looks like it's stuck at "Fl::lock(); redraw(); Fl::unlock();" - and as such it can't end.

At any rate I implemented Fl::awake as in step (4) and (5) in Albrecht's message and that worked.

Quick question: Do I need Fl::lock() / unlock() pair surrounding Fl::awake ?


MacArthur, Ian (Selex ES, UK)

ungelesen,
09.07.2013, 10:35:5909.07.13
an fltkg...@googlegroups.com
> Thanks! But if it's not Fl::lock() then I don't
> see why the worker thread gets stuck - it no longer
> updates the graphics - looks like it's stuck at
> "Fl::lock(); redraw(); Fl::unlock();" - and as such
> it can't end.

That's a symptom, not a cause, however.

Here's the breakdown:

- All drawing is handled by the main thread.
- Calling thread.join() in the main thread, causes the main thread to block.
- Since the main thread is blocked, no GUI redraws can occur.
- Eventually, everything that wants to draw from a worker thread will try to lock, and at that point the worker threads will also be blocked, since the lock will never be given (the main thread is blocked...)
- Deadlock...

So the kicker is; *Never* block the main thread in a fltk GUI program.


> At any rate I implemented Fl::awake as in step (4)
> and (5) in Albrecht's message and that worked.
> Quick question: Do I need Fl::lock() / unlock() pair
> surrounding Fl::awake ?

I don't think you need to lock() around an awake() call.



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

Team Prii

ungelesen,
09.07.2013, 10:47:3209.07.13
an fltkg...@googlegroups.com
Thanks! That's exactly the same as I thought. I just didn't express it clearly enough I guess. And I see the point of not blocking the main thread in general. Thanks!


Team Prii

ungelesen,
09.07.2013, 13:43:0409.07.13
an fltkg...@googlegroups.com
In the worker thread, instead of "Fl::lock(); redraw(); Fl::unlock();", can I say

Fl::awake(redraw, userdata);

? Seems the laziest lock-free approach if it works.

Ian MacArthur

ungelesen,
09.07.2013, 14:17:2209.07.13
an fltkg...@googlegroups.com
On 9 Jul 2013, at 18:43, Team Prii wrote:

> In the worker thread, instead of "Fl::lock(); redraw(); Fl::unlock();", can I say
>
> Fl::awake(redraw, userdata);
>
> ? Seems the laziest lock-free approach if it works.

As written, that will not work - redraw has the wrong signature for passing to awake(), and redraw is a widget method, so needs to know what widget it refers to.

Instead you could do something like....


void do_the_redrawing (void *data)
{
widget1->redraw();
widget2->redraw();
...etc...
}

then call

Fl::awake(do_the_redrawing, data);


And that will sort of work.

Except that really, it will not.

Why not?

Because calling awake() asks the main thread to run the do_the_redrawing() callback, at some point in the near future.

If the worker thread goes off and modifies the data in the meantime, then you can not know what state the data will be in when the do_the_redrawing() callback actually runs, and you may get a race condition, or inconsistent data updates.

Now, if you are not too bothered about inconsistent updates (and in this case, we maybe are not) then that could well be "good enough".

Might be worth a shot at any rate.


Greg Ercolano

ungelesen,
09.07.2013, 14:56:1809.07.13
an fltkg...@googlegroups.com
On 07/09/13 10:43, Team Prii wrote:
> In the worker thread, instead of "Fl::lock(); redraw(); Fl::unlock();", can I say
>
> Fl::awake(redraw, userdata);
>
> ? Seems the laziest lock-free approach if it works.

I don't think you ever want to be 'lock free' when it comes
to calling any FLTK functions + methods from a child thread.

I believe the whole point of having the child thread call
Fl::lock() / Fl::unlock() is to make it safe to then call
any of the FLTK functions within to prevent reentrancy in
the fltk code.

Fl::awake() is itself an FLTK function, so I'd think it unsafe
to be called from a child thread without enclosing locks.


Team Prii

ungelesen,
09.07.2013, 15:55:5009.07.13
an fltkg...@googlegroups.com
Thanks for the advice! I will lock it then.


Ian MacArthur

ungelesen,
09.07.2013, 17:15:0109.07.13
an fltkg...@googlegroups.com

On 9 Jul 2013, at 20:55, Team Prii wrote:

> Thanks for the advice! I will lock it then.


Despite Greg's reservations, I think that the design of Fl::awake() is such that it ought to be safe to call it from a worker thread, without locking.

But... I'm not certain.

Might be handy to get Matt to pitch in here, I seem to recall he did a lot of the work on Fl::awake() along the way, so he probably has the best grasp of what it can / cannot do!




Ian MacArthur

ungelesen,
09.07.2013, 17:37:3309.07.13
an fltkg...@googlegroups.com
OK, I just stared at the code for Fl::awake(func, data); for a bit, and what it does is add the func and data parameters to a ring buffer, to be enacted later by the main thread.

The key point is that access to the ring buffer itself is protected by a mutex, so it should be safe to call Fl::awake(func, data); at any time without calling Fl::lock() first. Indeed, it might even be better to *not* lock first...






Greg Ercolano

ungelesen,
09.07.2013, 18:56:3709.07.13
an fltkg...@googlegroups.com
On 07/09/13 14:37, Ian MacArthur wrote:
> OK, I just stared at the code for Fl::awake(func, data); for a bit, and what
> it does is add the func and data parameters to a ring buffer, to be enacted
> later by the main thread.
> The key point is that access to the ring buffer itself is protected by a mutex,
> so it should be safe to call Fl::awake(func, data); at any time without calling
> Fl::lock() first. Indeed, it might even be better to *not* lock first...

Yes, if that's the case, I'd agree that it might be better not
to call a locking function to avoid a possible deadlock.

But if that's the case then I think we have a documentation problem, as it's not clear
which calls are protected and which aren't.

I read the docs for Fl::awake(func,data) and it wasn't clear to me this
was something that was safe to call from a child thread.

For me as a general rule, unless the docs explicitly say the code
is reentrant, it shouldn't be expected to be.

The docs for that method makes a sidehand reference to awake(msg),
which itself mentions the context of a threaded app, but to my eye
didn't seem clear about the calling conventions and being reentrant-safe.

I think we need to be crystal clear about reentrancy in the docs
for functions that support it, esp. if calling it from within a lock
can actually /cause/ a deadlock.

I think in this case Fl::lock() and the ring lock awake() uses
won't deadlock each other.. seems like the ringbuffer lock makes
awake(func,data) reentrant safe.


Team Prii

ungelesen,
09.07.2013, 23:08:4609.07.13
an fltkg...@googlegroups.com
So after all we should not Fl::lock() the Fl::awake() ?
 
If so then I suppose one could implement something along the line of Ian's earlier suggestion like below?

 void do_the_redrawing (void *widget){
  widget->redraw();
}

 Then in the worker thread, inside a widget, call

   Fl::awake(do_the_redrawing, this);
 
Would this be better than calling
 
Fl::lock(); redraw(); Fl::unlock();
 
inside the same widget?
 
I guess it depends on the amount of processing required by Fl::lock() versus that by locking the mutex for the ring buffer for Fl::awake(). I would guess locking the mutex would be faster but not sure.


MacArthur, Ian (Selex ES, UK)

ungelesen,
10.07.2013, 04:27:2410.07.13
an fltkg...@googlegroups.com
> Yes, if that's the case, I'd agree that it might be better not
> to call a locking function to avoid a possible deadlock.
>
> But if that's the case then I think we have a documentation
> problem, as it's not clear
> which calls are protected and which aren't.
>
> I read the docs for Fl::awake(func,data) and it wasn't clear to me
> this
> was something that was safe to call from a child thread.

I agree, the docs are not helpful here; ISTR checking the docs on a number of occasions to see whether awake was "thread safe" or not and been disappointed...

Knowing that awake is thread safe here (and that applies to the awake(func,data) form and the awake(msg) form, both should be safe) would clear things up, so probably should be stated explicitly!


> I think in this case Fl::lock() and the ring lock awake() uses
> won't deadlock each other.. seems like the ringbuffer lock makes
> awake(func,data) reentrant safe.

Yes, in this case, the mutex on the ring buffer, and the main fltk lock, are pretty much independent, so there's no real danger of a deadlock I believe.

MacArthur, Ian (Selex ES, UK)

ungelesen,
10.07.2013, 04:48:1910.07.13
an fltkg...@googlegroups.com
> So after all we should not Fl::lock() the Fl::awake() ?

You can if you like, but it is not *necessary* to do so. It should not be *harmful* to do so either, other than the implicit serialization between threads that it will introduce.

 
> If so then I suppose one could implement something
> along the line of Ian's earlier suggestion like below?
>
> void do_the_redrawing (void *widget){
>  widget->redraw();
> }

Yes, that would likely work - though I would be very, very wary of replacing individual calls to redraw() with multiple independent calls to awake().

I think I'd try to sweep up multiple redraws into a list then call awake() just once and have it iterate the list. That should be easier for the fltk run loop to process effectively, as it should be easier for it to coalesce multiple widget redraws if all the redraw requests occur "at the same time" rather than spread out.


 
> I guess it depends on the amount of processing
> required by Fl::lock() versus that by locking
> the mutex for the ring buffer for Fl::awake().
> I would guess locking the mutex would be faster
> but not sure.

Premature optimisation.[1]

You want to be calling lock/unlock or awake as *infrequently* as possible.

Worrying about their relative efficiency is not helpful, as the best way to make this work is to call them so rarely that their respective runtimes have no discernible impact on the overall timing of the program.

Worrying about how long each takes to run, because you are planning to call them *often* is not going to work, as that will still cause some implicit serialization of your threads, rendering the threading ineffective.
Even calling awake() from the worker thread will impact the operation of the main thread (though it will be relatively lock-free, it still manipulates the ring buffer and its associated mutex, and "nudges" the main thread to run.)

So calling awake() just once, and passing it a list of widgets to redraw, on a not-too-frequent basis, is perhaps your best bet.

At least it would be worth a try!




[1] It occurs to me that phrase may not have meaning for you, so I hope this might help:
http://en.wikipedia.org/wiki/Premature_optimization#When_to_optimize

Albrecht Schlosser

ungelesen,
10.07.2013, 06:19:3410.07.13
an fltkg...@googlegroups.com
On 09.07.2013 23:37, Ian MacArthur wrote:

> OK, I just stared at the code for Fl::awake(func, data); for a bit, and what it does is add the func and data parameters to a ring buffer, to be enacted later by the main thread.
>
> The key point is that access to the ring buffer itself is protected by a mutex, so it should be safe to call Fl::awake(func, data); at any time without calling Fl::lock() first.

That's true, it is not NECESSARY to lock, but ...

> Indeed, it might even be better to *not* lock first...

... that depends...

If you wanted to do more than one "atomic" updates to the GUI from a
thread, and if you also wanted to send more than one Fl::awake messages
within a worker thread in an "atomic" way, then you would enclose all
widget updates in the worker thread in Fl::lock/unlock, like so:

Fl::lock();
...
widget1->value(n);
widget1->label("new label");
Fl::awake(widget1_modified,data1);
...
widget2->value(m);
widget2->label("another label");
Fl::awake(widget2_modified,data2);
...
Fl::unlock();

This sequence assures that the FLTK main loop processes both messages
(i.e. the callbacks widget1_modified and widget2_modified) only after
the thread releases the lock and thus all modifications within the
worker thread appear to be atomic from a user's point of view.

However, if you want to delegate processing of widget updates to the
main thread at any time within a worker thread w/o blocking the worker
thread (!) then you should NOT use Fl::lock/unlock in the worker thread
around Fl::awake() calls.

Particularly this sequence would ALWAYS be wrong (see below) in a worker
thread:

Fl::lock(); Fl::awake(...); Fl::unlock();

Although this is *technically* correct, this would only block the worker
thread unnecessarily (or burn a few cpu cycles, if the lock would be
granted immediately).

PS: see test/threads.cxx for a call to Fl::awake() outside the
lock/unlock done before.

Albrecht Schlosser

ungelesen,
10.07.2013, 06:43:4010.07.13
an fltkg...@googlegroups.com
On 10.07.2013 05:08, Team Prii wrote:
> So after all we should not Fl::lock() the Fl::awake() ?

Yes. See my previous reply for an explanation.

> If so then I suppose one could implement something along the line of
> Ian's earlier suggestion like below?
>
> void do_the_redrawing (void *widget){
> widget->redraw();
> }
>
> Then in the worker thread, inside a widget, call
>
> Fl::awake(do_the_redrawing, this);
> Would this be better than calling
> Fl::lock(); redraw(); Fl::unlock();
> inside the same widget?

That's all correct and COULD be done, but is this what you want to do in
your application? I guess not!

Note that whenever you schedule a redraw() from within a worker thread,
no matter which way you use, FLTK will draw the widget ASAP, i.e. at the
next time the FLTK run loop is done with processing events (including
Fl::awake() calls/messages) and before it waits for more events.
Remember that you call Fl::lock() once in the main thread before you
call Fl::run(). This is what FLTK does in Fl::run(), simplified and
parts in pseudo-code.

// here, within in Fl::run() FLTK holds the lock.

start:

// check, whether there are new messages (events, timers, others)
// if there are messages, process one, go back to start.

// now that we don't have any more messages pending:

Fl::flush(); // draw all widgets requested by redraw()

Fl::unlock(); // release the FLTK lock
wait_for_events(); // wait until some event wakes FLTK up
Fl::lock(); // acquire the lock

The (pseudo-)function wait_for_events() waits for all window events like
mouse moves, key presses, window resize events, timers, Fl::awake()
messages, and so on. Hence, each Fl::awake() call lets FLTK acquire the
lock again and thus potentially block a worker thread that calls
Fl::lock() until all drawing has been done.

If you would send multiple Fl::awake() messages from one or more
threads, processing would be strict sequentially, but there is a lot of
place for unpredictable behavior depending on timing issues (X11 version
would work differently than Windows native version, CPU speed, number of
cores, ...).

In YOUR case you DO NOT want to call redraw more frequently than you
need to update the widgets in the given FPS rate, so you DON'T want to
call redraw() from the worker thread. That's why my proposal added the
indirection through the "new_value" method or whatever I wrote before ;-)

> I guess it depends on the amount of processing required by Fl::lock()
> versus that by locking the mutex for the ring buffer for Fl::awake(). I
> would guess locking the mutex would be faster but not sure.

See Ian's reply.

Albrecht Schlosser

ungelesen,
10.07.2013, 06:49:3210.07.13
an fltkg...@googlegroups.com
On 10.07.2013 10:48, MacArthur, Ian (Selex ES, UK) wrote:

>> So after all we should not Fl::lock() the Fl::awake() ?
>
> You can if you like, but it is not *necessary* to do so. It should not be *harmful* to do so either, other than the implicit serialization between threads that it will introduce.

... but this serialization COULD be considered harmful, depending on
what you want to achieve.

I agree 100% with the following and can only emphasize that it is
essential in the given application to REDUCE the calls of redraw() to
the necessary update rate, as discusse before.

>> If so then I suppose one could implement something
>> along the line of Ian's earlier suggestion like below?
>>
>> void do_the_redrawing (void *widget){
>> widget->redraw();
>> }
>
> Yes, that would likely work - though I would be very, very wary of replacing individual calls to redraw() with multiple independent calls to awake().
>
> I think I'd try to sweep up multiple redraws into a list then call awake() just once and have it iterate the list. That should be easier for the fltk run loop to process effectively, as it should be easier for it to coalesce multiple widget redraws if all the redraw requests occur "at the same time" rather than spread out.
>
>
>
>> I guess it depends on the amount of processing
>> required by Fl::lock() versus that by locking
>> the mutex for the ring buffer for Fl::awake().
>> I would guess locking the mutex would be faster
>> but not sure.
>
> Premature optimisation.[1]
>
> You want to be calling lock/unlock or awake as *infrequently* as possible.
>
> Worrying about their relative efficiency is not helpful, as the best way to make this work is to call them so rarely that their respective runtimes have no discernible impact on the overall timing of the program.
>
> Worrying about how long each takes to run, because you are planning to call them *often* is not going to work, as that will still cause some implicit serialization of your threads, rendering the threading ineffective.
> Even calling awake() from the worker thread will impact the operation of the main thread (though it will be relatively lock-free, it still manipulates the ring buffer and its associated mutex, and "nudges" the main thread to run.)
>
> So calling awake() just once, and passing it a list of widgets to redraw, on a not-too-frequent basis, is perhaps your best bet.
>
> At least it would be worth a try!

Absolutely true. Aren't we discussing this right now anyway?

Albrecht Schlosser

ungelesen,
10.07.2013, 06:58:2510.07.13
an fltkg...@googlegroups.com
On 10.07.2013 10:27, MacArthur, Ian (Selex ES, UK) wrote:

>> Yes, if that's the case, I'd agree that it might be better not
>> to call a locking function to avoid a possible deadlock.
>>
>> But if that's the case then I think we have a documentation
>> problem, as it's not clear
>> which calls are protected and which aren't.
>>
>> I read the docs for Fl::awake(func,data) and it wasn't clear to me
>> this
>> was something that was safe to call from a child thread.
>
> I agree, the docs are not helpful here; ISTR checking the docs on a number of occasions to see whether awake was "thread safe" or not and been disappointed...

Indeed, for someone not familiar with the FLTK (or any GUI) way of event
processing the threads documentation leaves more questions open than it
should. In the past I never used threads with FLTK, so I didn't even try
to understand what was documented and how it worked. Now that I know
more about all this, I can clearly see that the docs should be improved.

> Knowing that awake is thread safe here (and that applies to the awake(func,data) form and the awake(msg) form, both should be safe) would clear things up, so probably should be stated explicitly!

Indeed, this is important. The entire Fl::awake() mechanism could be
documented much better.

>> I think in this case Fl::lock() and the ring lock awake() uses
>> won't deadlock each other.. seems like the ringbuffer lock makes
>> awake(func,data) reentrant safe.
>
> Yes, in this case, the mutex on the ring buffer, and the main fltk lock, are pretty much independent, so there's no real danger of a deadlock I believe.

I'd strongly guess this is by design - yes, I really believe it MUST be
so, otherwise it would be a bug.

Team Prii

ungelesen,
10.07.2013, 11:11:1710.07.13
an fltkg...@googlegroups.com
Thank you! I have been wondering what Fl::run() does for a long time.

============== quote ===============


// here, within in Fl::run() FLTK holds the lock.

start:

// check, whether there are new messages (events, timers, others)
// if there are messages, process one, go back to start.

// now that we don't have any more messages pending:

Fl::flush();            // draw all widgets requested by redraw()

Fl::unlock();           // release the FLTK lock
wait_for_events();      // wait until some event wakes FLTK up
Fl::lock();             // acquire the lock

The (pseudo-)function wait_for_events() waits for all window events like mouse moves, key presses, window resize events, timers, Fl::awake() messages, and so on. Hence, each Fl::awake() call lets FLTK acquire the lock again and thus potentially block a worker thread that calls Fl::lock() until all drawing has been done.

============ end quote ==============

If I understand it correctly, events such as timers and Fl::awake() cause wait_for_events() to end, the main thread acquires Fl::lock() , and goes back to "start:".

How about "Fl::lock(); redraw(); Fl::unlock();" from a worker thread? Would that also trigger the wait_for_events() to end?


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral+unsubscribe@googlegroups.com.

MacArthur, Ian (Selex ES, UK)

ungelesen,
10.07.2013, 11:47:5310.07.13
an fltkg...@googlegroups.com
> If I understand it correctly, events such as timers and
> Fl::awake() cause wait_for_events() to end, the main thread
> acquires Fl::lock() , and goes back to "start:".
> How about "Fl::lock(); redraw(); Fl::unlock();" from a
> worker thread? Would that also trigger the wait_for_events()
> to end?

Yes. If you are in the "wait_for_events" state then a redraw from *anywhere* can cause the wait to end.

The main thread will then attempt to acquire the lock (and may have to wait for the worker thread to release it to do so) and will then flush the pending drawing operations, before releasing the lock and waiting once more.

Which is why you do not want to send a lot of awake or redraw calls from the worker thread, you don't want to be triggering the main thread to flush the pending events all the time.
You are much better off "coalescing" a number of events then flushing them all in one go, which will allow the re-drawing of the widgets to be done more efficiently.

Also, if the main thread and the worker thread are constantly arguing over whose turn it is to hold the lock, the two threads become serialized, which is to say that they stop being independent threads and in effect become one thread of execution, but written in a really incomprehensible manner...

So send the minimum amount of awake or redraw events from the worker to the main thread, and things can run a lot more effectively. This is counterintuitive: sending *fewer* updates from the worker to the main thread can cause the display updates to appear smoother...

Albrecht Schlosser

ungelesen,
10.07.2013, 12:10:4410.07.13
an fltkg...@googlegroups.com
On 10.07.2013 17:47, MacArthur, Ian (Selex ES, UK) wrote:
>> If I understand it correctly, events such as timers and
>> Fl::awake() cause wait_for_events() to end, the main thread
>> acquires Fl::lock() , and goes back to "start:".
>> How about "Fl::lock(); redraw(); Fl::unlock();" from a
>> worker thread? Would that also trigger the wait_for_events()
>> to end?
>
> Yes. If you are in the "wait_for_events" state then a redraw from *anywhere* can cause the wait to end.

Sorry, no, I have to correct you (and I hope I'm not wrong with this).

redraw() by itself does NOT make the main FLTK thread wake up (that's
why the OP had to "wiggle the mouse" to get the display updated in the
old program version). Side note: redraw() = damage(something) and does
not do much more that flagging a widget (and maybe its parent(s)) to be
"dirty". No messages and no awake() involved.

However, this makes the program *indeterminate* as of WHEN the draw may
happen. Remember that the "Cygwin/X11 version" worked *differently* when
we started this thread, although the redraw() logic (within the thread)
was the same.

Again, to the "Team Prii": you DON'T want to call redraw() from the
worker thread frequently, since that would make the display updates much
faster than a human could grok, and would waste CPU time and eventually
flood (and overflow) the FLTK event processing with unnecessary draw()
calls.

> The main thread will then attempt to acquire the lock (and may have to wait for the worker thread to release it to do so) and will then flush the pending drawing operations, before releasing the lock and waiting once more.
>
> Which is why you do not want to send a lot of awake or redraw calls from the worker thread, you don't want to be triggering the main thread to flush the pending events all the time.
> You are much better off "coalescing" a number of events then flushing them all in one go, which will allow the re-drawing of the widgets to be done more efficiently.
>
> Also, if the main thread and the worker thread are constantly arguing over whose turn it is to hold the lock, the two threads become serialized, which is to say that they stop being independent threads and in effect become one thread of execution, but written in a really incomprehensible manner...
>
> So send the minimum amount of awake or redraw events from the worker to the main thread, and things can run a lot more effectively. This is counterintuitive: sending *fewer* updates from the worker to the main thread can cause the display updates to appear smoother...

Yep.

MacArthur, Ian (Selex ES, UK)

ungelesen,
10.07.2013, 12:36:1410.07.13
an fltkg...@googlegroups.com

> > Yes. If you are in the "wait_for_events" state then a redraw from
> *anywhere* can cause the wait to end.
>
> Sorry, no, I have to correct you (and I hope I'm not wrong with this).
>
> redraw() by itself does NOT make the main FLTK thread wake up (that's
> why the OP had to "wiggle the mouse" to get the display updated in the
> old program version). Side note: redraw() = damage(something) and does
> not do much more that flagging a widget (and maybe its parent(s)) to be
> "dirty". No messages and no awake() involved.

Yes, that makes sense.
OK, I stand corrected!

Team Prii

ungelesen,
10.07.2013, 13:42:2310.07.13
an fltkg...@googlegroups.com
I did a quick test by changing one of the timer callback from 30 FPS to 1 FPS. Now the updates are visibly much less frequent. So indeed the redraw() does not seem to wake up the main FLTK thread.

For now I have "Fl::lock(); redraw(); Fl::unlock();" from the worker thread. In actual driving the frequency of (all of the) redraw() ranges from a couple of dozen per second to a few hundred per second, no more than five or six hundred per second. The actual drawing is triggered by the timer callback at 30 FPS. I think this works out pretty well. The main thread locks only at 30 Hz. Most of the time in between the timer callbacks the main thread is unlocked and the worker thread can acquire lock easily.

The next step would be to implement the "lock free" approach but that will take some more time. Thanks!


--
You received this message because you are subscribed to a topic in the Google Groups "fltk.general" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/fltkgeneral/-bIjXnLQCL4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to fltkgeneral...@googlegroups.com.

Team Prii

ungelesen,
11.07.2013, 12:49:0611.07.13
an fltkg...@googlegroups.com
I was curious to see how accurate the timer callback is so I put in a simple counter:

const double FPS = 30; // frame per second
void timer_cb_draw(void *v) {
   Fl::repeat_timeout(double(1.0)/FPS,timer_cb_draw);
   ++timer_cb_draw_count;
} // timer_cb_draw()

and in a separate, slower timer callback I tally the number and calculate the actual FPS:

static int timer_cb_draw_count=0;

void timer_cb_clock(void *v) {
   Fl::repeat_timeout(double(1.0),timer_cb_clock);
  
   static double t = gettime(), dt=0;
   static int prev_timer_cb_draw_count = timer_cb_draw_count;
   static int prev_redraw_count = Fl_Gauge::num_redraws;
   static int prev_draw_count = Fl_Gauge::num_draws;
  
   dt = gettime() - t;
   if(dt>0){
       g_gs.FPS_G->value((timer_cb_draw_count-prev_timer_cb_draw_count)/dt);
       g_gs.redrawpers_G->value((Fl_Gauge::num_redraws-prev_redraw_count)/dt);
       g_gs.drawpers_G->value((Fl_Gauge::num_draws-prev_draw_count)/dt);
       t = gettime();
       prev_timer_cb_draw_count = timer_cb_draw_count;
       prev_redraw_count = Fl_Gauge::num_redraws;
       prev_draw_count = Fl_Gauge::num_draws;
   }
...

Guess what I found? The Cygwin version keeps the FPS pretty much right on the expected value of 30. On the other hand the MinGW version only runs around 20 FPS! This may relate to the quantization that Ian has described before.


MacArthur, Ian (Selex ES, UK)

ungelesen,
11.07.2013, 13:11:1111.07.13
an fltkg...@googlegroups.com
> Guess what I found? The Cygwin version keeps the
> FPS pretty much right on the expected value of 30.
> On the other hand the MinGW version only runs around
> 20 FPS! This may relate to the quantization that Ian
> has described before.

Quite possibly. The Cygwin/X11 version will be rendering into some X11 buffer surface, so what you are measuring is how often that internal buffer is updated - which is not necessarily the same thing as how often that buffer is mapped onto the display...

With the mingw version, you are probably interacting more directly with the physical h/w, and it is possible that the results will be quantised in some way, perhaps related to the refresh rate of your display, for example.

What is the refresh rate of your monitor? 60Hz is quite common and that may mean refreshes of the display are quantised in steps of 16.67ms or thereabouts.
Though, that said, 30Hz ought to be attainable with a 16.7ms step!

I wonder if "repeat_timeout" really works on WIN32? I recall it doesn't on some systems, though not sure which ones... If it is not working on WIN32 then you'll likely get a bit of drift, and miss every other timeslot or so.

Note that, if you do miss every other update slot (i.e. 2 updates in every 3 slots, at 16.7ms) then you do get 20Hz out...

Mind you, for this purpose, I'd imagine 20Hz is fast enough anyway!


Also, note that monitor refresh rate sets an upper limit on how fast to bother refreshing - I recall having a lengthy discussion with some "keen" video gamer who had spent a lot of time building his custom games PC and was insisting that he was getting some very high FPS out of this rig (as reported by the GL renderer the game was using.)
He seemed fundamentally unable to grasp my view that he was just wasting cycles doing that, since his monitor was only refreshed at 60Hz anyway, so a lot of these frames were simply never displayed...

Albrecht Schlosser

ungelesen,
12.07.2013, 08:25:3812.07.13
an fltkg...@googlegroups.com
On 11.07.2013 19:11, MacArthur, Ian (Selex ES, UK) wrote:

> I wonder if "repeat_timeout" really works on WIN32? I recall it doesn't on some systems, though not sure which ones...

I believe that the WIN32 version of repeat_timeout does not correct the
drift correctly, but the X11 (aka Cygwin) version does. See the docs of
Fl::repeat_timeout().

This would mean that small timer misses would not be adjusted, getting
maybe only 29 FPS if you try 30 FPS.

> If it is not working on WIN32 then you'll likely get a bit of drift, and miss every other timeslot or so.

Yes, if there IS a quantisation in the Windows timer mechanics, then
you'll likely get MUCH slower FPS rates. However, I doubt that it is to
do with the display refresh rate, since the timer would be restarted
before the actual drawing takes place. Hence, there should be enough
time to do the drawing while waiting for the next timer expiration -
unless drawing takes too long, so that the timer expires while drawing
is still active, and that would add to the drift. Or something like that.

FWIW: I found my old test program with a 1-sec. timer with
repeat_timeout. This program simulates a work load *before*
repeat_timeout() is called to see if repeat_timeout works as defined.

Result (summary):

repeat_timeout() works as expected with pretty good accuracy on Linux
and Cygwin/X11.

On native Windows (WIN32) it does NOT compensate for the difference as
documented *and* there is some quantisation, as Ian described.

Note that all tests were executed on the same Windows 7 box. The Linux
test was run using a Virtualbox VM with Ubuntu 12.04 LTS 64-bit.
Interesting point is that the Linux VM timing does NOT suffer from the
seen Windows timer quantisation.

FYI, I'm attaching 2 files:

(1) repeat_timeout.cxx, the test program used
(2) repeat_timeout.txt, the result lists from different tests

The results file information should be obvious. Note that Cygwin/win32
(i.e. Cygwin compiled with the MinGW cross compiler) behaves like the
version compiled under MinGW. I did not test Cygwin (--enable-cygwin)
with native (win32) drawing, but I don't think that this would change
anything (though it ought to work like Linux).

Have fun,
Albrecht

repeat_timeout.cxx
repeat_timeout.txt

MacArthur, Ian (Selex ES, UK)

ungelesen,
12.07.2013, 08:57:2112.07.13
an fltkg...@googlegroups.com
> > I wonder if "repeat_timeout" really works on WIN32? I recall it
> doesn't on some systems, though not sure which ones...
>
> I believe that the WIN32 version of repeat_timeout does not correct the
> drift correctly, but the X11 (aka Cygwin) version does. See the docs of
> Fl::repeat_timeout().


Yup.
I stared at the code for a bit yesterday evening.
Linux and OSX will attempt to compensate for timer deltas in the repeat timeout call - and I assume also Cygwin will, since it uses its own Unix-like timer scheme.

Native win32 timers do not compensate... And there is quite bad quantization, by default.

> This would mean that small timer misses would not be adjusted, getting
> maybe only 29 FPS if you try 30 FPS.

That's what I expected, but in tests on the Win7 box, the rate is dominated by the timer quantization, so a little bit of drift here or there is undetectable...



> Yes, if there IS a quantisation in the Windows timer mechanics, then
> you'll likely get MUCH slower FPS rates. However, I doubt that it is to
> do with the display refresh rate, since the timer would be restarted
> before the actual drawing takes place. Hence, there should be enough
> time to do the drawing while waiting for the next timer expiration -
> unless drawing takes too long, so that the timer expires while drawing
> is still active, and that would add to the drift. Or something like
> that.

Oh yes indeed... See below!


> FWIW: I found my old test program with a 1-sec. timer with
> repeat_timeout. This program simulates a work load *before*
> repeat_timeout() is called to see if repeat_timeout works as defined.

I also found my old test program from the last time we looked at this; this renders a complicated-ish GL scene, and has a valuator so I can dial in a desired FPS, and an output that reports the attained FPS.

There's also a check-box that bypasses the add_timeout altogether and just free-runs to see how fast we might go.

Tested on OSX 10.6.8 and Win7 (not tried on Linux.)

OSX pretty much gives the requested value, but always just a smidge slow. That is, if I ask for 40 FPS I maybe get 39.2 or some such. But basically it works. As the valuator changes, the FPS attained tracks it pretty closely, so not too bad.


On Win7, things are really horribly quantized, in ~15.5ms steps.

So, small number are not too bad. If I ask for 5 FPS, I get 5 FPS.
But as the frame rate rises I start to see the effects of quantization more, so if I ask for 16 FPS, I get ~15.8 (not too bad) but I go on getting about 15.8 FPS as I increase the demand until, at a demand of 22 FPS the attained rate suddenly jumps from 15.8 to 21.6...

Then it stays as 21.6 until the demand reaches 33 FPS when the attained suddenly steps up to 32.2. The next step happens at a demand of 63 FPS when I suddenly see the attained jump to 64.2 (i.e. faster than I asked for...)
It then sticks at 64.2 and never goes any faster, regardless of what timer value I set.

If I bypass the timer and let the code run free, it does approx. 1500 FPS, so the timers must be limiting the operation here, not the actual hardware or the complexity of the scene.

Basically, it looks like the Win32 timers we are using can/will not tick any faster than ~15.5ms on Win7, and that is what dominates the behaviour seen here. (So, this is true independent of what the monitor refresh etc. might be, regardless of what I said yesterday!)

Team Prii

ungelesen,
12.07.2013, 09:26:4712.07.13
an fltkg...@googlegroups.com
Thanks! It's good to confirm the weird behavior of Win7. On my laptop the FPS fluctuates around 20.x and 21.x when I ask for 30, consistent with Ian's 21.6 number.

I suppose in general it's better to put Fl::repeat_timeout at the beginning of the timer callback function, right?

I have a separate question/problem now. In my program there is a "Gauges" window that contains a bunch of "FL_Gauge" widgets. To make the program lock-free, as discussed earlier, we want to have a list of the widgets in the main thread for the timer callback function to walk through to redraw. Now instead of hard-coding the list (and requiring changes later if new widgets are added), I figured I could add the "this" pointer to the list when each widget is created. So in the Fl_Gauge.cxx file I have

extern std::vector<Fl_Gauge*> roll_Gs;


/******************************************************************************
 *                                 type
 ******************************************************************************/
  void Fl_Gauge::type(int t){ // set type of gauge
      int rp=2, dp=0;
      Type=t;
      switch (Type){
          case FL_GAUGE_VERTICAL:{
              max(65);min(10);step(10);stepdiv(5);rp=2; dp=0;valueplaces(rp,dp);
              redlinestart(45);greenzoneend(20);greenzonestart(10);valuecolor(labelcolor());scalefontsize(10);
              textcolor(my_fl_light_gray);textcolor0(my_fl_light_gray);textcolorG(FL_DARK_CYAN);textcolorR(FL_YELLOW);
              valuefontsize(28);
              dialcolor(my_fl_dark_gray);dialcolor0(my_fl_dark_gray);dialcolorG(FL_DARK_BLUE);dialcolorR(FL_RED);
              pointercolor(FL_DARK_RED);pointercolor0(FL_DARK_RED);pointercolorG(FL_DARK_RED);pointercolorR(FL_YELLOW);
          } break;
          case FL_GAUGE_XY:{
              //min(-20);max(20);step(10);stepdiv(5);
              redlinemode(FL_GAUGE_RL_OFF);greenzonemode(FL_GAUGE_GZ_OFF);
              pointercolor(FL_GREEN);valuecolor(labelcolor());
              v2mode(FL_GAUGE_V2_ON);
          } break;
          case FL_GAUGE_SQUARE:{
              //pointercolor(labelcolor()); //not working because need to put in fl file otherwise labelcolor() is not set yet and default to black
          } break;
          case FL_GAUGE_PSDPWR:{
              scalefontsize(10);
          } break;
          case FL_GAUGE_ROLL:{
              try {
                roll_Gs.push_back(this);
              } catch(exception& e) {
                  cout<<"roll_Gs.pushback(this) exception="<<e.what()<<endl;
              }
       cout<<"length of roll_Gs="<<roll_Gs.size()<<endl;
              yRollsLen = ((w()-2)<maxyRollsLen) ? (w()-2) : maxyRollsLen;
              yRolls = new int[yRollsLen];
                int Y = y(), H = h();
                double v = 0;
                if (v > Max){v = Max;} else if (v < Min){v = Min;}
                int gy = Y+1 + (v-Max)*(H-2)/(Min-Max);
              for (int j=0; j<yRollsLen; ++j){
                  yRolls[j] = gy;
              }
          } break;
         
          default:{
          } break;
      }
  }

Basically just for testing I only register certain type of widget to the list.

Then in the main.cpp file I have

vector<Fl_Gauge*> roll_Gs;

const double FPS = 30; // frame per second
void timer_cb_draw(void *v) {
   Fl::repeat_timeout(double(1.0)/FPS,timer_cb_draw);
   ++timer_cb_draw_count;
  
   static int jh=0;
   if (jh<6){++jh;}else{
       jh=0;
       cout<<"length of roll_Gs="<<roll_Gs.size()<<endl;
       for (int j=0; j<roll_Gs.size(); ++j){
           roll_Gs[j]->roll_val();
           roll_Gs[j]->redraw();
       }

       g_gs.kW_G->roll_val();
       // update rolling history graphs
   }
} // timer_cb_draw()

Basically the timer callback function look up the list and do redraw. In the main() function, all widgets are created and the window is showed long before the timer callback function is started near the end.

Now here is what I got when I ran the program:

$ ./PriiDash3
length of roll_Gs=1
length of roll_Gs=2
length of roll_Gs=3
length of roll_Gs=4

**********************************************************************
    PriiDash(TM)  Copyright (C) 2011 by TeamPrii at sourceforge.net
    This program is licensed under the GNU General Public License,
    with ADDITIONAL TERMS. For details click
    "About PriiDash(TM) ..." button in "Main Control" window.
**********************************************************************
portname=/dev/ttyS4, fuel vol unit=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0
length of roll_Gs=0

main_window "X" or gauges_window Quit button pressed

(By the way everything is in the main thread; no worker thread has been started yet.)
It looks like the 4 widgets are added to the list (vector roll_Gs) fine, but the timer callback function thinks the length of the list is zero!? What's going on?




MacArthur, Ian (Selex ES, UK)

ungelesen,
12.07.2013, 09:42:2012.07.13
an fltkg...@googlegroups.com
> Thanks! It's good to confirm the weird behaviour
> of Win7. On my laptop the FPS fluctuates around
> 20.x and 21.x when I ask for 30, consistent with
> Ian's 21.6 number.

Yes - if you want to get 30, you need to ask for more than 33. Then you'll get ~32...


> It looks like the 4 widgets are added to the list
> (vector roll_Gs) fine, but the timer callback function
> thinks the length of the list is zero!? What's going on?

It looks like your code thinks there are two different roll_Gs vectors, one of which is empty, rather than just the one.

Not sure why, without studying the code more.
You'd need to check the scope of your declarations, the externals and such, and make sure it's all sensible...

Other than that, not sure what to try!

Team Prii

ungelesen,
12.07.2013, 09:56:0912.07.13
an fltkg...@googlegroups.com
Thanks! In the main.cpp file I have

vector<Fl_Gauge*> roll_Gs;

and in the Fl_Gauge.cxx file I have


extern std::vector<Fl_Gauge*> roll_Gs;

and that's the only two places where roll_Gs is declared. Shouldn't the extern keyword tell the compiler to look for roll_Gs somewhere else (and find it in the main.cpp) as opposed to making another local variable? Maybe the syntax for complicated things like vector is different from simple variable types like int or double ?


Team Prii

ungelesen,
12.07.2013, 10:03:3812.07.13
an fltkg...@googlegroups.com
OK I added printout of the address (cout<<"length of roll_Gs="<<roll_Gs.size()<<" address at"<<hex<<&roll_Gs<<endl;) and it looks the same:

$ ./PriiDash3
length of roll_Gs=1 address at0x561520
length of roll_Gs=2 address at0x561520
length of roll_Gs=3 address at0x561520
length of roll_Gs=4 address at0x561520

**********************************************************************
    PriiDash(TM)  Copyright (C) 2011 by TeamPrii at sourceforge.net
    This program is licensed under the GNU General Public License,
    with ADDITIONAL TERMS. For details click
    "About PriiDash(TM) ..." button in "Main Control" window.
**********************************************************************
portname=/dev/ttyS4, fuel vol unit=0
length of roll_Gs=0 address at0x561520
length of roll_Gs=0 address at0x561520
length of roll_Gs=0 address at0x561520
length of roll_Gs=0 address at0x561520
length of roll_Gs=0 address at0x561520

Ian MacArthur

ungelesen,
12.07.2013, 15:05:5312.07.13
an fltkg...@googlegroups.com
On 12 Jul 2013, at 15:03, Team Prii wrote:

> OK I added printout of the address (cout<<"length of roll_Gs="<<roll_Gs.size()<<" address at"<<hex<<&roll_Gs<<endl;) and it looks the same:

Still, vector et al are C++ template code, so the thing you are testing the address of is not necessarily a "thing" in the sense of being a simple variable, or a function, or something...

That said, templates is going outside my comfort zone, as I mainly do embedded and high-integrity stuff, and use of templates is actively discouraged (RTCA DO-178C refers...) so I don't use them all that much.

Note that templates are (hand wave alert) sort of like complicated macros that auto-generate the code you need at compile time, so it is possible (I do not know enough about templates to say) that accessing the template code in two distinct compilation units means that two distinct elements of code will be auto-generated and executed - that would certainly explain the behaviours you are seeing.

For the purposes of testing, why don't you just declare an array of Fl_Gauge* and use that - in the grand scheme of things, declaring a fixed array with, say, 128 pointers is still a tiny amount of RAM compared to everything else that's going on!

So, declare the array, then memset it to 0 at startup.
In your add routine, just walk the array until you find the first empty slot and add your gauge.
In reading them back, just walk the array processing each entry until you hit a null.
Seems simple enough. You could even maintain a counter to record the size if you wanted...




Team Prii

ungelesen,
12.07.2013, 16:04:0712.07.13
an fltkg...@googlegroups.com
Gee, why do I have a feeling that all the nice improvements of C++ over C are just beautiful on the surface but ugly and even dangerous inside?

I was hoping to save some work by using vector but if needed I will make an array as suggested. Thank you!

For now I moved the vector inside the "Gauges" window (as a class member) and that seems to work. The only complaint is that now Fl_Gauge has to know about the "Gauges" window to access the vector, forcing another header file to be added in Fl_Gauge.cxx. Maybe I should try to make the vector a static member of the Fl_Gauge class instead. Thanks again for the continuing help!


Weitere Nachrichten werden geladen.
0 neue Nachrichten