This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwlib/mutex.h

174 lines
4.5 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MUTEX_H
#define MUTEX_H
#if defined(_MSC_VER)
#pragma once
#endif
#include "always.h"
#include "thread.h"
// Always use mutex or critical section when accessing the same data from multiple threads!
// ----------------------------------------------------------------------------
//
// Mutex class is an expensive way of synchronization! Use critical sections
// (below) for all synchronization. Use mutexes for inter-process locking.
//
// ----------------------------------------------------------------------------
class MutexClass
{
void* handle;
unsigned locked;
// Lock and unlock are private so that you can't use them directly. Use LockClass as a sentry instead!
// Lock returns true if lock was succesful, false otherwise
bool Lock(int time);
void Unlock();
public:
// Name can (and usually should) be NULL. Use name only if you wish to create a globally unique mutex
MutexClass(const char* name = NULL);
~MutexClass();
enum {
WAIT_INFINITE=-1
};
class LockClass
{
MutexClass& mutex;
bool failed;
public:
// In order to lock a mutex create a local instance of LockClass with mutex as a parameter.
// Time is in milliseconds, INFINITE means infinite wait.
LockClass(MutexClass& m, int time=MutexClass::WAIT_INFINITE);
~LockClass();
// Returns true if the lock failed
bool Failed() { return failed; }
private:
LockClass &operator=(const LockClass&) { return(*this); }
};
friend class LockClass;
};
// ----------------------------------------------------------------------------
//
// Critical sections are faster than mutex classes and they should be used
// for all synchronization.
//
// ----------------------------------------------------------------------------
class CriticalSectionClass
{
void* handle;
unsigned locked;
// Lock and unlock are private so that you can't use them directly. Use LockClass as a sentry instead!
void Lock();
void Unlock();
public:
// Name can (and usually should) be NULL. Use name only if you wish to create a globally unique mutex
CriticalSectionClass();
~CriticalSectionClass();
class LockClass
{
CriticalSectionClass& CriticalSection;
public:
// In order to lock a mutex create a local instance of LockClass with mutex as a parameter.
LockClass(CriticalSectionClass& c);
~LockClass();
private:
LockClass &operator=(const LockClass&) { return(*this); }
};
friend class LockClass;
};
// ----------------------------------------------------------------------------
//
// Fast critical section is really fast version of CriticalSection. The downside
// of it is that it can't be locked multiple times from the same thread.
//
// ----------------------------------------------------------------------------
class FastCriticalSectionClass
{
volatile unsigned Flag;
void Thread_Safe_Set_Flag()
{
volatile unsigned& nFlag=Flag;
#define ts_lock _emit 0xF0
assert(((unsigned)&nFlag % 4) == 0);
__asm mov ebx, [nFlag]
__asm ts_lock
__asm bts dword ptr [ebx], 0
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
return;
The_Bit_Was_Previously_Set_So_Try_Again:
ThreadClass::Switch_Thread();
__asm mov ebx, [nFlag]
__asm ts_lock
__asm bts dword ptr [ebx], 0
__asm jc The_Bit_Was_Previously_Set_So_Try_Again
}
WWINLINE void Thread_Safe_Clear_Flag()
{
Flag = 0;
}
public:
// Name can (and usually should) be NULL. Use name only if you wish to create a globally unique mutex
FastCriticalSectionClass() : Flag(0) {}
class LockClass
{
FastCriticalSectionClass& CriticalSection;
public:
LockClass(FastCriticalSectionClass& critical_section) : CriticalSection(critical_section)
{
CriticalSection.Thread_Safe_Set_Flag();
}
~LockClass()
{
CriticalSection.Thread_Safe_Clear_Flag();
}
private:
LockClass &operator=(const LockClass&) { return(*this); }
};
friend class LockClass;
};
#endif