Transacciones en memoria en AjSharp usando References

Published on Author lopezLeave a comment

El año pasado me encontré con Clojure, un dialecto de Lisp que compila a Java. Estuve codificando AjSharpure (ver AjLisp Family: Implementing Lisp Interpreters in C#)(La familia AjLisp, implementando intérpretes Lisp en C#), un intérprete tipo Clojure (no un compilador) en .NET (si necesitan una más completa implementación en .NET, hay un ClojureCLR, compilado usando DLR, Dynamic Language Runtime).

Clojure tiene varias características interesantes, especialmente las estructuras persistentes (persistencia acá no es grabar en una base de datos, sino tener listas, diccionarios, árboles “inmutables”; cuando uno agregar algo a una lista, la lista original queda igual, y se construye una nueva lista…), concurrencia, agentes, y más. Una de sus características es su soporte de STM, Software Transactional Memory. Más sobre estos temas en links al final de este post. De acuerdo a:

http://en.wikipedia.org/wiki/Software_transactional_memory

In computer science, software transactional memory (STM) is a concurrency control mechanism analogous to database transactions for controlling access to shared memory in concurrent computing. It is an alternative to lock-based synchronization. A transaction in this context is a piece of code that executes a series of reads and writes to shared memory. These reads and writes logically occur at a single instant in time; intermediate states are not visible to other (successful) transactions.

Hace unas semanas, leí:

Clojure concurrency Part 4

Ese artículo describe la conducta de “ref”, “dosync” y operaciones relacionadas sobre “ref”. Quería experimentar con esas características, pero en mi intéprete AjSharp. Así, que escribí una clase Reference, y agregué soporte de transacciones al lenguaje.

Ahora, puedo escribir:

reference = new Reference();
reference <- 1;
result = @reference;

La clase Reference es una “built’-in”. En AjLanguage/AjSharp el operador <- es equivalente a ref.SetValue(newvalue). Ahora, agregué el operador @ para obtener el valor, y es equivalente a ref.GetValue() (incidentalmente, GetValue and SetValue son los métodos de las interfaces de Channels y Futures, ver:

AjSharp: Implementing Futures

AjSharp: Implementando Futures

Channels and Go Routines in AjSharp (Part 1)

Canales y Go Routines en AjSharp (Parte 1)

Channels and Go Routines in AjSharp (Part 2)

Canales y Go Routines en AjSharp (Parte 2)

En esos posts, todavía escribía <-channel o <-future para OBTENER el valor desde un channel o future; ahora, estoy decidiendo poner como obsoleta esa notación, en favor de @channel, @future, que me parece más clara).

Decidí hacer que las operaciones de get/set de valor sean explícitas, usando esos operadores, en vez de hacerlas transparentes. Eso mismo había decidido para manipular canales y valores futuros.

Pero, ¿cómo se usa una referencia? Bien, empieza a cobrar sentido con la nueva palabra clave transaction:

channel1 = new Channel();
end1 = new Channel();
end2 = new Channel();
reference1 = new Reference();
reference2 = new Reference();
reference1 <- 1;
reference2 <- 2;
go transaction 
{ 
  reference1 <- @channel1; 
  result2original = @reference2; 
  end1 <- @reference1; 
}
go transaction 
{ 
  channel1 <- @reference2; 
  reference2 <- @reference2 + 1; 
  result1original = @reference1; 
  end2 <- @reference2; 
}
result1 = @end1; // 2
result2 = @end2; // 3

Las dos transacciones son lanzadas con el comando go (ver los posts citados más arriba para ver go routines en AjSharp). La primera transacción espera a recibir un valor de la segunda, usando un canal que está en la variable channel1. Los valores en reference1, reference2 en la primer transacción son los originales, aunque al mismo tiempo están siendo cambiados en la segunda. Esto es, una transacción ve un “snapshot” de los valores almacenados en referencias, tomados al comienzo de su vida. Un cambio en un valor de una referencia durante una transacción, es confirmada al final de la transacción, y entonces, se vuelve visible al resto de la transacción. Pero las transacciones que aún se encuentren en ejecución, ven el valor del “snapshot”. Si una transacción altera el valor de una referencia que ha sido alterado por otra transacción concurrente, se lanza una excepción.

Mi implementación es sencilla e ingenua. Podría mejorarla. Pero por ahora, los tests dan verde, y es lo bastante buena para mis experimentos. Internamente, cada referencia tiene un valor actual visible, un posible valor actualizado por una transacción en ejecución (no se admiten DOS valores actualizados), una lista de valores de “snapshots”, para alimentar a transacciones que aún estan en ejecución. Una instancia de Machine, asociada al thread en ejecución mantiene una lista de referencias con “snapshots”, y una lista de transacciones en ejecución.

Mis enlaces:

http://delicious.com/ajlopez/clojure

http://delicious.com/ajlopez/clojure+concurrency

http://delicious.com/ajlopez/clojure+tutorial

http://delicious.com/ajlopez/stm

http://delicious.com/ajlopez/clojure+stm

Como siempre, pueden bajarse el código en:

http://code.google.com/p/ajcodekatas/source/browse/

en el directorio trunk/AjLanguage.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *