Browse Source

Created faults.hpp for program fault handling (replace assert())

Replaced all assert() in threading.* with appropriate checks.

git-svn-id: https://svn.microneil.com/svn/CodeDweller/trunk@7 d34b734f-a00e-4b39-a726-e4eeb87269ab
wx
madscientist 15 years ago
parent
commit
ce0ca5ea86
3 changed files with 243 additions and 36 deletions
  1. 173
    0
      faults.hpp
  2. 69
    35
      threading.cpp
  3. 1
    1
      threading.hpp

+ 173
- 0
faults.hpp View File

@@ -0,0 +1,173 @@
// faults.hpp
//
// Copyright (C) MicroNeil Research Corporation 2009
// This file is part of the CodeDweller library.
// See www.codedweller.com for details.
//
// Faults and Checks are classes we can use in place of assert() to handle
// unreasonable or necessary conditions in our code. They are constructed with
// friendly descriptions (and optionally error codes) and then used just
// like assert() would be used-- except they are designed to remain in the
// production code. After all, assert() is a C (not C++) concept.
//
// A ...Check(test_expression) activates when the test_expression is not true.
// A ...Fault(text_expression) activates when the test_expression is true.
//
// An Abort...() sends it's description to cerr then aborts (no cleanup).
// An Exit...() sends it's description to cerr then exits with a result code.
// A Runtime...() throws a runtime_error (self) with it's description in what().
// A Logic...() throws a logic_error (self) with it's description in what().
#ifndef MNR_faults
#define MNR_faults
#include <stdexcept>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
const int DefaultExitCode = EXIT_FAILURE; // Use this when no code is provided.
class AbortCheck { // If this check is false we will abort.
private:
const string myDescription; // This is what I have to say.
public:
AbortCheck(const string& Text) : myDescription(Text) {} // I am constructed with a description
void operator()(bool X) const { // Apply me like assert(exp)
if(false == X) { // If the expression is false then we
cerr << myDescription << endl; // failed the check so we display our
abort(); // description and abort.
}
}
const string Description() { return myDescription; } // You can ask for my Description.
};
class AbortFault { // If this fault occurs we will abort.
private:
const string myDescription; // This is what I have to say.
public:
AbortFault(const string& Text) : myDescription(Text) {} // I am constructed with a description
void operator()(bool X) const { // Apply me like assert(! exp)
if(true == X) { // If the expression is true then we
cerr << myDescription << endl; // have a fault so we display our fault
abort(); // description and abort.
}
}
const string Description() const { return myDescription; } // You can ask for my Description.
};
class ExitCheck { // If this check is false we will exit.
private:
const string myDescription; // This is what I have to say.
const int myExitCode; // This is what I send to exit().
public:
ExitCheck(const string& Text, int Code=DefaultExitCode) : // I am constructed with a description
myDescription(Text), myExitCode(Code) {} // and (optionlly) an exit code.
void operator()(bool X) const { // Apply me like assert(exp)
if(false == X) { // If the expression is false then we
cerr << myDescription << endl; // failed the check so we display our
exit(myExitCode); // description and exit with our code.
}
}
const string Description() { return myDescription; } // You can ask for my Description.
const int ExitCode() { return myExitCode; } // You can ask for my ExitCode.
};
class ExitFault { // If this fault occurs we will exit.
private:
const string myDescription; // This is what I have to say.
const int myExitCode; // This is what I send to exit().
public:
ExitFault(const string& Text, int Code=DefaultExitCode) : // I am constructed with a description
myDescription(Text), myExitCode(Code) {} // and (optionlly) an exit code.
void operator()(bool X) const { // Apply me like assert(! exp)
if(true == X) { // If the expression is true then we
cerr << myDescription << endl; // have a fault so we display our fault
exit(myExitCode); // description and exit with our code.
}
}
const string Description() const { return myDescription; } // You can ask for my Description.
const int ExitCode() const { return myExitCode; } // You can ask for my ExitCode.
};
class RuntimeCheck : public runtime_error { // Throw if this check fails.
public:
RuntimeCheck(const string& Text) : runtime_error(Text) {} // Construct me with a description.
void operator()(bool X) const { // Apply me like assert(exp)
if(false == X) { // If the expression is false then we
throw *this; // failed the check so we throw.
}
}
};
class RuntimeFault : public runtime_error { // Throw if we find this fault.
public:
RuntimeFault(const string& Text) : runtime_error(Text) {} // Construct me with a description.
void operator()(bool X) const { // Apply me like assert(exp)
if(true == X) { // If the expression is true then we
throw *this; // found the fault so we throw.
}
}
};
class LogicCheck : public logic_error { // Throw if this check fails.
public:
LogicCheck(const string& Text) : logic_error(Text) {} // Construct me with a description.
void operator()(bool X) const { // Apply me like assert(exp)
if(false == X) { // If the expression is false then we
throw *this; // failed the check so we throw.
}
}
};
class LogicFault : public logic_error { // Throw if we find this fault.
public:
LogicFault(const string& Text) : logic_error(Text) {} // Construct me with a description.
void operator()(bool X) const { // Apply me like assert(exp)
if(true == X) { // If the expression is true then we
throw *this; // found the fault so we throw.
}
}
};
#endif
// End Of Include MNR_faults Once Only =========================================

