How would I go about properly managing this multithreaded program?

Newbie here,
So I've been working on this program in rust which controls the lighting of the keyboard in my laptop.

The program has two main parts, a file which holds the "backend" for talking to the keyboard made into something resembling a library, and a folder with all the GUI (fltk) separated into files which each correspond to one of the elements of the program. These files are then executed as necessary inside builder.rs to generate the GUI and handle its logic.

Now the important bit comes from keyboard_effect_manager.rs which sits on top of keyboard_utils.rs as a middleman bettween the gui and the keyboard HID device. This is used to both selecting the basic effects and creating custom ones by essentially just spamming requests to change the current color.

The latter requires me to create a separate thread so that it does not lock up the GUI, and here's where the problem resides. My current rather questionable setup consists of two AtomicBools which handle "is a thread running/did the thread stop" (thread_ended_signal) and "the thread needs to end/are we actively stopping a thread" (stop_signal) respectively.

In essence what I want is to:

  • Create a thread on demand
  • Run it until a new effect is selected
  • When that happens, tell the thread to break out of the loop its placed in (standard for all custom effects) or just kill it directly
  • Wait until the thread has stopped
  • Update the GUI and start a new thread, repeating the process

I'm aware that thread_ended_signal can be substituted with a JoinHandle<()>, but I haven't found a way to be able to keep track of the latest handle that links to the current active thread inside my project structure, as putting it inside EffectManager would need a dummy one when creating the struct (I think?) and I cannot come up with another solution. Also adding to this is the fact my current stop_signal implementation only half-works since while inside this loop when being called from keyboard_effect_manager.rs it wont do anything at all as Keyboard would be locked to the current call to transition_colors_to essentially making it impossible to change the AtomicBool until it has finished.

Anyways, any help in getting an idea of what direction to take to solve this issue is greatly appreciated. Feedback for general code quality, other improvements and whatnot is also welcome.

Edit: Marking as solution since its mostly what I needed, second part of the fix discussed in reddit. (Thanks mo_al_!)

It sounds to me like your code would be simpler if you left the keyboard controlling thread running, and just sent it messages through a mpsc channel. When it received a message, it would then change its behavior. With that design you wouldn't need to implement any locking or deal with any possible races.

4 Likes

That seems like a much more reasonable approach, thanks! I can imagine how to use mpsc but do you mean throwing the keyboard inside the thread as well? In that case do you have an idea on how to avoid having to lock here and the other handles not related to the current effect? Since I'd need to have a clone of the keyboard there (dont know if separate instances for each handle would be ideal cannot open the same HID device twice).

Edit: Got this far refactoring code before stumbling with the previously mentioned issue. Due to:

use of partially moved value: `manager`
partial move occurs because `manager.keyboard` has type `Keyboard`, which does not implement the `Copy` trait

I don't quite follow your errors or your code, but my suggestion (guessing in the dark) would be to have plain old data (something with no lifetime parameter and probably deriving clone) moved between threads through the channel and to have no locks at all, with a single thread opening and complicated structure that might make you tempted to use a lock.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.