@Piru
I do. Perhaps not at the same time as others. I also like blood, and... Ehh, wait, forget that.
A bit hypocritical of me, considering what time I posted. Surprised you never picked up on that :lol:
If you mean that the tc_Trapcode would be set while executing your own code. Yes, that would work. But it could still get lockup condition, unless if you really isolate ALL OS call stuff from user code (for example no user code gets to run at all with any OS lock obtained).
Well now, see that's the thing :-) The purpose of the implementation layer is to hide away the OS. It's even namespaced so that unless you are really trying to deliberately code at an OS level, you can't even reach it.
That said, OS Locks may be held within my code as a consequence of using synchronised calls (using the Lock class, for example). However, the destructor for this class guarentees that any lock it holds will be released.
Consider this:
class Lockable {
// service class, wraps signalsemaphore
// inherit this class to get threadsafe locking properties
public:
void waitLock(); // May throw ObjectDestroyed
void freeLock();
// ....other locking methods
public:
~Lockable();
private:
OSNative::SignalSemaphore sem;
bool destroyed;
// guts...
};
void Lockable::waitLock()
{
// if this throws ObjectDestroyed, it is considered
// illegal for the caller to access the object again
// in any way, shape or form.
if (destroyed) {
// too late
throw ObjectDestroyed();
}
OSNative::ObtainSemaphore(&sem);
if (destroyed) {
// we got unlucky whilst waiting
OSNative::releaseSemaphore(&sem);
throw ObjectDestroyed();
}
// ok, we got the lock.
}
void Lockable::freeLock()
{
if (!destroyed) {
OSNative::ReleaseSemaphore(&sem);
}
}
Lockable::~Lockable()
{
OSNative::ObtainSemaphore(&sem);
destroyed = true;
OSNative::ReleaseSemaphore(&sem);
// Any pending calls to waitLock() in concurrent threads
// will now throw ObjectDestroyed() as soon as they obtain
// the semaphore. Wait for the semaphore again before
// exiting the destructor, to ensure this is the last
// thread that will touch the object.
OSNative::ObtainSemaphore(&sem);
OSNative::ReleaseSemaphore(&sem);
}
// auxilliary class, used to make functions safely synchronizable:
class LockExclusive {
public:
LockExclusive(Lockable* obj) : item(obj) { item->waitLock(); }
~LockExclusive() { item->freeLock(); }
private:
Lockable* item;
};
The above is more or less the locking strategy my high level code uses.
To incorporate our handler, consider:
class MyClass : public Lockable {
public:
int riskyOperation(int a, int b);
// ....
private:
int n;
}
int MyClass::riskyOperation(int a, int b)
{
// Irrespective of how this function terminates, the
// automatic instance of ExclusiveLock should be destroyed
// correctly.
LockEsclusive lock(this);
n = a / b;
return n;
}
This is the principle way I use locking in high level code. If something throws after 'lock' is instansiated, 'lock' itself will be destroyed and consequently will release the lock on the object it was called for (in this trivial case, our "MyClass" object).
So, if our a/b invokes our tc_TrapCode exception mechanism and 'appears' to magically throw ZeroDivide, our lock ought to be released cleanly.
Or at least, this is the idea.