Pregunta

Tengo dos funciones:

void prepare () y nsoid acabado () que se llamará secuencialmente como:

prepare();
<do something>;
finish(); 
... 
prepare(); 
<do something>; 
finish();

Quiero hacer una afirmación simple para probar simplemente que, de hecho, se les llama de esta manera y que no se les llama simultáneamente o fuera de servicio en la aplicación.

Esta aplicación es una aplicación única. Esta es una simple verificación de cordura de desarrollo/prueba para asegurarse de que estas funciones se llamen en orden y que por cualquier razón, no se llaman simultáneamente. Además, estas afirmaciones/controles de cordura deben omitirse del código de producción, ya que el rendimiento es crucial.

¿Un simple afirmación () le gustaría que este funcione mejor?

int test = 0;

void prepare() {
   assert(++test == 1);

   .
   .
   .
}

void finish() {
    assert(--test == 0);

    .
    .
    .
}
¿Fue útil?

Solución

Su código está bien, a menos que necesite permitir la anidación prepare y finish llamadas.

Si no se permite la anidación, podría usar un bool en lugar de un int:

bool locked = false;;

void prepare() {
    assert( ! locked );
    locked = true;
    ...
}

void finish() {
    assert( locked );
    locked = false;
    ...
}

Otros consejos

Es posible que quieras cambiar

int test = 0;

a

#ifndef NDEBUG
int test = 0;
#endif

Para satisfacer su requisito de que "cualquier código relacionado con esta prueba debe omitirse de la producción".

Probablemente quieras:

int test = 0;

void prepare() {
    // enter critical section
    assert(test++ == 0);

    .
    .
    .
    // leave critical section 
}

void finish() {
    // enter critical section
    assert(--test == 0);

    .
    .
    .
    // leave critical section
}

Aquí hay una condición de carrera: dos instancias concurrentes de prepare podría tomar el valor de test Al mismo tiempo, ambos lo incrementan en un registro para obtener ambos 1, luego haga la comparación para obtener true.

Haciendolo volatile es no voy a ayudar. En cambio, debes poner un mutex en test, al igual que:

boost::mutex mtx;
int test = 0;

void prepare()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(test++ == 0);
    // ...
}

void finish()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(--test == 0);
}

Si pones <do something>; en un class Puede reducir la necesidad de un cheque:

Solo tenga la llamada del constructor prepare y la llamada del destructor finish. Luego se aplica automáticamente que se llaman apropiadamente.

Tenga en cuenta que los problemas de concurrencia y anidación aún se aplican: si desea evitar la anidación, entonces necesitaría algún tipo de estado global (¿miembro de clase estática?) Para realizar un seguimiento de eso, y si se usa en más de un hilo de acceso a eso. El contador necesitaría estar protegido mutex.

También tenga en cuenta que también podría hacer privado operator new/delete Para evitar que alguien cree uno en el montón y no lo destruya.

Como estás usando C ++, ¿por qué no usar RAII? Todavía debe verificar el uso de reentrantes, pero RAII simplifica las cosas considerablemente. Combinado con Mutex de Larsmans y Eliminación de Raedwald en ndebug:

struct Frobber {
  Frobber() {
    assert(mtx.try_lock());
#ifndef NDEBUG
    try {  // in case prepare throws
#endif
      prepare();
#ifndef NDEBUG
    }
    catch (...) {
      mtx.unlock();
      throw;
    }
#endif
  }

  void something();
  // And the other actions that can be performed between preparation and finishing.

  ~Frobber() {
    finish();
#ifndef NDEBUG
    mtx.unlock();
#endif
  }

private:
#ifndef NDEBUG
  static boost::mutex mtx;
#endif

  Frobber(Frobber const&);  // not defined; 0x: = delete
  Frobber& operator=(Frobber const&);  // not defined; 0x: = delete
};
#ifndef NDEBUG
boost::mutex Frobber::mtx;
#endif

void example() {
  Frobber blah;  // instead of prepare()
  blah.something();
  // implicit finish()
}

Dentro de ejemplo, simplemente no poder Haga algo sin prepararse primero, y el final siempre sucederá, incluso si se lanza una excepción.

Nota al margen sobre ndebug: si lo usa de esta manera, asegúrese de que siempre esté definido o siempre indefinido en todos Unidades de traducción, a diferencia de cómo se usa para afirmar (lo que permite definir y indefinir en varios puntos).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top