martes, noviembre 11, 2008

Antología del Disparate - II

Advertencia: Cualquier parecido con
la realidad, es sólo una sospechosa coincidencia...
Boxingstamos otra vez en una aplicación que maneja un buen puñado de hilos. El "genio" que la programa ya ha tomado consciencia de los peligros de la falta de sincronización, y dedica su tiempo libre a repartir bloqueos a diestra y siniestra. Este es un bello ejemplo de cómo se las gasta el caballerete (imaginario, of course):
private bool flag;
 
public void YoQueSe()
{
if (Monitor.TryEnter(flag))
{
// Código "sincronizado" ...
Monitor.Exit(flag);
}
}
¿Puede ver los disparates? Son varios, por supuesto. ¿Cómo cree que se manifestó el problema? ¿Se atreve a imaginar cómo se le pudo haber ocurrido esta barbaridad a su perpetrador?

Esta es, amigo mío, la declaración del método TryEnter de la clase Monitor:
public static bool TryEnter(object obj);
Como puede ver, el parámetro del método debe ser un Object, o más exactamente, una referencia a un objeto. Como Object es la clase base final del CLR, cualquier cosa que pasemos como parámetro servirá, a efectos del sistema de tipos. El método incluso admite que pasemos un valor de tipo Boolean...
... con la única pega de que Boolean es una estructura, un tipo con semántica de asignación por valor. Para poder pasar un Boolean donde se espera un objeto, .NET aplica la operación llamada boxing al valor. Es decir, crea un objeto temporal del tamaño adecuado, y copia en su interior el valor que queríamos pasar. ¿No queríamos una referencia? Tras el boxing ya tenemos una.
¿Y qué hay con la llamada final a Exit? Este es el prototipo de este segundo método:
public static void Exit(object obj);
Nuevamente, nos piden un objeto. Y el compilador no protesta si pasamos un valor de tipo Boolean. Simplemente mete el valor en una "cajita" y pasa el puntero a ésta. ¿La pena? Pues que las llamadas a TryEnter y Exit deberían utilizar el mismo objeto... y cada vez que se realiza el boxing, ¡se crea una instancia temporal diferente!
¿Qué consecuencias cree que tenía el disparate? Sorprendentemente, ¡ninguna!... al menos, mientras la aplicación se mantuvo ejecutando sobre .NET v1.1. Fue al migrar a .NET 3.5 que estos errores empezaron a ser detectados en tiempo de depuración. La llamada a Exit comenzó a generar una excepción, advirtiendo que se intentaba desbloquear a través de un objeto no bloqueado. Claro, sería una estupidez que el CLR tuviese que verificar en cada llamada a Exit que el objeto pasado no sea una instancia creada por boxing: penalizaría a los buenos programadores para evitar que los peores metiesen la pata.
¿Se da cuenta de que, ni siquiera en .NET v1.1, la aplicación estaba protegiendo la sección de código de marras? Si en algún momento se produjeron errores de sincronización en dicha sección, o fueron ignorados... o se perdieron gracias a la demencial política de manejos de excepciones de mi aplicación imaginaria. Observe que hay un segundo error en el código mostrado: se supone que las llamadas a TryEnter y Exit deberían estar asociadas a una instrucción try/finally.
Y ahora viene lo mejor: ¿cómo imagina que se le ocurrió la burrada al genio? El uso de TryEnter nos permite reconstruir los hechos. En realidad, el aprendiz de brujo intentó usar la instrucción lock en primer lugar. ¡Pero lock sí que puede detectar cuando alguien le pasa una estructura en vez de una referencia! Y como lock no se dejaba violentar, el muy pillo tradujo el código que suponía que se generaba para dicha instrucción. Pero como manazas que es, incluso eso lo hizo mal...
Moraleja: odio repetirme pero, por favor, esos genios que deambulan por ahí, ¡RTFM!

Etiquetas: , , , , ,

7 Comments:

Blogger Rox said...

Pelín borde con el RTFM !!!...pero en fin, sino no serías Ian ¿verdad? :)

P.D. La pena esque yo soy de esos mierdecillas que escribimos codigo basura, bueno ya no, me quede en los programillas Delphi de hace 3-4 años. Seguro que si te paso una aplicación mia me pones a caer de un burro.

jueves, noviembre 13, 2008 5:44:00 p. m.  
Blogger Rox said...

Por cierto... geniales los ultimos articulos ;)

jueves, noviembre 13, 2008 5:45:00 p. m.  
Blogger Ian Marteens said...

Seguro que si te paso una aplicación mia me pones a caer de un burro

