Up to now, almost all considered samples worked using only SingleThreadVat created by doAsync operator. However the framework provides a number of other vats that implement event loops in a different way.
These vats are based on Scala Actors and are practically identical with only difference that BatchedActorVat should be slightly more efficient than ActorVat, since it dispatches events in bigger batches, but this has not yet been tested with performance tests. Likely only one of them remain in the future versions of the framework. You could use both vats on the current thread with onCurrent method. This method uses blocking Actor.receive method to dispatch events.
@Test def testCurrentThreadSimple() { val t = ActorVat.onCurrent { expectEquals(4) { aLater(4) } } assertEquals(4, t) } @Test def testBatchedCurrentThreadSimple() { val t = BatchedActorVat.onCurrent { expectEquals(4) { aLater(4) } } assertEquals(4, t) }
The both calls are equivalent to doAsync method in AsyncControl class. Note that both of the classes discard unknown events sent to actor (there is an option intercept them, but that API will likely to change to something better than it is now). This behavior might be reconsidered in the future. But using these vats in this way is not so interesting. The method doAsync will always use the most efficient vat implementation for running application in the current thread. The benefits from ActorVat come when it used over actors spawned in other threads on Scala actor thread pool. The vat is started with start method and stops with stop method. The vat stops accepting events after stopping and will throw exception on enqueue method.
@Test def testBatchedOtherThreadSimple() { doAsync { expectEquals(4) { val v = BatchedActorVat.start aSeq { aLater(v) { assertSame(v, Vat.current) 4 } } finallyDo { v.stop() } } } }
If you actually need other actor vat only for duration of single asynchronous operation, there is even simpler method spawn supported by both vats:
@Test def testSpawn() { doAsync { expectEquals(4) { val t = Thread.currentThread() assertNotSame(classOf[ActorVat], Vat.current.getClass) ActorVat.spawn { assertNotSame(t, Thread.currentThread()) assertSame(classOf[ActorVat], Vat.current.getClass) 4 } } } }
Note, that it is not generally safe to share mutable state between different vats. Stick to message passing and prefer immutable objects or objects passed as a token between different threads. The AsyncScala ensures that all relevant memory barriers are done on sending and receiving the messages, so it is possible to pass mutable objects around when needed (like java.io.ByteBuffer or arrays), you just need to be careful with them and to specify clear guidelines.
The SingleThreadVat is intended to be the simplest and most efficient vat for running in the single thread context. If you have ideas on how it could improved further, please tell me. It is intended to be a very quick way to create temporary asynchronous context when needed. Use AsyncControl .doAsync method start on the current thread. You have seen a plenty examples of this method in the samples, and you could reread about it here.
The AsyncScala components could live over practically any event loop. And most overused event loop in history of Java programming is AWT EventQueue. Many applications (for example, IntelliJ IDEA), use this event loop solve their concurrency problems. The AsyncScala offers a lot of different ways to solve concurrency problems, so please give AWT EventQueue a rest and use other vats types. However, from time to time there is a need to do something really AWT related from other threads, and than AWT EventQueue really helps. Also AWT Vat could be used to invoke asynchronous components from AWT EventQueue on other threads, and in such case it makes sense to use AWTVat
import net.sf.asyncobjects.asyncscala.AsyncControl._ import net.sf.asyncobjects.asyncscala.vats.AWTVat import javax.swing.JOptionPane object AWTSimple { def main(args: Array[String]) { doAsync { aSeq { aLater(AWTVat.get) { JOptionPane.showInputDialog("Write here somethig") } } thenLast {t => println("Written text: " + t) } } } }
Note, that dialog creates input dialog on AWT vat and gets its result. The method AWTVat.get automatically installs AWTVat to AWT event dispatch thread. If you are in doubt, whether the AWTVat is already installed or not, use AWTVat.get or AWTVat.install() method. Note that AWTVat will get installed only when a next scheduled event will be dispatched if you calling this method from other thread.
ActorVat use the the default Scala actor thread pool. This sometimes could lead to interesting problems. There is also ExecutorVat that uses standard executors from the packagejava.util.concurrent.
@Test def testExplicit() { doAsync { expectEquals(33) { val t = Thread.currentThread() assertNotSame(classOf[ExecutorVat], Vat.current.getClass) aLater(new ExecutorVat(ExecutorVat.DAEMONS)) { assertNotSame(t, Thread.currentThread()) assertSame(classOf[ExecutorVat], Vat.current.getClass) 33 } } } }
Note that it uses an executor ExecutorVat .DAEMONS that executes activities on the daemon threads, so it would not block application exit. This executor is supposed to be used to execute blocking operations like File IO. There is also an utility method ExecutorVat .daemon that allows creation of daemon vats. Note that you should not use this executor for computationally intensive operations. Use it only for operation that can block because of IO. For computationally intensive use actor vats that schedule on limited pool by default.
And as you have probably guessed, there is a ExecutorVat .spawn method as well, that creates new instance of executor vat and spawns an asynchronous operation over it.
@Test def testSpawn() { doAsync { expectEquals(33) { val t = Thread.currentThread() assertNotSame(classOf[ExecutorVat], Vat.current.getClass) ExecutorVat.spawn { assertNotSame(t, Thread.currentThread()) assertSame(classOf[ExecutorVat], Vat.current.getClass) 33 } } } }