+ npthread_rwlock_t * rwlock = (npthread_rwlock_t *)orwlock;
+ uint32_t lcntval, ucntval, rw_seq, newval, newsval, updateval, ulval;
+ int error = 0, wrlock = 0, haswbit = 0, hasubit = 0, hasybit = 0;
+ uint64_t oldval64, newval64;
+ volatile uint32_t * lcntaddr, *ucntaddr, *seqaddr;
+ uint64_t myid = 0;
+
+ if (rwlock->sig != _PTHREAD_RWLOCK_SIG) {
+ LOCK(rwlock->lock);
+ if (rwlock->sig == _PTHREAD_RWLOCK_SIG_init) {
+ if ((error = __pthread_rwlock_init(orwlock, NULL)) != 0) {
+ UNLOCK(rwlock->lock);
+ PLOCKSTAT_RW_ERROR(orwlock, wrlock, error);
+ return(error);
+ }
+ } else if (rwlock->sig != _PTHREAD_RWLOCK_SIG){
+ UNLOCK(rwlock->lock);
+ PLOCKSTAT_RW_ERROR(orwlock, wrlock, EINVAL);
+ return(EINVAL);
+ }
+ UNLOCK(rwlock->lock);
+ }
+
+ if (rwlock->pshared == PTHREAD_PROCESS_SHARED) {
+ RWLOCK_GETSEQ_ADDR(rwlock, lcntaddr, ucntaddr, seqaddr);
+ } else {
+ lcntaddr = rwlock->rw_lcntaddr;
+ ucntaddr = rwlock->rw_ucntaddr;
+ seqaddr = rwlock->rw_seqaddr;
+ }
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_START, (uint32_t)rwlock, 0, 0, 0, 0);
+#endif
+loop:
+ lcntval = *lcntaddr;
+ ucntval = *ucntaddr;
+ rw_seq = *seqaddr;
+
+
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0x51515151, lcntval, ucntval, 0);
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0x51515151, rw_seq, 0, 0);
+#endif
+ /* check for spurious unlocks */
+ if ((lcntval & PTH_RWL_RBIT) != 0) {
+ newval = lcntval ;
+ newsval = rw_seq;
+
+ oldval64 = (((uint64_t)rw_seq) << 32);
+ oldval64 |= lcntval;
+
+ newval64 = (((uint64_t)newsval) << 32);
+ newval64 |= newval;
+
+ if (OSAtomicCompareAndSwap64(oldval64, newval64, (volatile int64_t *)lcntaddr) == TRUE) {
+ /* spurious unlock, return */
+ error = EINVAL;
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0x1a1b1c1d, lcntval, ucntval, 0);
+#endif
+ goto succout;
+ } else
+ goto loop;
+ }
+
+ if (is_rwl_ebit_set(lcntval)) {
+ wrlock = 1;
+#if __DARWIN_UNIX03
+ rwlock->rw_owner = (pthread_t)0;
+#endif /* __DARWIN_UNIX03 */
+ }
+
+ /* update U */
+
+ ulval = (ucntval + PTHRW_INC);
+
+ if (OSAtomicCompareAndSwap32(ucntval, ulval, (volatile int32_t *)ucntaddr) != TRUE)
+ goto loop;
+
+lp11:
+ /* just validate the l and S values */
+ oldval64 = (((uint64_t)rw_seq) << 32);
+ oldval64 |= lcntval;
+
+ newval64 = oldval64;
+
+ if (OSAtomicCompareAndSwap64(oldval64, newval64, (volatile int64_t *)lcntaddr) != TRUE) {
+ lcntval = *lcntaddr;
+ rw_seq = *seqaddr;
+ goto lp11;
+ }
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0xd1d2d3d4, lcntval, rw_seq, 0);
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0xd1d2d3d4, ulval, 0, 0);
+#endif
+
+ /* last unlock, note U is already updated ? */
+ if((lcntval & PTHRW_COUNT_MASK) == (ulval & PTHRW_COUNT_MASK)) {
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0xbbbbbbbb, lcntval, ucntval, 0);
+#endif
+ /* Set L with R and init bits and set S to L */
+ newval = (lcntval & PTHRW_COUNT_MASK)| PTHRW_RWLOCK_INIT;
+ newsval = (lcntval & PTHRW_COUNT_MASK)| PTHRW_RWS_INIT;
+
+ oldval64 = (((uint64_t)rw_seq) << 32);
+ oldval64 |= lcntval;
+
+ newval64 = (((uint64_t)newsval) << 32);
+ newval64 |= newval;
+
+ if (OSAtomicCompareAndSwap64(oldval64, newval64, (volatile int64_t *)lcntaddr) != TRUE) {
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0xcccccccc, 0, 0, 0);
+#endif
+ lcntval = *lcntaddr;
+ rw_seq = *seqaddr;
+ goto lp11;
+ }
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0xdddddddd, lcntval, ucntval, 0);
+#endif
+ goto succout;
+ }
+
+ /* if it is not exclusive or no Writer/yield pending, skip */
+ if ((lcntval & (PTH_RWL_EBIT | PTH_RWL_WBIT | PTH_RWL_YBIT | PTH_RWL_KBIT)) == 0) {
+ goto succout;
+ }
+
+ /* kernel transition needed? */
+ /* U+1 == S? */
+ if ((ulval + PTHRW_INC) != (rw_seq & PTHRW_COUNT_MASK)) {
+ if ((lcntval & PTH_RWL_UBIT) != 0) {
+ /* if U bit is set U + 2 == S ? */
+ if ((ulval + PTHRW_INC + PTHRW_INC) != (rw_seq & PTHRW_COUNT_MASK))
+ goto succout;
+ } else
+ goto succout;
+ }
+
+ haswbit = lcntval & PTH_RWL_WBIT;
+ hasubit = lcntval & PTH_RWL_UBIT;
+ hasybit = lcntval & PTH_RWL_YBIT;
+
+ /* reset all bits and set k */
+ newval = (lcntval & PTHRW_COUNT_MASK) | PTH_RWL_KBIT;
+ /* set I bit on S word */
+ newsval = rw_seq | PTH_RWS_IBIT;
+ if (haswbit != 0)
+ newsval |= PTH_RWS_WSVBIT;
+ if (hasubit != 0)
+ newsval |= PTH_RWS_USVBIT;
+ if (hasybit != 0)
+ newsval |= PTH_RWS_YSVBIT;
+
+ oldval64 = (((uint64_t)rw_seq) << 32);
+ oldval64 |= lcntval;
+
+ newval64 = (((uint64_t)newsval) << 32);
+ newval64 |= newval;
+
+ if (OSAtomicCompareAndSwap64(oldval64, newval64, (volatile int64_t *)lcntaddr) != TRUE)
+ goto lp11;
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0x55555511, 1, ulval, 0);
+#endif
+ updateval = __psynch_rw_unlock(orwlock, lcntval, ulval, newsval, rwlock->rw_flags);
+ if (updateval == (uint32_t)-1) {
+ error = errno;
+ } else
+ error = 0;
+
+ if(error != 0) {
+
+ /* not sure what is the scenario */
+ if(error != EINTR) {
+#if _KSYN_TRACE_
+ set_enable(4);
+#endif /* _KSYN_TRACE_ */
+ (void)pthread_threadid_np(pthread_self(), &myid);
+ LIBC_ABORT("rwunlock from kernel with unknown error %x: tid %x\n", error, (uint32_t)myid);
+ goto succout;
+ }
+ error = 0;
+ }
+
+#if _KSYN_TRACE_
+ if (__pthread_lock_debug != 0)
+ (void)__kdebug_trace(_KSYN_TRACE_RW_UNLOCK | DBG_FUNC_NONE, (uint32_t)rwlock, 0x55555522, 3, lcntval, 0);
+#endif