summaryrefslogtreecommitdiff
path: root/hurd/futex.h
blob: e1e71a306a18701e34c4777279f566013f2cad33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/* futex.h - Futex definitions.
   Copyright (C) 2008 Free Software Foundation, Inc.
   Written by Neal H. Walfield <neal@gnu.org>.

   GNU Hurd is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   GNU Hurd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Hurd.  If not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _HURD_FUTEX_H
#define _HURD_FUTEX_H 1

#include <hurd/addr.h>
#include <hurd/startup.h>
#include <hurd/error.h>
#include <stdbool.h>
#define __need_timespec
#include <time.h>

/* The interface to the kernel futex implementation.  This is only
   here because glibc really wants futexes.  If this project gets
   sufficient momentum, the kernel futex implementation should be
   replaced with a more microkernel friendly approach to locks.  */

enum
  {
    RM_futex = 800,
  };

#define RPC_STUB_PREFIX rm
#define RPC_ID_PREFIX RM

#include <hurd/rpc.h>

/* Operations.  */
enum
  {
    FUTEX_WAIT,
    FUTEX_WAKE,
    FUTEX_WAKE_OP,
    FUTEX_CMP_REQUEUE,
#if 0
    /* We don't support these operations.  The first is deprecated and
       the second requires FDs which the kernel doesn't support.
       Although we could return EOPNOTSUPP, commenting them out
       catches any uses at compile-time.  */
    FUTEX_REQUEUE,
    FUTEX_FD,
#endif
  };

enum
  {
    FUTEX_OP_SET = 0,
    FUTEX_OP_ADD = 1,
    FUTEX_OP_OR = 2,
    FUTEX_OP_ANDN = 3,
    FUTEX_OP_XOR = 4
  };

enum
  {
    FUTEX_OP_CMP_EQ = 0,
    FUTEX_OP_CMP_NE = 1,
    FUTEX_OP_CMP_LT = 2,
    FUTEX_OP_CMP_LE = 3,
    FUTEX_OP_CMP_GT = 4,
    FUTEX_OP_CMP_GE = 5
  };

union futex_val2
{
  struct timespec timespec;
  int value;
};

union futex_val3
{
  int value;
  struct
  {
    int cmparg: 12;
    int oparg: 12;
    int cmp: 4;
    int op: 4;
  };
};
#define FUTEX_OP_CLEAR_WAKE_IF_GT_ONE \
  (union futex_val3) { { 1, 0, FUTEX_OP_CMP_GT, FUTEX_OP_SET } }

RPC (futex, 7, 1, 0,
     /* cap_t principal, cap_t thread, */
     void *, addr1, int, op, int, val1,
     bool, timeout, union futex_val2, val2,
     void *, addr2, union futex_val3, val3,
     /* Out: */
     long, out);

#undef RPC_STUB_PREFIX
#undef RPC_ID_PREFIX

#ifndef RM_INTERN
#include <errno.h>

struct futex_return
{
  error_t err;
  long ret;
};

static inline struct futex_return
__attribute__((always_inline))
futex_using (struct hurd_message_buffer *mb,
	     void *addr1, int op, int val1, struct timespec *timespec,
	     void *addr2, int val3)
{
  union futex_val2 val2;
  if (timespec)
    val2.timespec = *timespec;
  else
    __builtin_memset (&val2, 0, sizeof (val2));

  error_t err;
  long ret = 0; /* Elide gcc warning.  */
  if (mb)
    err = rm_futex_using (mb,
			  ADDR_VOID, ADDR_VOID,
			  addr1, op, val1, !! timespec, val2, addr2,
			  (union futex_val3) val3, &ret);
  else
    err = rm_futex (ADDR_VOID, ADDR_VOID,
		    addr1, op, val1, !! timespec, val2, addr2,
		    (union futex_val3) val3, &ret);
  return (struct futex_return) { err, ret };
}

/* Standard futex signatures.  See futex documentation, e.g., Futexes
   are Tricky by Ulrich Drepper.  */
static inline struct futex_return
__attribute__((always_inline))
futex (void *addr1, int op, int val1, struct timespec *timespec,
       void *addr2, int val3)
{
  return futex_using (NULL, addr1, op, val1, timespec, addr2, val3);
}


/* If *F is VAL, wait until woken.  */
static inline long
__attribute__((always_inline))
futex_wait_using (struct hurd_message_buffer *mb, int *f, int val)
{
  struct futex_return ret;
  ret = futex_using (mb, f, FUTEX_WAIT, val, NULL, 0, 0);
  if (ret.err)
    {
      errno = ret.err;
      return -1;
    }
  return ret.ret;
}

static inline long
__attribute__((always_inline))
futex_wait (int *f, int val)
{
  return futex_wait_using (NULL, f, val);
}


/* If *F is VAL, wait until woken.  */
static inline long
__attribute__((always_inline))
futex_timed_wait (int *f, int val, struct timespec *timespec)
{
  struct futex_return ret;
  ret = futex (f, FUTEX_WAIT, val, timespec, 0, 0);
  if (ret.err)
    {
      errno = ret.err;
      return -1;
    }
  return ret.ret;
}


/* Signal NWAKE waiters waiting on futex F.  */
static inline long
__attribute__((always_inline))
futex_wake_using (struct hurd_message_buffer *mb, int *f, int nwake)
{
  struct futex_return ret;
  ret = futex_using (mb, f, FUTEX_WAKE, nwake, NULL, 0, 0);
  if (ret.err)
    {
      errno = ret.err;
      return -1;
    }
  return ret.ret;
}

static inline long
__attribute__((always_inline))
futex_wake (int *f, int nwake)
{
  return futex_wake_using (NULL, f, nwake);
}
#endif /* !RM_INTERN */

#endif