parlib: Add recursive uthread mutexes
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 6 Mar 2017 20:44:08 +0000 (15:44 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
I'm not a huge fan of recursive mutexes, but GCC needs them for C++.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/parlib/include/parlib/uthread.h
user/parlib/mutex.c

index 3d1679b..ed651bf 100644 (file)
@@ -44,6 +44,7 @@ extern __thread struct uthread *current_uthread;
  * compiler warnings if someone passes the wrong pointer type.  Internally, we
  * use another struct type for mtx and cvs. */
 typedef struct __uth_mtx_opaque * uth_mutex_t;
+typedef struct __uth_recurse_mtx_opaque * uth_recurse_mutex_t;
 typedef struct __uth_cv_opaque * uth_cond_var_t;
 
 /* 2L-Scheduler operations.  Examples in pthread.c. */
@@ -163,6 +164,14 @@ void uth_mutex_lock(uth_mutex_t m);
 bool uth_mutex_trylock(uth_mutex_t m);
 void uth_mutex_unlock(uth_mutex_t m);
 
+/* Recursive mutexes.  Internally, these are built on top of the regular
+ * mutexes, and 2LSs do not have their own version. */
+uth_recurse_mutex_t uth_recurse_mutex_alloc(void);
+void uth_recurse_mutex_free(uth_recurse_mutex_t r_m);
+void uth_recurse_mutex_lock(uth_recurse_mutex_t r_m);
+bool uth_recurse_mutex_trylock(uth_recurse_mutex_t r_m);
+void uth_recurse_mutex_unlock(uth_recurse_mutex_t r_m);
+
 /* Generic Uthread Condition Variables.  2LSs can implement their own methods.
  * Callers to cv_wait must hold the mutex, which it will atomically wait and
  * unlock, then relock when it returns.  Callers to signal and broadcast may
index 891a567..e955390 100644 (file)
@@ -169,6 +169,88 @@ void uth_mutex_unlock(uth_mutex_t m)
 }
 
 
+/************** Recursive mutexes **************/
+
+/* Note the interface type uth_recurse_mtx_t is a pointer to this struct. */
+struct uth_recurse_mtx {
+       uth_mutex_t                                     mtx;
+       struct uthread                          *lockholder;
+       unsigned int                            count;
+};
+
+uth_recurse_mutex_t uth_recurse_mutex_alloc(void)
+{
+       struct uth_recurse_mtx *r_mtx = malloc(sizeof(struct uth_recurse_mtx));
+
+       assert(r_mtx);
+       r_mtx->mtx = uth_mutex_alloc();
+       r_mtx->lockholder = NULL;
+       r_mtx->count = 0;
+       return (uth_recurse_mutex_t)r_mtx;
+}
+
+void uth_recurse_mutex_free(uth_recurse_mutex_t r_m)
+{
+       struct uth_recurse_mtx *r_mtx = (struct uth_recurse_mtx*)r_m;
+
+       uth_mutex_free(r_mtx->mtx);
+       free(r_mtx);
+}
+
+void uth_recurse_mutex_lock(uth_recurse_mutex_t r_m)
+{
+       struct uth_recurse_mtx *r_mtx = (struct uth_recurse_mtx*)r_m;
+
+       assert(!in_vcore_context());
+       assert(current_uthread);
+       /* We don't have to worry about races on current_uthread or count.  They are
+        * only written by the initial lockholder, and this check will only be true
+        * for the initial lockholder, which cannot concurrently call this function
+        * twice (a thread is single-threaded).
+        *
+        * A signal handler running for a thread should not attempt to grab a
+        * recursive mutex (that's probably a bug).  If we need to support that,
+        * we'll have to disable notifs temporarily. */
+       if (r_mtx->lockholder == current_uthread) {
+               r_mtx->count++;
+               return;
+       }
+       uth_mutex_lock(r_mtx->mtx);
+       r_mtx->lockholder = current_uthread;
+       r_mtx->count = 1;
+}
+
+bool uth_recurse_mutex_trylock(uth_recurse_mutex_t r_m)
+{
+       struct uth_recurse_mtx *r_mtx = (struct uth_recurse_mtx*)r_m;
+       bool ret;
+
+       assert(!in_vcore_context());
+       assert(current_uthread);
+       if (r_mtx->lockholder == current_uthread) {
+               r_mtx->count++;
+               return TRUE;
+       }
+       ret = uth_mutex_trylock(r_mtx->mtx);
+       if (ret) {
+               r_mtx->lockholder = current_uthread;
+               r_mtx->count = 1;
+       }
+       return ret;
+}
+
+void uth_recurse_mutex_unlock(uth_recurse_mutex_t r_m)
+{
+       struct uth_recurse_mtx *r_mtx = (struct uth_recurse_mtx*)r_m;
+
+       r_mtx->count--;
+       if (!r_mtx->count) {
+               r_mtx->lockholder = NULL;
+               uth_mutex_unlock(r_mtx->mtx);
+       }
+}
+
+
 /************** Default Condition Variable Implementation **************/