cdp_avp: added README file
[sip-router] / futexlock.h
1 /* 
2  * $Id$
3  * 
4  * Copyright (C) 2007 iptelorg GmbH
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  * futex based lock (mutex) implementation  (linux 2.6+ only)
20  * based on Ulrich Drepper implementation in "Futexes Are Tricky"
21  * (http://people.redhat.com/drepper/futex.pdf)
22  *
23  * Implements:
24  *   void futex_get(futex_lock_t* lock);     - mutex lock
25  *   void futex_release(futex_lock_t* lock); - unlock
26  *   int  futex_try(futex_lock_t* lock);     - tries to get lock, returns 0
27  *                                              on success and !=0 on failure
28  *                                              (1 or 2)
29  *
30  *  Config defines:
31  */
32 /* 
33  * History:
34  * --------
35  *  2007-05-13  created by andrei
36  *  2007-06-12  added ADAPTIVE_WAIT busy waiting (andrei)
37  */
38
39 #ifndef _futexlock_h
40 #define _futexlock_h
41
42
43 #include "atomic/atomic_common.h"
44 #include "atomic/atomic_native.h"
45
46 #ifdef HAVE_ASM_INLINE_ATOMIC_OPS
47 #define HAVE_FUTEX
48 #include <sys/types.h> /* hack to workaround some type conflicts 
49                           between linux-libc-dev andlibc headers
50                           in recent (6.08.2008) x86_64 debian sid
51                           installations */
52 /* hack to work with old linux/futex.h versions, that depend on sched.h in
53    __KERNEL__ mode (futex.h < 2.6.20) */
54 #include <linux/types.h>
55 typedef __u32 u32;
56 struct task_struct;
57 /* end of the hack */
58 /* another hack this time for OpenSuse 10.2:
59    futex.h uses a __user attribute, which is defined in linux/compiler.h
60    However linux/compiler.h is not part of the kernel headers package in
61    most distributions. Instead they ship a modified linux/futex.h that does
62    not include <linux/compile.h> and does not user __user.
63 */
64 #ifndef __user
65 #define __user
66 #endif /* __user__*/
67 /* end of hack */
68 #include <linux/futex.h>
69 #include <sys/syscall.h>
70 #include <unistd.h>
71 #include "compiler_opt.h"
72
73 /* either syscall directly or #include <sys/linux/syscall.h> and use
74  * sys_futex directly */
75 #define sys_futex(addr, op, val, timeout, addr2, val3) \
76         syscall(__NR_futex , (addr), (op), (val), (timeout), (addr2), (val3))
77
78 typedef atomic_t futex_lock_t;
79
80 /* the mutex has 3 states: 0 - free/unlocked and nobody waiting
81  *                         1 - locked and nobody waiting for it
82  *                         2 - locked w/ 0 or more waiting processes/threads
83  */
84
85 inline static futex_lock_t* futex_init(futex_lock_t* lock)
86 {
87         atomic_set(lock, 0);
88         return lock;
89 }
90
91
92 inline static void futex_get(futex_lock_t* lock)
93 {
94         int v;
95 #ifdef ADAPTIVE_WAIT
96         register int i=ADAPTIVE_WAIT_LOOPS;
97         
98 retry:
99 #endif
100         
101         v=atomic_cmpxchg(lock, 0, 1); /* lock if 0 */
102         if (likely(v==0)){  /* optimize for the uncontended case */
103                 /* success */
104                 membar_enter_lock();
105                 return;
106         }else if (unlikely(v==2)){ /* if contended, optimize for the one waiter
107                                                                 case */
108                 /* waiting processes/threads => add ourselves to the queue */
109                 do{
110                         sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
111                         v=atomic_get_and_set(lock, 2);
112                 }while(v);
113         }else{
114                 /* v==1 */
115 #ifdef ADAPTIVE_WAIT
116                 if (i>0){
117                         i--;
118                         goto retry;
119                 }
120 #endif
121                 v=atomic_get_and_set(lock, 2);
122                 while(v){
123                         sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0);
124                         v=atomic_get_and_set(lock, 2);
125                 }
126         }
127         membar_enter_lock();
128 }
129
130
131 inline static void futex_release(futex_lock_t* lock)
132 {
133         int v;
134         
135         membar_leave_lock();
136         v=atomic_get_and_set(lock, 0);
137         if (unlikely(v==2)){ /* optimize for the uncontended case */
138                 sys_futex(&(lock)->val, FUTEX_WAKE, 1, 0, 0, 0);
139         }
140 }
141
142
143 static inline int futex_try(futex_lock_t* lock)
144 {
145         int c;
146         c=atomic_cmpxchg(lock, 0, 1);
147         if (likely(c))
148                 membar_enter_lock();
149         return c;
150 }
151
152
153 #else /*HAVE_ASM_INLINE_ATOMIC_OPS*/
154 #undef USE_FUTEX
155 #endif /*HAVE_ASM_INLINE_ATOMIC_OPS*/
156
157 #endif /* _futexlocks_h*/