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

The implementation of the AsyncEventingBasicConsumer is not safe when there are multiple handlers attached #838

Closed
quixoticaxis opened this issue May 15, 2020 · 4 comments · Fixed by #839
Assignees
Milestone

Comments

@quixoticaxis
Copy link

The invocations of the events in AsyncEventingBasicConsumer are not safe when there are multiple handlers attached in a sense that they only await the handler that had been attached last.
For example, the Received event is invoked like this:

await (Shutdown?.Invoke(this, reason) ?? Task.CompletedTask).ConfigureAwait(false);

This invocation is starting the handlers one by one, and, as soon as one returns a Task, it moves to the next one. When it reaches the last handler, it grabs its resulting task and awaits only this single Task (or Task.CompletedTask if the last handler happens to return null). I suppose this is not intended.

If the idea is to await all of the handlers (and, considering the remarks that state that "Handlers must copy or fully use delivery body before returning", it is), something along the lines of

var received = Received;
await Task.WhenAll(received.GetInvocationList()
    .Select(handler => handler.DynamicInvoke(
        this,
        new BasicDeliverEventArgs(
            consumerTag,
            deliveryTag,
            redelivered,
            exchange,
            routingKey,
            properties,
            body)) as Task
        ?? Task.CompletedTask))
        .ConfigureAwait(false);

may fix the issue.

P.s. I've initially asked the question in the mailing lists, but I guess it's more comfortable to discuss the code on github.

@lukebakken
Copy link
Contributor

I think I understand what is being asked here. This project is open-source and we would welcome a pull request that implements a solution with a test that demonstrates the problem with the current implementation. Thank you.

@bording
Copy link
Collaborator

bording commented May 15, 2020

While I'm not sure why you'd want multiple handlers attached to the the events, you are correct that the the way they are being invoked right now prevents that from working properly.

I wouldn't to see LINQ introduced into this code path, though, so a foreach loop over the GetInvocationList results probably makes more sense.

@bording
Copy link
Collaborator

bording commented May 15, 2020

Microsoft does have an MIT licensed InvokeAsync extension method that could potentially be used as inspiration, but I'll leave it to @lukebakken to determine if there are any licensing considerations around that.

@stebet
Copy link
Contributor

stebet commented May 15, 2020

Good catch!

stebet added a commit to stebet/rabbitmq-dotnet-client that referenced this issue May 19, 2020
@lukebakken lukebakken added this to the 6.1.0 milestone May 20, 2020
lukebakken pushed a commit to stebet/rabbitmq-dotnet-client that referenced this issue May 20, 2020
lukebakken added a commit that referenced this issue May 20, 2020
Making sure AsyncEventHandlers are all run. Fixes issue #838.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants