

class Lock {  // (pure abstract) interface
public :
    virtual ~Lock() = 0;  // [to have it in vtbl]
    virtual void lock() = 0;
    virtual void unlock() = 0;
};


class Id {  // (pure abstract) interface
public :
    virtual ~Id() = 0;  // [to have it in vtbl]
    virtual bool isSelf() = 0;
    virtual void setSelf() = 0;
};


class RecursiveLock : public Lock {
public :
    RecursiveLock(Lock* twoLocks, Id& anId);
    virtual void lock();    // implements Lock::lock
    virtual void unlock();  // implements Lock::unlock
private :
    Lock& waitLock;  // lock to block upon
    int lockCount;   // lock's reference count
    Id& ownerId;     // lock's owner
    Lock& metaLock;  // this data's guard
};


RecursiveLock::RecursiveLock(Lock* twoLocks, Id& anId) :
    waitLock(twoLocks[0]),
    lockCount(0),   // lock is initially unused, i.e. reference count zero
    ownerId(anId),  // [not explicitly initialized here]
    metaLock(twoLocks[1]) {
}


void RecursiveLock::lock() {

    // loop to try acquiring lock;
    // may take several times since two locks are involved
    for (;;) {

        // data guard
        metaLock.lock();  // will only block shortly

        bool nonBlocking = (lockCount == 0 || ownerId.isSelf());

        if (nonBlocking) {  // lock is available or ours

            if (lockCount == 0) {  // unused at the moment

                // get it
                waitLock.lock();  // won't block here
                ownerId.setSelf();
            }

            lockCount++;  // reference count
        }

        metaLock.unlock();

        if (nonBlocking) {
            break;
        }

        // wait on actual lock
        waitLock.lock();  // may block longer

        // [needed, to acquire locks in a defined sequence,
        // to avoid deadlocks]
        waitLock.unlock();
    }
}


void RecursiveLock::unlock() {

    // data guard
    metaLock.lock();  // will only block shortly

    // preconditions [not checked in this sample code]:
    // lockCount > 0 && ownerId.isSelf()

    lockCount--;  // reference count

    if (lockCount == 0) {

        waitLock.unlock();
    }

    metaLock.unlock();
}


