- LOAD_STRING_ARG0(rwl_release_error_str)
- CALL_PANIC()
-
-
-
-/*
- * lck_rw_type_t lck_rw_lock_exclusive_to_shared(lck_rw_t *)
- *
- */
-Entry(lck_rw_lock_exclusive_to_shared)
- LOAD_LCK_RW_REGISTER
-1:
- LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
- testl $(LCK_RW_INTERLOCK), %eax
- jne 6f /* wait for interlock to clear */
-
- movl %eax, %ecx /* keep original value in %eax for cmpxchgl */
- incl %ecx /* Increment reader count */
-
- testl $(LCK_RW_WANT_UPGRADE), %ecx
- je 2f
- andl $(~LCK_RW_WANT_UPGRADE), %ecx
- jmp 3f
-2:
- andl $(~LCK_RW_WANT_WRITE), %ecx
-3:
- /*
- * test the original values to match what
- * lck_rw_lock_exclusive_to_shared_gen is going to do to determine
- * which wakeups need to happen...
- *
- * if !(fake_lck->lck_rw_priv_excl && fake_lck->lck_w_waiting)
- */
- testl $(LCK_W_WAITING), %eax
- je 4f
- testl $(LCK_RW_PRIV_EXCL), %eax
- jne 5f
-4:
- andl $(~LCK_R_WAITING), %ecx
-5:
- lock
- cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
- jne 6f
-
-#if __i386__
- pushl %eax
- push %edx
- call EXT(lck_rw_lock_exclusive_to_shared_gen)
- addl $8, %esp
-#else
- mov %eax,%esi
- call EXT(lck_rw_lock_exclusive_to_shared_gen)
-#endif
- ret
-6:
- PAUSE
- jmp 1b
-
-
-
-/*
- * int lck_rw_grab_want(lck_rw_t *)
- *
- */
-Entry(lck_rw_grab_want)
- LOAD_LCK_RW_REGISTER
-1:
- LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
- testl $(LCK_RW_INTERLOCK), %eax
- jne 3f /* wait for interlock to clear */
- testl $(LCK_RW_WANT_WRITE), %eax /* want_write has been grabbed by someone else */
- jne 2f /* go return failure */
-
- movl %eax, %ecx /* original value in %eax for cmpxchgl */
- orl $(LCK_RW_WANT_WRITE), %ecx
- lock
- cmpxchgl %ecx, (LCK_RW_REGISTER) /* Attempt atomic exchange */
- jne 2f
- /* we now own want_write */
- movl $1, %eax /* return success */
- ret
-2:
- xorl %eax, %eax /* return failure */
- ret
-3:
- PAUSE
- jmp 1b
-
-
-#define RW_LOCK_SHARED_OR_UPGRADE_MASK (LCK_RW_SHARED_MASK | LCK_RW_INTERLOCK | LCK_RW_WANT_UPGRADE)
-/*
- * int lck_rw_held_read_or_upgrade(lck_rw_t *)
- *
- */
-Entry(lck_rw_held_read_or_upgrade)
- LOAD_LCK_RW_REGISTER
- LOAD_LCK_RW_FLAGS_REGISTER /* Load state bitfield, interlock and reader count */
- andl $(RW_LOCK_SHARED_OR_UPGRADE_MASK), %eax
- ret
-
-
-
-/*
- * N.B.: On x86, statistics are currently recorded for all indirect mutexes.
- * Also, only the acquire attempt count (GRP_MTX_STAT_UTIL) is maintained
- * as a 64-bit quantity (this matches the existing PowerPC implementation,
- * and the new x86 specific statistics are also maintained as 32-bit
- * quantities).
- *
- *
- * Enable this preprocessor define to record the first miss alone
- * By default, we count every miss, hence multiple misses may be
- * recorded for a single lock acquire attempt via lck_mtx_lock
- */
-#undef LOG_FIRST_MISS_ALONE
-
-/*
- * This preprocessor define controls whether the R-M-W update of the
- * per-group statistics elements are atomic (LOCK-prefixed)
- * Enabled by default.
- */
-#define ATOMIC_STAT_UPDATES 1
-
-#if defined(ATOMIC_STAT_UPDATES)
-#define LOCK_IF_ATOMIC_STAT_UPDATES lock
-#else
-#define LOCK_IF_ATOMIC_STAT_UPDATES
-#endif /* ATOMIC_STAT_UPDATES */
-
-
-/*
- * For most routines, the lck_mtx_t pointer is loaded into a
- * register initially, and the owner field checked for indirection.
- * Eventually the lock owner is loaded into a register and examined.
- */
-
-#define M_OWNER MUTEX_OWNER
-#define M_PTR MUTEX_PTR
-#define M_STATE MUTEX_STATE
-
-#if defined(__i386__)
-
-#define LMTX_ARG0 B_ARG0
-#define LMTX_ARG1 B_ARG1
-#define LMTX_REG %edx
-#define LMTX_A_REG %eax
-#define LMTX_A_REG32 %eax
-#define LMTX_C_REG %ecx
-#define LMTX_C_REG32 %ecx
-#define LMTX_RET_REG %eax
-#define LMTX_RET_REG32 %eax
-#define LMTX_LGROUP_REG %esi
-#define LMTX_SSTATE_REG %edi
-#define LOAD_LMTX_REG(arg) mov arg, LMTX_REG
-#define LMTX_CHK_EXTENDED cmp LMTX_REG, LMTX_ARG0
-#define LMTX_ASSERT_OWNED cmpl $(MUTEX_ASSERT_OWNED), LMTX_ARG1
-
-#define LMTX_ENTER_EXTENDED \
- mov M_PTR(LMTX_REG), LMTX_REG ; \
- push LMTX_LGROUP_REG ; \
- push LMTX_SSTATE_REG ; \
- xor LMTX_SSTATE_REG, LMTX_SSTATE_REG ; \
- mov MUTEX_GRP(LMTX_REG), LMTX_LGROUP_REG ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- addl $1, GRP_MTX_STAT_UTIL(LMTX_LGROUP_REG) ; \
- jnc 11f ; \
- incl GRP_MTX_STAT_UTIL+4(LMTX_LGROUP_REG) ; \
-11:
-
-#define LMTX_EXIT_EXTENDED \
- pop LMTX_SSTATE_REG ; \
- pop LMTX_LGROUP_REG
-
-
-#define LMTX_CHK_EXTENDED_EXIT \
- cmp LMTX_REG, LMTX_ARG0 ; \
- je 12f ; \
- pop LMTX_SSTATE_REG ; \
- pop LMTX_LGROUP_REG ; \
-12:
-
-
-#if LOG_FIRST_MISS_ALONE
-#define LMTX_UPDATE_MISS \
- test $1, LMTX_SSTATE_REG ; \
- jnz 11f ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG) ; \
- or $1, LMTX_SSTATE_REG ; \
-11:
-#else
-#define LMTX_UPDATE_MISS \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG)
-#endif
-
-
-#if LOG_FIRST_MISS_ALONE
-#define LMTX_UPDATE_WAIT \
- test $2, LMTX_SSTATE_REG ; \
- jnz 11f ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG) ; \
- or $2, LMTX_SSTATE_REG ; \
-11:
-#else
-#define LMTX_UPDATE_WAIT \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG)
-#endif
-
-
-/*
- * Record the "direct wait" statistic, which indicates if a
- * miss proceeded to block directly without spinning--occurs
- * if the owner of the mutex isn't running on another processor
- * at the time of the check.
- */
-#define LMTX_UPDATE_DIRECT_WAIT \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_DIRECT_WAIT(LMTX_LGROUP_REG)
-
-
-#define LMTX_CALLEXT1(func_name) \
- push LMTX_REG ; \
- push LMTX_REG ; \
- call EXT(func_name) ; \
- add $4, %esp ; \
- pop LMTX_REG
-
-#define LMTX_CALLEXT2(func_name, reg) \
- push LMTX_REG ; \
- push reg ; \
- push LMTX_REG ; \
- call EXT(func_name) ; \
- add $8, %esp ; \
- pop LMTX_REG
-
-#elif defined(__x86_64__)
-
-#define LMTX_ARG0 %rdi
-#define LMTX_ARG1 %rsi
-#define LMTX_REG_ORIG %rdi
-#define LMTX_REG %rdx
-#define LMTX_A_REG %rax
-#define LMTX_A_REG32 %eax
-#define LMTX_C_REG %rcx
-#define LMTX_C_REG32 %ecx
-#define LMTX_RET_REG %rax
-#define LMTX_RET_REG32 %eax
-#define LMTX_LGROUP_REG %r10
-#define LMTX_SSTATE_REG %r11
-#define LOAD_LMTX_REG(arg) mov %rdi, %rdx
-#define LMTX_CHK_EXTENDED cmp LMTX_REG, LMTX_REG_ORIG
-#define LMTX_ASSERT_OWNED cmp $(MUTEX_ASSERT_OWNED), LMTX_ARG1
-
-#define LMTX_ENTER_EXTENDED \
- mov M_PTR(LMTX_REG), LMTX_REG ; \
- xor LMTX_SSTATE_REG, LMTX_SSTATE_REG ; \
- mov MUTEX_GRP(LMTX_REG), LMTX_LGROUP_REG ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incq GRP_MTX_STAT_UTIL(LMTX_LGROUP_REG)
-
-#define LMTX_EXIT_EXTENDED
-
-#define LMTX_CHK_EXTENDED_EXIT
-
-
-#if LOG_FIRST_MISS_ALONE
-#define LMTX_UPDATE_MISS \
- test $1, LMTX_SSTATE_REG ; \
- jnz 11f ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG) ; \
- or $1, LMTX_SSTATE_REG ; \
-11:
-#else
-#define LMTX_UPDATE_MISS \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_MISS(LMTX_LGROUP_REG)
-#endif
-
-
-#if LOG_FIRST_MISS_ALONE
-#define LMTX_UPDATE_WAIT \
- test $2, LMTX_SSTATE_REG ; \
- jnz 11f ; \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG) ; \
- or $2, LMTX_SSTATE_REG ; \
-11:
-#else
-#define LMTX_UPDATE_WAIT \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_WAIT(LMTX_LGROUP_REG)
-#endif
-
-
-/*
- * Record the "direct wait" statistic, which indicates if a
- * miss proceeded to block directly without spinning--occurs
- * if the owner of the mutex isn't running on another processor
- * at the time of the check.
- */
-#define LMTX_UPDATE_DIRECT_WAIT \
- LOCK_IF_ATOMIC_STAT_UPDATES ; \
- incl GRP_MTX_STAT_DIRECT_WAIT(LMTX_LGROUP_REG)
-
-
-#define LMTX_CALLEXT1(func_name) \
- LMTX_CHK_EXTENDED ; \
- je 12f ; \
- push LMTX_LGROUP_REG ; \
- push LMTX_SSTATE_REG ; \
-12: push LMTX_REG_ORIG ; \
- push LMTX_REG ; \
- mov LMTX_REG, LMTX_ARG0 ; \
- call EXT(func_name) ; \
- pop LMTX_REG ; \
- pop LMTX_REG_ORIG ; \
- LMTX_CHK_EXTENDED ; \
- je 12f ; \
- pop LMTX_SSTATE_REG ; \
- pop LMTX_LGROUP_REG ; \
-12:
-
-#define LMTX_CALLEXT2(func_name, reg) \
- LMTX_CHK_EXTENDED ; \
- je 12f ; \
- push LMTX_LGROUP_REG ; \
- push LMTX_SSTATE_REG ; \
-12: push LMTX_REG_ORIG ; \
- push LMTX_REG ; \
- mov reg, LMTX_ARG1 ; \
- mov LMTX_REG, LMTX_ARG0 ; \
- call EXT(func_name) ; \
- pop LMTX_REG ; \
- pop LMTX_REG_ORIG ; \
- LMTX_CHK_EXTENDED ; \
- je 12f ; \
- pop LMTX_SSTATE_REG ; \
- pop LMTX_LGROUP_REG ; \
-12:
-
-#else
-#error Unsupported architecture
-#endif
-
-
-#define M_WAITERS_MSK 0x0000ffff
-#define M_PRIORITY_MSK 0x00ff0000
-#define M_ILOCKED_MSK 0x01000000
-#define M_MLOCKED_MSK 0x02000000
-#define M_PROMOTED_MSK 0x04000000
-#define M_SPIN_MSK 0x08000000
-
-/*
- * void lck_mtx_assert(lck_mtx_t* l, unsigned int)
- * Takes the address of a lock, and an assertion type as parameters.
- * The assertion can take one of two forms determine by the type
- * parameter: either the lock is held by the current thread, and the
- * type is LCK_MTX_ASSERT_OWNED, or it isn't and the type is
- * LCK_MTX_ASSERT_NOTOWNED. Calls panic on assertion failure.
- *
- */
-
-NONLEAF_ENTRY(lck_mtx_assert)
- LOAD_LMTX_REG(B_ARG0) /* Load lock address */
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG /* Load current thread */
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne 0f
- mov M_PTR(LMTX_REG), LMTX_REG /* If so, take indirection */
-0:
- mov M_OWNER(LMTX_REG), LMTX_C_REG /* Load owner */
- LMTX_ASSERT_OWNED
- jne 2f /* Assert ownership? */
- cmp LMTX_A_REG, LMTX_C_REG /* Current thread match? */
- jne 3f /* no, go panic */
- testl $(M_ILOCKED_MSK | M_MLOCKED_MSK), M_STATE(LMTX_REG)
- je 3f
-1: /* yes, we own it */
- NONLEAF_RET
-2:
- cmp LMTX_A_REG, LMTX_C_REG /* Current thread match? */
- jne 1b /* No, return */
- ALIGN_STACK()
- LOAD_PTR_ARG1(LMTX_REG)
- LOAD_STRING_ARG0(mutex_assert_owned_str)
- jmp 4f
-3:
- ALIGN_STACK()
- LOAD_PTR_ARG1(LMTX_REG)
- LOAD_STRING_ARG0(mutex_assert_not_owned_str)
-4:
- CALL_PANIC()
-
-
-lck_mtx_destroyed:
- ALIGN_STACK()
- LOAD_PTR_ARG1(LMTX_REG)
- LOAD_STRING_ARG0(mutex_interlock_destroyed_str)
- CALL_PANIC()
-
-
-.data
-mutex_assert_not_owned_str:
- .asciz "mutex (%p) not owned\n"
-mutex_assert_owned_str:
- .asciz "mutex (%p) owned\n"
-mutex_interlock_destroyed_str:
- .asciz "trying to interlock destroyed mutex (%p)"
-.text
-
-
-
-/*
- * lck_mtx_lock()
- * lck_mtx_try_lock()
- * lck_mtx_unlock()
- * lck_mtx_lock_spin()
- * lck_mtx_lock_spin_always()
- * lck_mtx_convert_spin()
- */
-NONLEAF_ENTRY(lck_mtx_lock_spin_always)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
- jmp Llmls_avoid_check
-
-NONLEAF_ENTRY(lck_mtx_lock_spin)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-
- CHECK_PREEMPTION_LEVEL()
-Llmls_avoid_check:
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
- jnz Llmls_slow
-Llmls_try: /* no - can't be INDIRECT, DESTROYED or locked */
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne Llmls_busy_disabled
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of interlock */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- LMTX_CHK_EXTENDED_EXIT
- /* return with the interlock held and preemption disabled */
- leave
-#if CONFIG_DTRACE
- LOCKSTAT_LABEL(_lck_mtx_lock_spin_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG above */
- LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_SPIN_ACQUIRE, LMTX_REG)
-#endif
- ret
-
-Llmls_slow:
- test $M_ILOCKED_MSK, LMTX_C_REG32 /* is the interlock held */
- jz Llml_contended /* no, must have been the mutex */
-
- cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
- je lck_mtx_destroyed
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex */
- jne Llmls_loop /* no... must be interlocked */
-
- LMTX_ENTER_EXTENDED
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_SPIN_MSK), LMTX_C_REG32
- jz Llmls_loop1
-
- LMTX_UPDATE_MISS /* M_SPIN_MSK was set, so M_ILOCKED_MSK must also be present */
-Llmls_loop:
- PAUSE
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-Llmls_loop1:
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
- jz Llmls_try
- test $(M_MLOCKED_MSK), LMTX_C_REG32
- jnz Llml_contended /* mutex owned by someone else, go contend for it */
- jmp Llmls_loop
-
-Llmls_busy_disabled:
- PREEMPTION_ENABLE
- jmp Llmls_loop
-
-
-
-NONLEAF_ENTRY(lck_mtx_lock)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-
- CHECK_PREEMPTION_LEVEL()
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
- jnz Llml_slow
-Llml_try: /* no - can't be INDIRECT, DESTROYED or locked */
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne Llml_busy_disabled
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- testl $(M_WAITERS_MSK), M_STATE(LMTX_REG)
- jz Llml_finish
-
- LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
-
-Llml_finish:
- andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
- PREEMPTION_ENABLE
-
- LMTX_CHK_EXTENDED /* is this an extended mutex */
- jne 2f
-
- leave
-#if CONFIG_DTRACE
- LOCKSTAT_LABEL(_lck_mtx_lock_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG above */
- LOCKSTAT_RECORD(LS_LCK_MTX_LOCK_ACQUIRE, LMTX_REG)
-#endif
- ret
-2:
- LMTX_EXIT_EXTENDED
- leave
-#if CONFIG_DTRACE
- LOCKSTAT_LABEL(_lck_mtx_lock_ext_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG above */
- LOCKSTAT_RECORD(LS_LCK_MTX_EXT_LOCK_ACQUIRE, LMTX_REG)
-#endif
- ret
-
-
-Llml_slow:
- test $M_ILOCKED_MSK, LMTX_C_REG32 /* is the interlock held */
- jz Llml_contended /* no, must have been the mutex */
-
- cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
- je lck_mtx_destroyed
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne Llml_loop /* no... must be interlocked */
-
- LMTX_ENTER_EXTENDED
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_SPIN_MSK), LMTX_C_REG32
- jz Llml_loop1
-
- LMTX_UPDATE_MISS /* M_SPIN_MSK was set, so M_ILOCKED_MSK must also be present */
-Llml_loop:
- PAUSE
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-Llml_loop1:
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
- jz Llml_try
- test $(M_MLOCKED_MSK), LMTX_C_REG32
- jnz Llml_contended /* mutex owned by someone else, go contend for it */
- jmp Llml_loop
-
-Llml_busy_disabled:
- PREEMPTION_ENABLE
- jmp Llml_loop
-
-
-Llml_contended:
- LMTX_CHK_EXTENDED /* is this an extended mutex */
- je 0f
- LMTX_UPDATE_MISS
-0:
- LMTX_CALLEXT1(lck_mtx_lock_spinwait_x86)
-
- test LMTX_RET_REG, LMTX_RET_REG
- jz Llml_acquired /* acquired mutex, interlock held and preemption disabled */
-
- cmp $1, LMTX_RET_REG /* check for direct wait status */
- je 2f
- LMTX_CHK_EXTENDED /* is this an extended mutex */
- je 2f
- LMTX_UPDATE_DIRECT_WAIT
-2:
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_ILOCKED_MSK), LMTX_C_REG32
- jnz 6f
-
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK), LMTX_C_REG32 /* try to take the interlock */
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne 5f
-
- test $(M_MLOCKED_MSK), LMTX_C_REG32 /* we've got the interlock and */
- jnz 3f
- or $(M_MLOCKED_MSK), LMTX_C_REG32 /* the mutex is free... grab it directly */
- mov LMTX_C_REG32, M_STATE(LMTX_REG)
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
-Llml_acquired:
- testl $(M_WAITERS_MSK), M_STATE(LMTX_REG)
- jnz 1f
- mov M_OWNER(LMTX_REG), LMTX_A_REG
- mov TH_WAS_PROMOTED_ON_WAKEUP(LMTX_A_REG), LMTX_A_REG32
- test LMTX_A_REG32, LMTX_A_REG32
- jz Llml_finish
-1:
- LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
- jmp Llml_finish
-
-3: /* interlock held, mutex busy */
- LMTX_CHK_EXTENDED /* is this an extended mutex */
- je 4f
- LMTX_UPDATE_WAIT
-4:
- LMTX_CALLEXT1(lck_mtx_lock_wait_x86)
- jmp Llml_contended
-5:
- PREEMPTION_ENABLE
-6:
- PAUSE
- jmp 2b
-
-
-
-NONLEAF_ENTRY(lck_mtx_try_lock_spin)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
- jnz Llmts_slow
-Llmts_try: /* no - can't be INDIRECT, DESTROYED or locked */
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK | M_SPIN_MSK), LMTX_C_REG
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne Llmts_busy_disabled
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- LMTX_CHK_EXTENDED_EXIT
- leave
-
-#if CONFIG_DTRACE
- mov $1, LMTX_RET_REG /* return success */
- LOCKSTAT_LABEL(_lck_mtx_try_lock_spin_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG above */
- LOCKSTAT_RECORD(LS_LCK_MTX_TRY_SPIN_LOCK_ACQUIRE, LMTX_REG)
-#endif
- mov $1, LMTX_RET_REG /* return success */
- ret
-
-Llmts_slow:
- test $(M_ILOCKED_MSK), LMTX_C_REG32 /* is the interlock held */
- jz Llmts_fail /* no, must be held as a mutex */
-
- cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
- je lck_mtx_destroyed
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne Llmts_loop1
-
- LMTX_ENTER_EXTENDED
-Llmts_loop:
- PAUSE
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-Llmts_loop1:
- test $(M_MLOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
- jnz Llmts_fail
- test $(M_ILOCKED_MSK), LMTX_C_REG32
- jz Llmts_try
- jmp Llmts_loop
-
-Llmts_busy_disabled:
- PREEMPTION_ENABLE
- jmp Llmts_loop
-
-
-
-NONLEAF_ENTRY(lck_mtx_try_lock)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* is the interlock or mutex held */
- jnz Llmt_slow
-Llmt_try: /* no - can't be INDIRECT, DESTROYED or locked */
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne Llmt_busy_disabled
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- LMTX_CHK_EXTENDED_EXIT
-
- test $(M_WAITERS_MSK), LMTX_C_REG32
- jz 0f
-
- LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
-0:
- andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
- PREEMPTION_ENABLE
-
- leave
-#if CONFIG_DTRACE
- mov $1, LMTX_RET_REG /* return success */
- /* Dtrace probe: LS_LCK_MTX_TRY_LOCK_ACQUIRE */
- LOCKSTAT_LABEL(_lck_mtx_try_lock_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG from above */
- LOCKSTAT_RECORD(LS_LCK_MTX_TRY_LOCK_ACQUIRE, LMTX_REG)
-#endif
- mov $1, LMTX_RET_REG /* return success */
- ret
-
-Llmt_slow:
- test $(M_ILOCKED_MSK), LMTX_C_REG32 /* is the interlock held */
- jz Llmt_fail /* no, must be held as a mutex */
-
- cmp $(MUTEX_DESTROYED), LMTX_C_REG32 /* check to see if its marked destroyed */
- je lck_mtx_destroyed
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne Llmt_loop
-
- LMTX_ENTER_EXTENDED
-Llmt_loop:
- PAUSE
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-Llmt_loop1:
- test $(M_MLOCKED_MSK | M_SPIN_MSK), LMTX_C_REG32
- jnz Llmt_fail
- test $(M_ILOCKED_MSK), LMTX_C_REG32
- jz Llmt_try
- jmp Llmt_loop
-
-Llmt_busy_disabled:
- PREEMPTION_ENABLE
- jmp Llmt_loop
-
-
-Llmt_fail:
-Llmts_fail:
- LMTX_CHK_EXTENDED /* is this an extended mutex */
- je 0f
- LMTX_UPDATE_MISS
- LMTX_EXIT_EXTENDED
-0:
- xor LMTX_RET_REG, LMTX_RET_REG
- NONLEAF_RET
-
-
-
-NONLEAF_ENTRY(lck_mtx_convert_spin)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne 0f
- mov M_PTR(LMTX_REG), LMTX_REG /* If so, take indirection */
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-0:
- test $(M_MLOCKED_MSK), LMTX_C_REG32 /* already owned as a mutex, just return */
- jnz 2f
- test $(M_WAITERS_MSK), LMTX_C_REG32 /* are there any waiters? */
- jz 1f
-
- LMTX_CALLEXT1(lck_mtx_lock_acquire_x86)
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-1:
- and $(~(M_ILOCKED_MSK | M_SPIN_MSK)), LMTX_C_REG32 /* convert from spin version to mutex */
- or $(M_MLOCKED_MSK), LMTX_C_REG32
- mov LMTX_C_REG32, M_STATE(LMTX_REG) /* since I own the interlock, I don't need an atomic update */
-
- PREEMPTION_ENABLE
-2:
- NONLEAF_RET
-
-
-
-#if defined(__i386__)
-NONLEAF_ENTRY(lck_mtx_unlock)
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
- mov M_OWNER(LMTX_REG), LMTX_A_REG
- test LMTX_A_REG, LMTX_A_REG
- jnz Llmu_entry
- leave
- ret
-NONLEAF_ENTRY(lck_mtx_unlock_darwin10)
-#else
-NONLEAF_ENTRY(lck_mtx_unlock)
-#endif
- LOAD_LMTX_REG(B_ARG0) /* fetch lock pointer */
-Llmu_entry:
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-Llmu_prim:
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- je Llmu_ext
-
-Llmu_chktype:
- test $(M_MLOCKED_MSK), LMTX_C_REG32 /* check for full mutex */
- jz Llmu_unlock
-Llmu_mutex:
- test $(M_ILOCKED_MSK), LMTX_C_REG /* have to wait for interlock to clear */
- jnz Llmu_busy
-
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- and $(~M_MLOCKED_MSK), LMTX_C_REG32 /* drop mutex */
- or $(M_ILOCKED_MSK), LMTX_C_REG32 /* pick up interlock */
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne Llmu_busy_disabled /* branch on failure to spin loop */
-
-Llmu_unlock:
- xor LMTX_A_REG, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG)
- mov LMTX_C_REG, LMTX_A_REG /* keep original state in %ecx for later evaluation */
- and $(~(M_ILOCKED_MSK | M_SPIN_MSK | M_PROMOTED_MSK)), LMTX_A_REG
-
- test $(M_WAITERS_MSK), LMTX_A_REG32
- jz 2f
- dec LMTX_A_REG32 /* decrement waiter count */
-2:
- mov LMTX_A_REG32, M_STATE(LMTX_REG) /* since I own the interlock, I don't need an atomic update */
-
-#if MACH_LDEBUG
- /* perform lock statistics after drop to prevent delay */
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- decl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- test $(M_PROMOTED_MSK | M_WAITERS_MSK), LMTX_C_REG32
- jz 3f
-
- LMTX_CALLEXT2(lck_mtx_unlock_wakeup_x86, LMTX_C_REG)
-3:
- PREEMPTION_ENABLE
-
- LMTX_CHK_EXTENDED
- jne 4f
-
- leave
-#if CONFIG_DTRACE
- /* Dtrace: LS_LCK_MTX_UNLOCK_RELEASE */
- LOCKSTAT_LABEL(_lck_mtx_unlock_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG from above */
- LOCKSTAT_RECORD(LS_LCK_MTX_UNLOCK_RELEASE, LMTX_REG)
-#endif
- ret
-4:
- leave
-#if CONFIG_DTRACE
- /* Dtrace: LS_LCK_MTX_EXT_UNLOCK_RELEASE */
- LOCKSTAT_LABEL(_lck_mtx_ext_unlock_lockstat_patch_point)
- ret
- /* inherit lock pointer in LMTX_REG from above */
- LOCKSTAT_RECORD(LS_LCK_MTX_EXT_UNLOCK_RELEASE, LMTX_REG)
-#endif
- ret
-
-
-Llmu_busy_disabled:
- PREEMPTION_ENABLE
-Llmu_busy:
- PAUSE
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- jmp Llmu_mutex
-
-Llmu_ext:
- mov M_PTR(LMTX_REG), LMTX_REG
- mov M_OWNER(LMTX_REG), LMTX_A_REG
- mov %gs:CPU_ACTIVE_THREAD, LMTX_C_REG
- CHECK_UNLOCK(LMTX_C_REG, LMTX_A_REG)
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- jmp Llmu_chktype
-
-
-
-LEAF_ENTRY(lck_mtx_ilk_unlock)
- LOAD_LMTX_REG(L_ARG0) /* fetch lock pointer - no indirection here */
-
- andl $(~M_ILOCKED_MSK), M_STATE(LMTX_REG)
-
- PREEMPTION_ENABLE /* need to re-enable preemption */
-
- LEAF_RET
-
-
-
-LEAF_ENTRY(lck_mtx_lock_grab_mutex)
- LOAD_LMTX_REG(L_ARG0) /* fetch lock pointer - no indirection here */
-
- mov M_STATE(LMTX_REG), LMTX_C_REG32
-
- test $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32 /* can't have the mutex yet */
- jnz 3f
-
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK | M_MLOCKED_MSK), LMTX_C_REG32
-
- PREEMPTION_DISABLE
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne 2f /* branch on failure to spin loop */
-
- mov %gs:CPU_ACTIVE_THREAD, LMTX_A_REG
- mov LMTX_A_REG, M_OWNER(LMTX_REG) /* record owner of mutex */
-#if MACH_LDEBUG
- test LMTX_A_REG, LMTX_A_REG
- jz 1f
- incl TH_MUTEX_COUNT(LMTX_A_REG) /* lock statistic */
-1:
-#endif /* MACH_LDEBUG */
-
- mov $1, LMTX_RET_REG /* return success */
- LEAF_RET
-2:
- PREEMPTION_ENABLE
-3:
- xor LMTX_RET_REG, LMTX_RET_REG /* return failure */
- LEAF_RET
-
-
-
-LEAF_ENTRY(lck_mtx_lock_mark_destroyed)
- LOAD_LMTX_REG(L_ARG0)
-1:
- mov M_STATE(LMTX_REG), LMTX_C_REG32
- cmp $(MUTEX_IND), LMTX_C_REG32 /* Is this an indirect mutex? */
- jne 2f
-
- movl $(MUTEX_DESTROYED), M_STATE(LMTX_REG) /* convert to destroyed state */
- jmp 3f
-2:
- test $(M_ILOCKED_MSK), LMTX_C_REG /* have to wait for interlock to clear */
- jnz 5f
-
- PREEMPTION_DISABLE
- mov LMTX_C_REG, LMTX_A_REG /* eax contains snapshot for cmpxchgl */
- or $(M_ILOCKED_MSK), LMTX_C_REG32
- lock
- cmpxchg LMTX_C_REG32, M_STATE(LMTX_REG) /* atomic compare and exchange */
- jne 4f /* branch on failure to spin loop */
- movl $(MUTEX_DESTROYED), M_STATE(LMTX_REG) /* convert to destroyed state */
- PREEMPTION_ENABLE
-3:
- LEAF_RET /* return with M_ILOCKED set */
-4:
- PREEMPTION_ENABLE
-5:
- PAUSE
- jmp 1b
-
-LEAF_ENTRY(preemption_underflow_panic)
- FRAME
- incl %gs:CPU_PREEMPTION_LEVEL
- ALIGN_STACK()
- LOAD_STRING_ARG0(16f)