So, I finally got the memory management system threaded and defeated the only race condition, which could be found on the close down because of the order it waited for shit in. I fixed it with a shut down event that would poke when both mutexes were released in a timeout waiting loop so there was no wrong order to wait for them in. Anyway, I’m not here to show you that, but instead my threading library. It’s very bare bones, and only has a Win32 implementation for now, but could easily be implemented in another threading library, and I will do so with pthreads sometime in the future.
But I want to show it. I will show it now. SHOWING!
It makes use of factories and singletons, but those should be obvious in terms of their functionality. Factory inherits Factory<Singleton > as well. I might change this if it becomes more efficient to create multiple factory instances for different creation modes (i.e.) each has a different set of creation parameters.
threading.h
#ifndef THREADING_H
#define THREADING_H
#include #include “dlmalloc_object.h” namespace SpaceSim static bool WaitForAll(std::list virtual bool Wait() = 0; class Mutex: public WaitingObject virtual bool Lock() = 0; virtual unsigned long SpinCount() const = 0; class GlobalMutex: public Mutex virtual bool Lock(); virtual bool Wait(); virtual unsigned long SpinCount() const; class LocalMutex: public Mutex public: virtual bool Lock(); virtual bool Wait(); virtual unsigned long SpinCount() const; class Event: public WaitingObject public: Event(); bool Wait(); typedef int (*ThreadMainProc)(void *args); class Thread: public WaitingObject public: Thread(ThreadMainProc main_proc, void *args); bool Run(); bool IsRunning() const; #endif threading.cpp namespace SpaceSim WaitingObject::WaitingObject() wobjs->push_back(this); WaitingObject::~WaitingObject() if (wobjs->empty()) bool WaitingObject::WaitForAll(std::list std::list return success; class MutexFactoryParameters: public FactoryParameters MutexFactoryParameters(bool is_global = false, unsigned long spincount = 0x80000400): template<> class Factory if (mparams.is_global) Mutex *Mutex::Create(bool is_global, unsigned long spincount) GlobalMutex::GlobalMutex() ltoa(mtctr++, cvbuf, 16); assert(mutex = CreateMutex(NULL, FALSE, name)); GlobalMutex::~GlobalMutex() bool GlobalMutex::Lock() void GlobalMutex::Unlock() bool GlobalMutex::Wait() unsigned long GlobalMutex::SpinCount() const {return 0;} LocalMutex::LocalMutex(unsigned long spincount): spincount(spincount) LocalMutex::~LocalMutex() bool LocalMutex::Lock() return true; void LocalMutex::Unlock() bool LocalMutex::Wait() unsigned long LocalMutex::SpinCount() const {return spincount;} Event *Event::Create() Event::Event() ltoa(evctr++, cvbuf, 16); assert(event = CreateEvent(NULL, TRUE, FALSE, name)); Event::~Event() bool Event::Wait() void Event::Poke() void Event::Reset() class ThreadFactoryParameters: public FactoryParameters ThreadFactoryParameters(ThreadMainProc proc, void *args): proc(proc), args(args) {} template<> class Factory return new Thread(mparams.proc, mparams.args); Thread *Thread::Create(ThreadMainProc main_proc, void *args) struct proc_attribs static DWORD WINAPI __ThreadMainProc(LPVOID param) ThreadMainProc proc = pa.proc; pa.mevent->Poke(); return proc(args); ThreadID Thread::GetID() {return GetCurrentThreadId();} Thread::Thread(ThreadMainProc main_proc, void *args): proc(main_proc), args(args) {} Thread::~Thread() bool Thread::Run() DWORD temp; proc_attribs pa; thread = CreateThread(NULL, 0, is_running = (bool)thread; pa.mevent->Wait(); bool Thread::Break() bool success = CloseHandle(thread); bool Thread::Wait() bool success = (WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0); bool Thread::IsRunning() const {return is_running;} Now, for stuff more interesting to the consumer who buys the final game (yes… the game will be sold, and I don’t care if people see snippets of the code). The Actor and StaticObject classes will extend the SceneObject class, and all that good stuff will be Lua bound. Actors will be partly virtual and will have to have trigger functions such as being told where to go by the controller filled in. This would be in certain implementations, especially ships and the like. Anyway, that about says it and all that good stuff. I’m making tracks, and it is AWESOME.
#include
#include
#include
#include
#include
#include “factory.h”
{
class WaitingObject: public DLMallocAlloced
{
protected:
static std::list
public:
WaitingObject();
~WaitingObject();
};
{
public:
static Mutex *Create(bool is_global = false, unsigned long spincount = 0x80000400);
virtual void Unlock() = 0;
};
{
protected:
HANDLE mutex;
public:
GlobalMutex();
~GlobalMutex();
virtual void Unlock();
};
{
protected:
unsigned long spincount;
CRITICAL_SECTION mutex;
LocalMutex(unsigned long spincount);
~LocalMutex();
virtual void Unlock();
};
{
protected:
HANDLE event;
static Event *Create();
~Event();
void Poke();
void Reset();
};
typedef unsigned long ThreadID;
{
protected:
void *args;
HANDLE thread;
ThreadMainProc proc;
bool is_running;
static Thread *Create(ThreadMainProc main_proc, void *args);
static ThreadID GetID();
~Thread();
bool Break();
bool Wait();
};
};
#include “threading.h”
#include “logger.h”
{
std::list
{
if (!wobjs)
wobjs = new std::list
}
{
wobjs->remove(this);
{
delete wobjs;
wobjs = NULL;
}
}
{
bool success = true;
if ((!objs) || objs->empty())
objs = wobjs;
for (it = objs->begin(); it != objs->end(); ++it)
success = success && (*it)->Wait();
}
{
public:
bool is_global;
unsigned long spincount;
is_global(is_global), spincount(spincount) {}
};
{
public:
virtual Mutex *Create(const FactoryParameters ¶ms)
{
const MutexFactoryParameters &mparams = param_cast
return new GlobalMutex();
else
return new LocalMutex(mparams.spincount);
}
};
{
return Factory
}
{
static unsigned long mtctr = 0;
char name[256], cvbuf[16];
strcpy(name, “SPACESIM_MUTEX_0x”);
strcat(name, cvbuf);
}
{
CloseHandle(mutex);
}
{
return WaitForSingleObject(mutex, INFINITE) == WAIT_OBJECT_0;
}
{
ReleaseMutex(mutex);
}
{
bool ls = Lock();
Unlock();
return ls;
}
{
assert(InitializeCriticalSectionAndSpinCount(&mutex, spincount));
}
{
DeleteCriticalSection(&mutex);
}
{
EnterCriticalSection(&mutex);
}
{
LeaveCriticalSection(&mutex);
}
{
bool ls = Lock();
Unlock();
return ls;
}
{
return Factory
}
{
static unsigned long evctr = 0;
char name[256], cvbuf[16];
strcpy(name, “SPACESIM_EVENT_0x”);
strcat(name, cvbuf);
}
{
CloseHandle(event);
}
{
return WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0;
}
{
SetEvent(event);
}
{
ResetEvent(event);
}
{
public:
void *args;
ThreadMainProc proc;
};
{
public:
virtual Thread *Create(const FactoryParameters ¶ms)
{
const ThreadFactoryParameters &mparams = param_cast
}
};
{
return Factory
}
{
ThreadMainProc proc;
void *args;
Event *mevent;
};
{
proc_attribs &pa = *((proc_attribs *)param);
void *args = pa.args;
}
{
if (is_running) Break();
}
{
if (is_running) return false;
pa.args = args;
pa.proc = proc;
pa.mevent = Event::Create();
__ThreadMainProc, (void *)&pa, 0, &temp);
return is_running;
}
{
if (!is_running) return false;
is_running = !success;
return success;
}
{
if (!is_running) return false;
return success && Break();
}
};