Thread safety accessing GUI - Page 1
Forum section: electronics / general

author: mag_therm
author_rank: Frequent Contributor
timestamp: November 24, 2020, 05:47:43 pm
content: We are updating projects originally written in <Name> IDE and running on linux, 6 core Intel embedded in a machine.Latest IDE version just out has disabled all ability of threads to communicate with graphics. Reason: Not Thread Safe.As workaround, a GUI update event is added to the threads as a channel forraising of dialog windows, accept/cancel buttons etc. All graphics access inside a thread now throw an error.In one of the projects involving a long time run (up to 8 minutes) of a numerical solver, this is causing a major re-edit,as there were direct dialogs all through the original process so user can steer it.I am wondering , can anybody explain the underlying reason for abrupt change like this?Was there a thread safety problem somewhere?Original code is rather untidy, ran without problems for 10 years deployed all over the world.Why can't threading be left to the developer?Or even, make the change and explain it but leave the old code still operable.Sorry I rant a bit, I did a day of edit yesterday and still have problems.

author: ataradov
author_rank: Super Contributor
timestamp: November 24, 2020, 08:14:54 pm
content: You need to be more specific. How is IDE related to your project? Or you are using IDE framework (like Eclipse) as a base for your project?Generally graphics toolkits are not thread-safe. In many cases this is because underlying OS APIs for UI work are not thread safe.But that is also not a problem usually. Your worker threads just need to do the work and communicate the results to the main UI thread.Again, hard to tell something concrete without knowing what you are talking about.

author: mag_therm
author_rank: Frequent Contributor
timestamp: November 24, 2020, 08:37:18 pm
content: Hi Alex, yes, the whole project (UI and code) is in the IDE which compiles an exe.I didn't want to name it because I am being a bit critical.The communication from within the thread, to a graphic object has been blocked.For example passing text and raising a message box in the main window.With that change they just made, the thread can only do that through a new event that has been added to the thread.That might be OK for newly written code, but to edit a large project, is a major job.Moreover I think there are other problems with that based on tests I am running today. Not sure yet.I really wanted to know why accessing UI graphics objects from code within a thread is now "Not Thread Safe"after it has been OK for 15 years.Thanks

author: ataradov
author_rank: Super Contributor
timestamp: November 24, 2020, 08:49:40 pm
content: Quote from: mag_therm on November 24, 2020, 08:37:18 pmThat is a correct approach. This is pretty typical for UI libraries.Quote from: mag_therm on November 24, 2020, 08:37:18 pmIt may have worked by accident for 15 years, especially if you don't use the UI calls too often. But it was never safe.Multi-threaded programming sucks, multi-threaded UI is even more so. There is not much you can do this. Just bite the bullet and write the code so it works in a safe way.

author: mag_therm
author_rank: Frequent Contributor
timestamp: November 24, 2020, 09:13:24 pm
content: Alex, thanks for the info.Without using GPUs, the only way to do heavy numerical calculations in reasonable time was to use parallel processing.I now have 6 parallel processes and 6 real cores.5 of processes are allocated high priority rr in linux using systemctl and that is working well.That leaves the process 6 that has one thread plus the GUI windows.The numerical calcs are given higher priority in the thread.That one thread is where I am having the problem.

author: ataradov
author_rank: Super Contributor
timestamp: November 24, 2020, 09:23:50 pm
content: That is fine. But I still don't see why calculating threads need to use the UI. Let them calculate and put the result into some memory. Let the main thread get that result from the memory and display it on the UI.You may notify the main thread using the event or just standard mutex/semaphore/conditional variable types.

author: mag_therm
author_rank: Frequent Contributor
timestamp: November 24, 2020, 09:49:50 pm
content: Well it a bit involved to explain here but user often needs information to steer the calculation, or restart or close it early if unsuccessful etc.Other than that the results work as you mention.With latest IDE change the only way to access UI from thread it to call the newly added event in the Thread.All it accepts is a Dictionary.So one way I can see to give user a button to respond to, is the make an instance based on the content of the pair arriving in the Dictionary.

