lock_ops.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /* $Id$ */
  2. /*
  3. *
  4. * Copyright (C) 2001-2003 FhG Fokus
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. /*
  19. * ser locking library
  20. * WARNING: do not include this file directly, use instead locking.h
  21. * (unless you don't need to alloc/dealloc locks)
  22. *
  23. * 2002-12-16 created by andrei
  24. * 2003-02-20 s/gen_lock_t/gen_lock_t/ to avoid a type conflict
  25. * on solaris (andrei)
  26. * 2003-03-05 lock set support added for FAST_LOCK & SYSV (andrei)
  27. * 2003-03-06 removed *_alloc,*_dealloc & moved them to lock_alloc.h
  28. * renamed locking.h to lock_ops.h (all this to solve
  29. * the locking.h<->shm_mem.h interdependency) (andrei)
  30. * 2003-03-10 lock set support added also for PTHREAD_MUTEX & POSIX_SEM
  31. * (andrei)
  32. * 2003-03-17 possible signal interruptions treated for sysv (andrei)
  33. * 2004-07-28 s/lock_set_t/gen_lock_set_t/ because of a type conflict
  34. * on darwin (andrei)
  35. * 2006-04-04 added lock_try(lock) and lock_set_try(s,i) (andrei)
  36. * 2007-05-13 added futex support (andrei)
  37. *
  38. Implements:
  39. simple locks:
  40. -------------
  41. gen_lock_t* lock_init(gen_lock_t* lock); - inits the lock
  42. void lock_destroy(gen_lock_t* lock); - removes the lock (e.g sysv rmid)
  43. void lock_get(gen_lock_t* lock); - lock (mutex down)
  44. void lock_release(gen_lock_t* lock); - unlock (mutex up)
  45. int lock_try(gen_lock_t* lock); - tries to get the lock, returns
  46. 0 on success and !=0 on failure
  47. lock sets:
  48. ----------
  49. gen_lock_set_t* lock_set_init(gen_lock_set_t* set); - inits the lock set
  50. void lock_set_destroy(gen_lock_set_t* s); - removes the lock set
  51. void lock_set_get(gen_lock_set_t* s, int i); - locks sem i from the set
  52. void lock_set_release(gen_lock_set_t* s, int i) - unlocks sem i from the
  53. set
  54. int lock_set_try(gen_lock_set_t* s, int i); - tries to lock the sem i,
  55. returns 0 on success and
  56. !=0 on failure
  57. defines:
  58. --------
  59. GEN_LOCK_T_PREFERRED - defined if using arrays of gen_lock_t is as good as
  60. using a lock set (gen_lock_set_t).
  61. In general is better to have the locks "close" or
  62. inside the protected data structure rather then
  63. having a separate array or lock set. However in some
  64. case (e.g. SYSV_LOCKS) is better to use lock sets,
  65. either due to lock number limitations, excesive
  66. performance or memory overhead. In this cases
  67. GEN_LOCK_T_PREFERRED will not be defined.
  68. GEN_LOCK_T_UNLIMITED - defined if there is no system imposed limit on
  69. the number of locks (other then the memory).
  70. GEN_LOCK_SET_T_UNLIMITED
  71. - like above but for the size of a lock set.
  72. WARNING: - lock_set_init may fail for large number of sems (e.g. sysv).
  73. - signals are not treated! (some locks are "awakened" by the signals)
  74. */
  75. #ifndef _lock_ops_h
  76. #define _lock_ops_h
  77. #ifdef USE_FUTEX
  78. #include "futexlock.h"
  79. /* if no native atomic ops support => USE_FUTEX will be undefined */
  80. #endif
  81. #ifdef USE_FUTEX
  82. typedef futex_lock_t gen_lock_t;
  83. #define lock_destroy(lock) /* do nothing */
  84. #define lock_init(lock) futex_init(lock)
  85. #define lock_try(lock) futex_try(lock)
  86. #define lock_get(lock) futex_get(lock)
  87. #define lock_release(lock) futex_release(lock)
  88. #elif defined FAST_LOCK
  89. #include "fastlock.h"
  90. typedef fl_lock_t gen_lock_t;
  91. #define lock_destroy(lock) /* do nothing */
  92. inline static gen_lock_t* lock_init(gen_lock_t* lock)
  93. {
  94. init_lock(*lock);
  95. return lock;
  96. }
  97. #define lock_try(lock) try_lock(lock)
  98. #define lock_get(lock) get_lock(lock)
  99. #define lock_release(lock) release_lock(lock)
  100. #elif defined USE_PTHREAD_MUTEX
  101. #include <pthread.h>
  102. typedef pthread_mutex_t gen_lock_t;
  103. #define lock_destroy(lock) /* do nothing */
  104. inline static gen_lock_t* lock_init(gen_lock_t* lock)
  105. {
  106. if (pthread_mutex_init(lock, 0)==0) return lock;
  107. else return 0;
  108. }
  109. #define lock_try(lock) pthread_mutex_trylock(lock)
  110. #define lock_get(lock) pthread_mutex_lock(lock)
  111. #define lock_release(lock) pthread_mutex_unlock(lock)
  112. #elif defined USE_POSIX_SEM
  113. #include <semaphore.h>
  114. typedef sem_t gen_lock_t;
  115. #define lock_destroy(lock) /* do nothing */
  116. inline static gen_lock_t* lock_init(gen_lock_t* lock)
  117. {
  118. if (sem_init(lock, 1, 1)<0) return 0;
  119. return lock;
  120. }
  121. #define lock_try(lock) sem_trywait(lock)
  122. #define lock_get(lock) sem_wait(lock)
  123. #define lock_release(lock) sem_post(lock)
  124. #elif defined USE_SYSV_SEM
  125. #include <sys/ipc.h>
  126. #include <sys/sem.h>
  127. #include <errno.h>
  128. #include <string.h>
  129. #include <sys/types.h>
  130. #include <unistd.h>
  131. #include "dprint.h"
  132. #include "globals.h" /* uid */
  133. #if ((defined(HAVE_UNION_SEMUN) || defined(__GNU_LIBRARY__) )&& !defined(_SEM_SEMUN_UNDEFINED))
  134. /* union semun is defined by including sem.h */
  135. #else
  136. /* according to X/OPEN we have to define it ourselves */
  137. union semun {
  138. int val; /* value for SETVAL */
  139. struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
  140. unsigned short int *array; /* array for GETALL, SETALL */
  141. struct seminfo *__buf; /* buffer for IPC_INFO */
  142. };
  143. #endif
  144. typedef int gen_lock_t;
  145. inline static gen_lock_t* lock_init(gen_lock_t* lock)
  146. {
  147. union semun su;
  148. int euid;
  149. euid=geteuid();
  150. if (uid && uid!=euid)
  151. seteuid(uid); /* set euid to the cfg. requested one */
  152. *lock=semget(IPC_PRIVATE, 1, 0700);
  153. if (uid && uid!=euid)
  154. seteuid(euid); /* restore it */
  155. if (*lock==-1) return 0;
  156. su.val=1;
  157. if (semctl(*lock, 0, SETVAL, su)==-1){
  158. /* init error*/
  159. return 0;
  160. }
  161. return lock;
  162. }
  163. inline static void lock_destroy(gen_lock_t* lock)
  164. {
  165. semctl(*lock, 0, IPC_RMID, (union semun)(int)0);
  166. }
  167. /* returns 0 if it got the lock, -1 otherwise */
  168. inline static int lock_try(gen_lock_t* lock)
  169. {
  170. struct sembuf sop;
  171. sop.sem_num=0;
  172. sop.sem_op=-1; /* down */
  173. sop.sem_flg=IPC_NOWAIT;
  174. tryagain:
  175. if (semop(*lock, &sop, 1)==-1){
  176. if (errno==EAGAIN){
  177. return -1;
  178. }else if (errno==EINTR){
  179. DBG("lock_get: signal received while waiting for on a mutex\n");
  180. goto tryagain;
  181. }else{
  182. LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
  183. errno);
  184. return -1;
  185. }
  186. }
  187. return 0;
  188. }
  189. inline static void lock_get(gen_lock_t* lock)
  190. {
  191. struct sembuf sop;
  192. sop.sem_num=0;
  193. sop.sem_op=-1; /* down */
  194. sop.sem_flg=0;
  195. tryagain:
  196. if (semop(*lock, &sop, 1)==-1){
  197. if (errno==EINTR){
  198. DBG("lock_get: signal received while waiting for on a mutex\n");
  199. goto tryagain;
  200. }else{
  201. LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
  202. errno);
  203. }
  204. }
  205. }
  206. inline static void lock_release(gen_lock_t* lock)
  207. {
  208. struct sembuf sop;
  209. sop.sem_num=0;
  210. sop.sem_op=1; /* up */
  211. sop.sem_flg=0;
  212. tryagain:
  213. if (semop(*lock, &sop, 1)==-1){
  214. if (errno==EINTR){
  215. /* very improbable*/
  216. DBG("lock_release: signal received while releasing a mutex\n");
  217. goto tryagain;
  218. }else{
  219. LOG(L_CRIT, "ERROR: lock_release sysv: %s (%d)\n",
  220. strerror(errno), errno);
  221. }
  222. }
  223. }
  224. #else
  225. #error "no locking method selected"
  226. #endif
  227. /* lock sets */
  228. #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || \
  229. defined(USE_POSIX_SEM) || defined(USE_FUTEX)
  230. #define GEN_LOCK_T_PREFERRED
  231. #define GEN_LOCK_T_PREFERED /* backwards compat. */
  232. #define GEN_LOCK_T_UNLIMITED
  233. #define GEN_LOCK_SET_T_UNLIMITED
  234. struct gen_lock_set_t_ {
  235. long size;
  236. gen_lock_t* locks;
  237. }; /* must be aligned (32 bits or 64 depending on the arch)*/
  238. typedef struct gen_lock_set_t_ gen_lock_set_t;
  239. #define lock_set_destroy(lock_set) /* do nothing */
  240. inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
  241. {
  242. int r;
  243. for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
  244. return s;
  245. }
  246. /* WARNING: no boundary checks!*/
  247. #define lock_set_try(set, i) lock_try(&set->locks[i])
  248. #define lock_set_get(set, i) lock_get(&set->locks[i])
  249. #define lock_set_release(set, i) lock_release(&set->locks[i])
  250. #elif defined(USE_SYSV_SEM)
  251. #undef GEN_LOCK_T_PREFERRED
  252. #undef GEN_LOCK_T_PREFERED /* backwards compat. */
  253. #undef GEN_LOCK_T_UNLIMITED
  254. #undef GEN_LOCK_SET_T_UNLIMITED
  255. #define GEN_LOCK_T_LIMITED
  256. #define GEN_LOCK_SET_T_LIMITED
  257. struct gen_lock_set_t_ {
  258. int size;
  259. int semid;
  260. };
  261. typedef struct gen_lock_set_t_ gen_lock_set_t;
  262. inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
  263. {
  264. union semun su;
  265. int r;
  266. int euid;
  267. euid=geteuid();
  268. if (uid && uid!=euid)
  269. seteuid(uid); /* set euid to the cfg. requested one */
  270. s->semid=semget(IPC_PRIVATE, s->size, 0700);
  271. if (uid && uid!=euid)
  272. seteuid(euid); /* restore euid */
  273. if (s->semid==-1){
  274. LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semget (..., %d, 0700)"
  275. " failed: %s\n",
  276. s->size, strerror(errno));
  277. return 0;
  278. }
  279. su.val=1;
  280. for (r=0; r<s->size; r++){
  281. if (semctl(s->semid, r, SETVAL, su)==-1){
  282. LOG(L_CRIT, "ERROR: lock_set_init (SYSV): semctl failed on sem %d"
  283. ": %s\n", r, strerror(errno));
  284. semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
  285. return 0;
  286. }
  287. }
  288. return s;
  289. }
  290. inline static void lock_set_destroy(gen_lock_set_t* s)
  291. {
  292. semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
  293. }
  294. /* returns 0 if it "gets" the lock, -1 otherwise */
  295. inline static int lock_set_try(gen_lock_set_t* s, int n)
  296. {
  297. struct sembuf sop;
  298. sop.sem_num=n;
  299. sop.sem_op=-1; /* down */
  300. sop.sem_flg=IPC_NOWAIT;
  301. tryagain:
  302. if (semop(s->semid, &sop, 1)==-1){
  303. if (errno==EAGAIN){
  304. return -1;
  305. }else if (errno==EINTR){
  306. DBG("lock_get: signal received while waiting for on a mutex\n");
  307. goto tryagain;
  308. }else{
  309. LOG(L_CRIT, "ERROR: lock_get sysv: %s (%d)\n", strerror(errno),
  310. errno);
  311. return -1;
  312. }
  313. }
  314. return 0;
  315. }
  316. inline static void lock_set_get(gen_lock_set_t* s, int n)
  317. {
  318. struct sembuf sop;
  319. sop.sem_num=n;
  320. sop.sem_op=-1; /* down */
  321. sop.sem_flg=0;
  322. tryagain:
  323. if (semop(s->semid, &sop, 1)==-1){
  324. if (errno==EINTR){
  325. DBG("lock_set_get: signal received while waiting on a mutex\n");
  326. goto tryagain;
  327. }else{
  328. LOG(L_CRIT, "ERROR: lock_set_get sysv: %s (%d)\n",
  329. strerror(errno), errno);
  330. }
  331. }
  332. }
  333. inline static void lock_set_release(gen_lock_set_t* s, int n)
  334. {
  335. struct sembuf sop;
  336. sop.sem_num=n;
  337. sop.sem_op=1; /* up */
  338. sop.sem_flg=0;
  339. tryagain:
  340. if (semop(s->semid, &sop, 1)==-1){
  341. if (errno==EINTR){
  342. /* very improbable */
  343. DBG("lock_set_release: signal received while releasing mutex\n");
  344. goto tryagain;
  345. }else{
  346. LOG(L_CRIT, "ERROR: lock_set_release sysv: %s (%d)\n",
  347. strerror(errno), errno);
  348. }
  349. }
  350. }
  351. #else
  352. #error "no lock set method selected"
  353. #endif
  354. #endif