miércoles, 9 de mayo de 2012

Refactorizando código de sección crítica con AOP y PostSharp

La sincronización expuesta aquí se basa en los procesos y nos referimos a ella por el hecho de que esta otorga varias técnicas de coordinación de hilos que se ejecutan en forma simultánea y procesos para poder completar una determinada tarea con el objetivo de que estas se ejecuten en un orden establecido y que no se dé lugar a eventos inesperados.


Mencionamos a continuación en forma general los tipos de sincronización existentes: Barrier, Bloqueo/Semáforo (Lock/Semaphore), Unión de hilos (Thread join), Exclusión Mutua (Mutex), Monitores (Monitors), Sincronización no bloqueante (Non-blocking synchronization), Rendezvous, Contadores de eventos y secuenciadores (Event counters and sequencers).

Utilizaremos el tipo de sincronización basado en Exclusión Mutua de acuerdo al código de ejemplo que expondremos seguidamente, dicho ejemplo se encuentra codificado en C# .net sin la utilización de aspectos:


Ejemplo de Sincronización sin AOP para Exclusión Mutua

public void SynchronizedMethod()
{
        lock(this)
       {
               //.. hacer algo, esta es una sección //crítica de código o de exclusión mutua
       }
}

Y más abajo exponemos lo mismo pero en su presentación en AOP a través de un atributo llamado Synchronized:


Ejemplo de Sincronización en AOP para Exclusión Mutua con PostSharp

[Synchronized]
public void SynchronizedMethod()
{
         //.. hacer algo, esta es una sección crítica de //código o de exclusión mutua
}

Básicamente la idea es hacer un código más elegante y a la vez más fácil de leer por parte de algún programador y mediante la utilización de los atributos se logra este objetivo. Finalmente colocamos la definición del aspecto de sincronización en el fragmento de código siguiente valiéndonos del framework PostSharp orientado a AOP:

Definición de Aspecto de Sincronización en AOP con PostSharp

[Serializable]
public class SynchronizedAttribute : OnMethodBoundaryAspect
{
         public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
                Monitor.Enter(eventArgs.Instance);
        }

        public override void OnExit(MethodExecutionEventArgs eventArgs)
       {
               Monitor.Exit(eventArgs.Instance);
       }
}


Con esto nos ahorramos el hecho de colocar en cada método la palabra clave lock, el cual garantiza la ejecución de una sección crítica de código o exclusión mutua también denominada, una sección crítica de código es aquella que no permite ser ejecutada al mismo tiempo, debido a que puede darse el caso de que haya lectura sucia o que al mismo tiempo dos o más hilos de ejecución traten de modificar el valor de una variable, por lo tanto se bloquea dicha sección de código en forma exclusiva en un ambiente concurrente, no permitiendo de esta forma a que otro hilo trate de ejecutar esa sección crítica en un instante dado.

Además al evitar repetir esta palabra clave nos aseguramos de no repetir código por un lado y por otro de separar la funcionalidad básica del aspecto de sincronización.

En la definición del aspecto de sincronización expuesto más arriba, podemos decir que existen dos métodos, el primero llamado OnEntry el cual habilita la entrada a la ejecución del código de sección crítica y se utiliza la clase Monitor que provee un mecanismo para sincronizar el acceso a los objetos y el segundo método llamado OnExit, que se usa para realizar el abandono de la sección crítica de la ejecución del código, por lo tanto se invoca al método Exit de la clase Monitor.




sábado, 5 de mayo de 2012

Direccionando el concepto de Tracing con AOP y PostSharp

En ingeniería de software, el tracing es una clase especializada de logging para recolectar información acerca de un programa en ejecución. Comúnmente esta información es utilizada por los programadores con el objetivo de depurar el código y dependiendo sobre todo del tipo y la información contenida en un log de traza (trace log), otras personas, tales como los administradores de sistemas o personal de soporte técnico pueden realizar detecciones y posibles soluciones a problemas comunes con algún software utilitario.


Con la siguiente tabla se trata de establecer algunas diferencias entre los conceptos de Tracing y Logging:

Diferencias entre Logging y Tracing


LoggingTracing
 Utilizados principalmente por administradores de sistemas. Utilizados principalmente por programadores.
 Registra informaciones de alto nivel y más amigables (ej. Instalación fallida de un programa)  Registra informaciones de bajo nivel y más crípticas (ej. Lanzamiento de una excepción)
 No debe ser “ruidoso”, es decir, no puede contener duplicación o información no relevante para la persona que lo va a auditar.  Puede ser “ruidoso”, pues generalmente se utiliza con fines de depuración.
 El formato de salida de la información debe o requiere ser estándar.  Pocas limitaciones en cuanto al formato de salida de la información.
 Los logs de eventos son a menudo localizadas, es decir, están expuestos en el idioma correspondiente a la región o país dado.  La localización no es una preocupación, comúnmente en las herramientas de tracing y los entornos de desarrollo en general se utilizan el idioma inglés.


A continuación presentamos un código sencillo de tracing en C# .net, lo que se expone aquí son dos trazas, una cuando ingresa al método realizando la traza del nombre del método y sus parámetros de entrada y otra al finalizar el método utilizando además otros parámetros:

Ejemplo de Tracing sin PostSharp

public bool Method(params string[] inputValues)
{
       Trace("Enter", "MethodName", inputValues);
       // Traza todos los parámetros de entrada al //ingresar al método
       bool someValue;
       Trace("Exit", "MethodName", someValue);
       // Traza los parámetros al abandonar el método
       return someValue;
}

En contra partida exponemos la diferencia con el mismo método (función) con PostSharp, podemos notar el mejor encapsulamiento de este aspecto no funcional, reduciendo las líneas de código y separando por ende las incumbencias funcionales y no funcionales y no tener además la necesidad de llamar al método de Trace tanto cuando ingresa al método o como cuando se acaba la ejecución del método:


Ejemplo de Tracing en AOP con PostSharp

[Trace]
public bool Method(params string[] inputValues)
{
         //hacer algo
}


La declaración y definición del aspecto de tracing se indica en el trozo de código siguiente:

Definición de Aspecto de Tracing en PostSharp


[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
          public override void OnEntry(MethodExecutionEventArgs eventArgs)
         {
                   Trace.TraceInformation("Ingresando {0}.", eventArgs.Method);
         }

         public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
                   Trace.TraceInformation("Saliendo {0}.", eventArgs.Method);
        }
}

En esta definición del aspecto implementado en PostSharp podemos observar dos métodos sobrescritos (overrides) una para detectar la entrada al método con OnEntry y otra para la salida del mismo con OnExit y por ende evitar las llamadas del método Trace, cuando uno ingresa o sale del método así como lo implementa el código sin el paradigma de AOP.