00001 /* 00002 * $Id$ 00003 * 00004 * Copyright (C) 2007 iptelorg GmbH 00005 * 00006 * Permission to use, copy, modify, and distribute this software for any 00007 * purpose with or without fee is hereby granted, provided that the above 00008 * copyright notice and this permission notice appear in all copies. 00009 * 00010 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 00011 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00012 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 00013 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00014 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00015 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00016 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00017 */ 00018 /* 00019 * futex based lock (mutex) implementation (linux 2.6+ only) 00020 * based on Ulrich Drepper implementation in "Futexes Are Tricky" 00021 * (http://people.redhat.com/drepper/futex.pdf) 00022 * 00023 * Implements: 00024 * void futex_get(futex_lock_t* lock); - mutex lock 00025 * void futex_release(futex_lock_t* lock); - unlock 00026 * int futex_try(futex_lock_t* lock); - tries to get lock, returns 0 00027 * on success and !=0 on failure 00028 * (1 or 2) 00029 * 00030 * Config defines: 00031 */ 00032 /* 00033 * History: 00034 * -------- 00035 * 2007-05-13 created by andrei 00036 * 2007-06-12 added ADAPTIVE_WAIT busy waiting (andrei) 00037 */ 00038 00039 #ifndef _futexlock_h 00040 #define _futexlock_h 00041 00042 00043 #include "atomic/atomic_common.h" 00044 #include "atomic/atomic_native.h" 00045 00046 #ifdef HAVE_ASM_INLINE_ATOMIC_OPS 00047 #define HAVE_FUTEX 00048 #include <sys/types.h> /* hack to workaround some type conflicts 00049 between linux-libc-dev andlibc headers 00050 in recent (6.08.2008) x86_64 debian sid 00051 installations */ 00052 /* hack to work with old linux/futex.h versions, that depend on sched.h in 00053 __KERNEL__ mode (futex.h < 2.6.20) */ 00054 #include <linux/types.h> 00055 typedef __u32 u32; 00056 struct task_struct; 00057 /* end of the hack */ 00058 /* another hack this time for OpenSuse 10.2: 00059 futex.h uses a __user attribute, which is defined in linux/compiler.h 00060 However linux/compiler.h is not part of the kernel headers package in 00061 most distributions. Instead they ship a modified linux/futex.h that does 00062 not include <linux/compile.h> and does not user __user. 00063 */ 00064 #ifndef __user 00065 #define __user 00066 #endif /* __user__*/ 00067 /* end of hack */ 00068 #include <linux/futex.h> 00069 #include <sys/syscall.h> 00070 #include <unistd.h> 00071 #include "compiler_opt.h" 00072 00073 /* either syscall directly or #include <sys/linux/syscall.h> and use 00074 * sys_futex directly */ 00075 #define sys_futex(addr, op, val, timeout, addr2, val3) \ 00076 syscall(__NR_futex , (addr), (op), (val), (timeout), (addr2), (val3)) 00077 00078 typedef atomic_t futex_lock_t; 00079 00080 /* the mutex has 3 states: 0 - free/unlocked and nobody waiting 00081 * 1 - locked and nobody waiting for it 00082 * 2 - locked w/ 0 or more waiting processes/threads 00083 */ 00084 00085 inline static futex_lock_t* futex_init(futex_lock_t* lock) 00086 { 00087 atomic_set(lock, 0); 00088 return lock; 00089 } 00090 00091 00092 inline static void futex_get(futex_lock_t* lock) 00093 { 00094 int v; 00095 #ifdef ADAPTIVE_WAIT 00096 register int i=ADAPTIVE_WAIT_LOOPS; 00097 00098 retry: 00099 #endif 00100 00101 v=atomic_cmpxchg(lock, 0, 1); /* lock if 0 */ 00102 if (likely(v==0)){ /* optimize for the uncontended case */ 00103 /* success */ 00104 membar_enter_lock(); 00105 return; 00106 }else if (unlikely(v==2)){ /* if contended, optimize for the one waiter 00107 case */ 00108 /* waiting processes/threads => add ourselves to the queue */ 00109 do{ 00110 sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0); 00111 v=atomic_get_and_set(lock, 2); 00112 }while(v); 00113 }else{ 00114 /* v==1 */ 00115 #ifdef ADAPTIVE_WAIT 00116 if (i>0){ 00117 i--; 00118 goto retry; 00119 } 00120 #endif 00121 v=atomic_get_and_set(lock, 2); 00122 while(v){ 00123 sys_futex(&(lock)->val, FUTEX_WAIT, 2, 0, 0, 0); 00124 v=atomic_get_and_set(lock, 2); 00125 } 00126 } 00127 membar_enter_lock(); 00128 } 00129 00130 00131 inline static void futex_release(futex_lock_t* lock) 00132 { 00133 int v; 00134 00135 membar_leave_lock(); 00136 v=atomic_get_and_set(lock, 0); 00137 if (unlikely(v==2)){ /* optimize for the uncontended case */ 00138 sys_futex(&(lock)->val, FUTEX_WAKE, 1, 0, 0, 0); 00139 } 00140 } 00141 00142 00143 static inline int futex_try(futex_lock_t* lock) 00144 { 00145 int c; 00146 c=atomic_cmpxchg(lock, 0, 1); 00147 if (likely(c)) 00148 membar_enter_lock(); 00149 return c; 00150 } 00151 00152 00153 #else /*HAVE_ASM_INLINE_ATOMIC_OPS*/ 00154 #undef USE_FUTEX 00155 #endif /*HAVE_ASM_INLINE_ATOMIC_OPS*/ 00156 00157 #endif /* _futexlocks_h*/
1.7.1