Sure :) Just hold an internal queue of jobs and enumerate through the queue when the job processor is in Start mode. In any other mode, just enqueue new jobs until the processor goes into start mode.
type 'a msg = // '
| Start
| Stop
| Pause
| Job of (unit -> unit)
type processQueue() =
let mb = MailboxProcessor.Start(fun inbox ->
let rec loop state (jobs : System.Collections.Generic.Queue<_>) =
async {
if state = Start then
while jobs.Count > 0 do
let f = jobs.Dequeue()
f()
let! msg = inbox.Receive()
match msg with
| Start -> return! loop Start jobs
| Pause -> return! loop Pause jobs
| Job(f) -> jobs.Enqueue(f); return! loop state jobs
| Stop -> return ()
}
loop Start (new System.Collections.Generic.Queue<_>()))
member this.Resume() = mb.Post(Start)
member this.Stop() = mb.Post(Stop)
member this.Pause() = mb.Post(Pause)
member this.QueueJob(f) = mb.Post(Job f)
This class behaves as expected: You can enqueue jobs in the Pause state, but they'll only run in the Start state. Once the processQueue is stopped, it can't be restarted, and none of the enqueued jobs will run (its easy enough to change this behavior so that, rather than killing the queue, it just doesn't enqueue a job in the Stop state).
Use MailboxProcessor.PostAndReply if you need two-way communication between the mailbox processor and your code.