Do asynchronous HttpApplication events wait until they return?

丶灬走出姿态 提交于 2019-12-05 21:21:19

It's not clear to me the exact workings when running in integrated pipeline mode, but I can tell you what I see for the non-integrated case, and the semantics should remain the same.

The short answer is that each event handler is fired in series, whether synchronous or asynchronous, and the next is not fired until the previous completes.

You can trace this through the source code.

A request comes in and is stored in a queue. Typically, when the HttpRuntime dequeues a request, it initializes an HttpApplication for the request by calling its InitInternal method, passing the HttpContext as an argument.

HttpApplication.InitInternal initializes a new HttpApplication.ApplicationStepManager class for the non-integrated-mode case. You can then see it calls the BuildSteps method on it. This creates an ArrayList to store the steps and constructs and stores all of the steps. Specifically, these steps are implementations of an IExecuteStep interface. Ultimately, when all of the steps are added, the list is finalized by copying it to an array and saving it for later in a member var _execSteps.

There are several sources for the steps, but the most used you'll see is the HttpApplication.CreateEventExecutionSteps which takes the event type (begin request, authorize, etc.) and the steps array to add its steps for that event in. If you drill into CreateEventExecutionSteps you can see it adding an IExecuteStep for each async and sync handler it knows about, from the AsyncEvents and Events tables, respectively. The IExecuteStep interface itself basically consists of an Execute method and a CompletedSynchronously flag.

Now, pause and look back at one of those Add methods like the one you mentioned, AddOnEndRequestAsync, and you can see it add info about the async handler to the AsyncEvents table. CreateEventExecutionSteps will then walk through this table and an AsyncEventExecutionStep will be constructed for each handler added.

Back to the request flow. After the HttpRuntime initializes the HttpApplication for the request, it calls its BeginProcessRequest method, which fires ResumeSteps.

ResumeSteps is the important one where you can see how the steps are used and what the wait strategy is in the async case. You can see it maintaining a _currentStepIndex into the array of execution steps. Eventually you see it grab the next step from the array and call its Execute method. If the step reports that its execution CompletedSynchronously, it loops and goes again. If not, it lets the method complete and enters into the async abyss.

To see what happens in this async case, you have to look at that AsyncEventExecutionStep implementation which was created for the async handlers. In its Execute implementation, you see it fire the begin handler and pass in a completion callback. In the constructor, you see this callback initialized to a method that eventually calls... HttpApplication.ResumeSteps again!

And so it keeps going, executing steps, sync or async, until the array is overrun at which point it "finishes" the request processing.

The point is, you can clearly see that steps, which translate to the event handlers you add, are executed one by one, and whether sync or async, the following steps are not executed until the current step completes. Your question was whether events are handled one-by-one in this way, but as you can see, in fact it's even more granular, with each event handler being handled this way, so each gets synchronized access to the HttpContext and can operate without worrying about whether they are still "in the right phase" of the pipeline.

Obviously there's other details in that source code, yada yada, but this is the gist.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!