Merge "Add semaphore tests, fix sem_destroy."
diff --git a/libc/Android.mk b/libc/Android.mk
index 1164301..a8778c5 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -53,7 +53,6 @@
     bionic/pututline.c \
     bionic/sched_cpualloc.c \
     bionic/sched_cpucount.c \
-    bionic/semaphore.c \
     bionic/sigblock.c \
     bionic/siginterrupt.c \
     bionic/sigsetmask.c \
@@ -182,6 +181,7 @@
     bionic/scandir.cpp \
     bionic/sched_getaffinity.cpp \
     bionic/sched_getcpu.cpp \
+    bionic/semaphore.cpp \
     bionic/send.cpp \
     bionic/setegid.cpp \
     bionic/__set_errno.cpp \
diff --git a/libc/bionic/bionic_time_conversions.cpp b/libc/bionic/bionic_time_conversions.cpp
index 7f3c026..75e8d49 100644
--- a/libc/bionic/bionic_time_conversions.cpp
+++ b/libc/bionic/bionic_time_conversions.cpp
@@ -28,6 +28,8 @@
 
 #include "private/bionic_time_conversions.h"
 
+#include "private/bionic_constants.h"
+
 bool timespec_from_timeval(timespec& ts, const timeval& tv) {
   // Whole seconds can just be copied.
   ts.tv_sec = tv.tv_sec;
@@ -49,3 +51,19 @@
   tv.tv_sec = ts.tv_sec;
   tv.tv_usec = ts.tv_nsec / 1000;
 }
+
+// Initializes 'ts' with the difference between 'abs_ts' and the current time
+// according to 'clock'. Returns false if abstime already expired, true otherwise.
+bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock) {
+  clock_gettime(clock, &ts);
+  ts.tv_sec = abs_ts.tv_sec - ts.tv_sec;
+  ts.tv_nsec = abs_ts.tv_nsec - ts.tv_nsec;
+  if (ts.tv_nsec < 0) {
+    ts.tv_sec--;
+    ts.tv_nsec += NS_PER_S;
+  }
+  if (ts.tv_nsec < 0 || ts.tv_sec < 0) {
+    return false;
+  }
+  return true;
+}
diff --git a/libc/bionic/pthread_cond.cpp b/libc/bionic/pthread_cond.cpp
index e623b62..32ff81a 100644
--- a/libc/bionic/pthread_cond.cpp
+++ b/libc/bionic/pthread_cond.cpp
@@ -163,12 +163,12 @@
 }
 
 __LIBC_HIDDEN__
