cons: Support epolling /dev/null
[akaros.git] / user / parlib / spinlock.c
index 148cd77..a833ab8 100644 (file)
@@ -2,64 +2,51 @@
  * Barret Rhoden <brho@cs.berkeley.edu>
  * Kevin Klues <klueska@cs.berkeley.edu>
  *
- * Spinlocks and Spin-PDR locks (preemption detection/recovery)
- *
- * This file is part of Parlib.
- * 
- * Parlib is free software: you can redistribute it and/or modify
- * it under the terms of the Lesser GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- * 
- * Parlib is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * Lesser GNU General Public License for more details.
- * 
- * See COPYING.LESSER for details on the GNU Lesser General Public License.
- * See COPYING for details on the GNU General Public License. */
+ * See LICENSE for details. */
 
 #include <stdlib.h>
 #include <errno.h>
-#include <assert.h>
+#include <parlib/assert.h>
 
-#include <spinlock.h>
-#include <vcore.h>
-#include <uthread.h>
+#include <parlib/spinlock.h>
+#include <parlib/vcore.h>
+#include <parlib/uthread.h>
 
 void spinlock_init(spinlock_t *lock)
 {
-  assert(lock);
-  lock->lock = 0;
+       lock->locked = FALSE;
 }
 
-int spinlock_trylock(spinlock_t *lock) 
+/* Returns TRUE if we grabbed the lock */
+bool spinlock_trylock(spinlock_t *lock)
 {
-  assert(lock);
-  return __sync_lock_test_and_set(&lock->lock, EBUSY);
+       if (lock->locked)
+               return FALSE;
+       return !__sync_lock_test_and_set(&lock->locked, TRUE);
 }
 
-/* TODO: this will perform worse than test, then test and set */
 void spinlock_lock(spinlock_t *lock) 
 {
-  assert(lock);
-  while (spinlock_trylock(lock))
-    cpu_relax();
+       while (!spinlock_trylock(lock))
+               cpu_relax();
 }
 
 void spinlock_unlock(spinlock_t *lock) 
 {
-  assert(lock);
-  __sync_lock_release(&lock->lock, 0);
+       __sync_lock_release(&lock->locked, FALSE);
 }
 
-/* Two different versions, with and without CAS.  Default is with CAS. */
-#ifndef __CONFIG_SPINPDR_NO_CAS__ /* CAS version */
+bool spinlock_locked(spinlock_t *lock)
+{
+       return lock->locked;
+}
 
 /* Spin-PRD locks (preemption detection/recovery).  Idea is to CAS and put the
  * lockholder's vcoreid in the lock, and all spinners ensure that vcore runs. */
 void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
 {
+       /* See glibc-2.19-akaros/sysdeps/akaros/lowlevellock.h for details. */
+       parlib_static_assert(sizeof(struct spin_pdr_lock) == sizeof(int));
        pdr_lock->lock = SPINPDR_UNLOCKED;
 }
 
@@ -68,7 +55,6 @@ void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
 {
        uint32_t vcoreid = vcore_id();
        uint32_t lock_val;
-       assert(vcoreid != SPINPDR_UNLOCKED);
        do {
                while ((lock_val = pdr_lock->lock) != SPINPDR_UNLOCKED) {
                        ensure_vcore_runs(lock_val);
@@ -86,42 +72,11 @@ void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
        pdr_lock->lock = SPINPDR_UNLOCKED;
 }
 
-#else /* NON-CAS version */
-
-/* Using regular spinlocks, with SPINPDR_VCOREID_UNKNOWN (-1) meaning 'no
- * lockholder advertised yet'.  There are two spots where the lockholder still
- * holds the lock but hasn't advertised its vcoreid, and in those cases we
- * ensure all vcores aren't preempted (linear scan). */
-void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
-{
-       spinlock_init(&pdr_lock->spinlock);
-       pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
-}
-
-void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
-{
-       uint32_t vcoreid = vcore_id();
-       uint32_t ensure_tgt;
-       assert(vcoreid != SPINPDR_VCOREID_UNKNOWN);
-       while (spinlock_trylock(&pdr_lock->spinlock)) {
-               ensure_tgt = pdr_lock->lockholder;
-               /* ensure will make sure *every* vcore runs if you pass it your self. */
-               if (ensure_tgt == SPINPDR_VCOREID_UNKNOWN)
-                       ensure_tgt = vcoreid;
-               ensure_vcore_runs(ensure_tgt);
-               cpu_relax();
-       }
-       pdr_lock->lockholder = vcoreid;
-}
-
-void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
+bool spin_pdr_locked(struct spin_pdr_lock *pdr_lock)
 {
-       pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
-       spinlock_unlock(&pdr_lock->spinlock);
+       return pdr_lock->lock != SPINPDR_UNLOCKED;
 }
 
-#endif /* __CONFIG_SPINPDR_NO_CAS__ */
-
 void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
 {
        /* Disable notifs, if we're an _M uthread */
@@ -135,3 +90,21 @@ void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
        /* Enable notifs, if we're an _M uthread */
        uth_enable_notifs();
 }
+
+bool spin_pdr_trylock(struct spin_pdr_lock *pdr_lock)
+{
+       uint32_t lock_val;
+
+       uth_disable_notifs();
+       lock_val = ACCESS_ONCE(pdr_lock->lock);
+       if (lock_val != SPINPDR_UNLOCKED) {
+               uth_enable_notifs();
+               return FALSE;
+       }
+       if (atomic_cas_u32(&pdr_lock->lock, lock_val, vcore_id())) {
+               return TRUE;
+       } else {
+               uth_enable_notifs();
+               return FALSE;
+       }
+}