Author: davidxu
Date: Wed Mar 21 07:12:52 2012
New Revision: 233262
URL: http://svn.freebsd.org/changeset/base/233262

Log:
  Use version 2 of semaphore provided by kernel umtx code, now if there is
  no waiters, we still increase and decrease count in user mode without
  entering kernel, once there is a waiter, sem_post will enter kernel to
  increase count and wake thread up, this is atomicy and allow us to
  gracefully destroy semaphore after sem_wait returned.

Modified:
  head/lib/libc/gen/sem_new.c

Modified: head/lib/libc/gen/sem_new.c
==============================================================================
--- head/lib/libc/gen/sem_new.c Wed Mar 21 07:07:43 2012        (r233261)
+++ head/lib/libc/gen/sem_new.c Wed Mar 21 07:12:52 2012        (r233262)
@@ -61,7 +61,8 @@ __weak_reference(_sem_unlink, sem_unlink
 __weak_reference(_sem_wait, sem_wait);
 
 #define SEM_PREFIX     "/tmp/SEMD"
-#define SEM_MAGIC      ((u_int32_t)0x73656d31)
+#define SEM_MAGIC1     ((u_int32_t)0x73656d31)
+#define SEM_MAGIC      ((u_int32_t)0x73656d32)
 
 struct sem_nameinfo {
        int open_count;
@@ -109,7 +110,7 @@ static inline int
 sem_check_validity(sem_t *sem)
 {
 
-       if (sem->_magic == SEM_MAGIC)
+       if (sem->_magic == SEM_MAGIC || sem->_magic == SEM_MAGIC1)
                return (0);
        else {
                errno = EINVAL;
@@ -130,7 +131,7 @@ _sem_init(sem_t *sem, int pshared, unsig
        sem->_magic = SEM_MAGIC;
        sem->_kern._count = (u_int32_t)value;
        sem->_kern._has_waiters = 0;
-       sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0;
+       sem->_kern._flags = (pshared ? USYNC_PROCESS_SHARED : 0) | SEM_VER2;
        return (0);
 }
 
@@ -207,7 +208,7 @@ _sem_open(const char *name, int flags, .
                tmp._magic = SEM_MAGIC;
                tmp._kern._has_waiters = 0;
                tmp._kern._count = value;
-               tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED;
+               tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED | SEM_VER2;
                if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
                        flock(fd, LOCK_UN);
                        goto error;
@@ -325,20 +326,11 @@ _sem_getvalue(sem_t * __restrict sem, in
        if (sem_check_validity(sem) != 0)
                return (-1);
 
-       *sval = (int)sem->_kern._count;
+       *sval = (int)sem->_kern._count & ~SEM_WAITERS;
        return (0);
 }
 
 static __inline int
-usem_wake(struct _usem *sem)
-{
-       rmb();
-       if (!sem->_has_waiters)
-               return (0);
-       return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL);
-}
-
-static __inline int
 usem_wait(struct _usem *sem, const struct timespec *abstime)
 {
        struct _umtx_time *tm_p, timeout;
@@ -358,48 +350,51 @@ usem_wait(struct _usem *sem, const struc
                    (void *)tm_size, __DECONST(void*, tm_p));
 }
 
+static inline int
+_trywait(sem_t *sem)
+{
+       int count;
+
+       if ((sem->_kern._flags & SEM_VER2) != 0) {
+               while (((count = sem->_kern._count) & ~SEM_WAITERS) > 0) {
+                       if (atomic_cmpset_acq_int(&sem->_kern._count, count, 
count - 1))
+                               return (0);
+               }
+       } else {
+               while ((count = sem->_kern._count) > 0) {
+                       if (atomic_cmpset_acq_int(&sem->_kern._count, count, 
count - 1))
+                               return (0);
+               }
+       }
+       return (EAGAIN);
+}
+
 int
 _sem_trywait(sem_t *sem)
 {
-       int val;
+       int status;
 
        if (sem_check_validity(sem) != 0)
                return (-1);
-
-       while ((val = sem->_kern._count) > 0) {
-               if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
-                       return (0);
-       }
-       errno = EAGAIN;
+       if ((status = _trywait(sem)) == 0)
+               return (0);
+       errno = status;
        return (-1);
 }
 
-#define TIMESPEC_SUB(dst, src, val)                             \
-        do {                                                    \
-                (dst)->tv_sec = (src)->tv_sec - (val)->tv_sec;  \
-                (dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \
-                if ((dst)->tv_nsec < 0) {                       \
-                        (dst)->tv_sec--;                        \
-                        (dst)->tv_nsec += 1000000000;           \
-                }                                               \
-        } while (0)
-
-
 int
 _sem_timedwait(sem_t * __restrict sem,
        const struct timespec * __restrict abstime)
 {
-       int val, retval;
+       int retval;
 
        if (sem_check_validity(sem) != 0)
                return (-1);
 
        retval = 0;
        for (;;) {
-               while ((val = sem->_kern._count) > 0) {
-                       if (atomic_cmpset_acq_int(&sem->_kern._count, val, val 
- 1))
-                               return (0);
-               }
+               if (_trywait(sem) == 0)
+                       return (0);
 
                if (retval) {
                        _pthread_testcancel();
@@ -438,10 +433,36 @@ _sem_wait(sem_t *sem)
 int
 _sem_post(sem_t *sem)
 {
+       int count;
 
        if (sem_check_validity(sem) != 0)
                return (-1);
 
-       atomic_add_rel_int(&sem->_kern._count, 1);
-       return usem_wake(&sem->_kern);
+       if ((sem->_kern._flags & SEM_VER2) != 0) {
+               for (;;) {
+                       count = sem->_kern._count;
+                       if ((count & SEM_WAITERS) == 0) {
+                               if (__predict_false(count == SEM_VALUE_MAX)) {
+                                       errno = ERANGE;
+                                       return (-1);
+                               }
+                               if (atomic_cmpset_rel_int(&sem->_kern._count, 
count, count+1))
+                                       return (0);
+                       } else {
+                               return _umtx_op(&sem->_kern, UMTX_OP_SEM_WAKE, 
0, NULL, NULL);
+                       }
+               }
+       } else {
+               do {
+                       count = sem->_kern._count;
+                       if (__predict_false(count == SEM_VALUE_MAX)) {
+                               errno = ERANGE;
+                               return (-1);
+                       }
+               } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, 
count+1));
+               rmb();
+               if (!sem->_kern._has_waiters)
+                       return (0);
+               return _umtx_op(&sem->_kern, UMTX_OP_SEM_WAKE, 0, NULL, NULL);
+       }
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to