Skip to content

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

@quixoticaxis

Description

@quixoticaxis

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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions