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

Internal exception surfacing when calling Post on disposed MailBoxProcessor #17849

Open
majocha opened this issue Oct 8, 2024 · 4 comments
Open

Comments

@majocha
Copy link
Contributor

majocha commented Oct 8, 2024

Message: 
System.ObjectDisposedException : Cannot access a disposed object.
Object name: 'Microsoft.Win32.SafeHandles.SafeWaitHandle'.

  Stack Trace: 
SafeHandle.DangerousAddRef(Boolean& success)
Kernel32.SetEvent(SafeWaitHandle handle)
EventWaitHandle.Set()
Mailbox`1.Post(TMsg msg) line 193
FSharpMailboxProcessor`1.Post(TMsg message) line 419

I can see the above exception occasionally in this test:

member this.``After dispose is called, mailbox should stop receiving and processing messages``() = task {

I assume the expected behavior here is no exception?

I'll try to come up with consistent minimal repro, but the problem seems to be here:

match pulse with
| null -> () // no one waiting, leaving the message in the queue is sufficient
| ev ->
// someone is waiting on the wait handle
ev.Set() |> ignore

ev is disposed and Set will fail.

The fix seems to be to also assign null to pulse when disposing it here:

interface System.IDisposable with
member _.Dispose() =
lock syncRoot (fun () ->
if isNotNull inboxStore then
inboxStore.Clear()
arrivals.Clear()
isDisposed <- true)
if isNotNull pulse then
(pulse :> IDisposable).Dispose()

@github-actions github-actions bot added this to the Backlog milestone Oct 8, 2024
@majocha
Copy link
Contributor Author

majocha commented Oct 8, 2024

This happens with timeouts or cancellation enabled, only then a wait handle is used in Mailbox.

@majocha
Copy link
Contributor Author

majocha commented Oct 8, 2024

Minimal repro:

open System.Threading

let cts = new CancellationTokenSource()
let mb =
    MailboxProcessor.Start( (fun inbox ->
        async {
            let! _ = inbox.Receive()
            let! _ = inbox.Receive()
            return ()
        }),
        cancellationToken = cts.Token
    )

// ensure pulse gets created
Thread.Sleep 100

mb.Post 1
mb.Dispose()
mb.Post 2

@T-Gro
Copy link
Member

T-Gro commented Oct 14, 2024

@T-Gro
Copy link
Member

T-Gro commented Oct 14, 2024

I think getting an ObjectDisposedException is the right scenario if the object was disposed.
But it should be better controlled by Fsharp.Core and not thrown from internal usage.

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

No branches or pull requests

2 participants