From 4cec63a9dbed86c4332b9e7fcd56d092046193e4 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Thu, 26 May 2016 10:42:53 +0000
Subject: [PATCH] py: Implement a simple global interpreter lock.

This makes the VM/runtime thread safe, at the cost of not being able to
run code in parallel.
---
 py/modthread.c | 6 ++++++
 py/mpconfig.h  | 6 ++++++
 py/mpstate.h   | 5 +++++
 py/mpthread.h  | 8 ++++++++
 py/runtime.c   | 6 ++++++
 py/vm.c        | 4 ++++
 6 files changed, 35 insertions(+)

diff --git a/py/modthread.c b/py/modthread.c
index 930ca45bb..7efad78c9 100644
--- a/py/modthread.c
+++ b/py/modthread.c
@@ -146,6 +146,8 @@ typedef struct _thread_entry_args_t {
 } thread_entry_args_t;
 
 STATIC void *thread_entry(void *args_in) {
+    // Execution begins here for a new thread.  We do not have the GIL.
+
     thread_entry_args_t *args = (thread_entry_args_t*)args_in;
 
     mp_state_thread_t ts;
@@ -154,6 +156,8 @@ STATIC void *thread_entry(void *args_in) {
     mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
     mp_stack_set_limit(16 * 1024); // fixed stack limit for now
 
+    MP_THREAD_GIL_ENTER();
+
     // signal that we are set up and running
     mp_thread_start();
 
@@ -188,6 +192,8 @@ STATIC void *thread_entry(void *args_in) {
     // signal that we are finished
     mp_thread_finish();
 
+    MP_THREAD_GIL_EXIT();
+
     return NULL;
 }
 
diff --git a/py/mpconfig.h b/py/mpconfig.h
index 998d1b692..b79ddac19 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -829,6 +829,12 @@ typedef double mp_float_t;
 #define MICROPY_PY_THREAD (0)
 #endif
 
+// Whether to make the VM/runtime thread-safe using a global lock
+// If not enabled then thread safety must be provided at the Python level
+#ifndef MICROPY_PY_THREAD_GIL
+#define MICROPY_PY_THREAD_GIL (MICROPY_PY_THREAD)
+#endif
+
 // Extended modules
 
 #ifndef MICROPY_PY_UCTYPES
diff --git a/py/mpstate.h b/py/mpstate.h
index 3ee243dff..b79fd7c5c 100644
--- a/py/mpstate.h
+++ b/py/mpstate.h
@@ -175,6 +175,11 @@ typedef struct _mp_state_vm_t {
     #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0
     mp_int_t mp_emergency_exception_buf_size;
     #endif
+
+    #if MICROPY_PY_THREAD_GIL
+    // This is a global mutex used to make the VM/runtime thread-safe.
+    mp_thread_mutex_t gil_mutex;
+    #endif
 } mp_state_vm_t;
 
 // This structure holds state that is specific to a given thread.
diff --git a/py/mpthread.h b/py/mpthread.h
index d0164ea29..747de60fe 100644
--- a/py/mpthread.h
+++ b/py/mpthread.h
@@ -38,6 +38,14 @@
 
 struct _mp_state_thread_t;
 
+#if MICROPY_PY_THREAD_GIL
+#define MP_THREAD_GIL_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(gil_mutex), 1)
+#define MP_THREAD_GIL_EXIT() mp_thread_mutex_unlock(&MP_STATE_VM(gil_mutex))
+#else
+#define MP_THREAD_GIL_ENTER()
+#define MP_THREAD_GIL_EXIT()
+#endif
+
 struct _mp_state_thread_t *mp_thread_get_state(void);
 void mp_thread_set_state(void *state);
 void mp_thread_create(void *(*entry)(void*), void *arg, size_t stack_size);
diff --git a/py/runtime.c b/py/runtime.c
index 7f28abbf4..f88c92be6 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -91,6 +91,12 @@ void mp_init(void) {
     // start with no extensions to builtins
     MP_STATE_VM(mp_module_builtins_override_dict) = NULL;
     #endif
+
+    #if MICROPY_PY_THREAD_GIL
+    mp_thread_mutex_init(&MP_STATE_VM(gil_mutex));
+    #endif
+
+    MP_THREAD_GIL_ENTER();
 }
 
 void mp_deinit(void) {
diff --git a/py/vm.c b/py/vm.c
index bd5bae115..65801401d 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1263,6 +1263,10 @@ pending_exception_check:
                     RAISE(obj);
                 }
 
+                // TODO make GIL release more efficient
+                MP_THREAD_GIL_EXIT();
+                MP_THREAD_GIL_ENTER();
+
             } // for loop
 
         } else {
-- 
GitLab