sé cómo evitar que suceda
Sólo que no sé por qué se hace que (todavía). Y parece que de hecho has encontrado un error en el .Net BCL o, más probablemente, en el JIT.
Acabo de comentar todas las líneas en el método MyButton_Click_Aux
y luego comencé a traerlas de nuevo, una por una.
Quite el volatile
de la int estática y ya no obtendrá un StackOverflowException
.
ahora a la investigación por qué ... Es evidente que algo que ver con las barreras de memoria está causando un problema - quizás alguna manera forzar el método MyButton_Click_Aux
a llamarse ...
ACTUALIZACIÓN
Bueno por lo que otras personas están descubriendo que .Net 3.5 no es un problema.
estoy usando .nt 4, así por lo que estos comentarios se refieren a lo siguiente:
Como ya he dicho, tarda el volátil y funciona.
Igualmente, si se pone la volatilidad de nuevo y quitar el try/finally también funciona:
private static void MyButton_Click_Aux()
{
//try { /*remove because stack overflows without*/ }
//finally
//{
var myLogData = new ArrayList();
myLogData.Add(reportCount);
//myLogData.Add("method MyButtonClickAux");
//Log(myLogData);
//}
}
También me preguntaba si era algo que ver con la no inicializado reportCount
cuando el try/finally está en . Pero no importa si lo inicias a cero.
estoy mirando a la IL ahora - aunque podría requerir alguien con algunas grietas ASM para que se involucren ...
Informe final de actualización Como digo, esto realmente va a requerir un análisis de la Salida JIT para entender realmente lo que está sucediendo y, aunque me resulta divertido analizar el ensamblador, creo que probablemente sea un trabajo para alguien en Microsoft, por lo que este error puede ser confirmado y solucionado. Dicho eso, parece ser un conjunto bastante limitado de circunstancias.
He pasado a una versión de lanzamiento para deshacerme de todo el ruido de IL (nops, etc.) para el análisis.
Esto, sin embargo, ha tenido un impacto complicado en el diagnóstico. Pensé que lo tenía pero no lo hice, pero ahora sé lo que es.
yo probamos este código:
private static void MyButton_Click_Aux()
{
try { }
finally
{
var myLogData = new ArrayList();
Console.WriteLine(reportCount);
//myLogData.Add("method MyButtonClickAux");
//Log(myLogData);
}
}
Con el int tan volátil. Funciona sin culpa. Aquí está la IL:
.maxstack 1
L_0000: leave.s L_0015
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
L_0007: pop
L_0008: volatile.
L_000a: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount
L_000f: call void [mscorlib]System.Console::WriteLine(int32)
L_0014: endfinally
L_0015: ret
.try L_0000 to L_0002 finally handler L_0002 to L_0015
Entonces nos fijamos en el código mínimo necesario para obtener de nuevo el error:
private static void MyButton_Click_Aux()
{
try { }
finally
{
var myLogData = new ArrayList();
myLogData.Add(reportCount);
}
}
Y es IL:
.maxstack 2
.locals init (
[0] class [mscorlib]System.Collections.ArrayList myLogData)
L_0000: leave.s L_001c
L_0002: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()
L_0007: stloc.0
L_0008: ldloc.0
L_0009: volatile.
L_000b: ldsfld int32 modreq([mscorlib]System.Runtime.CompilerServices.IsVolatile) WindowsFormsApplication1.Form1::reportCount
L_0010: box int32
L_0015: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)
L_001a: pop
L_001b: endfinally
L_001c: ret
.try L_0000 to L_0002 finally handler L_0002 to L_001c
La diferencia? Bueno, hay dos que vi: el boxeo de la int volátil y una llamada virtual. Por lo tanto, estas dos clases de configuración:
public class DoesNothingBase
{
public void NonVirtualFooBox(object arg) { }
public void NonVirtualFooNonBox(int arg) { }
public virtual void FooBox(object arg) { }
public virtual void FooNonBox(int arg) { }
}
public class DoesNothing : DoesNothingBase
{
public override void FooBox(object arg) { }
public override void FooNonBox(int arg) { }
}
y luego trató de cada una de estas cuatro versiones del método infractor:
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.FooNonBox(reportCount);
}
que trabaja.
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.NonVirtualFooNonBox(reportCount);
}
Cual también funciona.
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.FooBox(reportCount);
}
Oops - StackOverflowException
.
Y:
try { }
finally
{
var doesNothing = new DoesNothing();
doesNothing.NonVirtualFooBox(reportCount);
}
Vaya de nuevo! StackOverflowException
!
Podríamos ir más lejos con esto, pero el problema es, creo, claramente causado por el boxeo del int volátil mientras dentro del último bloque de un try/catch ... puse el código dentro del try, y No hay problema. Agregué una cláusula catch (y puse el código allí), tampoco hay problema.
También podría aplicarse al boxeo de otros tipos de valores, supongo.
Por lo tanto, para resumir, en .Net 4.0, en compilaciones tanto de depuración como de liberación, el boxeo de un int volátil en un bloque finally parece causar que el JIT genere código que termina llenando la pila. El hecho de que el seguimiento de la pila simplemente muestre "código externo" también respalda esta proposición.
Incluso existe la posibilidad de que no siempre se pueda reproducir e incluso dependa de la disposición y el tamaño del código generado por try/finally. Está claro que tiene algo que ver con un error jmp
o algo similar que se genera en la ubicación incorrecta que finalmente repite uno o más comandos push en la pila. ¡La idea de que eso sea causado por una operación de caja es, francamente, fascinante!
final Informe final de actualización
Si nos fijamos en el error de MS Conectar que @Hasty G encontró (responder más abajo) - que se ve allí que los manifiestos errores de una forma similar, pero con un bool volátil en una declaración catch.
Además, MS en cola una solución para esto después de obtenerlo para reproducir, pero aún no hay una revisión disponible después de 7 meses. Ya lo he registrado anteriormente como soporte de MS Connect, así que no diré nada más: ¡no creo que sea necesario!
actualización final final final (23/02/2011)
Se fija - pero aún no depurados. Cita del MS Team en el error MS Connect:
Sí, está solucionado. Estamos en el proceso de descubrir la mejor forma de enviar una solución. Ya está arreglado en 4.5, pero realmente nos gustaría arreglar un lote de errores de generación de código antes de la versión 4.5.
"debe, por lo tanto, ser un error en .net" Probablemente no. – jason
Por favor, publique el código para 'MyButton_Click_Aux' –
Incluya el código en MyButton_Click_Aux. –