C++ Improvement “mutable”

Today I came across something simple I though might be interesting to others.

bool Client_Globals::IsInitialReplicationComplete()
{
    m_mutex.Enter();
    const bool isComplete = m_isInitialReplicationSent && m_galaxyUpdates.empty();
    m_mutex.Leave();
    return isComplete;
}

I was reviewing some code changes for a check-in today, and I thought to myself, this function should really be const because it doesn’t modify anything in Client_Globals. I proceeded to add const, and was immediately reminded that Mutex::Enter/Leave was not marked const. I went and looked at my implementation of Mutex::Enter, and was reminded of course that I was modifying some things.

class Mutex
{
public:
    Mutex();
    ~Mutex();
	
    void Enter() const;
    void Leave() const;
    bool IsInMutex() const;
	
private:
    friend class Thread;
	
#if defined(_WIN32)
    CRITICAL_SECTION m_cs;
    DWORD m_currentThreadID;
#elif defined(__APPLE__) || defined(__linux__)
    pthread_mutex_t m_mutex;
    pthread_t m_currentThread;
#endif
    s32 m_currentEntryCount;
};

void Mutex::Enter()
{
    EnterCriticalSection(&m_cs);
    if (0 == m_currentEntryCount)
    {
        m_currentThreadID = GetCurrentThreadId();
    }
    ++m_currentEntryCount;
    assert(m_currentThreadID == GetCurrentThreadId());
}

After internally debating the pros & cons of actually making Mutex::Enter const despite it modifying members, I concluded the benefit outweighed the cost in this case, and I proceeded to make Mutex::Enter const using a const_cast<>.

void Mutex::Enter() const
{
    Mutex* pThis = const_cast<Mutex*>(this);
    EnterCriticalSection(&(pThis->m_cs));
    if (0 == pThis->m_currentEntryCount)
    {
        pThis->m_currentThreadID = GetCurrentThreadId();
    }
    ++(pThis->m_currentEntryCount);
    assert(pThis->m_currentThreadID == GetCurrentThreadId());
}

As I was making the change, I started wondering, hey this is 2024, I wonder if C++ has added a better way to do this. After some searching, I discovered in fact they did add something to help with this case in C++11 … the new keyword “mutable”. The “mutable” keyword allows you to mark a member variable as modifiable from within a const function. I modified my Mutex class as follows:

    mutable CRITICAL_SECTION m_cs;
    mutable DWORD m_currentThreadID;
    mutable s32 m_currentEntryCount;

Once the members in Mutex were declared mutable, I was able to eliminate the const_cast<> trick in Mutex::Enter, which made the method simpler:

void Mutex::Enter() const
{
    EnterCriticalSection(&m_cs);
    if (0 == m_currentEntryCount)
    {
        m_currentThreadID = GetCurrentThreadId();
    }
    ++m_currentEntryCount;
    assert(m_currentThreadID == GetCurrentThreadId());
}

Of course, this new keyword can be used in dangerous ways. I probably wouldn’t use it to work around every const correctness problem, but it seemed like a fantastic compromise in this specific case.