Convert the kernel to C++: Lock-holding class

From: David Howells
Date: Thu Feb 27 2025 - 11:44:32 EST


Hi Peter,

I played with the code a bit and implemented a generic template class to hold
a lock:

template<typename LOCK>
struct Lock {
LOCK &m;
bool is_locked;
Lock(LOCK &mutex) : m(mutex) {
m.lock();
is_locked = true;
}
~Lock() {
if (is_locked)
m.unlock();
}
void drop() {
if (is_locked)
m.unlock();
is_locked = false;
}
void reget() {
m.lock();
is_locked = true;
}
operator bool() {
return is_locked;
}
};

used something like with the Mutex, Semaphore and Inode classes below:

int bar(Inode *inode, int x)
{
Lock m_lock = inode->m;

for (int i = 0; i < 10; i++) {
Lock m_sem(inode->s);
do_something(inode);
m_lock.drop();
do_something_else(inode);
m_lock.reget();
}
}

It seems that the storage for objects of this lock-holding class entirely
optimise away under reasonable conditions. The compiler can keep track of
both the lock pointer/ref *and* the is_locked member without having to
actually store them - even when exception handling is thrown into the mix.

A further thing I've noticed is that zero-size classes can be passed in the
argument list without the consumption of register/stack space. I wonder if it
might be possible to use an argument to indicate the possession of a lock?
E.g.:

class Have_lock {};
class Pass_lock {};
template<typename LOCK>
struct Lock {
...
operator Have_lock() {
__builtin_compile_time_assert(is_locked);
BUG_ON(!is_locked);
return Have_lock();
}
operator Pass_lock() {
__builtin_compile_time_assert(is_locked);
BUG_ON(!is_locked);
is_locked = false;
return Have_lock();
}
};

int do_something(Inode *i, Have_lock l);
int do_something_and_unlock(Inode *i, Pass_lock l);

David
---

Silly, non-functional implementation of a mutex and a semaphore class so that
I could see it trivially in the .o file:

struct Mutex {
int counter;
void lock() {
__atomic_fetch_sub(&counter, 1, __ATOMIC_RELEASE);
}
void unlock() {
__atomic_fetch_add(&counter, 1, __ATOMIC_ACQUIRE);
}
};

struct Semaphore {
int counter;
void lock() {
__atomic_fetch_sub(&counter, 1, __ATOMIC_RELEASE);
}
void unlock() {
__atomic_fetch_add(&counter, 1, __ATOMIC_ACQUIRE);
}
};

struct Inode {
int ref;
Mutex m;
Semaphore s;
};