Changing Slider text box style, leads to a threading problem


#1

Firstly, may i request a way to create a Slider without a text box (or is there a way i’ve missed!). Currently you create the text box and then `setTexTboxStyle(Slider::NoTextBox …). This has the effect of deleting the default one.

Why does this matter?

Because setting things up and changing things are two different situations. When you create things, you’re just initialising but when you change, you’re sending out change notifications, repaints, etc.

threading problem?

Ok, in order to assist app start up, i do much of my initialisation in a background thread. I’ve discovered that i can create almost all my UI in a background thread on startup providing none of it is attached to the main window during this time.

so, thread creates everything. thread posts “i’m done”, main UI performs `MainWindow::setContentOwned’. works a treat! While this thread is in progress, the main UI can show a splash screen or even make a very simple UI to entertain the user.

Everything works fab except the Slider. changing the style to “NoTextBox” causes a the old Label to be deleted and ~AsyncUpdater gets upset:

AsyncUpdater::~AsyncUpdater() { // You're deleting this object with a background thread while there's an update // pending on the main event thread - that's pretty dodgy threading, as the callback could // happen after this destructor has finished. You should either use a MessageManagerLock while // deleting this object, or find some other way to avoid such a race condition. jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); ... }

It’s correct actually. i don’t want to be going around updating things not on the main UI. However, this is not what i’m really doing here. ie changing. it’d prefer a way to simply create once as i want it.

what do you think?

– hugh.


#2

I think creating UI components on a separate thread is a case of over-engineering.

I have to wonder what is so computationally expensive about creating your widgets that it needs its own thread.


#3

not so unfortunately. i was forced to put everything on a background thread to make my app startup short enough. as it is, there’s still a 6 second delay before the splash screen.

i’m running my app on iOS and on the iPhone, if your app doesn’t show something after 20 seconds the OS, rather rudely, kills it.

On the slower models, i hit this limit. I was forced to put everything on a separate thread in order to show some UI like a loading bar etc.

– hugh.


#4

Why does it take so long to bring up the UI ?


#5

I just timed it again. it takes 5 seconds before the splash screen appears. about 4 of these are the OS itself launching. Then i take 10 more seconds in the background thread loading assets and creating the UI. Total is now about 15, down from over 20.

On the iPhone, the first non-splash UI needs to have lots of artwork, funky music and animating buttons - with sound effects. Otherwise people think it sucks.

I have to load and prep the sound system, decompress images and create the UI. Juce and iOS make heavy use of floating point and i have a 433Mhz embedded ARM. 15 seconds overall isn’t too bad, some apps i’ve tried take over 30 to start properly.

It’s important to show something as quick as possible. I know the faster devices will be a lot quicker, but even then, the quicker you can show some UI, the better the app.


#6

Ah well that’s a different story. Its not the creation of Components that is taking the time, its the loading of your assets. Create the Components on the main thread and load the assets in the separate thread then.

But you have to show something so load the minimum of what you need to look good on launch and load the rest in the background.


#7

Then i take 10 more seconds in the background thread loading assets and creating the UI.

Hmm, that’s still a very long time to create a UI!

I believe that the asset loading during that startup period is the bottleneck.

IMHO, a better way to do it is to create the actual GUI objects in a high-priority thread without any asset loading, and load all the assets in a single, lower priority thread. This means that you can start displaying something almost immediately and the GUI objects will refine as their assets are loaded. You can order the assets in your background thread in exactly the order you think most important for the user; you can delay actually displaying the GUI until “enough” assets are loaded with just a single shared flag; and careful choices of background colors and initial dimensions for your GUI elements to be similar to the assets that they’ll eventually load can dramatically reduce the visual effect of the actual assets being loaded a little later.

If loading time is still an issue and you have a large number of small images, you should look into “sprites” - that is, putting all your images into one big file and then displaying small portions of that file for individual images. Here’s a good article describing how to do that for CSS - the code is Juce is different but the technique exactly the same.

Oh, and this would probably fix your threading issue as well, because your UI creation would probably return to the main thread, and it’d be your asset loading only that lived in a separate thread.


#8

Yes indeed, it is the assets.

I could do it the way you describe: I’d have an asset loader that loaded all the stuff. this would be in the background. it would signal the main thread on completion, which would (quickly) transfer all these to the relevant components.

The problem is that it gets very messy. This “loader” would have to know exactly everything to be loaded and to know which component needed what. or, more precisely, the main thread would have to create the UI components and fetch from the loader the relevant assets and put them in the right places. Each time i write a new component, i’d have to mess with this loader. The code for the component wouldn’t be complete without the loader.

Actually, i do this already, but within the scope of a specific component. This happens during the app normal running. The component used a thread to load upcoming assets. This is fine because it’s all tied up inside one component’s implementation.

This is not a good idea across multiple components. What are components anyhow. really, until they have a heavyweight peer, they are just classes that host their properties and assets. This is exactly where all the relevant loading and initialisation code should live.
I think it’s a good idea to be able to create a bunch of components together with all their required bits outside the UI thread in readiness for use.

Once the components are assigned a heavweight peer, such as being added to the UI display tree, then we’re in a different world; now they are windows or panels or UI or whatever.

I think it would suck if there was no way to ever create components except on the UI thread (or one that locks it).


#9

Hi Tom, thanks for the suggestions.

I understand what you’re saying, but basically on these iOS apps, you pretty much need to load everything you can up front. This basically means the assets for the whole UI. Supposing you have several windows and most surfaces are covered with imagery, then you have a reasonable amount of assets to load.

this is where your 10 seconds goes on slow HW.

If you try to load stuff in the background at times when the user expects a fast tap response, you get a stutter.

You guys are all forgetting we’re dealing with much much slower hardware than you’re used to on desktops.


#10

Nah, you could do it in a very clean way using…a thread queue!

MyComponent::MyComponent ()
{
  m_otherThread.call (&MyComponent::doLoadAssets, this);
}

void MyComponent::doLoadAssets ()
{
  // load stuff

  // all done
  MessageThread::getInstance().call (&MyComponents::assetsLoaded, this);
}

void MyComponent::assetsLoaded ()
{
  repaint (); // or whatever
}

All you would be exposing is the other thread. Each Component would have all of its asset loading code within its class instead of a central place.


#11

Damn - 10 seconds is a helluva long time for loading all this stuff! That seems painful enough that I can’t blame the OS for killing the app after 20 seconds of still constructing it, tbh. (Look at it as a crude version of quality assurance.)

Sprite sheeting is an important optimization; likewise, simply combining assets together to minimize the total amount. Must not forget that selectively lowering quality (therefore reducing space an asset may take) would be a good option to look at (ie: 24-bit audio to 16-bit, 48 kHz audio to 44.1 kHz, 3 minute background music loop in WAV versus a 1 minute long version in OGG). Doing that would give a considerable amount of memory headroom!

Also noteworthy, although captain obvious-like; loading only what you need when you need it.


#12

jrlanglois: I agree with all your suggestions, except:

loading only what you need when you need it.

That really doesn’t work for UI applications, particularly mobile UI applications. Every operation has to “execute” as fast as possible - the controls need to feel fast even if there is some delay before operations are actually completed, and that means you need to have the next assets loaded before you need them.


#13

I can suggest a sneaky hack if you want to avoid that background thread… Assuming that nothing is actually happening on the screen while all this is going on, you could just make occasional calls to MessageManager::runDispatchLoopUntil (1) in between loading assets, so that the event loop doesn’t seem to be frozen. Call that a few times a second and the OS should be kept happy.

But going back to your original post, it is a bit wasteful for the Slider to create a text editor and then delete it if you don’t want one - it might be a good idea for me to add another constructor that gives more immediate control over it.


#14

Vinn, i like your thread queue idea. where is the thread queue class in Juce.

i need to be sure that this will work for several components in a row sharing the same thread. i’ll try an experiment with this and see what i get.

– hugh.


#15

Jules,yes please and additional constructor is a good idea anyhow, whether or not background components are a good idea or not.

thanks.


#16

Umm…ermm…its not in Juce :slight_smile: Too much template metaprogramming for Jules’ tastes, I think. There’s a basic one in DSP Filters Demo (ThreadQueue.cpp, ThreadQueue.h)

A more complex one is in VFLib but for your purposes, its not needed. You will, however, want the “automatic binding” part. Its in CallQueue.h:

This is the automatic binding:

  template <class Fn>
  void call (Fn f)
  { callf (vf::bind (f)); }

  template <class Fn, typename T1>
  void call (Fn f, const T1& t1)
  { callf (vf::bind (f, t1)); }

  template <class Fn, typename T1, typename T2>
  void call (Fn f, const T1& t1, const T2& t2)
  { callf (vf::bind (f, t1, t2)); }

  template <class Fn, typename T1, typename T2, typename T3>
  void call (Fn f, const T1& t1, const T2& t2, const T3& t3)
  { callf (vf::bind (f, t1, t2, t3)); }

  template <class Fn, typename T1, typename T2,
                      typename T3, typename T4>
  void call (Fn f, const T1& t1, const T2& t2,
                      const T3& t3, const T4& t4)
  { callf (vf::bind (f, t1, t2, t3, t4)); }

  template <class Fn, typename T1, typename T2, typename T3,
                      typename T4, typename T5>
  void call (Fn f, const T1& t1, const T2& t2, const T3& t3,
                      const T4& t4, const T5& t5)
  { callf (vf::bind (f, t1, t2, t3, t4, t5)); }

  template <class Fn, typename T1, typename T2, typename T3,
                      typename T4, typename T5, typename T6>
  void call (Fn f, const T1& t1, const T2& t2, const T3& t3,
                      const T4& t4, const T5& t5, const T6& t6)
  { callf (vf::bind (f, t1, t2, t3, t4, t5, t6)); }

  template <class Fn, typename T1, typename T2, typename T3, typename T4,
                      typename T5, typename T6, typename T7>
  void call (Fn f, const T1& t1, const T2& t2, const T3& t3, const T4& t4,
                      const T5& t5, const T6& t6, const T7& t7)
  { callf (vf::bind (f, t1, t2, t3, t4, t5, t6, t7)); }

  template <class Fn, typename T1, typename T2, typename T3, typename T4,
                      typename T5, typename T6, typename T7, typename T8>
  void call (Fn f, const T1& t1, const T2& t2, const T3& t3, const T4& t4,
                      const T5& t5, const T6& t6, const T7& t7, const T8& t8)
  { callf (vf::bind (f, t1, t2, t3, t4, t5, t6, t7, t8)); }

You can adapt it to suit your needs.

MessageThread is a thread queue singleton that gets processed with an AsyncUpdater automatically:


Well yeah, it will work. The calls would be processed sequentially. Of course, nothing stops you from creating more than one thread.


#17

Hi, thanks for the info. I had a go at this idea.

I took the idea from your ThreadQueue and incorporated the whole show into a loader class i’m calling `BGLoader’. Like this, you don’t need any binding templates.

So, what you do is create your initial UI (eg splash), then start creating your main UI on the main thread, except that your components perform load requests, which all queue up and are processed by a background thread.

here’s one of my components,

[code]class Fatty: public Component
{
public:

Fatty()
{
    BGLoader::getLoader().post(&Fatty::load, this);
}

void paint(Graphics& g)
{
    g.fillAll(Colours::red);
}

void load()
{
    for (int i = 0; i < 10; ++i)
        Thread::sleep(1000);
}

};[/code]

when you’re done posting loads, your main thread posts a final request indicating a main thread function to continue.

[code]MainAppWindow::MainAppWindow()
: DocumentWindow (JUCEApplication::getInstance()->getApplicationName(),
Colours::lightgrey,
DocumentWindow::allButtons)
{
centreWithSize (500, 400);
setResizable(true, true);

// first show splash
s = new Splash();
setContentOwned(s, false);
setVisible (true);

// fatty will use loader
f = new Fatty;

// post done message to continue to `go'
BGLoader::getLoader().postDone(&MainAppWindow::go, this);

}

void MainAppWindow::go()
{
// here we go!

// switch over to fatty!
setContentOwned(f, false);    
f->setVisible(true);

}[/code]

complete project attached. see bgloader.h for the action.

– hugh.