File Coverage

deque.h
Criterion Covered Total %
statement 222 257 86.3
branch 102 218 46.7
condition n/a
subroutine n/a
pod n/a
total 324 475 68.2


line stmt bran cond sub pod time code
1             /*
2             * deque.h -- Fixed-size shared-memory double-ended queue for Linux
3             *
4             * Ring buffer with CAS-based push/pop at both ends.
5             * Futex blocking when empty or full.
6             *
7             * Head and tail are monotonic uint64_t. Size = tail - head (unsigned).
8             * Slot index = cursor % capacity.
9             */
10              
11             #ifndef DEQUE_H
12             #define DEQUE_H
13              
14             #include
15             #include
16             #include
17             #include
18             #include
19             #include
20             #include
21             #include
22             #include
23             #include
24             #include
25             #include
26             #include
27             #include
28             #include
29              
30             #define DEQ_MAGIC 0x44455131U /* "DEQ1" */
31             #define DEQ_VERSION 1
32             #define DEQ_ERR_BUFLEN 256
33              
34             #define DEQ_VAR_INT 0
35             #define DEQ_VAR_STR 1
36              
37             typedef struct {
38             uint32_t magic;
39             uint32_t version;
40             uint32_t elem_size;
41             uint32_t variant_id;
42             uint64_t capacity;
43             uint64_t total_size;
44             uint64_t data_off;
45             uint8_t _pad0[24];
46              
47             uint64_t head; /* 64: consumer side (monotonic) */
48             uint64_t tail; /* 72: producer side (monotonic) */
49             uint32_t waiters_push; /* 80 */
50             uint32_t waiters_pop; /* 84 */
51             uint64_t stat_pushes; /* 88 */
52             uint64_t stat_pops; /* 96 */
53             uint64_t stat_waits; /* 104 */
54             uint64_t stat_timeouts; /* 112 */
55             uint8_t _pad1[8]; /* 120-127 */
56             } DeqHeader;
57              
58             #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
59             _Static_assert(sizeof(DeqHeader) == 128, "DeqHeader must be 128 bytes");
60             #endif
61              
62             typedef struct {
63             DeqHeader *hdr;
64             uint8_t *data;
65             size_t mmap_size;
66             uint32_t elem_size;
67             char *path;
68             int notify_fd;
69             int backing_fd;
70             } DeqHandle;
71              
72             /* ================================================================ */
73              
74 5           static inline void deq_make_deadline(double t, struct timespec *dl) {
75 5           clock_gettime(CLOCK_MONOTONIC, dl);
76 5           dl->tv_sec += (time_t)t;
77 5           dl->tv_nsec += (long)((t - (double)(time_t)t) * 1e9);
78 5 100         if (dl->tv_nsec >= 1000000000L) { dl->tv_sec++; dl->tv_nsec -= 1000000000L; }
79 5           }
80              
81 9           static inline int deq_remaining(const struct timespec *dl, struct timespec *rem) {
82             struct timespec now;
83 9           clock_gettime(CLOCK_MONOTONIC, &now);
84 9           rem->tv_sec = dl->tv_sec - now.tv_sec;
85 9           rem->tv_nsec = dl->tv_nsec - now.tv_nsec;
86 9 100         if (rem->tv_nsec < 0) { rem->tv_sec--; rem->tv_nsec += 1000000000L; }
87 9           return rem->tv_sec >= 0;
88             }
89              
90 69           static inline uint8_t *deq_slot(DeqHandle *h, uint64_t idx) {
91 69           return h->data + (idx % h->hdr->capacity) * h->elem_size;
92             }
93              
94 9           static inline uint64_t deq_size(DeqHandle *h) {
95 9           uint64_t t = __atomic_load_n(&h->hdr->tail, __ATOMIC_ACQUIRE);
96 9           uint64_t hd = __atomic_load_n(&h->hdr->head, __ATOMIC_ACQUIRE);
97 9           return t - hd;
98             }
99              
100             /* ================================================================
101             * Push back (tail++)
102             * ================================================================ */
103              
104 46           static inline int deq_try_push_back(DeqHandle *h, const void *val, uint32_t vlen) {
105 46           DeqHeader *hdr = h->hdr;
106 0           for (;;) {
107 46           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_RELAXED);
108 46           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_ACQUIRE);
109 89 100         if (t - hd >= hdr->capacity) return 0;
110 43 50         if (__atomic_compare_exchange_n(&hdr->tail, &t, t + 1,
111             1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
112 43           uint32_t sz = h->elem_size;
113 43           uint32_t cp = vlen < sz ? vlen : sz;
114 43           memcpy(deq_slot(h, t), val, cp);
115 43 50         if (cp < sz) memset(deq_slot(h, t) + cp, 0, sz - cp);
116 43           __atomic_thread_fence(__ATOMIC_RELEASE);
117 43           __atomic_add_fetch(&hdr->stat_pushes, 1, __ATOMIC_RELAXED);
118 43 50         if (__atomic_load_n(&hdr->waiters_pop, __ATOMIC_RELAXED) > 0)
119 0           syscall(SYS_futex, &hdr->tail, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
120 43           return 1;
121             }
122             }
123             }
124              
125             /* ================================================================
126             * Push front (head--)
127             * ================================================================ */
128              
129 10           static inline int deq_try_push_front(DeqHandle *h, const void *val, uint32_t vlen) {
130 10           DeqHeader *hdr = h->hdr;
131 0           for (;;) {
132 10           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_ACQUIRE);
133 10           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_RELAXED);
134 17 100         if (t - hd >= hdr->capacity) return 0;
135 7           uint64_t new_hd = hd - 1;
136 7 50         if (__atomic_compare_exchange_n(&hdr->head, &hd, new_hd,
137             1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
138 7           uint32_t sz = h->elem_size;
139 7           uint32_t cp = vlen < sz ? vlen : sz;
140 7           memcpy(deq_slot(h, new_hd), val, cp);
141 7 50         if (cp < sz) memset(deq_slot(h, new_hd) + cp, 0, sz - cp);
142 7           __atomic_thread_fence(__ATOMIC_RELEASE);
143 7           __atomic_add_fetch(&hdr->stat_pushes, 1, __ATOMIC_RELAXED);
144 7 50         if (__atomic_load_n(&hdr->waiters_pop, __ATOMIC_RELAXED) > 0)
145 0           syscall(SYS_futex, &hdr->tail, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
146 7           return 1;
147             }
148             }
149             }
150              
151             /* ================================================================
152             * Pop front (head++)
153             * ================================================================ */
154              
155 15           static inline int deq_try_pop_front(DeqHandle *h, void *out) {
156 15           DeqHeader *hdr = h->hdr;
157 0           for (;;) {
158 15           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_RELAXED);
159 15           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_ACQUIRE);
160 26 100         if (t - hd == 0) return 0;
161 11 50         if (__atomic_compare_exchange_n(&hdr->head, &hd, hd + 1,
162             1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
163 11           memcpy(out, deq_slot(h, hd), h->elem_size);
164 11           __atomic_add_fetch(&hdr->stat_pops, 1, __ATOMIC_RELAXED);
165 11 50         if (__atomic_load_n(&hdr->waiters_push, __ATOMIC_RELAXED) > 0)
166 0           syscall(SYS_futex, &hdr->head, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
167 11           return 1;
168             }
169             }
170             }
171              
172             /* ================================================================
173             * Pop back (tail--)
174             * ================================================================ */
175              
176 11           static inline int deq_try_pop_back(DeqHandle *h, void *out) {
177 11           DeqHeader *hdr = h->hdr;
178 0           for (;;) {
179 11           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_RELAXED);
180 11           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_ACQUIRE);
181 19 100         if (t - hd == 0) return 0;
182 8 50         if (__atomic_compare_exchange_n(&hdr->tail, &t, t - 1,
183             1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
184 8           memcpy(out, deq_slot(h, t - 1), h->elem_size);
185 8           __atomic_add_fetch(&hdr->stat_pops, 1, __ATOMIC_RELAXED);
186 8 50         if (__atomic_load_n(&hdr->waiters_push, __ATOMIC_RELAXED) > 0)
187 0           syscall(SYS_futex, &hdr->head, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
188 8           return 1;
189             }
190             }
191             }
192              
193             /* ================================================================
194             * Blocking push/pop
195             * ================================================================ */
196              
197 2           static inline int deq_push_wait(DeqHandle *h, const void *val, uint32_t vlen,
198             int front, double timeout) {
199 2           int (*try_fn)(DeqHandle*, const void*, uint32_t) =
200 2 100         front ? deq_try_push_front : deq_try_push_back;
201 2 50         if (try_fn(h, val, vlen)) return 1;
202 2 50         if (timeout == 0) return 0;
203              
204 2           DeqHeader *hdr = h->hdr;
205             struct timespec dl, rem;
206 2           int has_dl = (timeout > 0);
207 2 50         if (has_dl) deq_make_deadline(timeout, &dl);
208 2           __atomic_add_fetch(&hdr->stat_waits, 1, __ATOMIC_RELAXED);
209              
210 0           for (;;) {
211 2           __atomic_add_fetch(&hdr->waiters_push, 1, __ATOMIC_RELEASE);
212 2           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_ACQUIRE);
213 2           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_ACQUIRE);
214 2 50         if (t - hd >= hdr->capacity) {
215 2           struct timespec *pts = NULL;
216 2 50         if (has_dl) {
217 2 50         if (!deq_remaining(&dl, &rem)) {
218 0           __atomic_sub_fetch(&hdr->waiters_push, 1, __ATOMIC_RELAXED);
219 0           __atomic_add_fetch(&hdr->stat_timeouts, 1, __ATOMIC_RELAXED);
220 0           return 0;
221             }
222 2           pts = &rem;
223             }
224 2           syscall(SYS_futex, &hdr->head, FUTEX_WAIT,
225             (uint32_t)(hd & 0xFFFFFFFF), pts, NULL, 0);
226             }
227 2           __atomic_sub_fetch(&hdr->waiters_push, 1, __ATOMIC_RELAXED);
228 2 50         if (try_fn(h, val, vlen)) return 1;
229 2 50         if (has_dl && !deq_remaining(&dl, &rem)) {
    50          
230 2           __atomic_add_fetch(&hdr->stat_timeouts, 1, __ATOMIC_RELAXED);
231 2           return 0;
232             }
233             }
234             }
235              
236 3           static inline int deq_pop_wait(DeqHandle *h, void *out, int back, double timeout) {
237 3           int (*try_fn)(DeqHandle*, void*) =
238 3 100         back ? deq_try_pop_back : deq_try_pop_front;
239 3 50         if (try_fn(h, out)) return 1;
240 3 50         if (timeout == 0) return 0;
241              
242 3           DeqHeader *hdr = h->hdr;
243             struct timespec dl, rem;
244 3           int has_dl = (timeout > 0);
245 3 50         if (has_dl) deq_make_deadline(timeout, &dl);
246 3           __atomic_add_fetch(&hdr->stat_waits, 1, __ATOMIC_RELAXED);
247              
248 0           for (;;) {
249 3           __atomic_add_fetch(&hdr->waiters_pop, 1, __ATOMIC_RELEASE);
250 3           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_ACQUIRE);
251 3           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_ACQUIRE);
252 3 50         if (t - hd == 0) {
253 3           struct timespec *pts = NULL;
254 3 50         if (has_dl) {
255 3 50         if (!deq_remaining(&dl, &rem)) {
256 0           __atomic_sub_fetch(&hdr->waiters_pop, 1, __ATOMIC_RELAXED);
257 0           __atomic_add_fetch(&hdr->stat_timeouts, 1, __ATOMIC_RELAXED);
258 0           return 0;
259             }
260 3           pts = &rem;
261             }
262 3           syscall(SYS_futex, &hdr->tail, FUTEX_WAIT,
263             (uint32_t)(t & 0xFFFFFFFF), pts, NULL, 0);
264             }
265 3           __atomic_sub_fetch(&hdr->waiters_pop, 1, __ATOMIC_RELAXED);
266 3 100         if (try_fn(h, out)) return 1;
267 2 50         if (has_dl && !deq_remaining(&dl, &rem)) {
    50          
268 2           __atomic_add_fetch(&hdr->stat_timeouts, 1, __ATOMIC_RELAXED);
269 2           return 0;
270             }
271             }
272             }
273              
274             /* ================================================================
275             * Create / Open / Close
276             * ================================================================ */
277              
278             #define DEQ_ERR(fmt, ...) do { if (errbuf) snprintf(errbuf, DEQ_ERR_BUFLEN, fmt, ##__VA_ARGS__); } while(0)
279              
280 5           static inline void deq_init_header(void *base, uint64_t total,
281             uint32_t elem_size, uint32_t variant_id,
282             uint64_t capacity) {
283 5           DeqHeader *hdr = (DeqHeader *)base;
284 5           memset(base, 0, (size_t)total);
285 5           hdr->magic = DEQ_MAGIC;
286 5           hdr->version = DEQ_VERSION;
287 5           hdr->elem_size = elem_size;
288 5           hdr->variant_id = variant_id;
289 5           hdr->capacity = capacity;
290 5           hdr->total_size = total;
291 5           hdr->data_off = sizeof(DeqHeader);
292 5           __atomic_thread_fence(__ATOMIC_SEQ_CST);
293 5           }
294              
295 7           static inline DeqHandle *deq_setup(void *base, size_t ms, const char *path, int bfd) {
296 7           DeqHeader *hdr = (DeqHeader *)base;
297 7           DeqHandle *h = (DeqHandle *)calloc(1, sizeof(DeqHandle));
298 7 50         if (!h) { munmap(base, ms); return NULL; }
299 7           h->hdr = hdr;
300 7           h->data = (uint8_t *)base + hdr->data_off;
301 7           h->mmap_size = ms;
302 7           h->elem_size = hdr->elem_size;
303 7 100         h->path = path ? strdup(path) : NULL;
304 7           h->notify_fd = -1;
305 7           h->backing_fd = bfd;
306 7           return h;
307             }
308              
309 5           static DeqHandle *deq_create(const char *path, uint64_t capacity,
310             uint32_t elem_size, uint32_t variant_id,
311             char *errbuf) {
312 5 50         if (errbuf) errbuf[0] = '\0';
313 5 50         if (capacity == 0) { DEQ_ERR("capacity must be > 0"); return NULL; }
    0          
314 5 50         if (elem_size == 0) { DEQ_ERR("elem_size must be > 0"); return NULL; }
    0          
315 5 50         if (capacity > (UINT64_MAX - sizeof(DeqHeader)) / elem_size) {
316 0 0         DEQ_ERR("capacity * elem_size overflow"); return NULL;
317             }
318              
319 5           uint64_t total = sizeof(DeqHeader) + capacity * elem_size;
320 5           int anonymous = (path == NULL);
321 5           int fd = -1;
322             size_t map_size;
323             void *base;
324              
325 5 100         if (anonymous) {
326 2           map_size = (size_t)total;
327 2           base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
328 2 50         if (base == MAP_FAILED) { DEQ_ERR("mmap: %s", strerror(errno)); return NULL; }
    0          
329             } else {
330 3           fd = open(path, O_RDWR|O_CREAT, 0666);
331 4 50         if (fd < 0) { DEQ_ERR("open: %s", strerror(errno)); return NULL; }
    0          
332 3 50         if (flock(fd, LOCK_EX) < 0) { DEQ_ERR("flock: %s", strerror(errno)); close(fd); return NULL; }
    0          
333             struct stat st;
334 3 50         if (fstat(fd, &st) < 0) {
335 0 0         DEQ_ERR("fstat: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL;
336             }
337 3           int is_new = (st.st_size == 0);
338 3 100         if (is_new && ftruncate(fd, (off_t)total) < 0) {
    50          
339 0 0         DEQ_ERR("ftruncate: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL;
340             }
341 3 100         map_size = is_new ? (size_t)total : (size_t)st.st_size;
342 3           base = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
343 3 50         if (base == MAP_FAILED) { DEQ_ERR("mmap: %s", strerror(errno)); flock(fd, LOCK_UN); close(fd); return NULL; }
    0          
344 3 100         if (!is_new) {
345 1           DeqHeader *hdr = (DeqHeader *)base;
346 1 50         if (hdr->magic != DEQ_MAGIC || hdr->version != DEQ_VERSION ||
    50          
347 1 50         hdr->variant_id != variant_id ||
348 1 50         hdr->total_size != (uint64_t)st.st_size) {
349 0 0         DEQ_ERR("invalid deque file"); munmap(base, map_size); flock(fd, LOCK_UN); close(fd); return NULL;
350             }
351 1           flock(fd, LOCK_UN); close(fd);
352 1           return deq_setup(base, map_size, path, -1);
353             }
354             }
355 4           deq_init_header(base, total, elem_size, variant_id, capacity);
356 4 100         if (fd >= 0) { flock(fd, LOCK_UN); close(fd); }
357 4           return deq_setup(base, map_size, path, -1);
358             }
359              
360 1           static DeqHandle *deq_create_memfd(const char *name, uint64_t capacity,
361             uint32_t elem_size, uint32_t variant_id,
362             char *errbuf) {
363 1 50         if (errbuf) errbuf[0] = '\0';
364 1 50         if (capacity == 0) { DEQ_ERR("capacity must be > 0"); return NULL; }
    0          
365 1 50         if (elem_size == 0) { DEQ_ERR("elem_size must be > 0"); return NULL; }
    0          
366 1 50         if (capacity > (UINT64_MAX - sizeof(DeqHeader)) / elem_size) {
367 0 0         DEQ_ERR("capacity * elem_size overflow"); return NULL;
368             }
369 1           uint64_t total = sizeof(DeqHeader) + capacity * elem_size;
370 1 50         int fd = memfd_create(name ? name : "deque", MFD_CLOEXEC);
371 1 50         if (fd < 0) { DEQ_ERR("memfd_create: %s", strerror(errno)); return NULL; }
    0          
372 1 50         if (ftruncate(fd, (off_t)total) < 0) { DEQ_ERR("ftruncate: %s", strerror(errno)); close(fd); return NULL; }
    0          
373 1           void *base = mmap(NULL, (size_t)total, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
374 1 50         if (base == MAP_FAILED) { DEQ_ERR("mmap: %s", strerror(errno)); close(fd); return NULL; }
    0          
375 1           deq_init_header(base, total, elem_size, variant_id, capacity);
376 1           return deq_setup(base, (size_t)total, NULL, fd);
377             }
378              
379 1           static DeqHandle *deq_open_fd(int fd, uint32_t variant_id, char *errbuf) {
380 1 50         if (errbuf) errbuf[0] = '\0';
381             struct stat st;
382 1 50         if (fstat(fd, &st) < 0) { DEQ_ERR("fstat: %s", strerror(errno)); return NULL; }
    0          
383 1 50         if ((uint64_t)st.st_size < sizeof(DeqHeader)) { DEQ_ERR("too small"); return NULL; }
    0          
384 1           size_t ms = (size_t)st.st_size;
385 1           void *base = mmap(NULL, ms, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
386 1 50         if (base == MAP_FAILED) { DEQ_ERR("mmap: %s", strerror(errno)); return NULL; }
    0          
387 1           DeqHeader *hdr = (DeqHeader *)base;
388 1 50         if (hdr->magic != DEQ_MAGIC || hdr->version != DEQ_VERSION ||
    50          
389 1 50         hdr->variant_id != variant_id ||
390 1 50         hdr->total_size != (uint64_t)st.st_size) {
391 0 0         DEQ_ERR("invalid deque"); munmap(base, ms); return NULL;
392             }
393 1           int myfd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
394 1 50         if (myfd < 0) { DEQ_ERR("fcntl: %s", strerror(errno)); munmap(base, ms); return NULL; }
    0          
395 1           return deq_setup(base, ms, NULL, myfd);
396             }
397              
398 7           static void deq_destroy(DeqHandle *h) {
399 7 50         if (!h) return;
400 7 100         if (h->notify_fd >= 0) close(h->notify_fd);
401 7 100         if (h->backing_fd >= 0) close(h->backing_fd);
402 7 50         if (h->hdr) munmap(h->hdr, h->mmap_size);
403 7           free(h->path);
404 7           free(h);
405             }
406              
407             /* NOT concurrency-safe — use drain() for concurrent scenarios */
408 3           static void deq_clear(DeqHandle *h) {
409 3           __atomic_store_n(&h->hdr->head, 0, __ATOMIC_RELEASE);
410 3           __atomic_store_n(&h->hdr->tail, 0, __ATOMIC_RELEASE);
411 3 50         if (__atomic_load_n(&h->hdr->waiters_push, __ATOMIC_RELAXED) > 0)
412 0           syscall(SYS_futex, &h->hdr->head, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
413 3 50         if (__atomic_load_n(&h->hdr->waiters_pop, __ATOMIC_RELAXED) > 0)
414 0           syscall(SYS_futex, &h->hdr->tail, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
415 3           }
416              
417             /* Concurrency-safe drain: CAS head to current tail, returns count drained */
418 0           static inline uint64_t deq_drain(DeqHandle *h) {
419 0           DeqHeader *hdr = h->hdr;
420 0           for (;;) {
421 0           uint64_t hd = __atomic_load_n(&hdr->head, __ATOMIC_RELAXED);
422 0           uint64_t t = __atomic_load_n(&hdr->tail, __ATOMIC_ACQUIRE);
423 0           uint64_t count = t - hd;
424 0 0         if (count == 0) return 0;
425 0 0         if (__atomic_compare_exchange_n(&hdr->head, &hd, t,
426             1, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) {
427 0 0         if (__atomic_load_n(&hdr->waiters_push, __ATOMIC_RELAXED) > 0)
428 0           syscall(SYS_futex, &hdr->head, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
429 0           return count;
430             }
431             }
432             }
433              
434 1           static int deq_create_eventfd(DeqHandle *h) {
435 1 50         if (h->notify_fd >= 0) close(h->notify_fd);
436 1           int efd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
437 1 50         if (efd < 0) return -1;
438 1           h->notify_fd = efd; return efd;
439             }
440 1           static int deq_notify(DeqHandle *h) {
441 1 50         if (h->notify_fd < 0) return 0;
442 1           uint64_t v = 1; return write(h->notify_fd, &v, sizeof(v)) == sizeof(v);
443             }
444 1           static int64_t deq_eventfd_consume(DeqHandle *h) {
445 1 50         if (h->notify_fd < 0) return -1;
446 1           uint64_t v = 0;
447 1 50         if (read(h->notify_fd, &v, sizeof(v)) != sizeof(v)) return -1;
448 1           return (int64_t)v;
449             }
450 1           static void deq_msync(DeqHandle *h) { msync(h->hdr, h->mmap_size, MS_SYNC); }
451              
452             #endif /* DEQUE_H */