lock.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #define _GNU_SOURCE
  2. #define _XOPEN_SOURCE 600
  3. #include "config.h"
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <stdint.h>
  7. #include <assert.h>
  8. #include <pthread.h>
  9. #include <stdint.h>
  10. #ifdef HAVE_STDATOMIC_H
  11. # include <stdatomic.h>
  12. #endif /* HAVE_STDATOMIC_H */
  13. #include "error.h"
  14. #include "lock.h"
  15. #include "pci.h"
  16. /**
  17. * structure to define a lock
  18. */
  19. struct pcilib_lock_s {
  20. pthread_mutex_t mutex; /**< the pthread robust mutex */
  21. pcilib_lock_flags_t flags; /**< flags to define the type of the mutex */
  22. #ifdef HAVE_STDATOMIC_H
  23. volatile atomic_uint refs; /**< approximate number of processes that hold the lock initialized, may desynchronize on crashes */
  24. #else /* HAVE_STDATOMIC_H */
  25. volatile uint32_t refs; /**< approximate number of processes that hold the lock initialized, may desynchronize on crashes */
  26. #endif /* HAVE_STDATOMIC_H */
  27. char name[]; /**< lock identifier */
  28. };
  29. int pcilib_init_lock(pcilib_lock_t *lock, pcilib_lock_flags_t flags, const char *lock_id) {
  30. int err;
  31. pthread_mutexattr_t attr;
  32. assert(lock);
  33. assert(lock_id);
  34. memset(lock, 0, PCILIB_LOCK_SIZE);
  35. if (strlen(lock_id) >= (PCILIB_LOCK_SIZE - offsetof(struct pcilib_lock_s, name))) {
  36. pcilib_error("The supplied lock id (%s) is too long...", lock_id);
  37. return PCILIB_ERROR_INVALID_ARGUMENT;
  38. }
  39. if ((err = pthread_mutexattr_init(&attr))!=0) {
  40. pcilib_error("Can't initialize mutex attribute, errno %i", errno);
  41. return PCILIB_ERROR_FAILED;
  42. }
  43. /* we declare the mutex as possibly shared amongst different processes*/
  44. if ((err = pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED))!=0) {
  45. pcilib_error("Can't configure a shared mutex attribute, errno %i", errno);
  46. return PCILIB_ERROR_FAILED;
  47. }
  48. /* we set the mutex as robust, so it would be automatically unlocked if the application crash*/
  49. if ((flags&PCILIB_LOCK_FLAG_PERSISTENT)==0) {
  50. if ((err = pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST))!=0) {
  51. pcilib_error("Can't configure a robust mutex attribute, errno: %i", errno);
  52. return PCILIB_ERROR_FAILED;
  53. }
  54. }
  55. if ((err = pthread_mutex_init(&lock->mutex, &attr))!=0) {
  56. pcilib_error("Can't create mutex, errno : %i",errno);
  57. return PCILIB_ERROR_FAILED;
  58. }
  59. strcpy(lock->name, lock_id);
  60. lock->refs = 1;
  61. lock->flags = flags;
  62. return 0;
  63. }
  64. void pcilib_free_lock(pcilib_lock_t *lock) {
  65. int err;
  66. assert(lock);
  67. // if (lock->refs)
  68. // pcilib_error("Forbidding to destroy the referenced mutex...");
  69. if ((err = pthread_mutex_destroy(&lock->mutex))==-1)
  70. pcilib_warning("Can't destroy POSIX mutex, errno %i",errno);
  71. }
  72. void pcilib_lock_ref(pcilib_lock_t *lock) {
  73. assert(lock);
  74. #ifdef HAVE_STDATOMIC_H
  75. atomic_fetch_add_explicit(&lock->refs, 1, memory_order_relaxed);
  76. #else /* HAVE_STDATOMIC_H */
  77. lock->refs++;
  78. #endif /* HAVE_STDATOMIC_H */
  79. }
  80. void pcilib_lock_unref(pcilib_lock_t *lock) {
  81. assert(lock);
  82. if (!lock->refs) {
  83. pcilib_warning("Lock is not referenced");
  84. return;
  85. }
  86. #ifdef HAVE_STDATOMIC_H
  87. atomic_fetch_sub_explicit(&lock->refs, 1, memory_order_relaxed);
  88. #else /* HAVE_STDATOMIC_H */
  89. lock->refs--;
  90. #endif /* HAVE_STDATOMIC_H */
  91. }
  92. size_t pcilib_lock_get_refs(pcilib_lock_t *lock) {
  93. return lock->refs;
  94. }
  95. pcilib_lock_flags_t pcilib_lock_get_flags(pcilib_lock_t *lock) {
  96. return lock->flags;
  97. }
  98. const char *pcilib_lock_get_name(pcilib_lock_t *lock) {
  99. assert(lock);
  100. if (lock->name[0]) return lock->name;
  101. return NULL;
  102. }
  103. int pcilib_lock_custom(pcilib_lock_t *lock, pcilib_lock_flags_t flags, pcilib_timeout_t timeout) {
  104. int err;
  105. if (!lock) {
  106. pcilib_error("The null lock pointer is passed to lock function");
  107. return PCILIB_ERROR_INVALID_ARGUMENT;
  108. }
  109. struct timespec tm;
  110. switch (timeout) {
  111. case PCILIB_TIMEOUT_INFINITE:
  112. /* the process will be hold till it can gain acquire the lock*/
  113. err = pthread_mutex_lock(&lock->mutex);
  114. break;
  115. case PCILIB_TIMEOUT_IMMEDIATE:
  116. /* the function returns immediatly if it can't acquire the lock*/
  117. err = pthread_mutex_trylock(&lock->mutex);
  118. break;
  119. default:
  120. /* the process will be hold till it can acquire the lock and timeout is not reached*/
  121. clock_gettime(CLOCK_REALTIME, &tm);
  122. tm.tv_nsec += 1000 * (timeout%1000000);
  123. if (tm.tv_nsec < 1000000000)
  124. tm.tv_sec += timeout/1000000;
  125. else {
  126. tm.tv_sec += 1 + timeout/1000000;
  127. tm.tv_nsec -= 1000000000;
  128. }
  129. err = pthread_mutex_timedlock(&lock->mutex, &tm);
  130. }
  131. if (!err)
  132. return 0;
  133. switch (err) {
  134. case EOWNERDEAD:
  135. /*in the case an application with a lock acquired crashes, this lock becomes inconsistent. we have so to make it consistent again to use it again.*/
  136. err = pthread_mutex_consistent(&lock->mutex);
  137. if (err) {
  138. pcilib_error("Failed to mark mutex as consistent, errno %i", err);
  139. break;
  140. }
  141. return 0;
  142. case ETIMEDOUT:
  143. case EBUSY:
  144. return PCILIB_ERROR_TIMEOUT;
  145. default:
  146. pcilib_error("Failed to obtain mutex, errno %i", err);
  147. }
  148. return PCILIB_ERROR_FAILED;
  149. }
  150. int pcilib_lock(pcilib_lock_t* lock) {
  151. return pcilib_lock_custom(lock, PCILIB_LOCK_FLAGS_DEFAULT, PCILIB_TIMEOUT_INFINITE);
  152. }
  153. int pcilib_try_lock(pcilib_lock_t* lock) {
  154. return pcilib_lock_custom(lock, PCILIB_LOCK_FLAGS_DEFAULT, PCILIB_TIMEOUT_IMMEDIATE);
  155. }
  156. void pcilib_unlock(pcilib_lock_t *lock) {
  157. int err;
  158. if (!lock)
  159. return;
  160. if ((err = pthread_mutex_unlock(&lock->mutex)) != 0) {
  161. switch (err) {
  162. case EPERM:
  163. pcilib_error("Trying to unlock not locked mutex (%s) or the mutex which was locked by a different thread", lock->name);
  164. break;
  165. default:
  166. pcilib_error("Can't unlock mutex, errno %i", err);
  167. }
  168. }
  169. }