+ 69
- 35
threading.cpp View File

@@ -59,13 +59,13 @@ bool ThreadManager::lockExistingThread(Thread* T) {
LockedThread = T; // set our locked thread and
return true; // return true;
}
// We use assert() in the code below because if these conditions fail then there
// is something seriously wrong and potentially dangerous with the calling code.
const RuntimeCheck ThreadingCheck1("ThreadManager::unlockExistingThread():ThreadingCheck1(0 != LockedThread)");
const RuntimeCheck ThreadingCheck2("ThreadManager::unlockExistingThread():ThreadingCheck2(T == LockedThread)");

void ThreadManager::unlockExistingThread(Thread* T) { // Unlocks ThreadManager if T locked.
assert(0 != LockedThread); // We had better have a locked thread.
assert(T == LockedThread); // The locked thread had better match.
ThreadingCheck1(0 != LockedThread); // We had better have a locked thread.
ThreadingCheck2(T == LockedThread); // The locked thread had better match.
LockedThread = 0; // Clear the locked thread.
MyMutex.unlock(); // Unlock the mutex.
}
@@ -272,15 +272,19 @@ void Thread::join() {
// additional research it was determined that the use of a Semaphore with an
// initial count of 1 would work better overall on multiple Winx platforms -
// especially SMP systems.
const RuntimeCheck ThreadingCheck3("Mutex::Mutex():ThreadingCheck3(NULL != MyMutex)");

Mutex::Mutex() : // Creating a WIN32 Mutex means
IAmLocked(false) { // Setting IAmLocked to false and
MyMutex = CreateSemaphore(NULL, 1, 1, NULL); // create a semaphore object with
assert(NULL != MyMutex); // a count of 1.
ThreadingCheck3(NULL != MyMutex); // a count of 1.
}
const ExitCheck ThreadingCheck4("Mutex::~Mutex():");

Mutex::~Mutex() { // Destroying a WIN32 Mutex means
assert(false == IAmLocked); // Make sure we're not in use and
ThreadingCheck4(false == IAmLocked); // Make sure we're not in use and
CloseHandle(MyMutex); // destroy the semaphore object.
}

@@ -295,14 +299,18 @@ bool Mutex::tryLock() {
}
return DoIHaveIt; // Return true if we got it (see above).
}
const RuntimeCheck ThreadingCheck5("Mutex::lock():ThreadingCheck5(WAIT_OBJECT_0 == WaitForSingleObject(MyMutex, INFINITE))");

void Mutex::lock() { // Locking the WIN32 Mutex means
assert(WAIT_OBJECT_0 == WaitForSingleObject(MyMutex, INFINITE)); // Wait on the semaphore - only 1 will
ThreadingCheck5(WAIT_OBJECT_0 == WaitForSingleObject(MyMutex, INFINITE)); // Wait on the semaphore - only 1 will
IAmLocked = true; // get through or we have a big problem.
}
const LogicCheck ThreadingCheck6("Mutex::unlock():ThreadingCheck6(true == IAmLocked)");

void Mutex::unlock() { // Unlocking the WIN32 Mutex means
assert(true == IAmLocked); // making sure we're really locked then
ThreadingCheck6(true == IAmLocked); // making sure we're really locked then
IAmLocked = false; // reset the IAmLocked flag and
ReleaseSemaphore(MyMutex, 1, NULL); // release the semaphore.
}
@@ -312,26 +320,36 @@ bool Mutex::isLocked() { return IAmLocked; }
#else

