core, lib, modules: restructured source code tree
[sip-router] / src / core / futexlock.h
1 /* 
2  * Copyright (C) 2007 iptelorg GmbH
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 /*!
17 * \file
18 * \brief Kamailio core :: locks
19 * \author andrei
20 * \ingroup core
21 * Module: \ref core
22  *
23  * futex based lock (mutex) implementation  (linux 2.6+ only)
24  * based on Ulrich Drepper implementation in "Futexes Are Tricky"
25  * (http://people.redhat.com/drepper/futex.pdf)
26  *
27  * Implements:
28  *   void futex_get(futex_lock_t* lock);     - mutex lock
29  *   void futex_release(futex_lock_t* lock); - unlock
30  *   int  futex_try(futex_lock_t* lock);     - tries to get lock, returns 0
31  *                                              on success and !=0 on failure
32  *                                              (1 or 2)
33  *
34  *  Config defines:
35  */
36
37 #ifndef _futexlock_h
38 #define _futexlock_h
39
40
41 #include "atomic/atomic_common.h"
42 #include "atomic/atomic_native.h"
43
44 #ifdef HAVE_ASM_INLINE_ATOMIC_OPS
45 #define HAVE_FUTEX
46 #include <sys/types.h> /* hack to workaround some type conflicts 
47                           between linux-libc-dev andlibc headers
48                           in recent (6.08.2008) x86_64 debian sid
49                           installations */
50 /* hack to work with old linux/futex.h versions, that depend on sched.h in
51    __KERNEL__ mode (futex.h < 2.6.20) */
52 #include <linux/types.h>
53 typedef __u32 u32;
54 struct task_struct;
55 /* end of the hack */
56 /* another hack this time for OpenSuse 10.2:
57    futex.h uses a __user attribute, which is defined in linux/compiler.h
58    However linux/compiler.h is not part of the kernel headers package in
59    most distributions. Instead they ship a modified linux/futex.h that does
60    not include <linux/compile.h> and does not user __user.
61 */
62 #ifndef __user
63 #define __user
64 #endif /* __user__*/
65 /* end of hack */
66 #include <linux/futex.h>
67 #include <sys/syscall.h>
68 #include <unistd.h>
69 #include "compiler_opt.h"
70
71 /* either syscall directly or #include <sys/linux/syscall.h> and use
72  * sys_futex directly */
73 #define sys_futex(addr, op, val, timeout, addr2, val3) \
74         syscall(__NR_futex , (addr), (op), (val), (timeout), (addr2), (val3))
75
76 typedef atomic_t futex_lock_t;
77
78 /* the mutex has 3 states: 0 - free/unlocked and nobody waiting
79  *                         1 - locked and nobody waiting for it
80  *                         2 - locked w/ 0 or more waiting processes/threads
81  */
82
83 inline static futex_lock_t* futex_init(futex_lock_t* lock)
84 {
85         atomic_set(lock, 0);
86         return lock;
87 }
88
89
90 inline static void futex_get(futex_lock_t* lock)
91 {
92         int v;
93 #ifdef ADAPTIVE_WAIT
94         register int i=ADAPTIVE_WAIT_LOOPS;
95         
96 retry:
97 #endif
98         
99         v=atomic_cmpxchg(lock, 0, 1); /* lock if 0 */
100         if (likely(v==0)){  /* optimize for the uncontended case */
101                 /* success */
102                 membar_enter_lock();
103                 return;
104         }else if (unlikely(v==2)){ /* if contended, optimize for the one waiter
105                                                                 case */
106                 /* waiting processes/threads => add ourselves to the queue */
107                 do{
108                         sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
109                         v=atomic_get_and_set(lock, 2);
110                 }while(v);
111         }else{
112                 /* v==1 */
113 #ifdef ADAPTIVE_WAIT
114                 if (i>0){
115                         i--;
116                         goto retry;
117                 }
118 #endif
119                 v=atomic_get_and_set(lock, 2);
120                 while(v){
121                         sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
122                         v=atomic_get_and_set(lock, 2);
123                 }
124         }
125         membar_enter_lock();
126 }
127
128
129 inline static void futex_release(futex_lock_t* lock)
130 {
131         int v;
132         
133         membar_leave_lock();
134         v=atomic_get_and_set(lock, 0);
135         if (unlikely(v==2)){ /* optimize for the uncontended case */
136                 sys_futex(&(lock)->val, FUTEX_WAKE, 1, 0, 0, 0);
137         }
138 }
139
140
141 static inline int futex_try(futex_lock_t* lock)
142 {
143         int c;
144         c=atomic_cmpxchg(lock, 0, 1);
145         if (likely(c==0))
146                 membar_enter_lock();
147         return c;
148 }
149
150
151 #else /*HAVE_ASM_INLINE_ATOMIC_OPS*/
152 #undef USE_FUTEX
153 #endif /*HAVE_ASM_INLINE_ATOMIC_OPS*/
154
155 #endif /* _futexlocks_h*/