this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Mach lockset impl on top of Linux futex()

+256 -57
+1
src/libSystem/CMakeLists.txt
··· 88 88 kernel-mach/task.cpp 89 89 kernel-mach/time.cpp 90 90 kernel-mach/vm.cpp 91 + kernel-mach/Futex.cpp 91 92 ) 92 93 93 94 add_library(System.B.dylib SHARED ${libc_SRCS} ${bsdkern_SRCS} ${machkern_SRCS})
+123
src/libSystem/kernel-mach/Futex.cpp
··· 1 + #include "Futex.h" 2 + #include <unistd.h> 3 + #include <sys/syscall.h> 4 + #include <sys/time.h> 5 + #include <linux/futex.h> 6 + #include <cassert> 7 + #include "../../util/log.h" 8 + 9 + Darling::Futex::Futex() 10 + : m_lock(1), m_owner(0), m_readyToHandOff(0), m_handOffTarget(0) 11 + { 12 + } 13 + 14 + void Darling::Futex::acquire() 15 + { 16 + if (m_owner == gettid()) 17 + LOG << "Deadlock will now happen in Darling::Futex::acquire()\n"; 18 + while (true) 19 + { 20 + int result = __sync_sub_and_fetch(&m_lock, 1); 21 + if (result < 0) 22 + { 23 + // contention case 24 + LOG << gettid() << ": Contention with value " << result << std::endl; 25 + futex(&m_lock, FUTEX_WAIT, result); 26 + } 27 + else 28 + { 29 + LOG << gettid() << ": Lock acquired\n"; 30 + m_owner = gettid(); 31 + break; 32 + } 33 + } 34 + } 35 + 36 + void Darling::Futex::release() 37 + { 38 + assert(m_owner == gettid()); 39 + m_owner = 0; 40 + 41 + int result = __sync_add_and_fetch(&m_lock, 1); 42 + 43 + if (result != 1) 44 + { 45 + // contention case 46 + LOG << gettid() << ": Lock released, contended\n"; 47 + m_lock = 1; 48 + futex(&m_lock, FUTEX_WAKE, 2); 49 + } 50 + else 51 + LOG << gettid() << ": Lock released\n"; 52 + } 53 + 54 + Darling::Futex::TryAcquireResult Darling::Futex::try_acquire() 55 + { 56 + if (m_owner == gettid()) 57 + return ResultAlreadyOwned; 58 + 59 + bool success = __sync_bool_compare_and_swap(&m_lock, 1, 0); 60 + if (success) 61 + m_owner = gettid(); 62 + 63 + return success ? ResultOK : ResultLocked; 64 + } 65 + 66 + void Darling::Futex::handoff() 67 + { 68 + assert(m_owner == gettid()); 69 + 70 + // Wait for the handoff target 71 + while (true) 72 + { 73 + pid_t target = m_handOffTarget; 74 + if (target != 0) 75 + break; 76 + else 77 + futex(&m_handOffTarget, FUTEX_WAIT, target); 78 + } 79 + 80 + // Let the target know we're here 81 + m_readyToHandOff = 1; 82 + m_owner = m_handOffTarget; // Pass the ownership to the target 83 + futex(&m_readyToHandOff, FUTEX_WAKE, 1); 84 + } 85 + 86 + bool Darling::Futex::handoff_accept() 87 + { 88 + assert(m_owner != gettid()); 89 + 90 + if (m_handOffTarget != 0) 91 + return false; 92 + 93 + // Let the owner know we're here 94 + m_handOffTarget = gettid(); 95 + futex(&m_handOffTarget, FUTEX_WAKE, 1); 96 + 97 + // Wait for the owner 98 + while (true) 99 + { 100 + int ready = m_readyToHandOff; 101 + if (ready != 0) 102 + break; 103 + else 104 + futex(&m_readyToHandOff, FUTEX_WAIT, ready); 105 + } 106 + 107 + // Reset the variables 108 + m_handOffTarget = 0; 109 + m_readyToHandOff = 0; 110 + 111 + return true; 112 + } 113 + 114 + pid_t Darling::Futex::gettid() 115 + { 116 + return syscall(SYS_gettid); 117 + } 118 + 119 + int Darling::Futex::futex(int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) 120 + { 121 + return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); 122 + } 123 +
+47
src/libSystem/kernel-mach/Futex.h
··· 1 + #ifndef LINUX_FUTEX_H 2 + #define LINUX_FUTEX_H 3 + #include <sys/types.h> 4 + 5 + namespace Darling 6 + { 7 + 8 + class Futex 9 + { 10 + public: 11 + Futex(); 12 + 13 + // Lock the futex. 14 + void acquire(); 15 + 16 + // Unlock the futex. 17 + void release(); 18 + 19 + // Try lock the futex 20 + enum TryAcquireResult { ResultOK = 0, ResultAlreadyOwned = 1, ResultLocked = 2 }; 21 + TryAcquireResult try_acquire(); 22 + 23 + // Transfer the lock ownership to another thread calling handoff_accept(). 24 + void handoff(); 25 + 26 + // Accept the lock ownership transfer from another thread calling handoff(). 27 + // Returns false if there is another thread waiting for a handoff. 28 + bool handoff_accept(); 29 + 30 + // Returns the (thread) PID that currently owns the futex or zero 31 + inline pid_t owner() const { return m_owner; } 32 + 33 + // Performs the gettid system call. 34 + static pid_t gettid(); 35 + 36 + // Performs the futex system call. 37 + static int futex(int *uaddr, int op, int val, const struct timespec *timeout = nullptr, int *uaddr2 = nullptr, int val3 = 0); 38 + private: 39 + int m_lock; 40 + pid_t m_owner; 41 + int m_readyToHandOff; 42 + pid_t m_handOffTarget; 43 + }; 44 + 45 + } 46 + 47 + #endif
+26 -57
src/libSystem/kernel-mach/lockset.cpp
··· 6 6 #include <unistd.h> 7 7 #include "mach-stub.h" 8 8 #include "trace.h" 9 + #include "Futex.h" 9 10 10 11 struct lock_set 11 12 { 12 13 int lock_count; 13 - pthread_mutex_t* mutexes; 14 - pthread_t* anonymous_recipients; 14 + Darling::Futex* futexes; 15 15 }; 16 16 17 17 kern_return_t lock_acquire(lock_set_t lock_set, int lock_id) ··· 20 20 if (!lock_set || lock_id >= lock_set->lock_count) 21 21 return KERN_INVALID_ARGUMENT; 22 22 23 - if (::pthread_mutex_lock(lock_set->mutexes+lock_id)) 24 - return KERN_FAILURE; 23 + lock_set->futexes[lock_id].acquire(); 25 24 26 25 return KERN_SUCCESS; 27 26 } ··· 31 30 if (!lock_set || lock_id >= lock_set->lock_count) 32 31 return KERN_INVALID_ARGUMENT; 33 32 34 - MACH_STUB(); 33 + lock_set->futexes[lock_id].handoff(); 34 + 35 + return KERN_SUCCESS; 35 36 } 36 37 37 38 kern_return_t lock_handoff_accept(lock_set_t lock_set, int lock_id) 38 39 { 39 - MACH_STUB(); 40 + if (!lock_set || lock_id >= lock_set->lock_count) 41 + return KERN_INVALID_ARGUMENT; 42 + 43 + if (!lock_set->futexes[lock_id].handoff_accept()) 44 + return KERN_ALREADY_WAITING; 45 + else 46 + return KERN_SUCCESS; 40 47 } 41 48 42 49 kern_return_t lock_make_stable(lock_set_t lock_set, int lock_id) 43 50 { 44 - MACH_STUB(); 51 + // Whatever the hell is an unstable lock 52 + return KERN_SUCCESS; 45 53 } 46 54 47 55 kern_return_t lock_release(lock_set_t lock_set, int lock_id) ··· 51 59 if (!lock_set || lock_id >= lock_set->lock_count) 52 60 return KERN_INVALID_ARGUMENT; 53 61 54 - if (::pthread_mutex_unlock(lock_set->mutexes+lock_id)) 55 - { 56 - if (errno == EPERM) 57 - return KERN_INVALID_RIGHT; 58 - else 59 - return KERN_FAILURE; 60 - } 62 + lock_set->futexes[lock_id].release(); 61 63 62 64 return KERN_SUCCESS; 63 65 } ··· 77 79 return KERN_RESOURCE_SHORTAGE; 78 80 79 81 (*lockset)->lock_count = locks; 80 - (*lockset)->mutexes = 0; 81 - (*lockset)->anonymous_recipients = 0; 82 82 83 - (*lockset)->mutexes = new pthread_mutex_t[locks]; 84 - if (!(*lockset)->mutexes) 83 + (*lockset)->futexes = new Darling::Futex[locks]; 84 + if (!(*lockset)->futexes) 85 85 return KERN_RESOURCE_SHORTAGE; 86 86 87 - ::memset((*lockset)->mutexes, 0, sizeof(pthread_mutex_t)*locks); 88 - 89 - (*lockset)->anonymous_recipients = new pthread_t[locks]; 90 - if (!(*lockset)->anonymous_recipients) 91 - { 92 - lock_set_destroy(mach_task_self(), *lockset); 93 - return KERN_RESOURCE_SHORTAGE; 94 - } 95 - 96 - ::memset((*lockset)->anonymous_recipients, 0, sizeof(pthread_t) * locks); 97 - 98 - for (int i = 0; i < locks; i++) 99 - { 100 - if (::pthread_mutex_init((*lockset)->mutexes+i, 0)) 101 - { 102 - lock_set_destroy(mach_task_self(), *lockset); 103 - return KERN_RESOURCE_SHORTAGE; 104 - } 105 - } 106 - 107 87 return KERN_SUCCESS; 108 88 } 109 89 ··· 115 95 if (!lockset) 116 96 return KERN_INVALID_ARGUMENT; 117 97 118 - if (lockset->mutexes) 119 - { 120 - for (int i = 0; i < lockset->lock_count; i++) 121 - { 122 - //if (lockset->mutexes[i]) 123 - ::pthread_mutex_destroy(lockset->mutexes+i); 124 - } 125 - } 126 - 127 - delete [] lockset->mutexes; 128 - delete [] lockset->anonymous_recipients; 98 + delete [] lockset->futexes; 129 99 delete lockset; 130 100 131 101 return KERN_SUCCESS; ··· 137 107 if (!lock_set || lock_id >= lock_set->lock_count) 138 108 return KERN_INVALID_ARGUMENT; 139 109 140 - if (::pthread_mutex_trylock(lock_set->mutexes+lock_id)) 141 - { 142 - if (errno == EBUSY) 143 - return KERN_LOCK_OWNED; 144 - return KERN_FAILURE; 145 - } 146 - 147 - return KERN_SUCCESS; 110 + Darling::Futex::TryAcquireResult result = lock_set->futexes[lock_id].try_acquire(); 111 + if (result == Darling::Futex::ResultAlreadyOwned) 112 + return KERN_LOCK_OWNED_SELF; 113 + else if (result == Darling::Futex::ResultLocked) 114 + return KERN_LOCK_OWNED; 115 + else 116 + return KERN_SUCCESS; 148 117 } 149 118
+59
tests/src/mach_lockset_basic.c
··· 1 + // CFLAGS: -std=c99 2 + #include <mach/task.h> 3 + #include <mach/lock_set.h> 4 + #include <mach/sync_policy.h> 5 + #include <mach/mach_init.h> 6 + #include <pthread.h> 7 + #include <assert.h> 8 + 9 + #define NUM_THREADS 30 10 + #define NUM_CYCLES 10000 11 + 12 + void* contender(void* p); 13 + 14 + lock_set_t g_locks; 15 + int g_variable = 0; 16 + long long g_nlocks = 0; 17 + 18 + int main() 19 + { 20 + int rv; 21 + pthread_t threads[NUM_THREADS]; 22 + 23 + rv = lock_set_create(mach_task_self(), &g_locks, 1, SYNC_POLICY_FIFO); 24 + 25 + assert(rv == KERN_SUCCESS); 26 + 27 + for (int i = 0; i < NUM_THREADS; i++) 28 + pthread_create(&threads[i], NULL, contender, NULL); 29 + 30 + for (int i = 0; i < NUM_THREADS; i++) 31 + pthread_join(threads[i], NULL); 32 + 33 + lock_set_destroy(mach_task_self(), g_locks); 34 + printf("Done, %ld locks done\n", g_nlocks); 35 + return 0; 36 + } 37 + 38 + void* contender(void* p) 39 + { 40 + for (int i = 0; i < NUM_CYCLES; i++) 41 + { 42 + int rv; 43 + rv = lock_acquire(g_locks, 0); 44 + assert(rv == KERN_SUCCESS); 45 + 46 + rv = __sync_add_and_fetch(&g_variable, 1); 47 + assert(rv == 1); 48 + 49 + rv = __sync_sub_and_fetch(&g_variable, 1); 50 + assert(rv == 0); 51 + g_nlocks++; 52 + 53 + rv = lock_release(g_locks, 0); 54 + assert(rv == KERN_SUCCESS); 55 + } 56 + 57 + return NULL; 58 + } 59 +