:) No. De hecho, a los imaginarios programadores actuales de mi imaginaria empresa de ejemplo, los considero entre mis amigos imaginarios. Para que ponga a alguien imaginario a caer de un burro hace falta que combine una sarta de errores (como los que cometemos todos) con un ego prepotente, una actitud elitista... y sobre todo, falta de voluntad para corregir la sarta de errores. Yo cumplo con las tres primeras condiciones, pero me enorgullezco de faltar sistemáticamente a la cuarta. Siempre hay algo que aprender.

jueves, noviembre 13, 2008 11:38:00 p. m.  
Blogger Alfredo Novoa said...

Esto son temas "muy avanzados". Es muy fácil encontrar disparates en cosas mucho más básicas.

Hace poco estaba intentando contratar a un programador y una de las preguntas que les hice a los primeros que vinieron fue: nombra los mecanismos de sincronización de hilos de ejecución disponibles en la plataforma .Net que recuerdes.

Todos la dejaron en blanco (uno escapó literalmente corriendo), y luego eliminé del examen las preguntas "difíciles" como esa pero volví a obtener resultados muy parecidos.

Por ejemplo otra pregunta "difícil" que todos dejaron en blanco fue:

Nombra los niveles de aislamiento de transacciones de SQL Server que recuerdes como por ejemplo: "Read Uncommitted".

Yo pensaba que habría alguno que por lo menos pondría: "Read Committed", pero ya me dicen que espero demasiado de la gente.

Todos los que se presentaron venían con certificaciones y masters en .Net y la mayoría eran ingenieros.

viernes, noviembre 14, 2008 3:05:00 p. m.  
Blogger Rox said...

¿? Pues sinceramente, yo el "Read Committed" le hubiera puesto casi seguro y eso que soy de FP y programo para entretenerme :) Eso si la pregunta de los mecanismos de sincronización en .Net me hubiera hecho sentir gilipoll***, también es cierto que no me atrevería a pedir trabajo como programador.

Lo que si es cierto es que mucho título algunas personas pero a la hora de ponerles un problema real delante para solucionarlo contestarian algo así como: "¿Puedo llamar a Microsoft?" :)

viernes, noviembre 14, 2008 5:21:00 p. m.  
Blogger Alfredo Novoa said...

Hombre, un programador de Delphi aficionado no tiene por que saber eso (aunque ya tienes varios ejemplos en los últimos posts de Ian), pero a un programador profesional que pone en su currículum que es experto en .NET y que pide que le pagues una pasta, pues le deberían de sonar esas cosas.

Lo que pasa es que las grandes consultoras no suelen preguntar nada.

viernes, noviembre 14, 2008 10:01:00 p. m.  
Blogger Ian Marteens said...

Lo que pasa es que las grandes consultoras no suelen preguntar nada.

... y volvemos a lo mismo: si se trata de escribir una facturación para un solo puesto, no pasa nada. Pero como la implantes en un súper, puedes ir preso si no controlas los niveles de aislamiento. Entiendo que Rox no lo supiera (es una suposición) hasta encontrarse con la necesidad (o verlo en la documentación), pero lo que sí es demencial es que gente que se supone que han hecho la carrera no sepan de qué va el asunto.

La historia del "hombre operado de los ovarios" no es invento mío: ocurrió con los datos de un famoso hospital privado de Madrid, cuando se pasaban los expedientes de una chapuza escrita en dBase a una aplicación sobre InterBase. El autor original (licenciado en Stanford, por cierto, como solía recordarnos de cuando en cuando), cuando iba a meter un nuevo expediente, hacía que la clave primaria fuese el número de registros en "el fichero" (lo de tabla era demasiado "fino") de pacientes. ¿Qué es lo que estaba violando? Pues precisamente, el principio de las lecturas repetibles. Mientras las altas hospitalarias se daban en un solo ordenador, estupendo. Incluso cuando metieron dos ordenadores, la actividad no era tan intensa como para generar un problema.

Cuando se duplicó una clave, sin embargo, la ausencia de un índice que verificase la unicidad (que al parecer, era algo posible en determinadas versiones de dBase), consiguió que no saltasen las alarmas. En este caso, no pasó nada, porque saltaría a la vista que el paciente carecía de lo necesario para ser operado. En muchas empresas, la existencia de un doble control (expediente sobre papel, por ejemplo) hubiese evitado el problema. Hasta un día...

... y por cierto, otra de las manías de la Loca Academia de Programadores (imaginaria, por supuesto) es que en sus bases de datos (Oracle) ¡no hay una puñetera tabla con una clave primaria!

domingo, noviembre 16, 2008 2:59:00 a. m.  

Publicar un comentario

<< Home