Data race? Or something else going wrong? - ponylang

Found the programming language "pony" just today... and started to play with it.
My code is supposed to do some simple producer consumer thing.
As claimed by language documentation, the language ensures there are no data races.
Here, main sends 10 messages to the producer, which in turn sends 10 messages to the consumer. The consumer increments a counter state variable. Then, main sends a message to the consumer, which in turn sends a message to main in order to display the current value. If all messages were in sequence, the expected value would be 9 (or 10). The result printed, though is 0.
As this is all in hour 1 of my playing with the language, of course I might have messed up something else.
Who can explain my mistake?
use "collections"
actor Consumer
var _received : I32
new create() =>
_received = 0
be tick() =>
_received = _received + 1
be query(main : Main) =>
main.status(_received)
actor Producer
var _consumer : Consumer
new create(consumer' : Consumer) =>
_consumer = consumer'
be produceOne () =>
_consumer.tick()
actor Main
var _env : Env
new create(env: Env) =>
_env = env
let c : Consumer = Consumer.create()
let p = Producer.create(c)
for i in Range[I32](0,10) do
p.produceOne()
end
c.query(this)
be status( count : I32) =>
// let fortyTwo : I32 = 42
// _env.out.print( "fortytwo? " + (fortyTwo.string()))
_env.out.print( "produced: " + (count.string()) )
Running on windows 10, 64 bit, btw. with the latest and greatest zip file installation I found.
0.10.0-1c33065 [release]
compiled with: llvm 3.9.0 -- ?

The data races prevented by Pony are the ones that occur at the memory level, when somebody reads from a memory location while somebody else is writing to it. This is prevented by forbidding shared mutable state with the type system.
However, your program can still have "logical" data races if a result depends on a message ordering that isn't guaranteed by Pony. Pony guarantees causal ordering of messages. This means that message sent or received is the cause of any future message that will be sent or received if the messages have the same destination, and of course causes must happen before their effects.
actor A
be ma(b: B, c: C) =>
b.mb()
c.mc(b)
actor B
be mb() =>
None
actor C
be mc(b: B) =>
b.mb()
In this example, B will always receive the message from A before the message from C because A sends a message to B before sending a message to C (note that the two messages can still be received in any order since they don't have the same destination). This means that the message sent to B by C is sent after the message sent to B by A and since both have the same destination, there is a causal relationship.
Let's look at the causal orderings in your program. With -> being "is the cause of", we have
Main.create -> Main.status (through Consumer.query)
Consumer.create -> Consumer.query
Consumer.create -> Consumer.tick (through Producer.produceOne)
Producer.create -> Producer.produceOne
As you can see, there is no causal relationship between Consumer.query and Consumer.tick. In the sense of the actual implementation, this means that Main can send the produceOne messages and then send the query message before any Producer starts executing the message it received and has a chance to send the tick message. If you run your program with one scheduler thread (--ponythreads=1 as a command line argument) it will always print produced: 0 because Main will monopolise the only scheduler until the end of create. With multiple scheduler threads, anything between 0 and 10 can happen because all schedulers could be busy executing other actors, or be available to start executing the Producers immediately.
In summary, your tick and query behaviours can be executed in any particular order. To fix the problem, you'd have to introduce causality between your messages, either by adding round-trip messages or by doing the accumulating and printing in the same actor.

Thanks to #Benoit Vey for the help with that.
It is indeed the case, that there is no express or implied casuality between the execution of the query and the time when the producer executes its tick () messaging to the consumer.
In that sense, no voodoo, no magic. It all just behaves as any actor system would be expected to behave.
Messages within an actor are processed in order (as it should be). Hence, in order to get the desired program behavior, the producer should trigger the query eventually (in order after processing the produceOne messages).
Here, how that can be accomplished:
use "collections"
actor Consumer
var _received : I32
new create() =>
_received = 0
be tick() =>
_received = _received + 1
be query(main : Main) =>
main.status(_received)
actor Producer
var _consumer : Consumer
new create(consumer' : Consumer) =>
_consumer = consumer'
be produceOne () =>
_consumer.tick()
be forward (main : Main) =>
main.doQuery(_consumer)
actor Main
var _env : Env
new create(env: Env) =>
_env = env
let c : Consumer = Consumer.create()
let p = Producer.create(c)
for i in Range[I32](0,10) do
p.produceOne()
end
//c.query(this)
p.forward(this)
be doQuery (target : Consumer) =>
target.query(this)
be status( count : I32) =>
// let fortyTwo : I32 = 42
// _env.out.print( "fortytwo? " + (fortyTwo.string()))
_env.out.print( "produced: " + (count.string()) )
Just for giggles (and comparison), I also implemented the same in F#. And to my surprise, pony wins in the category of compactness. 39 lines of code in Pony, 80 lines in F#. That, along with the native code generation makes Pony an interesting language choice indeed.
open FSharp.Control
type ConsumerMessage =
| Tick
| Query of MailboxProcessor<MainMessage>
and ProducerMessage =
| ProduceOne of MailboxProcessor<ConsumerMessage>
| Forward of (MailboxProcessor<MainMessage> * MainMessage)
and MainMessage =
| Status of int
| DoQuery of MailboxProcessor<ConsumerMessage>
let consumer =
new MailboxProcessor<ConsumerMessage>
(fun inbox ->
let rec loop count =
async {
let! m = inbox.Receive()
match m with
| Tick ->
return! loop (count+1)
| Query(target) ->
do target.Post(Status count)
return! loop count
}
loop 0
)
let producer =
new MailboxProcessor<ProducerMessage>
(fun inbox ->
let rec loop () =
async {
let! m = inbox.Receive()
match m with
| ProduceOne(consumer') ->
consumer'.Post(Tick)
return! loop ()
| Forward (target, msg) ->
target.Post(msg)
return! loop ()
}
loop ()
)
let main =
new MailboxProcessor<MainMessage>
(fun inbox ->
let rec loop () =
async {
let! m = inbox.Receive()
match m with
| Status(count) ->
printfn "Status: %d" count
return! loop ()
| DoQuery(target) ->
target.Post(Query inbox)
return! loop ()
}
loop ()
)
let init() =
main.Start()
consumer.Start()
producer.Start()
let run() =
for _ in [1..10] do
producer.Post(ProduceOne consumer)
producer.Post(Forward(main,DoQuery consumer))
let query () =
consumer.Post(Query main)
let go() =
init ()
run ()
//query ()

Related

KafkaReceiver not running parallel for each partition?

I'm trying to do read the messages concurrently with partition-based Ordering. I referred to the below example and I added delay (10sec) to mimic the workload. but I have noticed even though it runs multiple threads, it's not running concurrently. It only runs one message by message.
My topic consists of 20 partitions so I'm expecting to run at least 20 threads parallel and wait 10 sec (delay I put) and then run again and again.
enter link description here
private fun onPayment(it: ReceiverRecord<String, Any>): Mono<ReceiverRecord<String, Any>> {
println(
"SMF message [${Thread.currentThread().name}], Offset - ${
it.receiverOffset().offset()
} , Partition ${
it.receiverOffset().topicPartition()
}, Value - ${it.value()} ${SimpleDateFormat("hh:mm:ss").format(Date())}"
)
return Mono.just(it).delayElement(Duration.ofMillis(10000))
}
fun doSubscribe(consumer: (ReceiverRecord<String, Any>) -> Mono<ReceiverRecord<String, Any>>) {
val reOption = KafkaConfig().kafkaConsumerConfig().subscription(Collections.singleton("payTopic"))
.commitInterval(Duration.ZERO)
.commitBatchSize(0)
val receiver = KafkaReceiver.create(reOption)
Flux.defer(receiver::receive).groupBy { m ->
m.receiverOffset().topicPartition()
}.flatMap { grpFlux ->
grpFlux.publishOn(schd).concatMap { m ->
consumer(m).map {
m.receiverOffset().commit()
}
}
}.subscribe()
}
And its prints sequentially one by one and consume only from one partition.

How to match events in two causal streams to detect excessive lagging

I have two hot observables, which are respectively a stream Q of requests to a network server, and a stream R of replies from the server. The replies are always delivered in the order of requests, and every request is going to receive exactly one reply eventually. Thus the first event in R, R1, is the reply to the first event in Q, Q1, and so on. I need to detect when a reply Rn takes longer than a defined timeout and signal this timeout condition.
Q --1---2---------3-------> // Requests Q1, Q2...
R ----1-------------------> // Replies
Out ------------------O-|> // Oops: Reply R2 to Q2 did not arrive within time τ.
|<----τ---->|
Events Qn and Rn do not contain any identifying information (think of plain colorless round marbles), and the indices in the diagram are just sequential numbers introduced for explanation.
I seem unable to solve this riddle. I tried the approach below, but it appears I am matching the latest request Qi to the latest response Rj. In the sample Q contains 5 requests, spaced 500ms apart, and replies in R come 750ms apart, starting at 200ms, but only 4 of them (the 5th is delayed indefinitely). The code does not detect that, since that last reply R4 comes within the set timeout of 1000ms after the latest request Q5 (in 200ms, actually).
var Q = Observable.Interval(TimeSpan.FromMilliseconds(500)).Select(_ => Unit.Default)
.Take(5).Concat(Observable.Never<Unit>());
var R = Observable.Interval(TimeSpan.FromMilliseconds(750)).Select(_ => Unit.Default)
.Delay(TimeSpan.FromMilliseconds(200))
.Take(4).Concat(Observable.Never<Unit>());
var dq = Q.Select(v => Observable.Return(v).Delay(TimeSpan.FromMilliseconds(1000)));
var dr = Observable.Zip(Q, R, (_1,_2) => Observable.Never<Unit>());
Observable.Merge(dq, dr).Dump().Switch().Dump();
I believe that you want to be notified that request 4 has timed out (due at 3s, but arrives at 3.2s) and also request 5 as it never arrives
void Main()
{
var scheduler = new TestScheduler();
var requests = scheduler.CreateHotObservable<int>(
ReactiveTest.OnNext(0500.Ms(), 1),
ReactiveTest.OnNext(1000.Ms(), 2),
ReactiveTest.OnNext(1500.Ms(), 3),
ReactiveTest.OnNext(2000.Ms(), 4),
ReactiveTest.OnNext(2500.Ms(), 5));
var responses = scheduler.CreateHotObservable<Unit>(
ReactiveTest.OnNext(0950.Ms(), Unit.Default),
ReactiveTest.OnNext(1700.Ms(), Unit.Default),
ReactiveTest.OnNext(2450.Ms(), Unit.Default),
ReactiveTest.OnNext(3200.Ms(), Unit.Default));
var expected = scheduler.CreateHotObservable<int>(
ReactiveTest.OnNext(3000.Ms(), 4),
ReactiveTest.OnNext(3500.Ms(), 5)
);
var observer = scheduler.CreateObserver<int>();
var query = responses
.Select((val, idx)=>idx)
.Publish(responseIdxs =>
{
return requests.SelectMany((q, qIdx) =>
Observable.Timer(TimeSpan.FromSeconds(1), scheduler)
.TakeUntil(responseIdxs.Where(rIdx => qIdx == rIdx))
.Select(_ => q));
});
query.Subscribe(observer);
scheduler.Start();
//This test passes
ReactiveAssert.AreElementsEqual(
expected.Messages,
observer.Messages);
}
// Define other methods and classes here
public static class TickExtensions
{
public static long Ms(this int ms)
{
return TimeSpan.FromMilliseconds(ms).Ticks;
}
}

Aggregate resource requests & dispatch responses to each subscriber

I'm fairly new to RxJava and struggling with an use case that seems quite common to me :
Gather multiple requests from different parts of the application, aggregate them, make a single resource call and dispatch the results to each subscriber.
I've tried a lot of different approaches, using subjects, connectable observables, deferred observables... none did the trick so far.
I was quite optimistic about this approach but turns out it fails just like the others :
//(...)
static HashMap<String, String> requests = new HashMap<>();
//(...)
#Test
public void myTest() throws InterruptedException {
TestScheduler scheduler = new TestScheduler();
Observable<String> interval = Observable.interval(10, TimeUnit.MILLISECONDS, scheduler)
.doOnSubscribe(() -> System.out.println("new subscriber!"))
.doOnUnsubscribe(() -> System.out.println("unsubscribed"))
.filter(l -> !requests.isEmpty())
.doOnNext(aLong -> System.out.println(requests.size() + " requests to send"))
.flatMap(aLong -> {
System.out.println("requests " + requests);
return Observable.from(requests.keySet()).take(10).distinct().toList();
})
.doOnNext(strings -> System.out.println("calling aggregate for " + strings + " (from " + requests + ")"))
.flatMap(Observable::from)
.doOnNext(s -> {
System.out.println("----");
System.out.println("removing " + s);
requests.remove(s);
})
.doOnNext(s -> System.out.println("remaining " + requests));
TestSubscriber<String> ts1 = new TestSubscriber<>();
TestSubscriber<String> ts2 = new TestSubscriber<>();
TestSubscriber<String> ts3 = new TestSubscriber<>();
TestSubscriber<String> ts4 = new TestSubscriber<>();
Observable<String> defer = buildObservable(interval, "1");
defer.subscribe(ts1);
Observable<String> defer2 = buildObservable(interval, "2");
defer2.subscribe(ts2);
Observable<String> defer3 = buildObservable(interval, "3");
defer3.subscribe(ts3);
scheduler.advanceTimeBy(200, TimeUnit.MILLISECONDS);
Observable<String> defer4 = buildObservable(interval, "4");
defer4.subscribe(ts4);
scheduler.advanceTimeBy(100, TimeUnit.MILLISECONDS);
ts1.awaitTerminalEvent(1, TimeUnit.SECONDS);
ts2.awaitTerminalEvent(1, TimeUnit.SECONDS);
ts3.awaitTerminalEvent(1, TimeUnit.SECONDS);
ts4.awaitTerminalEvent(1, TimeUnit.SECONDS);
ts1.assertValue("1");
ts2.assertValue("2"); //fails (test stops here)
ts3.assertValue("3"); //fails
ts4.assertValue("4"); //fails
}
public Observable<String> buildObservable(Observable<String> interval, String key) {
return Observable.defer(() -> {
System.out.printf("creating observable for key " + key);
return Observable.create(subscriber -> {
requests.put(key, "xxx");
interval.doOnNext(s -> System.out.println("filtering : key/val " + key + "/" + s))
.filter(s1 -> s1.equals(key))
.doOnError(subscriber::onError)
.subscribe(s -> {
System.out.println("intern " + s);
subscriber.onNext(s);
subscriber.onCompleted();
subscriber.unsubscribe();
});
});
}
)
;
}
Output :
creating observable for key 1new subscriber!
creating observable for key 2new subscriber!
creating observable for key 3new subscriber!
3 requests to send
requests {3=xxx, 2=xxx, 1=xxx}
calling aggregate for [3, 2, 1] (from {3=xxx, 2=xxx, 1=xxx})
----
removing 3
remaining {2=xxx, 1=xxx}
filtering : key/val 1/3
----
removing 2
remaining {1=xxx}
filtering : key/val 1/2
----
removing 1
remaining {}
filtering : key/val 1/1
intern 1
creating observable for key 4new subscriber!
1 requests to send
requests {4=xxx}
calling aggregate for [4] (from {4=xxx})
----
removing 4
remaining {}
filtering : key/val 1/4
The test fails at the second assertion (ts2 not receiving "2")
Turns out the pseudo-aggregation works as expected, but the values are not dispatched to the corresponding subscribers (only the first subscriber receives it)
Any idea why?
Also, I feel like I'm missing the obvious here. If you think of a better approach, I'm more than willing to hear about it.
EDIT : Adding some context regarding what I want to achieve.
I have a REST API exposing data via multiple endpoints (eg. user/{userid}). This API also makes it possible to aggregate requests (eg. user/user1 & user/user2) and get the corresponding data in one single http request instead of two.
My goal is to be able to automatically aggregate the requests made from different parts of my application in a given time frame (say 10ms) with a max batch size (say 10), make an aggregate http request, then dispatch the results to the corresponding subscribers.
Something like this :
// NOTE: those calls can be fired from anywhere in the app, and randomly combined. The timing and order is completely unpredictable
//ts : 0ms
api.call(userProfileRequest1).subscribe(this::show);
api.call(userProfileRequest2).subscribe(this::show);
//--> after 10ms, should fire one single http aggregate request with those 2 calls, map the response items & send them to the corresponding subscribers (that will show the right user profile)
//ts : 20ms
api.call(userProfileRequest3).subscribe(this::show);
api.call(userProfileRequest4).subscribe(this::show);
api.call(userProfileRequest5).subscribe(this::show);
api.call(userProfileRequest6).subscribe(this::show);
api.call(userProfileRequest7).subscribe(this::show);
api.call(userProfileRequest8).subscribe(this::show);
api.call(userProfileRequest9).subscribe(this::show);
api.call(userProfileRequest10).subscribe(this::show);
api.call(userProfileRequest11).subscribe(this::show);
api.call(userProfileRequest12).subscribe(this::show);
//--> should fire a single http aggregate request RIGHT AWAY (we hit the max batch size) with the 10 items, map the response items & send them to the corresponding subscribers (that will show the right user profile)
The test code I wrote (with just strings) and pasted at the top of this question is meant to be a proof of concept for my final implementation.
Your Observable is not well constructed
public Observable<String> buildObservable(Observable<String> interval, String key) {
return interval.doOnSubscribe(() -> System.out.printf("creating observable for key " + key))
.doOnSubscribe(() -> requests.put(key, "xxx"))
.doOnNext(s -> System.out.println("filtering : key/val " + key + "/" + s))
.filter(s1 -> s1.equals(key));
}
When you subsribe in a subscriber : it's offen a bad design.
I'm not shure to understand what you want to achieve, but I think my code should be pretty close to yours.
Please note that, for all side effects, I use doMethods (like doOnNext, doOnSubscribe) to show I explicitly show that I want to do a side effect.
I replace your defer call by returning directly the interval : as you want to emit all interval events in your custom observable build in your defer call, returning the interval observable is better.
Please note, that you filtering your interval Observable :
Observable<String> interval = Observable.interval(10, TimeUnit.MILLISECONDS, scheduler)
.filter(l -> !requests.isEmpty()).
// ...
So, as soon you'll put something into requests map, interval will stop emmiting.
I don't understand what you wants to achieve with the request map, but please note that you may want to avoid side effects, and updating this map is clearly a side effect.
Update regarding comments
You may want to use the buffer operator to aggregate request, and then perform request in a bulk way :
PublishSubject<String> subject = PublishSubject.create();
TestScheduler scheduler = new TestScheduler();
Observable<Pair> broker = subject.buffer(100, TimeUnit.MILLISECONDS, 10, scheduler)
.flatMapIterable(list -> list) // you can bulk calls here
.flatMap(id -> Observable.fromCallable(() -> api.call(id)).map(response -> Pair.of(id, response)));
TestSubscriber<Object> ts1 = new TestSubscriber<>();
TestSubscriber<Object> ts2 = new TestSubscriber<>();
TestSubscriber<Object> ts3 = new TestSubscriber<>();
TestSubscriber<Object> ts4 = new TestSubscriber<>();
broker.filter(pair -> pair.id.equals("1")).take(1).map(pair -> pair.response).subscribe(ts1);
broker.filter(pair -> pair.id.equals("2")).take(1).map(pair -> pair.response).subscribe(ts2);
broker.filter(pair -> pair.id.equals("3")).take(1).map(pair -> pair.response).subscribe(ts3);
broker.filter(pair -> pair.id.equals("4")).take(1).map(pair -> pair.response).subscribe(ts4);
subject.onNext("1");
subject.onNext("2");
subject.onNext("3");
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
ts1.assertValue("resp1");
ts2.assertValue("resp2");
ts3.assertValue("resp3");
ts4.assertNotCompleted();
subject.onNext("4");
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
ts4.assertValue("resp4");
ts4.assertCompleted();
If you want to perform network request collapsin, you may want to check Hystrix : https://github.com/Netflix/Hystrix

Omitting all Scala Actor messages except the last

I want omit all the same type of messages except the last one:
def receive = {
case Message(type:MessageType, data:Int) =>
// remove previous and get only last message of passed MessageType
}
for example when I send:
actor ! Message(MessageType.RUN, 1)
actor ! Message(MessageType.RUN, 2)
actor ! Message(MessageType.FLY, 1)
then I want to recevie only:
Message(MessageType.RUN, 2)
Message(MessageType.FLY, 1)
Of course if they will be send very fast, or on high CPU load
You could wait a very short amount of time, storing the most recent messages that arrive, and then process only those most recent ones. This can be accomplished by sending messages to yourself, and scheduleOnce. See the second example under the Akka HowTo: Common Patterns, Scheduling Periodic Messages. Instead of scheduling ticks whenever the last tick ends, you can wait until new messages arrive. Here's an example of something like that:
case class ProcessThis(msg: Message)
case object ProcessNow
var onHold = Map.empty[MessageType, Message]
var timer: Option[Cancellable] = None
def receive = {
case msg # Message(t, _) =>
onHold += t -> msg
if (timer.isEmpty) {
import context.dispatcher
timer = Some(context.system.scheduler.scheduleOnce(1 millis, self, ProcessNow))
}
case ProcessNow =>
timer foreach { _.cancel() }
timer = None
for (m <- onHold.values) self ! ProcessThis(m)
onHold = Map.empty
case ProcessThis(Message(t, data)) =>
// really process the message
}
Incoming Messages are not actually processed right away, but are stored in a Map that keeps only the last of each MessageType. On the ProcessNow tick message, they are really processed.
You can change the length of time you wait (in my example set to 1 millisecond) to strike a balance between responsivity (length of time from a message arriving to response) and efficiency (CPU or other resources used or held up).
type is not a good name for a field, so let's use messageType instead. This code should do what you want:
var lastMessage: Option[Message] = None
def receive = {
case m => {
if (lastMessage.fold(false)(_.messageType != m.messageType)) {
// do something with lastMessage.get
}
lastMessage = Some(m)
}
}

How can I retrieve the first-completed Actor in a group of Actors in Scala?

I have a moderate number of long-running Actors and I wish to write a synchronous function that returns the first one of these that completes. I can do it with a spin-wait on futures (e.g.,:
while (! fs.exists(f => f.isSet) ) {
Thread.sleep(100)
}
val completeds = fs.filter(f => f.isSet)
completeds.head()
), but that seems very "un-Actor-y"
The scala.actors.Futures class has two methods awaitAll() and awaitEither() that seem awfully close; if there were an awaitAny() I'd jump on it. Am I missing a simple way to do this or is there a common pattern that is applicable?
A more "actorish" way of waiting for completion is creating an actor in charge of handling completed result (lets call it ResultHandler)
Instead of replying, workers send their answer to ResultHandler in fire-and-forget manner. The latter will continue processing the result while other workers complete their job.
The key for me was the discovery that every (?) Scala object is, implicitly, an Actor, so you can use Actor.react{ } to block. Here is my source code:
import scala.actors._
import scala.actors.Actor._
//Top-level class that wants to return the first-completed result from some long-running actors
class ConcurrentQuerier() {
//Synchronous function; perhaps fulfilling some legacy interface
def synchronousQuery : String = {
//Instantiate and start the monitoring Actor
val progressReporter = new ProgressReporter(self) //All (?) objects are Actors
progressReporter.start()
//Instantiate the long-running Actors, giving each a handle to the monitor
val lrfs = List (
new LongRunningFunction(0, 2000, progressReporter), new LongRunningFunction(1, 2500, progressReporter), new LongRunningFunction(3, 1500, progressReporter),
new LongRunningFunction(4, 1495, progressReporter), new LongRunningFunction(5, 1500, progressReporter), new LongRunningFunction(6, 5000, progressReporter) )
//Start 'em
lrfs.map{ lrf =>
lrf.start()
}
println("All actors started...")
val start = System.currentTimeMillis()
/*
This blocks until it receives a String in the Inbox.
Who sends the string? A: the progressReporter, which is monitoring the LongRunningFunctions
*/
val s = receive {
case s:String => s
}
println("Received " + s + " after " + (System.currentTimeMillis() - start) + " ms")
s
}
}
/*
An Actor that reacts to a message that is a tuple ("COMPLETED", someResult) and sends the
result to this Actor's owner. Not strictly necessary (the LongRunningFunctions could post
directly to the owner's mailbox), but I like the idea that monitoring is important enough
to deserve its own object
*/
class ProgressReporter(val owner : Actor) extends Actor {
def act() = {
println("progressReporter awaiting news...")
react {
case ("COMPLETED", s) =>
println("progressReporter received a completed signal " + s);
owner ! s
case s =>
println("Unexpected message: " + s ); act()
}
}
}
/*
Some long running function
*/
class LongRunningFunction(val id : Int, val timeout : Int, val supervisor : Actor) extends Actor {
def act() = {
//Do the long-running query
val s = longRunningQuery()
println(id.toString + " finished, sending results")
//Send the results back to the monitoring Actor (the progressReporter)
supervisor ! ("COMPLETED", s)
}
def longRunningQuery() : String = {
println("Starting Agent " + id + " with timeout " + timeout)
Thread.sleep(timeout)
"Query result from agent " + id
}
}
val cq = new ConcurrentQuerier()
//I don't think the Actor semantics guarantee that the result is absolutely, positively the first to have posted the "COMPLETED" message
println("Among the first to finish was : " + cq.synchronousQuery)
Typical results look like:
scala ActorsNoSpin.scala
progressReporter awaiting news...
All actors started...
Starting Agent 1 with timeout 2500
Starting Agent 5 with timeout 1500
Starting Agent 3 with timeout 1500
Starting Agent 4 with timeout 1495
Starting Agent 6 with timeout 5000
Starting Agent 0 with timeout 2000
4 finished, sending results
progressReporter received a completed signal Query result from agent 4
Received Query result from agent 4 after 1499 ms
Among the first to finish was : Query result from agent 4
5 finished, sending results
3 finished, sending results
0 finished, sending results
1 finished, sending results
6 finished, sending results