-int __pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abstime, clockid_t clock) {
+int __pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, const timespec* abs_ts, clockid_t clock) {
   timespec ts;
   timespec* tsp;
 
-  if (abstime != NULL) {
-    if (__timespec_from_absolute(&ts, abstime, clock) < 0) {
+  if (abs_ts != NULL) {
+    if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) {
       return ETIMEDOUT;
     }
     tsp = &ts;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index a5b3002..392e781 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -117,8 +117,6 @@
 __LIBC_HIDDEN__ extern pthread_internal_t* g_thread_list;
 __LIBC_HIDDEN__ extern pthread_mutex_t g_thread_list_lock;
 
-__LIBC_HIDDEN__ int __timespec_from_absolute(timespec*, const timespec*, clockid_t);
-
 /* Needed by fork. */
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_prepare();
 __LIBC_HIDDEN__ extern void __bionic_atfork_run_child();
diff --git a/libc/bionic/pthread_internals.cpp b/libc/bionic/pthread_internals.cpp
index 4c08ba8..19c00d4 100644
--- a/libc/bionic/pthread_internals.cpp
+++ b/libc/bionic/pthread_internals.cpp
@@ -67,19 +67,3 @@
 pthread_internal_t* __get_thread(void) {
   return reinterpret_cast<pthread_internal_t*>(__get_tls()[TLS_SLOT_THREAD_ID]);
 }
-
-// Initialize 'ts' with the difference between 'abstime' and the current time
-// according to 'clock'. Returns -1 if abstime already expired, or 0 otherwise.
-int __timespec_from_absolute(timespec* ts, const timespec* abstime, clockid_t clock) {
-  clock_gettime(clock, ts);
-  ts->tv_sec  = abstime->tv_sec - ts->tv_sec;
-  ts->tv_nsec = abstime->tv_nsec - ts->tv_nsec;
-  if (ts->tv_nsec < 0) {
-    ts->tv_sec--;
-    ts->tv_nsec += 1000000000;
-  }
-  if ((ts->tv_nsec < 0) || (ts->tv_sec < 0)) {
-    return -1;
-  }
-  return 0;
-}
diff --git a/libc/bionic/pthread_mutex.cpp b/libc/bionic/pthread_mutex.cpp
index e00ffb4..cbb6ef7 100644
--- a/libc/bionic/pthread_mutex.cpp
+++ b/libc/bionic/pthread_mutex.cpp
@@ -36,7 +36,9 @@
 #include "pthread_internal.h"
 
 #include "private/bionic_atomic_inline.h"
+#include "private/bionic_constants.h"
 #include "private/bionic_futex.h"
+#include "private/bionic_time_conversions.h"
 #include "private/bionic_tls.h"
 
 #include "private/bionic_systrace.h"
@@ -615,7 +617,7 @@
     return EBUSY;
 }
 
-static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_timeout, clockid_t clock) {
+static int __pthread_mutex_timedlock(pthread_mutex_t* mutex, const timespec* abs_ts, clockid_t clock) {
   timespec ts;
 
   int mvalue = mutex->value;
@@ -638,7 +640,7 @@
 
     // Loop while needed.
     while (__bionic_swap(locked_contended, &mutex->value) != unlocked) {
-      if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+      if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) {
         return ETIMEDOUT;
       }
       __futex_wait_ex(&mutex->value, shared, locked_contended, &ts);
@@ -681,7 +683,7 @@
       }
       // The value changed before we could lock it. We need to check
       // the time to avoid livelocks, reload the value, then loop again.
-      if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+      if (!timespec_from_absolute_timespec(ts, *abs_ts, clock)) {
         return ETIMEDOUT;
       }
 
@@ -703,7 +705,7 @@
     }
 
     // Check time and update 'ts'.
-    if (__timespec_from_absolute(&ts, abs_timeout, clock) < 0) {
+    if (timespec_from_absolute_timespec(ts, *abs_ts, clock)) {
       return ETIMEDOUT;
     }
 
@@ -726,9 +728,9 @@
   clock_gettime(CLOCK_MONOTONIC, &abs_timeout);
   abs_timeout.tv_sec  += ms / 1000;
   abs_timeout.tv_nsec += (ms % 1000) * 1000000;
-  if (abs_timeout.tv_nsec >= 1000000000) {
+  if (abs_timeout.tv_nsec >= NS_PER_S) {
     abs_timeout.tv_sec++;
-    abs_timeout.tv_nsec -= 1000000000;
+    abs_timeout.tv_nsec -= NS_PER_S;
   }
 
   int error = __pthread_mutex_timedlock(mutex, &abs_timeout, CLOCK_MONOTONIC);
diff --git a/libc/bionic/pthread_rwlock.cpp b/libc/bionic/pthread_rwlock.cpp
index 063137b..0d63457 100644
--- a/libc/bionic/pthread_rwlock.cpp
+++ b/libc/bionic/pthread_rwlock.cpp
@@ -30,6 +30,7 @@
 
 #include "pthread_internal.h"
 #include "private/bionic_futex.h"
+#include "private/bionic_time_conversions.h"
 
 /* Technical note:
  *
@@ -71,7 +72,7 @@
 
 static bool timespec_from_absolute(timespec* rel_timeout, const timespec* abs_timeout) {
   if (abs_timeout != NULL) {
-    if (__timespec_from_absolute(rel_timeout, abs_timeout, CLOCK_REALTIME) < 0) {
+    if (!timespec_from_absolute_timespec(*rel_timeout, *abs_timeout, CLOCK_REALTIME)) {
       return false;
     }
   }
diff --git a/libc/bionic/semaphore.c b/libc/bionic/semaphore.c
deleted file mode 100644
index 1fa019e..0000000
--- a/libc/bionic/semaphore.c
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-#include <semaphore.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <time.h>
-#include <limits.h>
-
-#include "private/bionic_atomic_inline.h"
-#include "private/bionic_futex.h"
-
-/* In this implementation, a semaphore contains a
- * 31-bit signed value and a 1-bit 'shared' flag
- * (for process-sharing purpose).
- *
- * We use the value -1 to indicate contention on the
- * semaphore, 0 or more to indicate uncontended state,
- * any value lower than -2 is invalid at runtime.
- *
- * State diagram:
- *
- * post(1)  ==> 2
- * post(0)  ==> 1
- * post(-1) ==> 1, then wake all waiters
- *
- * wait(2)  ==> 1
- * wait(1)  ==> 0
- * wait(0)  ==> -1 then wait for a wake up + loop
- * wait(-1) ==> -1 then wait for a wake up + loop
- *
- */
-
-/* Use the upper 31-bits for the counter, and the lower one
- * for the shared flag.
- */
-#define SEMCOUNT_SHARED_MASK      0x00000001
-#define SEMCOUNT_VALUE_MASK       0xfffffffe
-#define SEMCOUNT_VALUE_SHIFT      1
-
-/* Maximum unsigned value that can be stored in the semaphore.
- * One bit is used for the shared flag, another one for the
- * sign bit, leaving us with only 30 bits.
- */
-#define SEM_MAX_VALUE             0x3fffffff
-
-/* convert a value into the corresponding sem->count bit pattern */
-#define SEMCOUNT_FROM_VALUE(val)    (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK)
-
-/* convert a sem->count bit pattern into the corresponding signed value */
-#define SEMCOUNT_TO_VALUE(sval)  ((int)(sval) >> SEMCOUNT_VALUE_SHIFT)
-
-/* the value +1 as a sem->count bit-pattern. */
-#define SEMCOUNT_ONE              SEMCOUNT_FROM_VALUE(1)
-
-/* the value -1 as a sem->count bit-pattern. */
-#define SEMCOUNT_MINUS_ONE        SEMCOUNT_FROM_VALUE(-1)
-
-#define SEMCOUNT_DECREMENT(sval)    (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
-#define SEMCOUNT_INCREMENT(sval)    (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
-
-/* return the shared bitflag from a semaphore */
-#define SEM_GET_SHARED(sem)       ((sem)->count & SEMCOUNT_SHARED_MASK)
-
-
-int sem_init(sem_t *sem, int pshared, unsigned int value)
-{
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    /* ensure that 'value' can be stored in the semaphore */
-    if (value > SEM_MAX_VALUE) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    sem->count = SEMCOUNT_FROM_VALUE(value);
-    if (pshared != 0)
-        sem->count |= SEMCOUNT_SHARED_MASK;
-
-    return 0;
-}
-
-
-int sem_destroy(sem_t *sem)
-{
-    int count;
-
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-    count = SEMCOUNT_TO_VALUE(sem->count);
-    if (count < 0) {
-        errno = EBUSY;
-        return -1;
-    }
-    sem->count = 0;
-    return 0;
-}
-
-
-sem_t *sem_open(const char *name __unused, int oflag __unused, ...)
-{
-    errno = ENOSYS;
-    return SEM_FAILED;
-}
-
-
-int sem_close(sem_t *sem)
-{
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-    errno = ENOSYS;
-    return -1;
-}
-
-
-int sem_unlink(const char* name __unused)
-{
-    errno = ENOSYS;
-    return -1;
-}
-
-
-/* Decrement a semaphore's value atomically,
- * and return the old one. As a special case,
- * this returns immediately if the value is
- * negative (i.e. -1)
- */
-static int
-__sem_dec(volatile unsigned int *pvalue)
-{
-    unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
-    unsigned int old, new;
-    int          ret;
-
-    do {
-        old = (*pvalue & SEMCOUNT_VALUE_MASK);
-        ret = SEMCOUNT_TO_VALUE(old);
-        if (ret < 0)
-            break;
-
-        new = SEMCOUNT_DECREMENT(old);
-    }
-    while (__bionic_cmpxchg((int)(old|shared),
-                            (int)(new|shared),
-                            (volatile int *)pvalue) != 0);
-    return ret;
-}
-
-/* Same as __sem_dec, but will not touch anything if the
- * value is already negative *or* 0. Returns the old value.
- */
-static int
-__sem_trydec(volatile unsigned int *pvalue)
-{
-    unsigned int shared = (*pvalue & SEMCOUNT_SHARED_MASK);
-    unsigned int old, new;
-    int          ret;
-
-    do {
-        old = (*pvalue & SEMCOUNT_VALUE_MASK);
-        ret = SEMCOUNT_TO_VALUE(old);
-        if (ret <= 0)
-            break;
-
-        new = SEMCOUNT_DECREMENT(old);
-    }
-    while (__bionic_cmpxchg((int)(old|shared),
-                            (int)(new|shared),
-                            (volatile int *)pvalue) != 0);
-
-    return ret;
-}
-
-
-/* "Increment" the value of a semaphore atomically and
- * return its old value. Note that this implements
- * the special case of "incrementing" any negative
- * value to +1 directly.
- *
- * NOTE: The value will _not_ wrap above SEM_VALUE_MAX
- */
-static int
-__sem_inc(volatile unsigned int *pvalue)
-{
-    unsigned int  shared = (*pvalue & SEMCOUNT_SHARED_MASK);
-    unsigned int  old, new;
-    int           ret;
-
-    do {
-        old = (*pvalue & SEMCOUNT_VALUE_MASK);
-        ret = SEMCOUNT_TO_VALUE(old);
-
-        /* Can't go higher than SEM_MAX_VALUE */
-        if (ret == SEM_MAX_VALUE)
-            break;
-
-        /* If the counter is negative, go directly to +1,
-         * otherwise just increment */
-        if (ret < 0)
-            new = SEMCOUNT_ONE;
-        else
-            new = SEMCOUNT_INCREMENT(old);
-    }
-    while ( __bionic_cmpxchg((int)(old|shared),
-                             (int)(new|shared),
-                             (volatile int*)pvalue) != 0);
-
-    return ret;
-}
-
-/* lock a semaphore */
-int sem_wait(sem_t *sem)
-{
-    unsigned shared;
-
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    shared = SEM_GET_SHARED(sem);
-
-    for (;;) {
-        if (__sem_dec(&sem->count) > 0)
-            break;
-
-        __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL);
-    }
-    ANDROID_MEMBAR_FULL();
-    return 0;
-}
-
-int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
-{
-    unsigned int shared;
-
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    /* POSIX says we need to try to decrement the semaphore
-     * before checking the timeout value. Note that if the
-     * value is currently 0, __sem_trydec() does nothing.
-     */
-    if (__sem_trydec(&sem->count) > 0) {
-        ANDROID_MEMBAR_FULL();
-        return 0;
-    }
-
-    /* Check it as per Posix */
-    if (abs_timeout == NULL    ||
-        abs_timeout->tv_sec < 0 ||
-        abs_timeout->tv_nsec < 0 ||
-        abs_timeout->tv_nsec >= 1000000000)
-    {
-        errno = EINVAL;
-        return -1;
-    }
-
-    shared = SEM_GET_SHARED(sem);
-
-    for (;;) {
-        struct timespec ts;
-        int             ret;
-
-        /* Posix mandates CLOCK_REALTIME here */
-        clock_gettime( CLOCK_REALTIME, &ts );
-        ts.tv_sec  = abs_timeout->tv_sec - ts.tv_sec;
-        ts.tv_nsec = abs_timeout->tv_nsec - ts.tv_nsec;
-        if (ts.tv_nsec < 0) {
-            ts.tv_nsec += 1000000000;
-            ts.tv_sec  -= 1;
-        }
-
-        if (ts.tv_sec < 0 || ts.tv_nsec < 0) {
-            errno = ETIMEDOUT;
-            return -1;
-        }
-
-        /* Try to grab the semaphore. If the value was 0, this
-         * will also change it to -1 */
-        if (__sem_dec(&sem->count) > 0) {
-            ANDROID_MEMBAR_FULL();
-            break;
-        }
-
-        /* Contention detected. wait for a wakeup event */
-        ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts);
-
-        /* return in case of timeout or interrupt */
-        if (ret == -ETIMEDOUT || ret == -EINTR) {
-            errno = -ret;
-            return -1;
-        }
-    }
-    return 0;
-}
-
-/* Unlock a semaphore */
-int sem_post(sem_t *sem)
-{
-    unsigned int shared;
-    int          old;
-
-    if (sem == NULL)
-        return EINVAL;
-
-    shared = SEM_GET_SHARED(sem);
-
-    ANDROID_MEMBAR_FULL();
-    old = __sem_inc(&sem->count);
-    if (old < 0) {
-        /* contention on the semaphore, wake up all waiters */
-        __futex_wake_ex(&sem->count, shared, INT_MAX);
-    }
-    else if (old == SEM_MAX_VALUE) {
-        /* overflow detected */
-        errno = EOVERFLOW;
-        return -1;
-    }
-
-    return 0;
-}
-
-int  sem_trywait(sem_t *sem)
-{
-    if (sem == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (__sem_trydec(&sem->count) > 0) {
-        ANDROID_MEMBAR_FULL();
-        return 0;
-    } else {
-        errno = EAGAIN;
-        return -1;
-    }
-}
-
-/* Note that Posix requires that sem_getvalue() returns, in
- * case of contention, the negative of the number of waiting
- * threads.
- *
- * However, code that depends on this negative value to be
- * meaningful is most probably racy. The GLibc sem_getvalue()
- * only returns the semaphore value, which is 0, in case of
- * contention, so we will mimick this behaviour here instead
- * for better compatibility.
- */
-int  sem_getvalue(sem_t *sem, int *sval)
-{
-    int  val;
-
-    if (sem == NULL || sval == NULL) {
-        errno = EINVAL;
-        return -1;
-    }
-
-    val = SEMCOUNT_TO_VALUE(sem->count);
-    if (val < 0)
-        val = 0;
-
-    *sval = val;
-    return 0;
-}
diff --git a/libc/bionic/semaphore.cpp b/libc/bionic/semaphore.cpp
new file mode 100644
index 0000000..c23eb75
--- /dev/null
+++ b/libc/bionic/semaphore.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <semaphore.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "private/bionic_atomic_inline.h"
+#include "private/bionic_constants.h"
+#include "private/bionic_futex.h"
+#include "private/bionic_time_conversions.h"
+
+// In this implementation, a semaphore contains a
+// 31-bit signed value and a 1-bit 'shared' flag
+// (for process-sharing purpose).
+//
+// We use the value -1 to indicate contention on the
+// semaphore, 0 or more to indicate uncontended state,
+// any value lower than -2 is invalid at runtime.
+//
+// State diagram:
+//
+// post(1)  ==> 2
+// post(0)  ==> 1
+// post(-1) ==> 1, then wake all waiters
+//
+// wait(2)  ==> 1
+// wait(1)  ==> 0
+// wait(0)  ==> -1 then wait for a wake up + loop
+// wait(-1) ==> -1 then wait for a wake up + loop
+
+// Use the upper 31-bits for the counter, and the lower one
+// for the shared flag.
+#define SEMCOUNT_SHARED_MASK      0x00000001
+#define SEMCOUNT_VALUE_MASK       0xfffffffe
+#define SEMCOUNT_VALUE_SHIFT      1
+
+// Convert a value into the corresponding sem->count bit pattern.
+#define SEMCOUNT_FROM_VALUE(val)    (((val) << SEMCOUNT_VALUE_SHIFT) & SEMCOUNT_VALUE_MASK)
+
+// Convert a sem->count bit pattern into the corresponding signed value.
+static inline int SEMCOUNT_TO_VALUE(uint32_t sval) {
+  return (static_cast<int>(sval) >> SEMCOUNT_VALUE_SHIFT);
+}
+
+// The value +1 as a sem->count bit-pattern.
+#define SEMCOUNT_ONE              SEMCOUNT_FROM_VALUE(1)
+
+// The value -1 as a sem->count bit-pattern.
+#define SEMCOUNT_MINUS_ONE        SEMCOUNT_FROM_VALUE(-1)
+
+#define SEMCOUNT_DECREMENT(sval)    (((sval) - (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
+#define SEMCOUNT_INCREMENT(sval)    (((sval) + (1U << SEMCOUNT_VALUE_SHIFT)) & SEMCOUNT_VALUE_MASK)
+
+// Return the shared bitflag from a semaphore.
+static inline uint32_t SEM_GET_SHARED(sem_t* sem) {
+  return (sem->count & SEMCOUNT_SHARED_MASK);
+}
+
+
+int sem_init(sem_t* sem, int pshared, unsigned int value) {
+  if (sem == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  // Ensure that 'value' can be stored in the semaphore.
+  if (value > SEM_VALUE_MAX) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  sem->count = SEMCOUNT_FROM_VALUE(value);
+  if (pshared != 0) {
+    sem->count |= SEMCOUNT_SHARED_MASK;
+  }
+  return 0;
+}
+
+int sem_destroy(sem_t* sem) {
+  if (sem == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+  return 0;
+}
+
+sem_t* sem_open(const char*, int, ...) {
+  errno = ENOSYS;
+  return SEM_FAILED;
+}
+
+int sem_close(sem_t*) {
+  errno = ENOSYS;
+  return -1;
+}
+
+int sem_unlink(const char*) {
+  errno = ENOSYS;
+  return -1;
+}
+
+// Decrement a semaphore's value atomically,
+// and return the old one. As a special case,
+// this returns immediately if the value is
+// negative (i.e. -1)
+static int __sem_dec(volatile uint32_t* sem) {
+  volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
+  uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
+  uint32_t old_value, new_value;
+  int ret;
+
+  do {
+    old_value = (*sem & SEMCOUNT_VALUE_MASK);
+    ret = SEMCOUNT_TO_VALUE(old_value);
+    if (ret < 0) {
+      break;
+    }
+
+    new_value = SEMCOUNT_DECREMENT(old_value);
+  } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
+
+  return ret;
+}
+
+// Same as __sem_dec, but will not touch anything if the
+// value is already negative *or* 0. Returns the old value.
+static int __sem_trydec(volatile uint32_t* sem) {
+  volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
+  uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
+  uint32_t old_value, new_value;
+  int          ret;
+
+  do {
+    old_value = (*sem & SEMCOUNT_VALUE_MASK);
+    ret = SEMCOUNT_TO_VALUE(old_value);
+    if (ret <= 0) {
+      break;
+    }
+
+    new_value = SEMCOUNT_DECREMENT(old_value);
+  } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
+
+  return ret;
+}
+
+
+// "Increment" the value of a semaphore atomically and
+// return its old value. Note that this implements
+// the special case of "incrementing" any negative
+// value to +1 directly.
+//
+// NOTE: The value will _not_ wrap above SEM_VALUE_MAX
+static int __sem_inc(volatile uint32_t* sem) {
+  volatile int32_t* ptr = reinterpret_cast<volatile int32_t*>(sem);
+  uint32_t shared = (*sem & SEMCOUNT_SHARED_MASK);
+  uint32_t old_value, new_value;
+  int ret;
+
+  do {
+    old_value = (*sem & SEMCOUNT_VALUE_MASK);
+    ret = SEMCOUNT_TO_VALUE(old_value);
+
+    // Can't go higher than SEM_VALUE_MAX.
+    if (ret == SEM_VALUE_MAX) {
+      break;
+    }
+
+    // If the counter is negative, go directly to +1, otherwise just increment.
+    if (ret < 0) {
+        new_value = SEMCOUNT_ONE;
+    } else {
+      new_value = SEMCOUNT_INCREMENT(old_value);
+    }
+  } while (__bionic_cmpxchg((old_value|shared), (new_value|shared), ptr) != 0);
+
+  return ret;
+}
+
+int sem_wait(sem_t* sem) {
+  if (sem == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  uint32_t shared = SEM_GET_SHARED(sem);
+
+  while (true) {
+    if (__sem_dec(&sem->count) > 0) {
+      ANDROID_MEMBAR_FULL();
+      return 0;
+    }
+
+    __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, NULL);
+  }
+}
+
+int sem_timedwait(sem_t* sem, const timespec* abs_timeout) {
+  if (sem == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  // POSIX says we need to try to decrement the semaphore
+  // before checking the timeout value. Note that if the
+  // value is currently 0, __sem_trydec() does nothing.
+  if (__sem_trydec(&sem->count) > 0) {
+    ANDROID_MEMBAR_FULL();
+    return 0;
+  }
+
+  // Check it as per POSIX.
+  if (abs_timeout == NULL || abs_timeout->tv_sec < 0 || abs_timeout->tv_nsec < 0 || abs_timeout->tv_nsec >= NS_PER_S) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  uint32_t shared = SEM_GET_SHARED(sem);
+
+  while (true) {
+    // POSIX mandates CLOCK_REALTIME here.
+    timespec ts;
+    if (!timespec_from_absolute_timespec(ts, *abs_timeout, CLOCK_REALTIME)) {
+      errno = ETIMEDOUT;
+      return -1;
+    }
+
+    // Try to grab the semaphore. If the value was 0, this will also change it to -1.
+    if (__sem_dec(&sem->count) > 0) {
+      ANDROID_MEMBAR_FULL();
+      break;
+    }
+
+    // Contention detected. Wait for a wakeup event.
+    int ret = __futex_wait_ex(&sem->count, shared, shared|SEMCOUNT_MINUS_ONE, &ts);
+
+    // Return in case of timeout or interrupt.
+    if (ret == -ETIMEDOUT || ret == -EINTR) {
+      errno = -ret;
+      return -1;
+    }
+  }
+  return 0;
+}
+
+int sem_post(sem_t* sem) {
+  if (sem == NULL) {
+    return EINVAL;
+  }
+
+  uint32_t shared = SEM_GET_SHARED(sem);
+
+  ANDROID_MEMBAR_FULL();
+  int old_value = __sem_inc(&sem->count);
+  if (old_value < 0) {
+    // Contention on the semaphore. Wake up all waiters.
+    __futex_wake_ex(&sem->count, shared, INT_MAX);
+  } else if (old_value == SEM_VALUE_MAX) {
+    // Overflow detected.
+    errno = EOVERFLOW;
+    return -1;
+  }
+
+  return 0;
+}
+
+int sem_trywait(sem_t* sem) {
+  if (sem == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  if (__sem_trydec(&sem->count) > 0) {
+    ANDROID_MEMBAR_FULL();
+    return 0;
+  } else {
+    errno = EAGAIN;
+    return -1;
+  }
+}
+
+int sem_getvalue(sem_t* sem, int* sval) {
+  if (sem == NULL || sval == NULL) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  int val = SEMCOUNT_TO_VALUE(sem->count);
+  if (val < 0) {
+    val = 0;
+  }
+
+  *sval = val;
+  return 0;
+}
diff --git a/libc/bionic/sysconf.cpp b/libc/bionic/sysconf.cpp
index d8aac4f..24a1ae7 100644
--- a/libc/bionic/sysconf.cpp
+++ b/libc/bionic/sysconf.cpp
@@ -49,7 +49,6 @@
 #define  SYSTEM_MQ_OPEN_MAX     8
 #define  SYSTEM_MQ_PRIO_MAX     32768
 #define  SYSTEM_SEM_NSEMS_MAX   256
-#define  SYSTEM_SEM_VALUE_MAX   0x3fffffff  /* see bionic/semaphore.c */
 #define  SYSTEM_SIGQUEUE_MAX    32
 #define  SYSTEM_TIMER_MAX       32
 #define  SYSTEM_LOGIN_NAME_MAX  256
@@ -151,33 +150,17 @@
 }
 
 int sysconf(int name) {
-    switch (name) {
-#ifdef _POSIX_ARG_MAX
+  switch (name) {
     case _SC_ARG_MAX:           return _POSIX_ARG_MAX;
-#endif
-#ifdef _POSIX2_BC_BASE_MAX
     case _SC_BC_BASE_MAX:       return _POSIX2_BC_BASE_MAX;
-#endif
-#ifdef _POSIX2_BC_DIM_MAX
     case _SC_BC_DIM_MAX:        return _POSIX2_BC_DIM_MAX;
-#endif
-#ifdef _POSIX2_BC_SCALE_MAX
     case _SC_BC_SCALE_MAX:      return _POSIX2_BC_SCALE_MAX;
-#endif
-#ifdef _POSIX2_BC_STRING_MAX
     case _SC_BC_STRING_MAX:     return _POSIX2_BC_STRING_MAX;
-#endif
     case _SC_CHILD_MAX:         return CHILD_MAX;
     case _SC_CLK_TCK:           return SYSTEM_CLK_TCK;
-#ifdef _POSIX2_COLL_WEIGHTS_MASK
-    case _SC_COLL_WEIGHTS_MAX:  return _POSIX2_COLL_WEIGHTS_MASK;
-#endif
-#ifdef _POSIX2_EXPR_NEST_MAX
-    case _SC_EXPR_NEST_MAX:    return _POSIX2_EXPR_NEST_MAX;
-#endif
-#ifdef _POSIX2_LINE_MAX
+    case _SC_COLL_WEIGHTS_MAX:  return _POSIX2_COLL_WEIGHTS_MAX;
+    case _SC_EXPR_NEST_MAX:     return _POSIX2_EXPR_NEST_MAX;
     case _SC_LINE_MAX:          return _POSIX2_LINE_MAX;
-#endif
     case _SC_NGROUPS_MAX:       return NGROUPS_MAX;
     case _SC_OPEN_MAX:          return OPEN_MAX;
     //case _SC_PASS_MAX:          return PASS_MAX;
@@ -191,42 +174,20 @@
     case _SC_2_SW_DEV:          return SYSTEM_2_SW_DEV;
     case _SC_2_UPE:             return SYSTEM_2_UPE;
     case _SC_2_VERSION:         return SYSTEM_2_VERSION;
-#ifdef _POSIX_JOB_CONTROL
     case _SC_JOB_CONTROL:       return _POSIX_JOB_CONTROL;
-#endif
-#ifdef _POSIX_SAVED_IDS
     case _SC_SAVED_IDS:         return _POSIX_SAVED_IDS;
-#endif
-#ifdef _POSIX_VERSION
     case _SC_VERSION:           return _POSIX_VERSION;
-#endif
-    //case _SC_RE_DUP_<AX:        return ;
+    case _SC_RE_DUP_MAX:        return _POSIX_RE_DUP_MAX;
     case _SC_STREAM_MAX:        return FOPEN_MAX;
-    //case _SC_TZNAME_MAX:        return ;
-#if _XOPEN_CRYPT
+    case _SC_TZNAME_MAX:        return _POSIX_TZNAME_MAX;
     case _SC_XOPEN_CRYPT:       return _XOPEN_CRYPT;
-#endif
-#ifdef _XOPEN_ENH_I18N
     case _SC_XOPEN_ENH_I18N:    return _XOPEN_ENH_I18N;
-#endif
-#ifdef _XOPEN_SHM
-    case _SC_XOPEN_SHM:         return _XOPEN_SHM;
-#endif
-#ifdef _XOPEN_VERSION
+    //case _SC_XOPEN_SHM:         return _XOPEN_SHM;
     case _SC_XOPEN_VERSION:     return _XOPEN_VERSION;
-#endif
-#ifdef _XOPEN_XCU_VERSION
     case _SC_XOPEN_XCU_VERSION: return _XOPEN_XCU_VERSION;
-#endif
-#ifdef _XOPEN_REALTIME
     case _SC_XOPEN_REALTIME:    return _XOPEN_REALTIME;
-#endif
-#ifdef _XOPEN_REALTIME_THREADS
     case _SC_XOPEN_REALTIME_THREADS: return _XOPEN_REALTIME_THREADS;
-#endif
-#ifdef _XOPEN_LEGACY
     case _SC_XOPEN_LEGACY:      return _XOPEN_LEGACY;
-#endif
     case _SC_ATEXIT_MAX:        return SYSTEM_ATEXIT_MAX;
     case _SC_IOV_MAX:           return SYSTEM_IOV_MAX;
 
@@ -234,71 +195,35 @@
     case _SC_PAGE_SIZE:
         return PAGE_SIZE;
 
-#ifdef _XOPEN_UNIX
     case _SC_XOPEN_UNIX:        return _XOPEN_UNIX;
-#endif
 
     // XXX: TODO: XBS5 nonsense
 
-#ifdef AIO_LISTIO_MAX
-    case _SC_AIO_LISTIO_MAX:       return AIO_LISTIO_MAX;
-#endif
-#ifdef AIO_MAX
-    case _SC_AIO_MAX:              return AIO_MAX;
-#endif
-#ifdef AIO_PRIO_DELTA_MAX
-    case _SC_AIO_PRIO_DELTA_MAX:   return AIO_PRIO_DELTA_MAX;
-#endif
+    //case _SC_AIO_LISTIO_MAX:       return AIO_LISTIO_MAX;
+    //case _SC_AIO_MAX:              return AIO_MAX;
+    //case _SC_AIO_PRIO_DELTA_MAX:   return AIO_PRIO_DELTA_MAX;
     case _SC_DELAYTIMER_MAX:    return SYSTEM_DELAYTIMER_MAX;
     case _SC_MQ_OPEN_MAX:       return SYSTEM_MQ_OPEN_MAX;
     case _SC_MQ_PRIO_MAX:       return SYSTEM_MQ_PRIO_MAX;
     case _SC_RTSIG_MAX:         return RTSIG_MAX;
     case _SC_SEM_NSEMS_MAX:     return SYSTEM_SEM_NSEMS_MAX;
-    case _SC_SEM_VALUE_MAX:     return SYSTEM_SEM_VALUE_MAX;
+    case _SC_SEM_VALUE_MAX:     return SEM_VALUE_MAX;
     case _SC_SIGQUEUE_MAX:      return SYSTEM_SIGQUEUE_MAX;
     case _SC_TIMER_MAX:         return SYSTEM_TIMER_MAX;
-#ifdef _POSIX_ASYNCHRONOUS_IO
-    case _SC_ASYNCHRONOUS_IO:   return _POSIX_ASYNCHRONOUS_IO;
-#endif
-#ifdef _POSIX_FSYNC
+    //case _SC_ASYNCHRONOUS_IO:   return _POSIX_ASYNCHRONOUS_IO;
     case _SC_FSYNC:             return _POSIX_FSYNC;
-#endif
-#ifdef _POSIX_MAPPED_FILES
     case _SC_MAPPED_FILES:      return _POSIX_MAPPED_FILES;
-#endif
-#ifdef _POSIX_MEMLOCK
-    case _SC_MEMLOCK:           return _POSIX_MEMLOCK;
-#endif
-#ifdef _POSIX_MEMLOCK_RANGE
-    case _SC_MEMLOCK_RANGE:     return _POSIX_MEMLOCK_RANGE
-#endif
-#ifdef _POSIX_MEMORY_PROTECTION
-    case _SC_MEMORY_PROTECTION: return _POSIX_MEMORY_PROTECTION;
-#endif
-#ifdef _POSIX_MESSAGE_PASSING
-    case _SC_MESSAGE_PASSING:   return _POSIX_MESSAGE_PASSING;
-#endif
-#ifdef _POSIX_PRIORITIZED_IO
-    case _SC_PRIORITIZED_IO:    return _POSIX_PRIORITIZED_IO;
-#endif
-#ifdef _POSIX_PRIORITY_SCHEDULING
+    //case _SC_MEMLOCK:           return _POSIX_MEMLOCK;
+    //case _SC_MEMLOCK_RANGE:     return _POSIX_MEMLOCK_RANGE;
+    //case _SC_MEMORY_PROTECTION: return _POSIX_MEMORY_PROTECTION;
+    //case _SC_MESSAGE_PASSING:   return _POSIX_MESSAGE_PASSING;
+    //case _SC_PRIORITIZED_IO:    return _POSIX_PRIORITIZED_IO;
     case _SC_PRIORITY_SCHEDULING:  return _POSIX_PRIORITY_SCHEDULING;
-#endif
-#ifdef _POSIX_REALTIME_SIGNALS
     case _SC_REALTIME_SIGNALS:  return _POSIX_REALTIME_SIGNALS;
-#endif
-#ifdef _POSIX_SEMAPHORES
     case _SC_SEMAPHORES:        return _POSIX_SEMAPHORES;
-#endif
-#ifdef _POSIX_SHARED_MEMORY_OBJECTS
-    case _SC_SHARED_MEMORY_OBJECTS:  return _POSIX_SHARED_MEMORY_OBJECTS;
-#endif
-#ifdef _POSIX_SYNCHRONIZED_IO
+    //case _SC_SHARED_MEMORY_OBJECTS:  return _POSIX_SHARED_MEMORY_OBJECTS;
     case _SC_SYNCHRONIZED_IO:   return _POSIX_SYNCHRONIZED_IO;
-#endif
-#ifdef _POSIX_TIMERS
     case _SC_TIMERS:            return _POSIX_TIMERS;
-#endif
 
     case _SC_GETGR_R_SIZE_MAX: return 1024;
     case _SC_GETPW_R_SIZE_MAX: return 1024;
@@ -314,25 +239,15 @@
     case _SC_THREAD_STACK_MIN:    return PTHREAD_STACK_MIN;
     case _SC_THREAD_THREADS_MAX:  return SYSTEM_THREAD_THREADS_MAX;
     case _SC_TTY_NAME_MAX:        return SYSTEM_TTY_NAME_MAX;
-#ifdef _POSIX_THREADS
     case _SC_THREADS:             return _POSIX_THREADS;
-#endif
 
     case _SC_THREAD_ATTR_STACKADDR:   return -1; // Removed in POSIX 2008
     case _SC_THREAD_ATTR_STACKSIZE:   return -1; // Removed in POSIX 2008
 
-#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING
-    case _SC_THREAD_PRIORITY_SCHEDULING:  return _POSIX_THREAD_PRIORITY_SCHEDULING;
-#endif
-#ifdef _POSIX_THREAD_PRIO_INHERIT
+    //case _SC_THREAD_PRIORITY_SCHEDULING:  return _POSIX_THREAD_PRIORITY_SCHEDULING;
     case _SC_THREAD_PRIO_INHERIT:         return _POSIX_THREAD_PRIO_INHERIT;
-#endif
-#ifdef _POSIX_THREAD_PRIO_PROTECT
     case _SC_THREAD_PRIO_PROTECT: return _POSIX_THREAD_PRIO_PROTECT;
-#endif
-#ifdef _POSIX_THREAD_SAFE_FUNCTIONS
-    case _SC_THREAD_SAFE_FUNCTIONS:  return _POSIX_THREAD_SAFE_FUNCTIONS
-#endif
+    //case _SC_THREAD_SAFE_FUNCTIONS:  return _POSIX_THREAD_SAFE_FUNCTIONS
 
     case _SC_MONOTONIC_CLOCK:   return __sysconf_monotonic_clock();
     case _SC_NPROCESSORS_CONF:  return __sysconf_nprocessors_conf();
@@ -341,9 +256,9 @@
     case _SC_AVPHYS_PAGES:      return __sysconf_avphys_pages();
 
     default:
-       /* Posix says EINVAL is the only error that shall be returned,
-        * but GLibc uses ENOSYS */
-        errno = ENOSYS;
-        return -1;
-    }
+      // Posix says EINVAL is the only error that shall be returned,
+      // but glibc uses ENOSYS.
+      errno = ENOSYS;
+      return -1;
+  }
 }
diff --git a/libc/include/limits.h b/libc/include/limits.h
index fb09657..f9f0c79 100644
--- a/libc/include/limits.h
+++ b/libc/include/limits.h
@@ -49,6 +49,7 @@
 #define	_POSIX_PATH_MAX		256
 #define _POSIX_PIPE_BUF		512
 #define	_POSIX_RE_DUP_MAX	255
+#define	_POSIX_SEM_VALUE_MAX	32767
 #define _POSIX_SSIZE_MAX	32767
 #define _POSIX_STREAM_MAX	8
 #define _POSIX_SYMLINK_MAX	255
@@ -125,4 +126,7 @@
 /* glibc's PAGE_MASK is the bitwise negation of BSD's! TODO: remove? */
 #define PAGE_MASK (~(PAGE_SIZE - 1))
 
+#define _POSIX_SEMAPHORES 200809L
+#define SEM_VALUE_MAX 0x3fffffff
+
 #endif /* !_LIMITS_H_ */
diff --git a/libc/include/semaphore.h b/libc/include/semaphore.h
index 7ae3c3a..5827870 100644
--- a/libc/include/semaphore.h
+++ b/libc/include/semaphore.h
@@ -25,6 +25,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+
 #ifndef _SEMAPHORE_H
 #define _SEMAPHORE_H
 
@@ -32,6 +33,8 @@
 
 __BEGIN_DECLS
 
+struct timespec;
+
 typedef struct {
   volatile unsigned int count;
 #ifdef __LP64__
@@ -41,20 +44,18 @@
 
 #define SEM_FAILED NULL
 
-extern int sem_init(sem_t *sem, int pshared, unsigned int value);
+int sem_destroy(sem_t*);
+int sem_getvalue(sem_t*, int*);
+int sem_init(sem_t*, int, unsigned int);
+int sem_post(sem_t*);
+int sem_timedwait(sem_t*, const struct timespec*);
+int sem_trywait(sem_t*);
+int sem_wait(sem_t*);
 
-extern int    sem_close(sem_t *);
-extern int    sem_destroy(sem_t *);
-extern int    sem_getvalue(sem_t *, int *);
-extern int    sem_init(sem_t *, int, unsigned int);
-extern sem_t *sem_open(const char *, int, ...);
-extern int    sem_post(sem_t *);
-extern int    sem_trywait(sem_t *);
-extern int    sem_unlink(const char *);
-extern int    sem_wait(sem_t *);
-
-struct timespec;
-extern int    sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+/* These aren't actually implemented. */
+sem_t* sem_open(const char*, int, ...);
+int sem_close(sem_t*);
+int sem_unlink(const char*);
 
 __END_DECLS
 
diff --git a/libc/private/bionic_constants.h b/libc/private/bionic_constants.h
new file mode 100644
index 0000000..9ae1c8d
--- /dev/null
+++ b/libc/private/bionic_constants.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _BIONIC_CONSTANTS_H_
+#define _BIONIC_CONSTANTS_H_
+
+#define NS_PER_S 1000000000
+
+#endif // _BIONIC_CONSTANTS_H_
diff --git a/libc/private/bionic_time_conversions.h b/libc/private/bionic_time_conversions.h
index 51f543f..cf0046a 100644
--- a/libc/private/bionic_time_conversions.h
+++ b/libc/private/bionic_time_conversions.h
@@ -39,6 +39,8 @@
 
 __LIBC_HIDDEN__ void timeval_from_timespec(timeval& tv, const timespec& ts);
 
+__LIBC_HIDDEN__ bool timespec_from_absolute_timespec(timespec& ts, const timespec& abs_ts, clockid_t clock);
+
 __END_DECLS
 
 #endif
diff --git a/tests/Android.mk b/tests/Android.mk
index 890e203..59fe57d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -86,6 +86,7 @@
     regex_test.cpp \
     sched_test.cpp \
     search_test.cpp \
+    semaphore_test.cpp \
     signal_test.cpp \
     stack_protector_test.cpp \
     stdatomic_test.cpp \
diff --git a/tests/semaphore_test.cpp b/tests/semaphore_test.cpp
new file mode 100644
index 0000000..e517f81
--- /dev/null
+++ b/tests/semaphore_test.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <semaphore.h>
+
+#include <errno.h>
+#include <gtest/gtest.h>
+#include <limits.h>
+#include <pthread.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "private/bionic_constants.h"
+
+TEST(semaphore, sem_init) {
+  sem_t s;
+
+  // Perfectly fine initial values.
+  ASSERT_EQ(0, sem_init(&s, 0, 0));
+  ASSERT_EQ(0, sem_init(&s, 0, 1));
+  ASSERT_EQ(0, sem_init(&s, 0, 123));
+
+  // Too small an initial value.
+  errno = 0;
+  ASSERT_EQ(-1, sem_init(&s, 0, -1));
+  ASSERT_EQ(EINVAL, errno);
+
+  ASSERT_EQ(SEM_VALUE_MAX, sysconf(_SC_SEM_VALUE_MAX));
+
+  // The largest initial value.
+  ASSERT_EQ(0, sem_init(&s, 0, SEM_VALUE_MAX));
+
+  // Too large an initial value.
+  errno = 0;
+  ASSERT_EQ(-1, sem_init(&s, 0, SEM_VALUE_MAX + 1));
+  ASSERT_EQ(EINVAL, errno);
+
+  ASSERT_EQ(0, sem_destroy(&s));
+}
+
+TEST(semaphore, sem_trywait) {
+  sem_t s;
+  ASSERT_EQ(0, sem_init(&s, 0, 3));
+  ASSERT_EQ(0, sem_trywait(&s));
+  ASSERT_EQ(0, sem_trywait(&s));
+  ASSERT_EQ(0, sem_trywait(&s));
+  errno = 0;
+  ASSERT_EQ(-1, sem_trywait(&s));
+  ASSERT_EQ(EAGAIN, errno);
+  ASSERT_EQ(0, sem_destroy(&s));
+}
+
+static void SemWaitThreadTestFn(sem_t& sem) {
+  ASSERT_EQ(0, sem_wait(&sem));
+}
+
+static void* SemWaitThreadFn(void* arg) {
+  SemWaitThreadTestFn(*reinterpret_cast<sem_t*>(arg));
+  return nullptr;
+}
+
+TEST(semaphore, sem_wait__sem_post) {
+  sem_t s;
+  ASSERT_EQ(0, sem_init(&s, 0, 0));
+
+  pthread_t t1, t2, t3;
+  ASSERT_EQ(0, pthread_create(&t1, NULL, SemWaitThreadFn, &s));
+  ASSERT_EQ(0, pthread_create(&t2, NULL, SemWaitThreadFn, &s));
+  ASSERT_EQ(0, pthread_create(&t3, NULL, SemWaitThreadFn, &s));
+
+  ASSERT_EQ(0, sem_post(&s));
+  ASSERT_EQ(0, sem_post(&s));
+  ASSERT_EQ(0, sem_post(&s));
+
+  void* result;
+  ASSERT_EQ(0, pthread_join(t1, &result));
+  ASSERT_EQ(0, pthread_join(t2, &result));
+  ASSERT_EQ(0, pthread_join(t3, &result));
+}
+
+static inline void timespec_add_ms(timespec& ts, size_t ms) {
+  ts.tv_sec  += ms / 1000;
+  ts.tv_nsec += (ms % 1000) * 1000000;
+  if (ts.tv_nsec >= NS_PER_S) {
+    ts.tv_sec++;
+    ts.tv_nsec -= NS_PER_S;
+  }
+}
+
+TEST(semaphore, sem_timedwait) {
+  sem_t s;
+  ASSERT_EQ(0, sem_init(&s, 0, 0));
+
+  timespec ts;
+  ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
+  timespec_add_ms(ts, 100);
+
+  errno = 0;
+  ASSERT_EQ(-1, sem_timedwait(&s, &ts));
+  ASSERT_EQ(ETIMEDOUT, errno);
+
+  // A negative timeout is an error.
+  errno = 0;
+  ts.tv_nsec = -1;
+  ASSERT_EQ(-1, sem_timedwait(&s, &ts));
+  ASSERT_EQ(EINVAL, errno);
+
+  ASSERT_EQ(0, sem_destroy(&s));
+}
+
+TEST(semaphore, sem_getvalue) {
+  sem_t s;
+  ASSERT_EQ(0, sem_init(&s, 0, 0));
+
+  int i;
+  ASSERT_EQ(0, sem_getvalue(&s, &i));
+  ASSERT_EQ(0, i);
+
+  ASSERT_EQ(0, sem_post(&s));
+  ASSERT_EQ(0, sem_getvalue(&s, &i));
+  ASSERT_EQ(1, i);
+
+  ASSERT_EQ(0, sem_post(&s));
+  ASSERT_EQ(0, sem_getvalue(&s, &i));
+  ASSERT_EQ(2, i);
+
+  ASSERT_EQ(0, sem_wait(&s));
+  ASSERT_EQ(0, sem_getvalue(&s, &i));
+  ASSERT_EQ(1, i);
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index c9ead8d..c631b6a 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -26,6 +26,8 @@
 
 #include "ScopedSignalHandler.h"
 
+#include "private/bionic_constants.h"
+
 TEST(time, gmtime) {
   time_t t = 0;
   tm* broken_down = gmtime(&t);
@@ -395,7 +397,7 @@
   ts2.tv_nsec -= ts1.tv_nsec;
   if (ts2.tv_nsec < 0) {
     --ts2.tv_sec;
-    ts2.tv_nsec += 1000000000;
+    ts2.tv_nsec += NS_PER_S;
   }
 
   // Should be less than (a very generous, to try to avoid flakiness) 1000000ns.