diff --git a/include/ucore/ticket_lock.h b/include/ucore/ticket_lock.h new file mode 100644 index 0000000..abaf3f1 --- /dev/null +++ b/include/ucore/ticket_lock.h @@ -0,0 +1,43 @@ +#ifndef UC_TICKET_LOCK_H_ +#define UC_TICKET_LOCK_H_ + +#include + +/** An fair alternative to a bare pthread_mutex_t to prevent starvation*/ +struct UCTicketLock { + pthread_cond_t cond; + pthread_mutex_t mutex; + unsigned long queue_head, queue_tail; +}; + +/** Static initializer for an UCTicketLock. Use as e.g + * + * struct UCTicketLock my_lock = UC_TICKET_LOCK_INITIALIZER; + */ +#define UC_TICKET_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,0, 0} + +/** Initialize a UCTicketLock. + * The default attributes for the pthread mutex and condition variable is used. + * + * @return 0 on success, errno value on failure +*/ +int uc_ticket_lock_init(struct UCTicketLock *lock); + +/** Initialize a UCTicketLock with attributes + * + * @param mattr attribute used for the mutex, or NULL + * @param cattr attribute used for the condition variable, or NULL + * + * @return 0 on success, errno value on failure +*/ +int uc_ticket_lock_init_ex(struct UCTicketLock *lock, pthread_mutexattr_t *mattr, pthread_condattr_t *cattr); + +/** Acquire the lock + */ +void uc_ticket_lock(struct UCTicketLock *lock); + +/** Release the lock + */ +void uc_ticket_unlock(struct UCTicketLock *lock); + +#endif diff --git a/src/ticket_lock.c b/src/ticket_lock.c new file mode 100644 index 0000000..b6b03f9 --- /dev/null +++ b/src/ticket_lock.c @@ -0,0 +1,49 @@ +#include +#include "ucore/ticket_lock.h" + +int uc_ticket_lock_init(struct UCTicketLock *lock) +{ + return uc_ticket_lock_init_ex(lock, NULL, NULL); +} + +int uc_ticket_lock_init_ex(struct UCTicketLock *lock, pthread_mutexattr_t *mattr, pthread_condattr_t *cattr) +{ + int rc; + lock->queue_head = 0; + lock->queue_tail = 0; + + rc = pthread_mutex_init(&lock->mutex, mattr); + assert(rc == 0); + if (rc != 0) { + return rc; + } + + rc = pthread_cond_init(&lock->cond, cattr); + if (rc != 0) { + pthread_mutex_destroy(&lock->mutex); + } + assert(rc == 0); + + return rc; +} + +void uc_ticket_lock(struct UCTicketLock *lock) +{ + unsigned long queue_me; + + pthread_mutex_lock(&lock->mutex); + queue_me = lock->queue_tail++; + while (queue_me != lock->queue_head) + { + pthread_cond_wait(&lock->cond, &lock->mutex); + } + pthread_mutex_unlock(&lock->mutex); +} + +void uc_ticket_unlock(struct UCTicketLock *lock) +{ + pthread_mutex_lock(&lock->mutex); + lock->queue_head++; + pthread_cond_broadcast(&lock->cond); + pthread_mutex_unlock(&lock->mutex); +}