author: Nominal Animal
author_rank: Super Contributor
timestamp: November 25, 2020, 01:34:47 am
content: Qt5 really expectseach thread to run an event loop.One solution is to use two helper threads running their own event loops, communicating with Qt signals and slots with the GUI thread. One helper thread passes commands and requests from the GUI thread to the "raw" worker threads, and the other passes results back to the GUI thread. (The first thread spins in the event loop, and the second thread blocks in the result queue.)Most worker thread patterns make it rather straightforward to add these two helper QThreads. When using mutexes and condition variables to implement queues of commands and results, you can often omit the first worker, and have the main thread directly "post" the commands to the command queue. The second helper thread, spinning on result queue, and using Qt signals to post the result using a slot in the main thread, is the most important detail.GTK+ is not thread-safe, but the underlyingglibis. In particular,GDK_THREADS_ENTER()has been deprecated, and will not be supported in the future. Worker threads can useg_main_context_invoke(NULL,priority,function,payload_pointer,destroyerfunc)to be run by the main thread.If your problem is with either of these, there is nothing negative in your statements that I can detect – but I do not do "social cues" or "good manners" as I'm a particularly blunt Finn –, even if one reads it as if a Qt/GTK+ developer, in my opinion. They are toolkits, after all, not "rule books".I may be able to offer more detailed suggestions if your problem is with either of these, your programming language is C, C++, or Python, and you describe your current mechanism for passing commands (start/stop/continuethus/etc) and results (or progress/queries) between the GUI and the worker threads.

author: AntiProtonBoy
author_rank: Frequent Contributor
timestamp: November 25, 2020, 06:33:58 am
content: What little information you gave us, sounds like there is quite a bit of spaghetti code going on across many different threads. I'm afraid you will have to seriously rethink how you organise your multi-threaded application.I recommend looking at concepts used by theActor Programming Model. Basically you have a bunch of self contained objects, called actors, that are tasked to perform a very specific duty. One might be the GUI handler, the other might be GPU scheduler, another that does networking. Each actor runs its own thread, and you can safely pass data between actors via a messaging system. Actors consume messages from a queue and serially executes them internally and sends results back to other actors (if appropriate) via each other's messaging system. You don't have to strictly adhere to the Actor Model, but thinking along the lines of each thread being a black box, or a completely self-contained system, where you copy data back and forth, is a good start for multi-threaded programming.Few other tips with respects to multi-threaded programming:* Data that was created by a thread, should be exclusively owned by that thread, and never shared.* When passing data between threads,movethe object from one thread to another (i.e. transfer ownership) instead of sharing.* If you can't transfer ownership,deep copy. This may sound expensive, but trust me, copying saves a lot of headaches and simplifies your APIs. And you might be surprised how performant copying can be.* If you can't copy, then share data that isimmutableto all threads touching it, and manage its lifetime with an atomic reference counter.* Try double/triple buffering if threads need to simultaneously read/write data. Atomically swap pointers.* If you absolutely must share a mutable object, use an atomic data type. If that's not possible, wrap it up with a mutex. This should be absolutely your last resort. Don't be tempted to wrap everything in a mutex and call it a day.* Never write your own spinlock algorithms. All you are doing is "Sheldon knocking" at an atomic flag, burn electricity, and introduce priority inversions.* Try using completion handlers for asynchronous tasks, rather than awaiting for a future object.* Use co-routines if you have such features available.* Copy, copy, copy.

