Skip to content

Commit

Permalink
Added experimental management of active database instances using weak…
Browse files Browse the repository at this point in the history
… tables
  • Loading branch information
FredyH committed Nov 28, 2021
1 parent 4cdd12e commit 0e95364
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 30 deletions.
23 changes: 17 additions & 6 deletions src/lua/GMModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ GMOD_MODULE_CLOSE() {
// Free the version check ConVar object reference
if (versionCheckConVar != 0) {
LUA->ReferenceFree(versionCheckConVar);
versionCheckConVar = 0;
}

delete LuaDatabase::luaDatabases;
LuaDatabase::luaDatabases = nullptr;
mysql_thread_end();
mysql_library_end();

Expand Down Expand Up @@ -136,11 +134,22 @@ LUA_FUNCTION(deallocationCount) {
return 1;
}

LUA_FUNCTION(mysqlooThink) {
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
LUA->GetField(-1, "mysqloo");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Nil)) {
LUA->Pop(2); //nil, Global
return 0;
}
LuaDatabase::runAllThinkHooks(LUA);
LUA->Pop(2); //nil, Global
return 0;
}

GMOD_MODULE_OPEN() {
if (mysql_library_init(0, nullptr, nullptr)) {
LUA->ThrowError("Could not initialize mysql library.");
}
LuaDatabase::luaDatabases = new std::unordered_set<LuaDatabase *>();

//Creating MetaTables
LuaObject::createUserDataMetaTable(LUA);
Expand All @@ -154,12 +163,12 @@ GMOD_MODULE_OPEN() {
LUA->GetField(-1, "Add");
LUA->PushString("Think");
LUA->PushString("__MySQLOOThinkHook");
LUA->PushCFunction(LuaObject::luaObjectThink);
LUA->PushCFunction(mysqlooThink);
LUA->Call(3, 0);
LUA->Pop();
LUA->Pop();
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
LUA->CreateTable();
LUA->CreateTable(); //mysqloo

LUA->PushString(MYSQLOO_VERSION);
LUA->SetField(-2, "VERSION");
Expand Down Expand Up @@ -206,6 +215,8 @@ GMOD_MODULE_OPEN() {
LUA->PushCFunction(deallocationCount);
LUA->SetField(-2, "deallocationCount");

LuaDatabase::createWeakTable(LUA);

LUA->SetField(-2, "mysqloo");
LUA->Pop();

Expand Down
83 changes: 79 additions & 4 deletions src/lua/LuaDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
#include "LuaPreparedQuery.h"
#include "LuaTransaction.h"

std::unordered_set<LuaDatabase*>* LuaDatabase::luaDatabases = nullptr;

static void pushLuaObjectTable(ILuaBase *LUA, void *data, int type) {
LUA->CreateTable();
LUA->PushUserType(data, LuaObject::TYPE_USERDATA);
Expand Down Expand Up @@ -35,6 +33,21 @@ LUA_CLASS_FUNCTION(LuaDatabase, create) {
auto luaDatabase = new LuaDatabase(createdDatabase);

pushLuaObjectTable(LUA, luaDatabase, LuaObject::TYPE_DATABASE);
int databaseTablePos = LUA->Top();

//Add database to weak database table
LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB);
LUA->GetField(-1, "mysqloo");
LUA->GetField(-1, "__weakDatabases");
if (LUA->IsType(-1, GarrysMod::Lua::Type::Nil)) {
LUA->Pop(3); //nil, mysqloo, Global
return 1;
}
LUA->Push(databaseTablePos);
LUA->PushBool(true);
LUA->RawSet(-3);
LUA->Pop(3); //__weakDatabases, mysqloo, Global

return 1;
}

Expand Down Expand Up @@ -196,7 +209,7 @@ MYSQLOO_LUA_FUNCTION(setCachePreparedStatements) {
MYSQLOO_LUA_FUNCTION(abortAllQueries) {
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA);
auto abortedQueries = database->m_database->abortAllQueries();
for (const auto& pair: abortedQueries) {
for (const auto &pair: abortedQueries) {
LuaIQuery::runAbortedCallback(LUA, pair.second);
LuaIQuery::finishQueryData(LUA, pair.first, pair.second);
}
Expand Down Expand Up @@ -328,5 +341,67 @@ void LuaDatabase::onDestroyedByLua(ILuaBase *LUA) {
//This needs to be cleared to avoid the queries leaking
m_database->takeFinishedQueries();
m_database->abortAllQueries();
}

}
/** Creates a weak table for all currently used databases.
* And stores it in the table at the top of the stack at key "__weakDatabases"
*
* Expects the mysqloo table to be at the top of the stack.
*
* See runAllThinkHooks for why this is used.
*/
void LuaDatabase::createWeakTable(ILuaBase *LUA) {
//Weak metatable
LUA->CreateTable();
LUA->PushString("k");
LUA->SetField(-2, "__mode");

//Weak table
LUA->CreateTable();
LUA->Push(-2); //Metatable
LUA->SetMetaTable(-2);

LUA->SetField(-3, "__weakDatabases");
LUA->Pop(); //Metatable
}

/**
* Runs the think hook for every database instance that is currently alive.
* Expects the mysqloo table to be at the top of the stack.
*
* The idea of this function is to store a weak reference to each database's lua table and then
* call the think function of each instance that is still alive.
* If the table of a database instance is still alive, then so is the UserData object (which is stored in the table).
*
* This method eliminates the need of storing a global list of all database instances that are currently alive.
*/
void LuaDatabase::runAllThinkHooks(ILuaBase *LUA) {
LUA->GetField(-1, "__weakDatabases");
if (!LUA->IsType(-1, GarrysMod::Lua::Type::Table)) {
LUA->Pop(); //Nil
return;
}
//We iterate all (alive) entries in the weak table and create references to them.
//This essentially creates a copy of the weak table that only contains strong references.
//We need the copy so that no elements of the weak table are collected while think hooks of the databases
//are executed, which might create new database instances, thus invalidating the iterator.
std::vector<int> databaseReferences;
LUA->PushNil(); //First key
while (LUA->Next(-2) != 0) {
//The key is the table of the database
LUA->Push(-2); //The key, i.e. the database table
databaseReferences.push_back(LUA->ReferenceCreate());

LUA->Pop(); //The value, keep key on stack for next()
}
LUA->Pop(); //__weakDatabases

//Call think function of each alive database instance
for (auto &ref: databaseReferences) {
LUA->ReferencePush(ref);
LUA->ReferenceFree(ref); //We can immediately free this, the variable on the stack keeps it alive.
auto database = LuaObject::getLuaObject<LuaDatabase>(LUA, -1);
database->think(LUA);
LUA->Pop(); //database
}
}
8 changes: 2 additions & 6 deletions src/lua/LuaDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,12 @@ class LuaDatabase : public LuaObject {

void onDestroyedByLua(ILuaBase *LUA) override;

~LuaDatabase() override {
luaDatabases->erase(this);
}

explicit LuaDatabase(std::shared_ptr<Database> database) : LuaObject("Database"),
m_database(std::move(database)) {
luaDatabases->insert(this);
}

static std::unordered_set<LuaDatabase*>* luaDatabases;
static void createWeakTable(ILuaBase *LUA);
static void runAllThinkHooks(ILuaBase *LUA);
};


Expand Down
12 changes: 0 additions & 12 deletions src/lua/LuaObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@ LUA_FUNCTION(luaObjectGc) {
return 0;
}

LUA_CLASS_FUNCTION(LuaObject, luaObjectThink) {
std::unordered_set<LuaDatabase*> databasesCopy = *LuaDatabase::luaDatabases;
for (auto &database: databasesCopy) {
if (LuaDatabase::luaDatabases->find(database) == LuaDatabase::luaDatabases->end()) {
//This means the database instance was collected during the think hook and is thus invalid.
continue;
}
database->think(LUA);
}
return 0;
}

void LuaObject::createUserDataMetaTable(GarrysMod::Lua::ILuaBase *LUA) {
TYPE_USERDATA = LUA->CreateMetaTable("MySQLOO UserData");
LUA->PushCFunction(luaObjectGc);
Expand Down
3 changes: 1 addition & 2 deletions src/lua/LuaObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class LuaObject {
static void createUserDataMetaTable(ILuaBase *lua);

//Lua functions
static int luaObjectThink(lua_State *L);

static void pcallWithErrorReporter(ILuaBase *LUA, int nargs);

Expand All @@ -62,7 +61,7 @@ class LuaObject {
if (returnValue == nullptr) {
LUA->ThrowError("[MySQLOO] Invalid CPP Object");
}
LUA->Pop();
LUA->Pop(); //__CppObject
return returnValue;
}

Expand Down

0 comments on commit 0e95364

Please sign in to comment.