// POSIX Mutex Implementation //////////////////////////////////////////////////
const RuntimeCheck ThreadingCheck7("Mutex::Mutex():ThreadingCheck7(0 == pthread_mutex_init(&MyMutex,NULL))");

Mutex::Mutex() : // Constructing a POSIX mutex means
IAmLocked(false) { // setting the IAmLocked flag to false and
assert(0 == pthread_mutex_init(&MyMutex,NULL)); // initializing the mutex_t object.
ThreadingCheck7(0 == pthread_mutex_init(&MyMutex,NULL)); // initializing the mutex_t object.
}
const ExitCheck ThreadingCheck8("Mutex::~Mutex():ThreadingCheck8(false == IAmLocked)");
const ExitCheck ThreadingCheck9("Mutex::~Mutex():ThreadingCheck9(0 == pthread_mutex_destroy(&MyMutex))");

Mutex::~Mutex() { // Before we destroy our mutex we check
assert(false == IAmLocked); // to see that it is not locked and
assert(0 == pthread_mutex_destroy(&MyMutex)); // destroy the primative.
ThreadingCheck8(false == IAmLocked); // to see that it is not locked and
ThreadingCheck9(0 == pthread_mutex_destroy(&MyMutex)); // destroy the primative.
}
const RuntimeCheck ThreadingCheck10("Mutex::lock():ThreadingCheck10(0 == pthread_mutex_lock(&MyMutex));");

void Mutex::lock() { // Locking a POSIX mutex means
assert(0 == pthread_mutex_lock(&MyMutex)); // asserting our lock was successful and
ThreadingCheck10(0 == pthread_mutex_lock(&MyMutex)); // asserting our lock was successful and
IAmLocked = true; // setting the IAmLocked flag.
}
const LogicCheck ThreadingCheck11("Mutex::unlock():ThreadingCheck11(true == IAmLocked)");
const RuntimeCheck ThreadingCheck12("Mutex::unlock():ThreadingCheck12(0 == pthread_mutex_unlock(&MyMutex))");

void Mutex::unlock() { // Unlocking a POSIX mutex means
assert(true == IAmLocked); // asserting that we are locked,
IAmLocked = false; // clearing the IAmLocked flag.
assert(0 == pthread_mutex_unlock(&MyMutex)); // asserting our unlock was successful and
ThreadingCheck11(true == IAmLocked); // asserting that we are locked,
IAmLocked = false; // clearing the IAmLocked flag, and
ThreadingCheck12(0 == pthread_mutex_unlock(&MyMutex)); // unlocking the actual mutex.
}