author: Nominal Animal
author_rank: Super Contributor
timestamp: November 25, 2020, 07:10:23 am
content: Quote from: AntiProtonBoy on November 25, 2020, 06:33:58 amNote that both Qt 5 and GTK+3 windowing toolkits restrict how the GUI and non-GUI threads should pass data/messages.The common worker thread model used with all GUI toolkits (where the GUI thread runs the toolkit event loop, and must communicate asynchronously with worker threads doing computation or communication) matches the Actor Programming Model quite well.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 25, 2020, 03:41:43 pm
content: Generally speaking multi-threaded UIs are hard stuff, so all in all best avoided when possible. And yes, on top of that, many UI libraries are not thread-safe, not that it isn't possible in theory, but just the way it is.I remember back when I was doing some GUI stuff on Windows (pure Windows API), this was generally possible, although a bit slippery. I remember having done it more than once, and with proper care, it was fine. There was even a function (probably others, just remember this one) to help deal with multi-threaded UI: AttachThreadInput(), so that was definitely possible on Windows (again directly using the Windows API, whereas many high-level libraries were/are themselves not thread-safe...)Another memory regarding this, although to be taken with a pinch of salt, since I may not remember quite accurately: I think BeOS allowed multi-threaded UI and that was even one of its strong points? (People knowing BeOS better than I do may add more details...)Back to the OP, not knowing what platform and libraries they use, it's impossible to directly comment on that, so above was just a general thought.

author: mag_therm
author_rank: Frequent Contributor
timestamp: November 25, 2020, 04:01:15 pm
content: Thanks for the info. You have all given me some terms to look up and some reading.Sorry I can't publish expicit info about the application.We are doing an update of the the whole system,while at the same time I am working with an electrical engineer from the client company.He will take over the maintenance of the project.I will retire completely on December 31. Yee-HaaThis threading change has been the major problem, and we are well down the update specification's list.The software was developed before 2010 with myself (electrical engineer not programmer) doing all the math and numerical code and linux o/s .I hired a computer engineer experienced in factory floor GUI to do the User Interfaces and the SQL to OPC UA interfacesCalculation time has always been an issue and over ten years as better computers became available the solving timehas reduced from about 10 minutes to about 3 to 4 minutes.Reliability has been good. There were about 2 ssd memory failure, and 3 cases where the users got root pw and played around where they should not.The latest fanless embedded computer uses Intel 8700 with six cores.The bulk of processing is done by five identical background processes started as linux daemons on boot.The thread that I am having trouble with does the overall control and some math processing.All comms with the daemons are via the linux ram file sytem /dev/shmI have added the systemctl scheduling to control the daemons. Now the Intel hyperthreading is disabled in UEFI.The daemons are set as round-robin with very high priority ( nice -18 )That causes the 6 cpu core loading to stay at 100% for a good part of the calculation time which is what we need.Various settings were tried to get the average solving time lowest.If I did the project again I would consider GPU with OpenCl or similar to do the time consuming Partial differental Equations. But that is not available yet in the hardened fanless boxes.We investigated cloud server, but customers don't want their factory machines connected to internet.(And I agree with that!)Regards

author: golden_labels
author_rank: Super Contributor
timestamp: November 25, 2020, 04:15:30 pm
content: Joining the choir of voices before me: without knowing the exact library and/or language it’s impossible to tell. Note that we’re not asking about the application in which the toolkit is used, but about the toolkit itself.But in general UI toolkits are neither thread-safe nor expected to handle calls between threads. As SiliconWizard has said, it is theoretically possible to make it thread-safe. That would be a nightmare. While less important nowadays, 15–20 years ago the zeroth rule of multithreaded programming was: do not use multithreaded programming, if possible. There are many good reasons for that.It’s a performance and latency variance tragedy in a piece of code that is expected to have both of those properties at a good level. Every function that is a part of the public API must be synchronized. All data accesses must be synchronized on every level — in the worst case down to single pixels being drawn, unless expensive defensive copies are performed for everything or data is immutable. All observers must be synchronized, all modifications to the internal state. And all this just to handle that one in ten millions calls that actually is between threads.It’s an explosion of code complexity and hard to grasp interactions, leading to higher chance of mistakes both in the toolkit itself and in dependent code.It may lead to lockups in a part of the application that should be possibly devoid of them. If any worker/logic thread “hangs”, it’s bad. If that happens to your UI, users will eat you alive.Rarely there is a real reason to have direct access to UI from other threads. Nearly always it’s an attempt to make shortcuts to avoid properly designing the application. Shortcuts that will bite later. It may also be a sign of a pre-existing bad design.