Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a polling Windows service

I usually write Windows services in C# but I'm giving it a go in F#. For a polling serivce, like this one, I ordinarily use a class I've written, which is similar to BackgroundWorker. It spawns a background thread and fires an OnWork method at regular intervals. (Complete code is here [github].)

Is there another, perhaps better or more idiomatic, way to do this in F#? It could be a better way to write the polling background worker class, or built-in alternatives to it.

EDIT

Here's what I came up, based on Joel's suggestion.

module Async =
  open System.Diagnostics

  let poll interval work =
    let sw = Stopwatch()
    let rec loop() =
      async {
        sw.Restart()
        work()
        sw.Stop()
        let elapsed = int sw.ElapsedMilliseconds
        if elapsed < interval then
          do! Async.Sleep(interval - elapsed)
        return! loop()
      }
    loop()

//Example
open System.Threading
let cts = new CancellationTokenSource()
Async.Start(Async.poll 2000 (fun () -> printfn "%A" DateTime.Now), cts.Token)
Thread.Sleep(TimeSpan.FromSeconds(10.0))
cts.Cancel()

The service using poll:

type MyService() =
  inherit System.ServiceProcess.ServiceBase()

  let mutable cts = new CancellationTokenSource()
  let interval = 2000

  override __.OnStart(_) = 
    let polling = Async.poll interval (fun () ->
      //do work
    )
    Async.Start(polling, cts.Token)

  override __.OnStop() = 
    cts.Cancel()
    cts.Dispose()
    cts <- new CancellationTokenSource()

  override __.Dispose(disposing) =
    if disposing then cts.Dispose()
    base.Dispose(true)

I wish there was a way to avoid the mutable CancellationTokenSource, but alas.

like image 237
Daniel Avatar asked Sep 22 '11 18:09

Daniel


1 Answers

I might be tempted to write a simple loop in an asynchronous workflow. You can use do! Async.Sleep interval to sleep in between polling - this has two advantages: you're not tying up a thread just to have it sit idle with Thread.Sleep, and the do! automatically checks for cancellation for you if you pass a CancellationToken into Async.Start.

Plus, if your polling operation involves network communication, you're already in an async workflow, making it trivial to make async network calls.

like image 53
Joel Mueller Avatar answered Nov 10 '22 19:11

Joel Mueller