Value vs. Threading


#1

Hello everyone!

I'm new to the forum, and first of all, I'd like to say a huge THANKS to Jules for his work, JUCE is just awesome! And also thank you for this forum, and to all the members, you helped me a lot during developement!

But now, I think I've reached the edge of my knowledge, and as far as I see, the edge of the forum too.

 

I'm working on a node-based application, where I have different nodes with different functions, and many types of input and output ports on them. I can wire the ports together to build complex compositions. I've choosen the Value class to hold the different kind of data the ports have. It's just an awesome class for all of my needs, I can bind (referTo()) the ports to each other, and also to controls, they're updating themselves, I have to update things only when the underlying data changed (valueChanged()), so it works like a charm.

But here comes the problem. My "work area" is a really big Component with lots of Nodes inside, and the whole thing is in a Viewport, and when I scroll it all of my results are getting laggy - not just visually, but i'm sending the result through the network, and it's also laggy.

So I decided to move all of my computations to another thread, because the "realtimeness" of my GUI is much less important than the computations, and the dataflow. I've read many times on the forum, that I should not do any timeconsuming calculation on the Message Thread, but I've also read, that Value is not thread-safe, and it sends messages about value changes on the Message Thread.

 

Is it a core problem in my own design, or do you have any ideas how to solve it? Right now I'm thinking about to (more or less) get rid of the beloved Value class, and it's valueChanged message :(, and replace it with some thread-safe solution, but it seems to be a huge redesign issue for me.

Any advice or tip is appreciated!

 

Thank you,

Timo


#2

Thanks!

Very hard to know what to advise based on that.. It depends so much on what you're actually doing. Obviously moving as much computation to other threads is the solution, but how exactly you should connect those threads to Values depends on what kind of data you're using and what changes are being made.


#3

Thank you for the quick answer.

Right now my system looks something like this:

  • NodeManager Component which is also a Timer to aim ~60 FPS. – it handles all of the Nodes and has the Viewport.
  • NodeManager :: Timer Update:
    • updates all Nodes to process their current state, and their actual Values.
    • sends the processed data strucutre via network.
    • calls repaint() on a GL Component, to update my on-screen visual feedback.
  • I also have a few time-based Nodes, they are also Timer subclasses. (Maybe I should rely on their update() method, without using more Timers?)

I've tried to "simply" replace the Timer base class with Thread (of course with the necessary modifications), and it worked fine, but here came the valueChanged(), and the GL repaint() problem. 

That's why I think I can forget mostly the Value class, and use the Node's update() method to collect the actual values, and use Values only like an interface for the GUI controls to modify the values of the unwired ports of a Node.

Basic variable types, like int, bool, String, etc are thread-safe anyway? I'm sorry, maybe it's a dumb question, but this Thread thing is new to me.


#4

Any ideas? Nobody? :D


#5

Okay - 

Simplify the question for a second... 

  • Two threads GUI and Processing. 
  • You want a callback on one thread when a value is changed on another thread? 
  • You want callbacks within one thread as well? 
  • What types of value do you have?
  • How many of them? 
  • Which threads write values, which read them? 

 

 


#6

At the moment I have only the Message Thread, and an OpenGL Thread with setContinuousRepainting(false), I call the component's repaint() method at a 60 FPS rate. The core problem, that all of my results getting laggy, when I scroll my work area, a Viewport. That's why I'd like to move my calculations, and data delivery on a new Thread.

  • So yes, the goal is to have a new Processing Thread
  • Values change on the Message Thread via the GUI, and I have to pass them to the Processing Thread
  • The Processing don't have to talk to the Message Thread
  • I use all of the basic Value types, and I have 3 special: Vec2, Vec3, Color, they are all subclasses of ReferenceCountedObject
  • In a complex project there can be easily 1000-2000 Values, and I have to process them at 60-30 FPS.

Maybe it's a huge mistake by me, but I don't really have an abstract data layer. So my GUI Components (Nodes) hold their Values, and I can modify them with the standard controls (Slider, TextEditor, ColorSelector, etc). And because it's a node based systems an output port of a Node can modifiy the input ports of others. In this case my controls are disabled, but shows their current state.

Currently a Node listens to it's Values change messages, and do its job only when they change. I think this point breaks my Threading attempts.


#7

I was wrong, I should call the GL Components reapint() from the Processing Thread every time a complete frame is calculated. So th process looks like this:

  • Message Thread with all the GUI and valueChanged stuff
  • Process Thread should "run at 60 FPS", and every "frame" looks like:
    1. cycles through all of my Nodes, calling their process() method (it's sometimes a heavy task)
    2. collects and sends a bunch of data through the network
    3. calls the repaint method of my GL preview (How can I do this without MessageManagerLock?)

#8

Be careful to make a difference between paint() and repaint().

When you call repaint, you indicat juce that something has to be redrawn by adding a message in the message queue but it's not guaranteed to be called immediately 


#9

Hmm. I knew that this is the way it works, but I thought it is also the way I should do. So shoul I call just paint() for example for my GL view?

I don't think calling paint() directly is a good idea. But if it is, how can I get the Graphics reference to pass it to the paint() method?


#10

No, you should never, ever call paint directly.


#11

Is the processing thread reliant on the change notifications to work, or does it just process the whole thing anyway 60 times a second?


#12

No, it just cycles through all the currently existing data 60 times per second, and use them to build the results I want to send via network. The value changes are not so important for the Processing side, but the smooth 60 FPS.