bool Mutex::tryLock() { // Trying to lock a POSIX mutex means
@@ -372,11 +390,13 @@ ScopeMutex::~ScopeMutex() {
#ifdef WIN32

// Win32 Implementation ////////////////////////////////////////////////////////
const RuntimeCheck ThreadingCheck13("ProductionGateway::ProductionGateway():ThreadingCheck13(NULL != MySemaphore)");

ProductionGateway::ProductionGateway() { // Construct in Windows like this:
const int HUGENUMBER = 0x7fffffL; // Work without any real limits.
MySemaphore = CreateSemaphore(NULL, 0, HUGENUMBER, NULL); // Create a Semaphore for signalling.
assert(NULL != MySemaphore); // That should always work.
ThreadingCheck13(NULL != MySemaphore); // That should always work.
}

ProductionGateway::~ProductionGateway() { // Be sure to close it when we're done.
@@ -394,40 +414,54 @@ void ProductionGateway::consume() {
#else

// POSIX Implementation ////////////////////////////////////////////////////////
const RuntimeCheck ThreadingCheck14("ProductionGateway::ProductionGateway():ThreadingCheck14(0 == pthread_mutex_init(&MyMutex, NULL));");
const RuntimeCheck ThreadingCheck15("ProductionGateway::ProductionGateway():ThreadingCheck15(0 == pthread_cond_init(&MyConditionVariable, NULL))");

ProductionGateway::ProductionGateway() : // Construct in POSIX like this:
Product(0), // All of our counts start at zero.
Waiting(0),
Signaled(0) {
assert(0 == pthread_mutex_init(&MyMutex, NULL)); // Initialize our mutex.
assert(0 == pthread_cond_init(&MyConditionVariable, NULL)); // Initialize our condition variable.
ThreadingCheck14(0 == pthread_mutex_init(&MyMutex, NULL)); // Initialize our mutex.
ThreadingCheck15(0 == pthread_cond_init(&MyConditionVariable, NULL)); // Initialize our condition variable.
}
const ExitCheck ThreadingCheck16("ProductionGateway::~ProductionGateway():ThreadingCheck16(0 == pthread_mutex_destroy(&MyMutex))");
const ExitCheck ThreadingCheck17("ProductionGateway::~ProductionGateway():ThreadingCheck17(0 == pthread_cond_destroy(&MyConditionVariable))");

ProductionGateway::~ProductionGateway() { // When we're done we must destroy
assert(0 == pthread_mutex_destroy(&MyMutex)); // our local mutex and
assert(0 == pthread_cond_destroy(&MyConditionVariable)); // our condition variable.
ThreadingCheck16(0 == pthread_mutex_destroy(&MyMutex)); // our local mutex and
ThreadingCheck17(0 == pthread_cond_destroy(&MyConditionVariable)); // our condition variable.
}
const RuntimeCheck ThreadingCheck18("ProductionGateway::produce():ThreadingCheck18(0 == pthread_mutex_lock(&MyMutex))");
const RuntimeCheck ThreadingCheck19("ProductionGateway::produce():ThreadingCheck19(0 == pthread_cond_signal(&MyConditionVariable))");
const RuntimeCheck ThreadingCheck20("ProductionGateway::produce():ThreadingCheck20(0 == pthread_mutex_unlock(&MyMutex))");

void ProductionGateway::produce() { // To produce in POSIX
assert(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex.
ThreadingCheck18(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex.
++Product; // Add an item to our product count.
if(Signaled < Waiting) { // If anybody is waiting that has not
assert(0 == pthread_cond_signal(&MyConditionVariable)); // yet been signaled then signal them
ThreadingCheck19(0 == pthread_cond_signal(&MyConditionVariable)); // yet been signaled then signal them
++Signaled; // and keep track. They will count this
} // down as they awaken.
assert(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so
ThreadingCheck20(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so
} // waiting threads can fly free :-)

void ProductionGateway::consume() { // To consume in POSIX
assert(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex.
while(0 >= Product) { // Until we have something to consume,
++Waiting; // wait for a signal from
assert(0 == pthread_cond_wait(&MyConditionVariable, &MyMutex)); // our producer. When we have a signal
--Waiting; // we are done waiting and we have
--Signaled; // been signaled. Of course, somebody
} // may have beaten us to it so check.
--Product; // If we have product then take it.
assert(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so
const RuntimeCheck ThreadingCheck21("ProductionGateway::consume():ThreadingCheck21(0 == pthread_mutex_lock(&MyMutex))");
const RuntimeCheck ThreadingCheck22("ProductionGateway::consume():ThreadingCheck22(0 == pthread_cond_wait(&MyConditionVariable, &MyMutex))");
const RuntimeCheck ThreadingCheck23("ProductionGateway::consume():ThreadingCheck23(0 == pthread_mutex_unlock(&MyMutex))");

void ProductionGateway::consume() { // To consume in POSIX
ThreadingCheck21(0 == pthread_mutex_lock(&MyMutex)); // Lock our mutex.
while(0 >= Product) { // Until we have something to consume,
++Waiting; // wait for a signal from
ThreadingCheck22(0 == pthread_cond_wait(&MyConditionVariable, &MyMutex)); // our producer. When we have a signal
--Waiting; // we are done waiting and we have
--Signaled; // been signaled. Of course, somebody
} // may have beaten us to it so check.
--Product; // If we have product then take it.
ThreadingCheck23(0 == pthread_mutex_unlock(&MyMutex)); // At the end unlock our mutex so
}

#endif

+ 1
- 1
threading.hpp View File

@@ -61,11 +61,11 @@
#ifndef MNR_threading
#define MNR_threading

#include <cassert>
#include <set>
#include <vector>
#include <string>
#include <queue>
#include "faults.hpp"

using namespace std;


Loading…
Cancel
Save