lock_ops.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  183. return -1;
  184. }
  185. }
  186. return 0;
  187. }
  188. inline static void lock_get(gen_lock_t* lock)
  189. {
  190. struct sembuf sop;
  191. sop.sem_num=0;
  192. sop.sem_op=-1; /* down */
  193. sop.sem_flg=0;
  194. tryagain:
  195. if (semop(*lock, &sop, 1)==-1){
  196. if (errno==EINTR){
  197. DBG("lock_get: signal received while waiting for on a mutex\n");
  198. goto tryagain;
  199. }else{
  200. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  201. }
  202. }
  203. }
  204. inline static void lock_release(gen_lock_t* lock)
  205. {
  206. struct sembuf sop;
  207. sop.sem_num=0;
  208. sop.sem_op=1; /* up */
  209. sop.sem_flg=0;
  210. tryagain:
  211. if (semop(*lock, &sop, 1)==-1){
  212. if (errno==EINTR){
  213. /* very improbable*/
  214. DBG("lock_release: signal received while releasing a mutex\n");
  215. goto tryagain;
  216. }else{
  217. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  218. }
  219. }
  220. }
  221. #else
  222. #error "no locking method selected"
  223. #endif
  224. /* lock sets */
  225. #if defined(FAST_LOCK) || defined(USE_PTHREAD_MUTEX) || \
  226. defined(USE_POSIX_SEM) || defined(USE_FUTEX)
  227. #define GEN_LOCK_T_PREFERRED
  228. #define GEN_LOCK_T_PREFERED /* backwards compat. */
  229. #define GEN_LOCK_T_UNLIMITED
  230. #define GEN_LOCK_SET_T_UNLIMITED
  231. struct gen_lock_set_t_ {
  232. long size;
  233. gen_lock_t* locks;
  234. }; /* must be aligned (32 bits or 64 depending on the arch)*/
  235. typedef struct gen_lock_set_t_ gen_lock_set_t;
  236. #define lock_set_destroy(lock_set) /* do nothing */
  237. inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
  238. {
  239. int r;
  240. for (r=0; r<s->size; r++) if (lock_init(&s->locks[r])==0) return 0;
  241. return s;
  242. }
  243. /* WARNING: no boundary checks!*/
  244. #define lock_set_try(set, i) lock_try(&set->locks[i])
  245. #define lock_set_get(set, i) lock_get(&set->locks[i])
  246. #define lock_set_release(set, i) lock_release(&set->locks[i])
  247. #elif defined(USE_SYSV_SEM)
  248. #undef GEN_LOCK_T_PREFERRED
  249. #undef GEN_LOCK_T_PREFERED /* backwards compat. */
  250. #undef GEN_LOCK_T_UNLIMITED
  251. #undef GEN_LOCK_SET_T_UNLIMITED
  252. #define GEN_LOCK_T_LIMITED
  253. #define GEN_LOCK_SET_T_LIMITED
  254. struct gen_lock_set_t_ {
  255. int size;
  256. int semid;
  257. };
  258. typedef struct gen_lock_set_t_ gen_lock_set_t;
  259. inline static gen_lock_set_t* lock_set_init(gen_lock_set_t* s)
  260. {
  261. union semun su;
  262. int r;
  263. int euid;
  264. euid=geteuid();
  265. if (uid && uid!=euid)
  266. seteuid(uid); /* set euid to the cfg. requested one */
  267. s->semid=semget(IPC_PRIVATE, s->size, 0700);
  268. if (uid && uid!=euid)
  269. seteuid(euid); /* restore euid */
  270. if (s->semid==-1){
  271. LM_CRIT("(SYSV): semget (..., %d, 0700) failed: %s\n",
  272. s->size, strerror(errno));
  273. return 0;
  274. }
  275. su.val=1;
  276. for (r=0; r<s->size; r++){
  277. if (semctl(s->semid, r, SETVAL, su)==-1){
  278. LM_CRIT("(SYSV): semctl failed on sem %d: %s\n", r, strerror(errno));
  279. semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
  280. return 0;
  281. }
  282. }
  283. return s;
  284. }
  285. inline static void lock_set_destroy(gen_lock_set_t* s)
  286. {
  287. semctl(s->semid, 0, IPC_RMID, (union semun)(int)0);
  288. }
  289. /* returns 0 if it "gets" the lock, -1 otherwise */
  290. inline static int lock_set_try(gen_lock_set_t* s, int n)
  291. {
  292. struct sembuf sop;
  293. sop.sem_num=n;
  294. sop.sem_op=-1; /* down */
  295. sop.sem_flg=IPC_NOWAIT;
  296. tryagain:
  297. if (semop(s->semid, &sop, 1)==-1){
  298. if (errno==EAGAIN){
  299. return -1;
  300. }else if (errno==EINTR){
  301. DBG("lock_get: signal received while waiting for on a mutex\n");
  302. goto tryagain;
  303. }else{
  304. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  305. return -1;
  306. }
  307. }
  308. return 0;
  309. }
  310. inline static void lock_set_get(gen_lock_set_t* s, int n)
  311. {
  312. struct sembuf sop;
  313. sop.sem_num=n;
  314. sop.sem_op=-1; /* down */
  315. sop.sem_flg=0;
  316. tryagain:
  317. if (semop(s->semid, &sop, 1)==-1){
  318. if (errno==EINTR){
  319. DBG("lock_set_get: signal received while waiting on a mutex\n");
  320. goto tryagain;
  321. }else{
  322. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  323. }
  324. }
  325. }
  326. inline static void lock_set_release(gen_lock_set_t* s, int n)
  327. {
  328. struct sembuf sop;
  329. sop.sem_num=n;
  330. sop.sem_op=1; /* up */
  331. sop.sem_flg=0;
  332. tryagain:
  333. if (semop(s->semid, &sop, 1)==-1){
  334. if (errno==EINTR){
  335. /* very improbable */
  336. DBG("lock_set_release: signal received while releasing mutex\n");
  337. goto tryagain;
  338. }else{
  339. LM_CRIT("sysv: %s (%d)\n", strerror(errno), errno);
  340. }
  341. }
  342. }
  343. #else
  344. #error "no lock set method selected"
  345. #endif
  346. #endif