ddunlea
ddunlea

Reputation: 91

F# program runs correctly in fsi, but hangs as an exe

I have a piece of code that adds a row to a database when a MailboxProcessor receives a message. It works correctly when run in fsi, but it hangs when compiled to an exe. The script is as follows:

#r "../packages/Newtonsoft.Json/lib/net40/Newtonsoft.Json.dll"
#r "../packages/SQLProvider/lib/FSharp.Data.SqlProvider.dll"

open Newtonsoft.Json
open FSharp.Data.Sql
open System

let [<Literal>] ResolutionPath = __SOURCE_DIRECTORY__ + "/../build/" 
let [<Literal>] ConnectionString = "Data Source=" + __SOURCE_DIRECTORY__ + @"/test.db;Version=3"

// test.db is initialized as follows:
//
// BEGIN TRANSACTION;
//    CREATE TABLE "Events" (
//        `id`INTEGER PRIMARY KEY AUTOINCREMENT,
//        `timestamp` DATETIME NOT NULL
//    );
//    COMMIT;

type Sql = SqlDataProvider< 
            ConnectionString = ConnectionString,
            DatabaseVendor = Common.DatabaseProviderTypes.SQLITE,
            ResolutionPath = ResolutionPath,
            IndividualsAmount = 1000,
            UseOptionTypes = true >
let ctx = Sql.GetDataContext()

let agent = MailboxProcessor.Start(fun (inbox:MailboxProcessor<String>) ->  
    let rec loop() =
        async {
            let! msg = inbox.Receive()
            match msg with
            | _ ->
              let row = ctx.Main.Events.Create()
              row.Timestamp <- DateTime.Now
              printfn "Submitting"
              ctx.SubmitUpdates()
              printfn "Submitted"
            return! loop() 
        }
    loop() 
)

agent.Post "Hello"

When compiled to an exe, "Submitting" is printed, but then it hangs. If you want to try it out, the full code is on github here

Upvotes: 4

Views: 200

Answers (1)

ddunlea
ddunlea

Reputation: 91

It seems the problem was that the main thread was exiting before the MailboxProcessor could process it's mailbox. FSI is long-lived and so this wasn't happening there. I changed:

[<EntryPoint>]
let main argv =
    agent.Post "Hello"
    agent.Post "Hello again"
    0

to

[<EntryPoint>]
let main argv =
    agent.Post "Hello"
    agent.Post "Hello again"
    let waitLoop = async {
        while agent.CurrentQueueLength > 0 do
            printfn "Sleeping"
            do! Async.Sleep 1000
        }
    Async.RunSynchronously waitLoop
    0

and now the code executes as I had intended.

Upvotes: 5

Related Questions