Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected deadlock using Alt.chooser #168

Open
btrepp opened this issue Sep 7, 2017 · 1 comment
Open

Unexpected deadlock using Alt.chooser #168

btrepp opened this issue Sep 7, 2017 · 1 comment

Comments

@btrepp
Copy link

btrepp commented Sep 7, 2017

So I am trying to learn hopac, and am making a little 'toy' fake eventstore.
At the moment this is just a server that multiple clients can write to

When I call it with Alt.choose, the world is happy and all works. When called with Alt.chooser, the app hangs forever. The code I'm calling is below. I was experimenting to get the feeling of using Alts to back out of comitting things. (based off https://haf.github.io/2017/04/13/ThreadSafeDictionary-in-Hopac-in-fsharp.html).

I would've expected Alt.chooser to just randomly commit to one of the alts, instead it deadlocks. Most likely this is a problem in my server, but I can't see what it is, so I am worried I'm drastically mis-understanding the purpose of Alternatives.

do! Alt.chooser [
                          Writer.saveEvent stream revision1 eventid event server
                          Writer.saveEvent stream revision2 eventid event server
                           Writer.saveEvent stream revision3 eventid event server
                                    ]

Below is the implementation of the server.

///Represents a stream of events
type Writer= 
    private {
        write: Ch<StreamIdentifier*Revision*EventIdentifier*Event* Ch<unit>*Promise<unit>>
    }
module Writer = 
    let private logger = Logging.getCurrentLogger()
    let private commitEventMessage (event:SavedEvent) :LogLevel -> Message =
        eventX "Commited event with {eventId} to {streamId} at {revision}" 
        >> setField "eventId" (EventIdentifier.format event.id)
        >> setField "revision" (Revision.format event.position)
        >> setField "streamId" (StreamIdentifier.format event.stream)

    let private abandonedMessage (event:SavedEvent) :LogLevel -> Message = 
        eventX "Cancelled event with {eventId} to {streamId} at {revision}"
        >> setField "eventId" (EventIdentifier.format event.id)
        >> setField "revision" (Revision.format event.position)
        >> setField "streamId" (StreamIdentifier.format event.stream)

    let private willSaveMessage (event:SavedEvent) :LogLevel -> Message = 
        eventX "Saving event {eventId} to {streamId} at {revision}"
        >> setField "eventId" (EventIdentifier.format event.id)
        >> setField "revision" (Revision.format event.position)
        >> setField "streamId" (StreamIdentifier.format event.stream)

    let createMemoryWriter : Job<Writer> = 
        job{
                let write = Ch()
                ///Dense version. Shows mutable access
                ///Exists mainly to highlight that we are sharing something concurrently
                let mutable list = List.empty
                let server = 
                    Alt.choose [
                        Ch.take write ^=> fun (stream,revision,eventid,event,repl,nack) ->
                                    let saved = SavedEvent.create stream revision eventid event
                                    let closure = list
                                    list <- event::list
                                    Logger.log logger Debug (willSaveMessage saved)
                                    >>=.
                                    Alt.choose [
                                        (repl *<- (printf "A")) ^=>. Logger.log logger Info (commitEventMessage saved)
                                        (nack ^-> fun () -> list <- closure) ^=>. Logger.log logger Debug (abandonedMessage saved)
                                    ]
                ]
                Job.foreverServer server |> start
                return {write = write}
        }


    let saveEvent stream revision eventid event server: Alt<unit>  =
        printf "SA"
        server.write *<+->- fun repl nack -> stream,revision,eventid,event,repl,nack

Also
What is the best way for asking questions to do with hopac?.
Should there be a gitter or something?. Or is posting in the github repo sufficient?.

@haf
Copy link
Member

haf commented Dec 22, 2017

Not sure why chooser<->choose makes a big difference. Guess it depends more on the client and what's already been posted on channels before you make force the Alt.chooser to commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants