Skip to content

Commit aa59445

Browse files
committed
repo: implement fast pcall & implement missing gc barrier
1 parent 664597f commit aa59445

File tree

10 files changed

+153
-52
lines changed

10 files changed

+153
-52
lines changed

source/lua.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ LuaUserData* Lua::GetHolyLibUserData(GarrysMod::Lua::ILuaInterface * LUA, int nS
267267
GCudata* luaData = udataV(val); // Very "safe" I know :3
268268
if (luaData->udtype >= GarrysMod::Lua::Type::UserData)
269269
{
270-
return (LuaUserData*)luaData;
270+
return static_cast<LuaUserData*>(static_cast<void*>(luaData));
271271
}
272272

273273
return nullptr;
@@ -457,6 +457,11 @@ void Lua::CreateLuaData(GarrysMod::Lua::ILuaInterface* LUA, bool bNullOut)
457457
Lua::StateData* data = new Lua::StateData;
458458
data->pLua = LUA;
459459
data->pProxy = new CLuaInterfaceProxy(LUA);
460+
461+
CLuaInterface* pLua = (CLuaInterface*)LUA;
462+
LUA->ReferencePush(pLua->m_nLuaErrorReporter);
463+
data->SetErrorFunc();
464+
460465
*reinterpret_cast<Lua::StateData**>(pathID + 24) = data;
461466
g_pLuaStates.insert(data);
462467
Msg("holylib - Created thread data %p (%s)\n", data, pathID);

source/lua.h

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ namespace Lua
8383
struct LuaMetaEntry {
8484
unsigned char iType = UCHAR_MAX;
8585
GCRef metatable; // Direct reference allowing for faster setting.
86+
87+
LuaMetaEntry()
88+
{
89+
setgcrefnull(metatable);
90+
}
8691
};
8792

8893
/*
@@ -141,9 +146,31 @@ namespace Lua
141146
std::unordered_map<void*, LuaUserData*> pPushedUserData; // Would love to get rid of this
142147
GarrysMod::Lua::ILuaInterface* pLua = NULL;
143148
CLuaInterfaceProxy* pProxy;
149+
GCRef nErrorFunc;
150+
151+
StateData()
152+
{
153+
setgcrefnull(nErrorFunc);
154+
}
144155

145156
~StateData();
146157

158+
// When called we expect the error function to be pushed onto the stack. We'll pop it.
159+
inline void SetErrorFunc()
160+
{
161+
lua_State* L = pLua->GetState();
162+
TValue* pVal = RawLua::index2adr(L, -1);
163+
if (!tvistab(pVal))
164+
{
165+
Warning(PROJECT_NAME " - RegisterMetaTable: MetaTable is NOT a table?!? What the heck!\n");
166+
pLua->Pop(1);
167+
return;
168+
}
169+
170+
setgcref(nErrorFunc, obj2gco(tabV(pVal)));
171+
pLua->Pop(1); // it's stored in the registry so everything will be fine :3
172+
}
173+
147174
inline void RegisterMetaTable(LuaTypes type, int metaID)
148175
{
149176
pLuaTypes[type].iType = (unsigned char)metaID;
@@ -222,6 +249,28 @@ namespace Lua
222249
{
223250
return pPushedUserData;
224251
}
252+
253+
inline bool FastPCall(int nArgs, int nRets, bool bShowError)
254+
{
255+
lua_State* L = pLua->GetState();
256+
int nRet = Util::func_lua_pcall(L, nArgs, nRets, 0);
257+
if (nRet > 0)
258+
{
259+
// We use the ILuaInterface here since on errors we really don't expect speed.
260+
if (bShowError)
261+
{
262+
const char* pError = pLua->GetString(-1);
263+
pLua->Pop(1);
264+
pLua->ErrorFromLua(pError);
265+
} else {
266+
pLua->Pop(1);
267+
}
268+
269+
return false;
270+
}
271+
272+
return true;
273+
}
225274
};
226275

227276
/*
@@ -464,7 +513,7 @@ struct LuaUserData : GCudata_holylib { // No constructor/deconstructor since its
464513
{
465514
setgcrefnull(nextgc);
466515
gct = 0x0;
467-
marked = 0x0;
516+
marked = 0x3; // mark white
468517
}
469518

470519
inline void Init(GarrysMod::Lua::ILuaInterface* LUA, const Lua::LuaMetaEntry& pMetaEntry, void* pData, bool bNoGC = false, bool bNoUserTable = false)
@@ -491,7 +540,7 @@ struct LuaUserData : GCudata_holylib { // No constructor/deconstructor since its
491540
flags |= UDATA_NO_USERTABLE;
492541
// setgcrefnull(usertable); // Verify: Do we need to always have a valid usertable? iirc the gc is missing a null check
493542
} else {
494-
ClearLuaTable(LUA);
543+
ClearLuaTable(LUA, true);
495544
}
496545

497546
/*global_StateGMOD *g = (global_StateGMOD*)G(LUA->GetState());
@@ -505,7 +554,7 @@ struct LuaUserData : GCudata_holylib { // No constructor/deconstructor since its
505554
#if HOLYLIB_UTIL_DEBUG_LUAUSERDATA
506555
g_pLuaUserData.insert(this);
507556
#if HOLYLIB_UTIL_DEBUG_LUAUSERDATA == 2
508-
Msg("holylib - util: LuaUserdata got initialized %p - %p - %i\n", this, LUA, type);
557+
Msg("holylib - util: LuaUserdata got initialized %p - %p - %i\n", this, LUA, pMetaEntry.iType);
509558
#endif
510559
#endif
511560
}
@@ -571,13 +620,13 @@ struct LuaUserData : GCudata_holylib { // No constructor/deconstructor since its
571620
}
572621
}
573622

574-
inline void ClearLuaTable(GarrysMod::Lua::ILuaInterface* pLua)
623+
inline void ClearLuaTable(GarrysMod::Lua::ILuaInterface* pLua, bool bFresh = false) // bFresh = if we got freshly created / are in a white state
575624
{
576625
if (flags & UDATA_NO_USERTABLE)
577626
return;
578627

579628
lua_State* L = pLua->GetState();
580-
if (Util::func_lj_tab_new)
629+
if (Util::func_lj_tab_new && (bFresh || Util::func_lj_gc_barrierf))
581630
{
582631
// We cannot free it since the GC would kill itself... We also SHOULDN'T since the gc handles it already, so NO touching >:(
583632
// Why does the GC kill itself? Because inside the GCHeader of the usertable, the next GC object is stored.
@@ -586,9 +635,30 @@ struct LuaUserData : GCudata_holylib { // No constructor/deconstructor since its
586635
// And lj_tab_free also doesn't take care of this, it just frees the memory, so this caused crashes, memory corruption and infinite loops.
587636
// Util::func_lj_tab_free(G(L), gco2tab(gcref(usertable)));
588637
setgcref(usertable, obj2gco(Util::func_lj_tab_new(L, 0, 0)));
638+
639+
if (!bFresh)
640+
{ // lj_gc_objbarrier unwrapped since we need to ensure this.
641+
if (((((GCobj*)(gcref(usertable))))->gch.marked & (0x01 | 0x02)) && ((((GCobj*)(this)))->gch.marked & 0x04))
642+
Util::func_lj_gc_barrierf(G(L), ((GCobj*)(this)), ((GCobj*)(gcref(usertable))));
643+
}
589644
} else {
590645
pLua->CreateTable();
591-
setgcref(usertable, obj2gco(tabV(L->top-1)));
646+
if (!bFresh)
647+
{
648+
if (Util::func_lj_gc_barrierf)
649+
{
650+
setgcref(usertable, obj2gco(tabV(L->top-1)));
651+
652+
{ // lj_gc_objbarrier unwrapped since we need to ensure this.
653+
if (((((GCobj*)(gcref(usertable))))->gch.marked & (0x01 | 0x02)) && ((((GCobj*)(this)))->gch.marked & 0x04))
654+
Util::func_lj_gc_barrierf(G(L), ((GCobj*)(this)), ((GCobj*)(gcref(usertable))));
655+
}
656+
} else {
657+
Util::func_lua_setfenv(L, -2); // Internally has the gc barrier
658+
}
659+
} else {
660+
setgcref(usertable, obj2gco(tabV(L->top-1)));
661+
}
592662
pLua->Pop(1);
593663
}
594664
}

source/lua/CLuaInterface.cpp

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ bool CLuaInterface::Init( GarrysMod::Lua::ILuaGameCallback* callback, bool bIsSe
787787
lua_atpanic(state, LuaPanic);
788788

789789
lua_pushcclosure(state, AdvancedLuaErrorReporter, 0);
790-
lua_setglobal(state, "AdvancedLuaErrorReporter");
790+
m_nLuaErrorReporter = luaL_ref(state, LUA_REGISTRYINDEX); // Since this is the first ever ref call luaErrorReporter will always be 1
791791

792792
{
793793
luaJIT_setmode(state, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON);
@@ -1514,37 +1514,22 @@ void CLuaInterface::Msg( const char* fmt, ... )
15141514
void CLuaInterface::PushPath( const char* path )
15151515
{
15161516
LuaDebugPrint(2, "CLuaInterface::PushPath %s\n", path);
1517-
char* str = new char[strlen(path)];
1518-
V_strncpy( str, path, strlen(path) );
1519-
str[ strlen(path) - 1 ] = '\0'; // nuke the last /
1520-
m_sCurrentPath = str;
1521-
m_pPaths.push_back( str );
1522-
++m_iPushedPaths;
1517+
1518+
m_CurrentPaths.Push();
1519+
V_strncpy(m_CurrentPaths.Top().path, path, MAX_PATH);
15231520
}
15241521

15251522
void CLuaInterface::PopPath()
15261523
{
15271524
LuaDebugPrint(2, "CLuaInterface::PopPath\n");
1528-
char* str = m_pPaths.back();
1529-
delete[] str;
1530-
m_pPaths.pop_back();
1531-
1532-
--m_iPushedPaths;
1533-
if ( m_iPushedPaths > 0 )
1534-
m_sCurrentPath = m_pPaths.back();
1535-
else
1536-
m_sCurrentPath = NULL;
1525+
1526+
m_CurrentPaths.Pop();
15371527
}
15381528

15391529
const char* CLuaInterface::GetPath()
15401530
{
1541-
LuaDebugPrint(2, "CLuaInterface::GetPath\n");
1542-
1543-
if ( m_iPushedPaths <= 0 )
1544-
return NULL;
1545-
1546-
LuaDebugPrint(2, "CLuaInterface::GetPath %s\n", (const char*)m_pPaths.back());
1547-
return m_pPaths.back();
1531+
LuaDebugPrint(2, "CLuaInterface::GetPath %s\n", (const char*)m_CurrentPaths.Top().path);
1532+
return m_CurrentPaths.Top().path;
15481533
}
15491534

15501535
int CLuaInterface::GetColor(int iStackPos) // Probably returns the StackPos
@@ -1785,7 +1770,11 @@ bool CLuaInterface::CallFunctionProtected(int iArgs, int iRets, bool showError)
17851770
return false;
17861771
}
17871772

1788-
int ret = PCall(iArgs, iRets, 0);
1773+
int nPos = lua_gettop(state) - iArgs;
1774+
lua_rawgeti(state, LUA_REGISTRYINDEX, m_nLuaErrorReporter);
1775+
lua_insert(state, nPos);
1776+
int ret = PCall(iArgs, iRets, -1);
1777+
lua_remove(state, nPos);
17891778
if (ret != 0)
17901779
{
17911780
GarrysMod::Lua::ILuaGameCallback::CLuaError* err = ReadStackIntoError(state);

source/lua/CLuaInterface.h

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "GarrysMod/Lua/LuaGameCallback.h"
77
#include "GarrysMod/Lua/LuaObject.h"
88
#include <list>
9+
#include "tier1/utlstack.h"
910

1011
#define GMOD
1112

@@ -177,21 +178,15 @@ class CLuaInterface : public GarrysMod::Lua::ILuaInterface
177178
m_pGameCallback = callback;
178179
}
179180

180-
private: // We keep gmod's structure in case any modules depend on it.
181-
int _1 = 1; // Always 1?
182-
char* m_sCurrentPath = new char[32]; // not how gmod does it :/
183-
int _3 = 0;
184-
int _4 = 0;
185-
int m_iPushedPaths = 0;
186-
const char* m_sLastPath = NULL;
187-
std::list<GarrysMod::Lua::ILuaThreadedCall*> m_pThreadedCalls;
188-
189-
#ifdef __APPLE__
190-
191-
size_t _7; // 1 * sizeof(size_t) = 4 (x86) or 8 (x86-64) bytes
192-
193-
#endif
181+
public: // We keep gmod's structure in case any modules depend on it.
182+
struct Path
183+
{
184+
char path[MAX_PATH];
185+
};
194186

187+
int m_nLuaErrorReporter = -1; // Always 1 since it's always the first registry reference.
188+
CUtlStack<Path> m_CurrentPaths;
189+
std::list<GarrysMod::Lua::ILuaThreadedCall*> m_pThreadedCalls;
195190
GarrysMod::Lua::ILuaObject* m_ProtectedFunctionReturns[4] = {nullptr};
196191
GarrysMod::Lua::ILuaObject* m_TempObjects[LUA_MAX_TEMP_OBJECTS] = {nullptr};
197192
unsigned char m_iRealm = (unsigned char)2; // CLIENT = 0, SERVER = 1, MENU = 2
@@ -204,7 +199,6 @@ class CLuaInterface : public GarrysMod::Lua::ILuaInterface
204199
unsigned char m_iMetaTableIDCounter = GarrysMod::Lua::Type::COUNT;
205200
GarrysMod::Lua::ILuaObject* m_pMetaTables[255] = {nullptr}; // Their index is based off their type. means m_MetaTables[Type::Entity] returns the Entity metatable.
206201
private: // NOT GMOD stuff
207-
std::list<char*> m_pPaths;
208202
CThreadFastMutex m_pThreadedCallsMutex;
209203

210204
public:

source/modules/bitbuf.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -945,12 +945,10 @@ LUA_FUNCTION_STATIC(bitbuf_CreateStackReadBuffer)
945945

946946
// Pushes it onto the stack, since we never use the Push_ HolyLib function.
947947
// this will be untracked by the GC BUT you'll have to pop it off the stack BEFORE we leave the scope!
948-
LuaUserData pStackLuaData;
949-
pStackLuaData.Init(LUA, Lua::GetLuaData(LUA)->GetMetaEntry(Lua::bf_read), &pNewBf, true, true);
950-
pStackLuaData.Push(LUA);
948+
LuaUserData* pStackLuaData = Push_bf_read(LUA, &pNewBf, false);
951949

952-
LUA->CallFunctionProtected(1, 0, true);
953-
pStackLuaData.Release(LUA);
950+
Lua::GetLuaData(LUA)->FastPCall(1, 0, true);
951+
pStackLuaData->Release(LUA);
954952

955953
return 0;
956954
}

source/modules/luajit.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,10 @@ void CLuaJITModule::InitDetour(bool bPreServer)
477477
Util::func_lua_touserdata = &lua_touserdata;
478478
Util::func_lua_type = &lua_type;
479479
Util::func_luaL_checklstring = &luaL_checklstring;
480+
Util::func_lua_pcall = &lua_pcall;
481+
Util::func_lua_insert = &lua_insert;
482+
Util::func_lua_toboolean = &lua_toboolean;
483+
Util::func_lj_gc_barrierf = (Symbols::lj_gc_barrierf)&lj_gc_barrierf;
480484

481485
m_bIsEnabled = true;
482486
}

source/symbols.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ namespace Symbols
5151

5252
const Symbol lj_tab_newSym = Symbol::FromName("lj_tab_new");
5353

54+
const Symbol lj_gc_barrierfSym = Symbol::FromName("lj_gc_barrierf");
55+
5456
const Symbol lua_setfenvSym = Symbol::FromName("lua_setfenv");
5557

5658
const Symbol lua_touserdataSym = Symbol::FromName("lua_touserdata");
@@ -59,6 +61,12 @@ namespace Symbols
5961

6062
const Symbol luaL_checklstringSym = Symbol::FromName("luaL_checklstring");
6163

64+
const Symbol lua_pcallSym = Symbol::FromName("lua_pcall");
65+
66+
const Symbol lua_insertSym = Symbol::FromName("lua_insert");
67+
68+
const Symbol lua_tobooleanSym = Symbol::FromName("lua_toboolean");
69+
6270
const std::vector<Symbol> CGetSym = { // 64x ToDo
6371
Symbol::FromName("get"),
6472
};

source/symbols.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ namespace Symbols
146146
typedef GCtab* (*lj_tab_new)(lua_State *L, uint32_t nArray, uint32_t nRec);
147147
extern const Symbol lj_tab_newSym;
148148

149+
typedef void (*lj_gc_barrierf)(global_State *g, void *o, void *v);
150+
extern const Symbol lj_gc_barrierfSym;
151+
149152
typedef int (*lua_setfenv)(lua_State *L, int idx);
150153
extern const Symbol lua_setfenvSym;
151154

@@ -158,6 +161,15 @@ namespace Symbols
158161
typedef const char* (*luaL_checklstring)(lua_State *L, int idx, size_t* len);
159162
extern const Symbol luaL_checklstringSym;
160163

164+
typedef int (*lua_pcall)(lua_State *L, int nArgs, int nRets, int nErrorFunc);
165+
extern const Symbol lua_pcallSym;
166+
167+
typedef void (*lua_insert)(lua_State *L, int idx);
168+
extern const Symbol lua_insertSym;
169+
170+
typedef int (*lua_toboolean)(lua_State *L, int idx);
171+
extern const Symbol lua_tobooleanSym;
172+
161173
extern const std::vector<Symbol> CGetSym;
162174
extern const std::vector<Symbol> gEntListSym;
163175

source/util.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,14 @@ Symbols::lua_rawgeti Util::func_lua_rawgeti = nullptr;
498498
IGameEventManager2* Util::gameeventmanager = nullptr;
499499
IServerGameDLL* Util::servergamedll = nullptr;
500500
Symbols::lj_tab_new Util::func_lj_tab_new = nullptr;
501+
Symbols::lj_gc_barrierf Util::func_lj_gc_barrierf = nullptr;
501502
Symbols::lua_setfenv Util::func_lua_setfenv = nullptr;
502503
Symbols::lua_touserdata Util::func_lua_touserdata = nullptr;
503504
Symbols::lua_type Util::func_lua_type = nullptr;
504505
Symbols::luaL_checklstring Util::func_luaL_checklstring = nullptr;
506+
Symbols::lua_pcall Util::func_lua_pcall = nullptr;
507+
Symbols::lua_insert Util::func_lua_insert = nullptr;
508+
Symbols::lua_toboolean Util::func_lua_toboolean = nullptr;
505509
void Util::AddDetour()
506510
{
507511
if (g_pModuleManager.GetAppFactory())
@@ -612,7 +616,19 @@ void Util::AddDetour()
612616
func_luaL_checklstring = (Symbols::luaL_checklstring)Detour::GetFunction(lua_shared_loader.GetModule(), Symbols::luaL_checklstringSym);
613617
Detour::CheckFunction((void*)func_luaL_checklstring, "luaL_checklstring");
614618

615-
if (!func_lua_touserdata || !func_lua_type || !func_lua_setfenv)
619+
func_lua_pcall = (Symbols::lua_pcall)Detour::GetFunction(lua_shared_loader.GetModule(), Symbols::lua_pcallSym);
620+
Detour::CheckFunction((void*)func_lua_pcall, "lua_pcall");
621+
622+
func_lua_insert = (Symbols::lua_insert)Detour::GetFunction(lua_shared_loader.GetModule(), Symbols::lua_insertSym);
623+
Detour::CheckFunction((void*)func_lua_insert, "lua_insert");
624+
625+
func_lua_toboolean = (Symbols::lua_toboolean)Detour::GetFunction(lua_shared_loader.GetModule(), Symbols::lua_tobooleanSym);
626+
Detour::CheckFunction((void*)func_lua_toboolean, "lua_toboolean");
627+
628+
func_lj_gc_barrierf = (Symbols::lj_gc_barrierf)Detour::GetFunction(lua_shared_loader.GetModule(), Symbols::lj_gc_barrierfSym);
629+
Detour::CheckFunction((void*)func_lj_gc_barrierf, "lj_gc_barrierf");
630+
631+
if (!func_lua_touserdata || !func_lua_type || !func_lua_setfenv || !func_luaL_checklstring || !func_lua_pcall || !func_lua_insert || !func_lua_toboolean)
616632
{
617633
// This is like the ONLY dependency we have on symbols that without we cannot function.
618634
Error(PROJECT_NAME " - core: Failed to load an important symbol which we utterly depend on.\n");

0 commit comments

Comments
 (0)