blob: ba19520339edaa69faba1ef0ea7e6bf6acf3cbf4 [file] [log] [blame]
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <limits.h>
19
20#include <sys/time.h>
21#include <sched.h>
22
23#include <errno.h>
24
25#include <private/utils/futex_synchro.h>
26
27
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -080028// This futex glue code is need on desktop linux, but is already part of bionic.
29#if !defined(HAVE_FUTEX_WRAPPERS)
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070030
31#include <sys/syscall.h>
32typedef unsigned int u32;
33#define asmlinkage
34#define __user
35#include <linux/futex.h>
36#include <utils/Atomic.h>
37
38
39int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3)
40{
41 int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
42 return err == 0 ? 0 : -errno;
43}
44
45int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout)
46{
47 return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0);
48}
49
50int __futex_wake(volatile void *ftx, int count)
51{
52 return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0);
53}
54
55int __atomic_cmpxchg(int old, int _new, volatile int *ptr)
56{
57 return android_atomic_cmpxchg(old, _new, ptr);
58}
59
60int __atomic_swap(int _new, volatile int *ptr)
61{
62 return android_atomic_swap(_new, ptr);
63}
64
65int __atomic_dec(volatile int *ptr)
66{
67 return android_atomic_dec(ptr);
68}
69
70#else // !defined(__arm__)
71
72int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
73int __futex_wake(volatile void *ftx, int count);
74
75int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
76int __atomic_swap(int _new, volatile int *ptr);
77int __atomic_dec(volatile int *ptr);
78
The Android Open Source Projecte09fd9e2008-12-17 18:05:43 -080079#endif // !defined(HAVE_FUTEX_WRAPPERS)
The Android Open Source Project7c1b96a2008-10-21 07:00:00 -070080
81
82// lock states
83//
84// 0: unlocked
85// 1: locked, no waiters
86// 2: locked, maybe waiters
87
88void futex_mutex_init(futex_mutex_t *m)
89{
90 m->value = 0;
91}
92
93int futex_mutex_lock(futex_mutex_t *m, unsigned msec)
94{
95 if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
96 return 0;
97 }
98 if(msec == FUTEX_WAIT_INFINITE) {
99 while(__atomic_swap(2, &m->value) != 0) {
100 __futex_wait(&m->value, 2, 0);
101 }
102 } else {
103 struct timespec ts;
104 ts.tv_sec = msec / 1000;
105 ts.tv_nsec = (msec % 1000) * 1000000;
106 while(__atomic_swap(2, &m->value) != 0) {
107 if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) {
108 return -1;
109 }
110 }
111 }
112 return 0;
113}
114
115int futex_mutex_trylock(futex_mutex_t *m)
116{
117 if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
118 return 0;
119 }
120 return -1;
121}
122
123void futex_mutex_unlock(futex_mutex_t *m)
124{
125 if(__atomic_dec(&m->value) != 1) {
126 m->value = 0;
127 __futex_wake(&m->value, 1);
128 }
129}
130
131/* XXX *technically* there is a race condition that could allow
132 * XXX a signal to be missed. If thread A is preempted in _wait()
133 * XXX after unlocking the mutex and before waiting, and if other
134 * XXX threads call signal or broadcast UINT_MAX times (exactly),
135 * XXX before thread A is scheduled again and calls futex_wait(),
136 * XXX then the signal will be lost.
137 */
138
139void futex_cond_init(futex_cond_t *c)
140{
141 c->value = 0;
142}
143
144int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec)
145{
146 if(msec == FUTEX_WAIT_INFINITE){
147 int oldvalue = c->value;
148 futex_mutex_unlock(m);
149 __futex_wait(&c->value, oldvalue, 0);
150 futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
151 return 0;
152 } else {
153 int oldvalue = c->value;
154 struct timespec ts;
155 ts.tv_sec = msec / 1000;
156 ts.tv_nsec = (msec % 1000) * 1000000;
157 futex_mutex_unlock(m);
158 const int err = __futex_wait(&c->value, oldvalue, &ts);
159 futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
160 return err;
161 }
162}
163
164void futex_cond_signal(futex_cond_t *c)
165{
166 __atomic_dec(&c->value);
167 __futex_wake(&c->value, 1);
168}
169
170void futex_cond_broadcast(futex_cond_t *c)
171{
172 __atomic_dec(&c->value);
173 __futex_wake(&c->value, INT_MAX);
174}
175