Última actividad

Implementando monitores inteligentes en C++

Continuando con nuestros trucos de programación de C++, hoy vamos a ver un mecanismo para implementar monitores inteligentes usando plantillas en C++.

Probablemente estarás pensando… ¿qué leches es un monitor inteligente? Bien, la verdad es que no he encontrado un nombre mejor. Con “monitor inteligente” me refiero a un monitor que puede ser empleado para proteger cualquier objeto del acceso concurrente en entornos multihebra. Con “cualquier objecto” me refiero a objetos de cualquier clase, tanto escritos por uno mismo como por terceras partes. Suena interesante, ¿o no?

Aquí tenemos un fragmento de código típicamente problemático.

   class IntGenerator {
   public:
      virtual int generateValue() const = 0;
   };

   typedef std::set<int> IntSet;

   IntGenerator *igen = ...; // initializes a integer generator
   IntSet dataset;

   void *entrypoint(void *data) {
      while (true)
         dataset.insert(igen->generateValue());
   }

   int main() {
      pthread_t thr[2];
      pthread_create(&thr[0], NULL, entrypoint, NULL);
      pthread_create(&thr[1], NULL, entrypoint, NULL);
   }

El acceso concurrente a dataset probablemente derive en una inconsistencia en el árbol binario que mantiene internamente dicha variable.

La semántica que queremos obtener es la siguiente. Tenemos una plantilla llamada Monitor que emplearemos de la siguiente forma (incluyo sólo el código modificado):

   IntSet dataset;
   Monitor<IntSet> mdataset(&dataset);

   void *entrypoint(void *data) {
      while (true)
         mdataset->insert(igen->generateValue());
   }

La principal diferencia es que hemos creado un nuevo objeto monitor usando dataset como entrada. Ahora podemos emplear el operador de miembro a través de puntero (o flecha) para invocar la operación insert(). El resultado de esa invocación es thread-safe, es decir, no habrá ninguna otra hebra accediendo a los miembros del conjunto mientras dicha operación es invocada.

¿Dónde está la magia? Bien, es evidente que el monitor mantiene una referencia al objeto monitorizado, la cual es empleada para invocar la operación insert(). Podemos sospechar fácilmente el siguiente código:

template <class T>
class Monitor {
public:

  Monitor(T *obj) : mObj(obj) {}

private:

  T *mObj;
};

La aparición del operador de acceso a miembro a través de puntero revela su sobrecarga.

  T *operator -> () { ... }

¿Qué hay dentro de ese operador? Bien, no tan rápido. Puesto que esta operación proporciona semántica thread-safe, es evidente que no encontraremos el siguiente código.

  T *operator -> () { return mObj;}

Es obvio que el siguiente código tampoco aparecerá:

  T *operator -> () {
    somemutex.lock();
    return mObj;
    somemutex.unlock();
  }

Está claro que podemos incluir cualquier código “prefijo” antes de la sentencia de retorno del valor monitorizado. Sin embargo, no podemos incluir ningún código “sufijo” después de la sentencia return. Os prometo que existe una solución que proporciona la semántica mencionada más arriba. ¿Sabrías decir cual?

Ejemplo de punteros inteligentes C++ en TIDorb

Al hilo del post de Alvaro, sobre punteros inteligentes, me gustaría presentar cómo los hemos implementado en el ORB TIDorb, incluyendo multithreading con POSIX threads. La especificación CORBA para C++ hace un uso intensivo de punteros inteligentes para manejar las referencias a objetos CORBA que suelen usarse por distintos threads concurrentemente.

Este trozo de código pertenece a la librería TIDThread que recubre la API de POSIX threads al estilo Java. Podeis ver el código completo aquí.

Lee el resto »

Tags: none

Punteros ¿inteligentes? en C++

Uno de los mayores quebraderos de cabeza que provoca la programación en C/C++ es el uso de punteros. Un puntero mal gestionado puede provocar desde la típica violación de segmento hasta la corrupción de la memoria del programa (algo muy muy muy muy muy muy muy divertido de depurar).

Otros entornos como Java nos permiten olvidarnos de este infierno gracias a los recolectores de basura. En esta entrada, vamos a ver cómo construir un sencillo mecanismo que nos ofrece algo muy parecido, como viene siendo habitual, en C++.

Lee el resto »

Ocultando detalles de implementación en C++

En ocasiones, por diversas consideraciones, puede resultar interesante ocultar ciertos detalles de implementación en nuestro código de tal forma que las interfaces estén lo más desacopladas posible del comportamiento que tienen asociadas. Mientras algunos lenguajes como Objective-C soportan de manera efectiva esta separación, C++ no proporciona demasiadas facilidades para tal fin. Hoy estudiaremos un par de idiomas que pueden reducir esta problemática.

Lee el resto »

Tipos covariantes en miembros virtuales

Después de mucho tiempo sin actividad, hoy vamos a hablar de uno de esos mecanismos que implementan los lenguajes modernos que nos hacen la vida más sencilla: el uso de tipos de retorno covariantes en las funciones miembro virtuales. Viendo el nombre, parece algo bastante complejo; pero no os asustéis. Es algo simple y, en ciertas ocasiones, muy útil.

Lee el resto »