У меня есть HttpHandler, который выглядит следующим образом:C# HttpHandler сбой IIS - но только в режиме выпуска с оптимизациями
public class GoodIISHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
try
{
context.Response.End();
}
catch (Exception)
{
}
}
finally
{
Console.WriteLine("Inside finally block");
}
}
public bool IsReusable => true;
}
Он отлично работает! За исключением случаев, когда они скомпилированы в режиме деблокирования с включенными и затем выполненными оптимизациями (в Windows Server 2012 выполняется IIS 8 с .NET 4.5.2). Затем он сбрасывает IIS.
Произошло необработанное исключение, и процесс был прекращен.
ID приложения:/LM/W3SVC/1592535739/ROOT/HttpHandlerApp
Идентификатор процесса: 648
Исключение: System.Threading.ThreadAbortException
Сообщение: Тема была быть прервана.
StackTrace: в System.Web.HttpRuntime.ProcessRequestNotificationPrivate (IIS7WorkerRequest сог, HttpContext контекст)
на System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper (IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 флаги)
в System.Web.Hosting.PipelineRuntime.ProcessRequestNotification (IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, Int32 флаги)
и
Faulting Имя приложения: w3wp.exe, версия: 8.0.9200.16384, штамп времени: 0x50108835
Faulting имя модуля: KERNELBASE.dll, версия: 6.2.9200.17366, штамп времени: 0x554d4531
код исключения: 0xe0434352
Неисправность смещения: 0x000000000004aea8
Сбойное идентификатор процесса: 0x288
Сбойное время запуска приложения: 0x01d1ec2c7db735a6
Faulting путь приложения: C: \ WINDOWS \ system32 \ Inetsrv \ w3wp.exe
Faulting путь модуля: C: \ Windows \ system32 \ KERNELBASE.dll
Отчет Id: bb7471fa-581f-11e6-93f1-00155d461b1c
Сбойного пакет полное наименование:
Сбойного пакет-относительный идентификатор приложения:
Я сузил проблему до определенного бита IL, который изменяется, когда оптимизация включена/отключена.
Вот IL для ProcessRequest
, который не откажет IIS:
.method public hidebysig newslot virtual final
instance void ProcessRequest(class [System.Web]System.Web.HttpContext context) cil managed
{
// Code size 30 (0x1e)
.maxstack 1
.try
{
.try
{
IL_0000: ldarg.1
IL_0001: callvirt instance class [System.Web]System.Web.HttpResponse [System.Web]System.Web.HttpContext::get_Response()
IL_0006: callvirt instance void [System.Web]System.Web.HttpResponse::End()
IL_000b: leave.s IL_0010
} // end .try
catch [mscorlib]System.Exception
{
IL_000d: pop
IL_000e: leave.s IL_0010
} // end handler
IL_0010: leave.s IL_001d
} // end .try
finally
{
IL_0012: ldstr "Inside finally block"
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: endfinally
} // end handler
IL_001d: ret
} // end of method GoodIISHandler::ProcessRequest
Чтобы сбой IIS, изменение линия 19 в
IL_000e: leave.s IL_001d
Это изменение делает вложенный выход поймать блок непосредственно к метод возвращает команду без посещения одной команды, оставшейся в блоке try try..finally.
Моя гипотеза заключается в том, что авария связана с магическим режимом автоматического реверберации ThreadAbortException
(один из которых выдает context.Response.End()
) и как это управляет CLR и/или IIS и/или ASP.NET; и поэтому, когда оптимизированный ИЛ выходит из блока catch непосредственно в инструкцию возврата, возникает некоторая гипотетическая ошибка сброса, которую CLR/IIS/ASP.NET пробивает в IL_0010
, и пропустил ThreadAbortException весь IIS.
Есть ключи разбросаны по всему интернету об этом поведении (например these msdn docs состояния ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.
и this blog упоминает ... notably ASP.NET, even aborts individual threads routinely without unloading the domain. They backstop the ThreadAbortExceptions, call ResetAbort on the thread and reuse it or return it to the CLR ThreadPool.
), но я не могу найти ничего конкретного о механизме.
Итак, я думаю, мой вопрос: что здесь происходит? Действительно ли я обнаружил ошибку CLR или IIS или ASP.NET, чтобы этот механизм прерывания потока был побежден оптимизацией IL?
Полностью оформленный в стиле репродукции можно найти по адресу https://github.com/jamezor/HttpHandlerRepro для всех, кто интересуется.
Какую версию .NET Framework вы использовали? Как известно, у 4.6.0 есть некоторые проблемы, и вы должны отключить RyuJIT для обхода некоторых. –
Мы работаем с .NET 4.5.2, поэтому не можем винить 4.6, к сожалению :) – Jamezor