views:

108

answers:

1

Hello,

I would like to know more about the extent it is possible to control where each Scala actor is running. I happen to be in a somewhat particular case : high reactivity is desired, a good part of the code is time-critical, and worst of all, I'm on Android. With this in mind, my main goal is : make the code as readable and straightforward as possible.

What I would ideally want to achieve (some of those sound unreasonable/downright stupid at first, read rationales below)
- I would like to be able to respond to some messages in some particular, always the same, arbitrary thread, but I need to not be blocking it to wait for messages.
- I would like to have most my processing done on a pool of worker threads, ideally auto-resizing as Scala actors support, while guaranteeing this processing will never take place on the arbitrary thread above.

These requirements stem from an Android necessity : the Android framework uses a special thread to touch UI, and if you touch any UI object from another thread you get an exception. In this way, it enforces some sort of threads/lock model which is exactly what I'm trying to work around. But anyway, that's the way it is : I have to ensure some of my processing, namely what is dealing with UI objects, is running on this thread and no other, because the framework annoyingly says that's how I should do it. An annoying side effect of this is as long as this thread is processing my code the UI stops updating and my application stops being responsive. So I need to ensure that this thread does not get randomly selected for long-running code I could have in some react{}, and ideally that it never processes something that may as well have be done by another thread.

The android framework provides a class called Handle, which implements some kind of message passing - you send it a Runnable, and it will run on the UI thread. I can use that if I need to. Creating a Runnable each and every time significantly clutters the code - one thing that can be done would be to encapsulate it in some method so that I could write something like
onUIThread { /* some code */ }
...which is much nicer than new Runnable() { def run () {}}. On the other hand, that's basically what the onUIThread function would be doing, so I'd be creating two closures - and then I'd have to deal with the details of memory allocation for closures. I'd have to, because each time I allocate an object, the GC gets a chance to run and on Android that's usually a 150ms pause in execution, which will ruin my user experience if it's happening in a critical execution path.

So in the end :
- Do I have any way of statically associating an actor with a thread, so that I could have a UI actor, react{} inside it and always have its code run on the UI thread ? /* I know it's bad design on its own, read the rationale above to see why I can't help it */
- Do I have any way of ensuring this particular thread will never be considered for responding to a message in a react{} ? - Any suggestions of what I could do, given my constraints, to get better code legibility ?

+4  A: 

You can implement a customer scheduler by extending the IScheduler trait that pushes tasks onto the ui thread, and then override the scheduler method on your actor that needs to run on the ui thread.

Someone tried this with Swing a while back I and I think it worked: http://scala-programming-language.1934581.n4.nabble.com/scala-Swing-event-thread-actors-td1987246.html

Then let the other actors use the normal scheduler.

But I'll point out that react {} results in creation of a closure that ultimately is wrapped up in a runnable. It also uses exceptions for flow control, which introduces considerable overhead (I have no idea how much on Dalvik). So if GC'ing closures is really hurting your app's performance I have doubts that actors will save you.

Erik Engbrecht
This sounds like exactly what I needed. Thank you very much, I'll look into that ! About closures it only means I have to be careful not to allocate the memory for them while critical code is being executed, because that's what triggers the (tentative) GC which breaks my performance even if it has nothing to clean. But it'd be the same if I was creating Runnables myself, so... no free lunch. Anyway thank you very much for the good answer!
Jean