Download framework here.
All posts are here:
- Part I - Workers and ParallelWorkers
- Part II - Agents and control messages
- Part III - Default error management
- Part IV - Custom error management
- Part V - Timeout management
- Part VI - Hot swapping of code
- Part VII - An auction framework
- Part VIII — Implementing MapReduce (user model)
- Part IX — Counting words …
Hot swapping of code
Let’s get back a couple of steps and consider what happens when you get an error. Sure, your agent will continue processing messages, but it might be doing the wrong thing. Your message handling code might be buggy.
Ideally you’d want to patch things on the ﬂy. You’d want to replace the message processing code for an agent without stopping it.
Here is how you do it:
let counter2 = spawnAgent (fun msg state -> printfn "From %i to %i" state (state + msg);
state + msg) 0 counter2 <-- 2 counter2 <-- SetAgentHandler(fun msg state –>
printfn "From %i to %i via multiplication" state (state * msg); msg * state) counter2 <-- 3
**From 0 to 2
From 2 to 6 via multiplication**
After the agent receives a SetAgentHandler message, it switch from a ‘+’ agent to a ‘*’ agent on the ﬂy!! All the messages that come after that one gets multiplied to the state. Also, the state is preserved between changes in behavior.
It might not be immediately apparent how to load a function at runtime, but it is really simple. Imagine that I get the data on the function to load from somewhere (i.e. a management console UI).
let assemblyNameFromSomewhere, typeNameFromSomewhere, methodNameFromSomewhere =
"mscorlib.dll", "System.Console", "WriteLine"
I can then use it to dynamically load a message handler (in this case Console.Writeline).
let a = Assembly.Load(assemblyNameFromSomewhere) let c = a.GetType(typeNameFromSomewhere) let m = c.GetMethod(methodNameFromSomewhere, [|"".GetType()|]) let newF = fun (msg:string) (state:obj) -> m.Invoke(null, [| (msg:>obj) |])
And then it is as simple as posting a SetAgentHandler.
counter2 <-- SetAgentHandler(newF) counter2 <-- "blah"
Now our counter2 agent has become an echo agent on the ﬂy, having loaded Console.WriteLine dynamically. Note how the agent moved from being a ‘+’ agent taking integers to being a ‘*’ agent taking integers to being an ‘echo’ agent taking strings. And it didn’t stop processing messages for the whole time.
Obviously, you can do the same thing with workers:
echo <-- SetWorkerHandler(fun msg -> printfn "I'm an echo and I say: %s" msg) echo <-- "Hello"
parallelEcho <-- SetWorkerHandler(fun msg -> tprint ("I'm new and " + msg)) messages |> Seq.iter (fun msg -> parallelEcho <-- msg)
A silly interlude
As a way to show some agents talking to each other, here is a simple program that simulates marital interactions (of the worst kind):
let rec husband = spawnWorker (fun (To, msg) -> printfn "Husband says: %s" msg; To <-- msg) let rec wife = spawnWorker (fun msg -> printfn "Wife says: screw you and your '%s'" msg) husband <-- (wife, "Hello") husband <-- (wife, "But darling ...") husband <-- (wife, "ok")
**Husband says: Hello
Husband says: But darling
Wife says: screw you and your ‘Hello’
Wife says: screw you and your ‘But darling’
Husband says: ok
Wife says: screw you and your ‘ok’**
And yes, you cannot expect messages to be in the right sequence … Next up is an auction application.