Skip to content

Commit 52e5b51

Browse files
committedNov 1, 2015
Fix Lua scripting synchronization
For several years now, the lua script lock has been completely broken. This commit fixes the main issue (creation of a temporary rather than scoped object), and fixes a subsequent deadlock issue caused by nested script API calls by adding support for recursive mutexes.
1 parent d198e42 commit 52e5b51

File tree

5 files changed

+57
-17
lines changed

5 files changed

+57
-17
lines changed
 

‎src/script/cpp_api/s_base.cpp

+8-2
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ class ModNameStorer
6767
ScriptApiBase
6868
*/
6969

70-
ScriptApiBase::ScriptApiBase()
70+
ScriptApiBase::ScriptApiBase() :
71+
m_luastackmutex(true)
7172
{
7273
#ifdef SCRIPTAPI_LOCK_DEBUG
73-
m_locked = false;
74+
m_lock_recursion_count = 0;
7475
#endif
7576

7677
m_luastack = luaL_newstate();
@@ -157,9 +158,14 @@ void ScriptApiBase::loadScript(const std::string &script_path)
157158
// - runs the callbacks
158159
// - replaces the table and arguments with the return value,
159160
// computed depending on mode
161+
// This function must only be called with scriptlock held (i.e. inside of a
162+
// code block with SCRIPTAPI_PRECHECKHEADER declared)
160163
void ScriptApiBase::runCallbacksRaw(int nargs,
161164
RunCallbacksMode mode, const char *fxn)
162165
{
166+
#ifdef SCRIPTAPI_LOCK_DEBUG
167+
assert(m_lock_recursion_count > 0);
168+
#endif
163169
lua_State *L = getStack();
164170
FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
165171

‎src/script/cpp_api/s_base.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extern "C" {
2828
}
2929

3030
#include "irrlichttypes.h"
31+
#include "threads.h"
3132
#include "threading/mutex.h"
3233
#include "threading/mutex_auto_lock.h"
3334
#include "common/c_types.h"
@@ -111,7 +112,8 @@ class ScriptApiBase {
111112
std::string m_last_run_mod;
112113
bool m_secure;
113114
#ifdef SCRIPTAPI_LOCK_DEBUG
114-
bool m_locked;
115+
int m_lock_recursion_count;
116+
threadid_t m_owning_thread;
115117
#endif
116118

117119
private:

‎src/script/cpp_api/s_internal.h

+32-10
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3232

3333
#ifdef SCRIPTAPI_LOCK_DEBUG
3434
#include "debug.h" // assert()
35+
3536
class LockChecker {
3637
public:
37-
LockChecker(bool *variable) {
38-
assert(*variable == false);
38+
LockChecker(int *recursion_counter, threadid_t *owning_thread)
39+
{
40+
m_lock_recursion_counter = recursion_counter;
41+
m_owning_thread = owning_thread;
42+
m_original_level = *recursion_counter;
43+
44+
if (*m_lock_recursion_counter > 0)
45+
assert(thr_is_current_thread(*m_owning_thread));
46+
else
47+
*m_owning_thread = thr_get_current_thread_id();
3948

40-
m_variable = variable;
41-
*m_variable = true;
49+
(*m_lock_recursion_counter)++;
4250
}
43-
~LockChecker() {
44-
*m_variable = false;
51+
52+
~LockChecker()
53+
{
54+
assert(thr_is_current_thread(*m_owning_thread));
55+
assert(*m_lock_recursion_counter > 0);
56+
57+
(*m_lock_recursion_counter)--;
58+
59+
assert(*m_lock_recursion_counter == m_original_level);
4560
}
61+
4662
private:
47-
bool *m_variable;
63+
int *m_lock_recursion_counter;
64+
int m_original_level;
65+
threadid_t *m_owning_thread;
4866
};
4967

50-
#define SCRIPTAPI_LOCK_CHECK LockChecker(&(this->m_locked))
68+
#define SCRIPTAPI_LOCK_CHECK \
69+
LockChecker scriptlock_checker( \
70+
&this->m_lock_recursion_count, \
71+
&this->m_owning_thread)
72+
5173
#else
52-
#define SCRIPTAPI_LOCK_CHECK while(0)
74+
#define SCRIPTAPI_LOCK_CHECK while(0)
5375
#endif
5476

5577
#define SCRIPTAPI_PRECHECKHEADER \
56-
MutexAutoLock(this->m_luastackmutex); \
78+
MutexAutoLock scriptlock(this->m_luastackmutex); \
5779
SCRIPTAPI_LOCK_CHECK; \
5880
realityCheck(); \
5981
lua_State *L = getStack(); \

‎src/threading/mutex.cpp

+13-3
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,25 @@ DEALINGS IN THE SOFTWARE.
3434

3535
#define UNUSED(expr) do { (void)(expr); } while (0)
3636

37-
38-
Mutex::Mutex()
37+
Mutex::Mutex(bool recursive)
3938
{
4039
#ifdef _WIN32
40+
// Windows critical sections are recursive by default
41+
UNUSED(recursive);
42+
4143
InitializeCriticalSection(&mutex);
4244
#else
43-
int ret = pthread_mutex_init(&mutex, NULL);
45+
pthread_mutexattr_t attr;
46+
pthread_mutexattr_init(&attr);
47+
48+
if (recursive)
49+
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
50+
51+
int ret = pthread_mutex_init(&mutex, &attr);
4452
assert(!ret);
4553
UNUSED(ret);
54+
55+
pthread_mutexattr_destroy(&attr);
4656
#endif
4757
}
4858

‎src/threading/mutex.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ DEALINGS IN THE SOFTWARE.
4949
class Mutex
5050
{
5151
public:
52-
Mutex();
52+
Mutex(bool recursive=false);
5353
~Mutex();
5454
void lock();
5555
void unlock();

0 commit comments

Comments
 (0)
Please sign in to comment.