eio 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.gitignore +8 -0
  2. data/COPYING +502 -0
  3. data/LICENSE +16 -0
  4. data/README.rdoc +201 -0
  5. data/Rakefile +48 -0
  6. data/bench/eventmachine.rb +134 -0
  7. data/eio.gemspec +17 -0
  8. data/ext/eio/eio_ext.c +1447 -0
  9. data/ext/eio/extconf.rb +11 -0
  10. data/ext/libeio/CVS/Entries +13 -0
  11. data/ext/libeio/CVS/Repository +1 -0
  12. data/ext/libeio/CVS/Root +1 -0
  13. data/ext/libeio/Changes +40 -0
  14. data/ext/libeio/LICENSE +36 -0
  15. data/ext/libeio/Makefile +692 -0
  16. data/ext/libeio/Makefile.am +15 -0
  17. data/ext/libeio/Makefile.in +692 -0
  18. data/ext/libeio/aclocal.m4 +8937 -0
  19. data/ext/libeio/autogen.sh +3 -0
  20. data/ext/libeio/autom4te.cache/output.0 +13871 -0
  21. data/ext/libeio/autom4te.cache/output.1 +13867 -0
  22. data/ext/libeio/autom4te.cache/requests +275 -0
  23. data/ext/libeio/autom4te.cache/traces.0 +2384 -0
  24. data/ext/libeio/autom4te.cache/traces.1 +621 -0
  25. data/ext/libeio/config.guess +1501 -0
  26. data/ext/libeio/config.h +122 -0
  27. data/ext/libeio/config.h.in +121 -0
  28. data/ext/libeio/config.status +2035 -0
  29. data/ext/libeio/config.sub +1705 -0
  30. data/ext/libeio/configure +13867 -0
  31. data/ext/libeio/configure.ac +22 -0
  32. data/ext/libeio/demo.c +194 -0
  33. data/ext/libeio/eio.3 +3428 -0
  34. data/ext/libeio/eio.c +2075 -0
  35. data/ext/libeio/eio.h +336 -0
  36. data/ext/libeio/eio.pod +303 -0
  37. data/ext/libeio/install-sh +520 -0
  38. data/ext/libeio/libeio.m4 +156 -0
  39. data/ext/libeio/libtool +8890 -0
  40. data/ext/libeio/ltmain.sh +8406 -0
  41. data/ext/libeio/missing +376 -0
  42. data/ext/libeio/stamp-h1 +1 -0
  43. data/ext/libeio/xthread.h +168 -0
  44. data/lib/eio.rb +9 -0
  45. data/lib/eio/eventmachine.rb +24 -0
  46. data/lib/eio/middleware.rb +21 -0
  47. data/test/test_eio.rb +1161 -0
  48. data/test/test_eventmachine.rb +23 -0
  49. data/test/test_middleware.rb +20 -0
  50. metadata +148 -0
@@ -0,0 +1,2075 @@
1
+ /*
2
+ * libeio implementation
3
+ *
4
+ * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libeio@schmorp.de>
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without modifica-
8
+ * tion, are permitted provided that the following conditions are met:
9
+ *
10
+ * 1. Redistributions of source code must retain the above copyright notice,
11
+ * this list of conditions and the following disclaimer.
12
+ *
13
+ * 2. Redistributions in binary form must reproduce the above copyright
14
+ * notice, this list of conditions and the following disclaimer in the
15
+ * documentation and/or other materials provided with the distribution.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
19
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
21
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
25
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
26
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ *
28
+ * Alternatively, the contents of this file may be used under the terms of
29
+ * the GNU General Public License ("GPL") version 2 or any later version,
30
+ * in which case the provisions of the GPL are applicable instead of
31
+ * the above. If you wish to allow the use of your version of this file
32
+ * only under the terms of the GPL and not to allow others to use your
33
+ * version of this file under the BSD license, indicate your decision
34
+ * by deleting the provisions above and replace them with the notice
35
+ * and other provisions required by the GPL. If you do not delete the
36
+ * provisions above, a recipient may use your version of this file under
37
+ * either the BSD or the GPL.
38
+ */
39
+
40
+ #ifndef _WIN32
41
+ # include "config.h"
42
+ #endif
43
+
44
+ #include "eio.h"
45
+
46
+ #ifdef EIO_STACKSIZE
47
+ # define XTHREAD_STACKSIZE EIO_STACKSIZE
48
+ #endif
49
+ #include "xthread.h"
50
+
51
+ #include <errno.h>
52
+ #include <stddef.h>
53
+ #include <stdlib.h>
54
+ #include <string.h>
55
+ #include <errno.h>
56
+ #include <sys/types.h>
57
+ #include <sys/stat.h>
58
+ #include <sys/statvfs.h>
59
+ #include <limits.h>
60
+ #include <fcntl.h>
61
+ #include <assert.h>
62
+
63
+ #ifndef EIO_FINISH
64
+ # define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0
65
+ #endif
66
+
67
+ #ifndef EIO_DESTROY
68
+ # define EIO_DESTROY(req) do { if ((req)->destroy) (req)->destroy (req); } while (0)
69
+ #endif
70
+
71
+ #ifndef EIO_FEED
72
+ # define EIO_FEED(req) do { if ((req)->feed ) (req)->feed (req); } while (0)
73
+ #endif
74
+
75
+ #ifdef _WIN32
76
+
77
+ /*doh*/
78
+ #else
79
+
80
+ # include <sys/time.h>
81
+ # include <sys/select.h>
82
+ # include <unistd.h>
83
+ # include <utime.h>
84
+ # include <signal.h>
85
+ # include <dirent.h>
86
+
87
+ #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES
88
+ # include <sys/mman.h>
89
+ #endif
90
+
91
+ /* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */
92
+ # if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
93
+ # define _DIRENT_HAVE_D_TYPE /* sigh */
94
+ # define D_INO(de) (de)->d_fileno
95
+ # define D_NAMLEN(de) (de)->d_namlen
96
+ # elif __linux || defined d_ino || _XOPEN_SOURCE >= 600
97
+ # define D_INO(de) (de)->d_ino
98
+ # endif
99
+
100
+ #ifdef _D_EXACT_NAMLEN
101
+ # undef D_NAMLEN
102
+ # define D_NAMLEN(de) _D_EXACT_NAMLEN (de)
103
+ #endif
104
+
105
+ # ifdef _DIRENT_HAVE_D_TYPE
106
+ # define D_TYPE(de) (de)->d_type
107
+ # endif
108
+
109
+ # ifndef EIO_STRUCT_DIRENT
110
+ # define EIO_STRUCT_DIRENT struct dirent
111
+ # endif
112
+
113
+ #endif
114
+
115
+ #if HAVE_SENDFILE
116
+ # if __linux
117
+ # include <sys/sendfile.h>
118
+ # elif __FreeBSD__ || defined __APPLE__
119
+ # include <sys/socket.h>
120
+ # include <sys/uio.h>
121
+ # elif __hpux
122
+ # include <sys/socket.h>
123
+ # elif __solaris
124
+ # include <sys/sendfile.h>
125
+ # else
126
+ # error sendfile support requested but not available
127
+ # endif
128
+ #endif
129
+
130
+ #ifndef D_TYPE
131
+ # define D_TYPE(de) 0
132
+ #endif
133
+ #ifndef D_INO
134
+ # define D_INO(de) 0
135
+ #endif
136
+ #ifndef D_NAMLEN
137
+ # define D_NAMLEN(de) strlen ((de)->d_name)
138
+ #endif
139
+
140
+ /* used for struct dirent, AIX doesn't provide it */
141
+ #ifndef NAME_MAX
142
+ # define NAME_MAX 4096
143
+ #endif
144
+
145
+ /* used for readlink etc. */
146
+ #ifndef PATH_MAX
147
+ # define PATH_MAX 4096
148
+ #endif
149
+
150
+ /* buffer size for various temporary buffers */
151
+ #define EIO_BUFSIZE 65536
152
+
153
+ #define dBUF \
154
+ char *eio_buf; \
155
+ ETP_WORKER_LOCK (self); \
156
+ self->dbuf = eio_buf = malloc (EIO_BUFSIZE); \
157
+ ETP_WORKER_UNLOCK (self); \
158
+ errno = ENOMEM; \
159
+ if (!eio_buf) \
160
+ return -1;
161
+
162
+ #define EIO_TICKS ((1000000 + 1023) >> 10)
163
+
164
+ /*****************************************************************************/
165
+
166
+ #if __GNUC__ >= 3
167
+ # define expect(expr,value) __builtin_expect ((expr),(value))
168
+ #else
169
+ # define expect(expr,value) (expr)
170
+ #endif
171
+
172
+ #define expect_false(expr) expect ((expr) != 0, 0)
173
+ #define expect_true(expr) expect ((expr) != 0, 1)
174
+
175
+ /*****************************************************************************/
176
+
177
+ #define ETP_PRI_MIN EIO_PRI_MIN
178
+ #define ETP_PRI_MAX EIO_PRI_MAX
179
+
180
+ struct etp_worker;
181
+
182
+ #define ETP_REQ eio_req
183
+ #define ETP_DESTROY(req) eio_destroy (req)
184
+ static int eio_finish (eio_req *req);
185
+ #define ETP_FINISH(req) eio_finish (req)
186
+ static void eio_execute (struct etp_worker *self, eio_req *req);
187
+ #define ETP_EXECUTE(wrk,req) eio_execute (wrk,req)
188
+
189
+ #define ETP_WORKER_CLEAR(req) \
190
+ if (wrk->dbuf) \
191
+ { \
192
+ free (wrk->dbuf); \
193
+ wrk->dbuf = 0; \
194
+ } \
195
+ \
196
+ if (wrk->dirp) \
197
+ { \
198
+ closedir (wrk->dirp); \
199
+ wrk->dirp = 0; \
200
+ }
201
+
202
+ #define ETP_WORKER_COMMON \
203
+ void *dbuf; \
204
+ DIR *dirp;
205
+
206
+ /*****************************************************************************/
207
+
208
+ #define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1)
209
+
210
+ /* calculate time difference in ~1/EIO_TICKS of a second */
211
+ static int tvdiff (struct timeval *tv1, struct timeval *tv2)
212
+ {
213
+ return (tv2->tv_sec - tv1->tv_sec ) * EIO_TICKS
214
+ + ((tv2->tv_usec - tv1->tv_usec) >> 10);
215
+ }
216
+
217
+ static unsigned int started, idle, wanted = 4;
218
+
219
+ static void (*want_poll_cb) (void);
220
+ static void (*done_poll_cb) (void);
221
+
222
+ static unsigned int max_poll_time; /* reslock */
223
+ static unsigned int max_poll_reqs; /* reslock */
224
+
225
+ static volatile unsigned int nreqs; /* reqlock */
226
+ static volatile unsigned int nready; /* reqlock */
227
+ static volatile unsigned int npending; /* reqlock */
228
+ static volatile unsigned int max_idle = 4; /* maximum number of threads that can idle indefinitely */
229
+ static volatile unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */
230
+
231
+ static xmutex_t wrklock;
232
+ static xmutex_t reslock;
233
+ static xmutex_t reqlock;
234
+ static xcond_t reqwait;
235
+
236
+ #if !HAVE_PREADWRITE
237
+ /*
238
+ * make our pread/pwrite emulation safe against themselves, but not against
239
+ * normal read/write by using a mutex. slows down execution a lot,
240
+ * but that's your problem, not mine.
241
+ */
242
+ static xmutex_t preadwritelock = X_MUTEX_INIT;
243
+ #endif
244
+
245
+ typedef struct etp_worker
246
+ {
247
+ /* locked by wrklock */
248
+ struct etp_worker *prev, *next;
249
+
250
+ xthread_t tid;
251
+
252
+ /* locked by reslock, reqlock or wrklock */
253
+ ETP_REQ *req; /* currently processed request */
254
+
255
+ ETP_WORKER_COMMON
256
+ } etp_worker;
257
+
258
+ static etp_worker wrk_first = { &wrk_first, &wrk_first, 0 }; /* NOT etp */
259
+
260
+ #define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock)
261
+ #define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock)
262
+
263
+ /* worker threads management */
264
+
265
+ static void etp_worker_clear (etp_worker *wrk)
266
+ {
267
+ ETP_WORKER_CLEAR (wrk);
268
+ }
269
+
270
+ static void etp_worker_free (etp_worker *wrk)
271
+ {
272
+ wrk->next->prev = wrk->prev;
273
+ wrk->prev->next = wrk->next;
274
+
275
+ free (wrk);
276
+ }
277
+
278
+ static unsigned int etp_nreqs (void)
279
+ {
280
+ int retval;
281
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
282
+ retval = nreqs;
283
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
284
+ return retval;
285
+ }
286
+
287
+ static unsigned int etp_nready (void)
288
+ {
289
+ unsigned int retval;
290
+
291
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
292
+ retval = nready;
293
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
294
+
295
+ return retval;
296
+ }
297
+
298
+ static unsigned int etp_npending (void)
299
+ {
300
+ unsigned int retval;
301
+
302
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
303
+ retval = npending;
304
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
305
+
306
+ return retval;
307
+ }
308
+
309
+ static unsigned int etp_nthreads (void)
310
+ {
311
+ unsigned int retval;
312
+
313
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
314
+ retval = started;
315
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
316
+
317
+ return retval;
318
+ }
319
+
320
+ /*
321
+ * a somewhat faster data structure might be nice, but
322
+ * with 8 priorities this actually needs <20 insns
323
+ * per shift, the most expensive operation.
324
+ */
325
+ typedef struct {
326
+ ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */
327
+ int size;
328
+ } etp_reqq;
329
+
330
+ static etp_reqq req_queue;
331
+ static etp_reqq res_queue;
332
+
333
+ static int reqq_push (etp_reqq *q, ETP_REQ *req)
334
+ {
335
+ int pri = req->pri;
336
+ req->next = 0;
337
+
338
+ if (q->qe[pri])
339
+ {
340
+ q->qe[pri]->next = req;
341
+ q->qe[pri] = req;
342
+ }
343
+ else
344
+ q->qe[pri] = q->qs[pri] = req;
345
+
346
+ return q->size++;
347
+ }
348
+
349
+ static ETP_REQ *reqq_shift (etp_reqq *q)
350
+ {
351
+ int pri;
352
+
353
+ if (!q->size)
354
+ return 0;
355
+
356
+ --q->size;
357
+
358
+ for (pri = ETP_NUM_PRI; pri--; )
359
+ {
360
+ eio_req *req = q->qs[pri];
361
+
362
+ if (req)
363
+ {
364
+ if (!(q->qs[pri] = (eio_req *)req->next))
365
+ q->qe[pri] = 0;
366
+
367
+ return req;
368
+ }
369
+ }
370
+
371
+ abort ();
372
+ }
373
+
374
+ static void etp_thread_init (void)
375
+ {
376
+ X_MUTEX_CREATE (wrklock);
377
+ X_MUTEX_CREATE (reslock);
378
+ X_MUTEX_CREATE (reqlock);
379
+ X_COND_CREATE (reqwait);
380
+ }
381
+
382
+ static void etp_atfork_prepare (void)
383
+ {
384
+ X_LOCK (wrklock);
385
+ X_LOCK (reqlock);
386
+ X_LOCK (reslock);
387
+ #if !HAVE_PREADWRITE
388
+ X_LOCK (preadwritelock);
389
+ #endif
390
+ }
391
+
392
+ static void etp_atfork_parent (void)
393
+ {
394
+ #if !HAVE_PREADWRITE
395
+ X_UNLOCK (preadwritelock);
396
+ #endif
397
+ X_UNLOCK (reslock);
398
+ X_UNLOCK (reqlock);
399
+ X_UNLOCK (wrklock);
400
+ }
401
+
402
+ static void etp_atfork_child (void)
403
+ {
404
+ ETP_REQ *prv;
405
+
406
+ while ((prv = reqq_shift (&req_queue)))
407
+ ETP_DESTROY (prv);
408
+
409
+ while ((prv = reqq_shift (&res_queue)))
410
+ ETP_DESTROY (prv);
411
+
412
+ while (wrk_first.next != &wrk_first)
413
+ {
414
+ etp_worker *wrk = wrk_first.next;
415
+
416
+ if (wrk->req)
417
+ ETP_DESTROY (wrk->req);
418
+
419
+ etp_worker_clear (wrk);
420
+ etp_worker_free (wrk);
421
+ }
422
+
423
+ started = 0;
424
+ idle = 0;
425
+ nreqs = 0;
426
+ nready = 0;
427
+ npending = 0;
428
+
429
+ etp_thread_init ();
430
+ }
431
+
432
+ static void
433
+ etp_once_init (void)
434
+ {
435
+ etp_thread_init ();
436
+ X_THREAD_ATFORK (etp_atfork_prepare, etp_atfork_parent, etp_atfork_child);
437
+ }
438
+
439
+ static int
440
+ etp_init (void (*want_poll)(void), void (*done_poll)(void))
441
+ {
442
+ static pthread_once_t doinit = PTHREAD_ONCE_INIT;
443
+
444
+ pthread_once (&doinit, etp_once_init);
445
+
446
+ want_poll_cb = want_poll;
447
+ done_poll_cb = done_poll;
448
+
449
+ return 0;
450
+ }
451
+
452
+ X_THREAD_PROC (etp_proc);
453
+
454
+ static void etp_start_thread (void)
455
+ {
456
+ etp_worker *wrk = calloc (1, sizeof (etp_worker));
457
+
458
+ /*TODO*/
459
+ assert (("unable to allocate worker thread data", wrk));
460
+
461
+ X_LOCK (wrklock);
462
+
463
+ if (thread_create (&wrk->tid, etp_proc, (void *)wrk))
464
+ {
465
+ wrk->prev = &wrk_first;
466
+ wrk->next = wrk_first.next;
467
+ wrk_first.next->prev = wrk;
468
+ wrk_first.next = wrk;
469
+ ++started;
470
+ }
471
+ else
472
+ free (wrk);
473
+
474
+ X_UNLOCK (wrklock);
475
+ }
476
+
477
+ static void etp_maybe_start_thread (void)
478
+ {
479
+ if (expect_true (etp_nthreads () >= wanted))
480
+ return;
481
+
482
+ /* todo: maybe use idle here, but might be less exact */
483
+ if (expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ()))
484
+ return;
485
+
486
+ etp_start_thread ();
487
+ }
488
+
489
+ static void etp_end_thread (void)
490
+ {
491
+ eio_req *req = calloc (1, sizeof (eio_req));
492
+
493
+ req->type = -1;
494
+ req->pri = ETP_PRI_MAX - ETP_PRI_MIN;
495
+
496
+ X_LOCK (reqlock);
497
+ reqq_push (&req_queue, req);
498
+ X_COND_SIGNAL (reqwait);
499
+ X_UNLOCK (reqlock);
500
+
501
+ X_LOCK (wrklock);
502
+ --started;
503
+ X_UNLOCK (wrklock);
504
+ }
505
+
506
+ static int etp_poll (void)
507
+ {
508
+ unsigned int maxreqs;
509
+ unsigned int maxtime;
510
+ struct timeval tv_start, tv_now;
511
+
512
+ X_LOCK (reslock);
513
+ maxreqs = max_poll_reqs;
514
+ maxtime = max_poll_time;
515
+ X_UNLOCK (reslock);
516
+
517
+ if (maxtime)
518
+ gettimeofday (&tv_start, 0);
519
+
520
+ for (;;)
521
+ {
522
+ ETP_REQ *req;
523
+
524
+ etp_maybe_start_thread ();
525
+
526
+ X_LOCK (reslock);
527
+ req = reqq_shift (&res_queue);
528
+
529
+ if (req)
530
+ {
531
+ --npending;
532
+
533
+ if (!res_queue.size && done_poll_cb)
534
+ done_poll_cb ();
535
+ }
536
+
537
+ X_UNLOCK (reslock);
538
+
539
+ if (!req)
540
+ return 0;
541
+
542
+ X_LOCK (reqlock);
543
+ --nreqs;
544
+ X_UNLOCK (reqlock);
545
+
546
+ if (expect_false (req->type == EIO_GROUP && req->size))
547
+ {
548
+ req->int1 = 1; /* mark request as delayed */
549
+ continue;
550
+ }
551
+ else
552
+ {
553
+ int res = ETP_FINISH (req);
554
+ if (expect_false (res))
555
+ return res;
556
+ }
557
+
558
+ if (expect_false (maxreqs && !--maxreqs))
559
+ break;
560
+
561
+ if (maxtime)
562
+ {
563
+ gettimeofday (&tv_now, 0);
564
+
565
+ if (tvdiff (&tv_start, &tv_now) >= maxtime)
566
+ break;
567
+ }
568
+ }
569
+
570
+ errno = EAGAIN;
571
+ return -1;
572
+ }
573
+
574
+ static void etp_cancel (ETP_REQ *req)
575
+ {
576
+ X_LOCK (wrklock);
577
+ req->flags |= EIO_FLAG_CANCELLED;
578
+ X_UNLOCK (wrklock);
579
+
580
+ eio_grp_cancel (req);
581
+ }
582
+
583
+ static void etp_submit (ETP_REQ *req)
584
+ {
585
+ req->pri -= ETP_PRI_MIN;
586
+
587
+ if (expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN;
588
+ if (expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN;
589
+
590
+ if (expect_false (req->type == EIO_GROUP))
591
+ {
592
+ /* I hope this is worth it :/ */
593
+ X_LOCK (reqlock);
594
+ ++nreqs;
595
+ X_UNLOCK (reqlock);
596
+
597
+ X_LOCK (reslock);
598
+
599
+ ++npending;
600
+
601
+ if (!reqq_push (&res_queue, req) && want_poll_cb)
602
+ want_poll_cb ();
603
+
604
+ X_UNLOCK (reslock);
605
+ }
606
+ else
607
+ {
608
+ X_LOCK (reqlock);
609
+ ++nreqs;
610
+ ++nready;
611
+ reqq_push (&req_queue, req);
612
+ X_COND_SIGNAL (reqwait);
613
+ X_UNLOCK (reqlock);
614
+
615
+ etp_maybe_start_thread ();
616
+ }
617
+ }
618
+
619
+ static void etp_set_max_poll_time (double nseconds)
620
+ {
621
+ if (WORDACCESS_UNSAFE) X_LOCK (reslock);
622
+ max_poll_time = nseconds * EIO_TICKS;
623
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
624
+ }
625
+
626
+ static void etp_set_max_poll_reqs (unsigned int maxreqs)
627
+ {
628
+ if (WORDACCESS_UNSAFE) X_LOCK (reslock);
629
+ max_poll_reqs = maxreqs;
630
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
631
+ }
632
+
633
+ static void etp_set_max_idle (unsigned int nthreads)
634
+ {
635
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
636
+ max_idle = nthreads;
637
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
638
+ }
639
+
640
+ static void etp_set_idle_timeout (unsigned int seconds)
641
+ {
642
+ if (WORDACCESS_UNSAFE) X_LOCK (reqlock);
643
+ idle_timeout = seconds;
644
+ if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
645
+ }
646
+
647
+ static void etp_set_min_parallel (unsigned int nthreads)
648
+ {
649
+ if (wanted < nthreads)
650
+ wanted = nthreads;
651
+ }
652
+
653
+ static void etp_set_max_parallel (unsigned int nthreads)
654
+ {
655
+ if (wanted > nthreads)
656
+ wanted = nthreads;
657
+
658
+ while (started > wanted)
659
+ etp_end_thread ();
660
+ }
661
+
662
+ /*****************************************************************************/
663
+
664
+ static void grp_try_feed (eio_req *grp)
665
+ {
666
+ while (grp->size < grp->int2 && !EIO_CANCELLED (grp))
667
+ {
668
+ grp->flags &= ~EIO_FLAG_GROUPADD;
669
+
670
+ EIO_FEED (grp);
671
+
672
+ /* stop if no progress has been made */
673
+ if (!(grp->flags & EIO_FLAG_GROUPADD))
674
+ {
675
+ grp->feed = 0;
676
+ break;
677
+ }
678
+ }
679
+ }
680
+
681
+ static int grp_dec (eio_req *grp)
682
+ {
683
+ --grp->size;
684
+
685
+ /* call feeder, if applicable */
686
+ grp_try_feed (grp);
687
+
688
+ /* finish, if done */
689
+ if (!grp->size && grp->int1)
690
+ return eio_finish (grp);
691
+ else
692
+ return 0;
693
+ }
694
+
695
+ void eio_destroy (eio_req *req)
696
+ {
697
+ if ((req)->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1);
698
+ if ((req)->flags & EIO_FLAG_PTR2_FREE) free (req->ptr2);
699
+
700
+ EIO_DESTROY (req);
701
+ }
702
+
703
+ static int eio_finish (eio_req *req)
704
+ {
705
+ int res = EIO_FINISH (req);
706
+
707
+ if (req->grp)
708
+ {
709
+ int res2;
710
+ eio_req *grp = req->grp;
711
+
712
+ /* unlink request */
713
+ if (req->grp_next) req->grp_next->grp_prev = req->grp_prev;
714
+ if (req->grp_prev) req->grp_prev->grp_next = req->grp_next;
715
+
716
+ if (grp->grp_first == req)
717
+ grp->grp_first = req->grp_next;
718
+
719
+ res2 = grp_dec (grp);
720
+
721
+ if (!res && res2)
722
+ res = res2;
723
+ }
724
+
725
+ eio_destroy (req);
726
+
727
+ return res;
728
+ }
729
+
730
+ void eio_grp_cancel (eio_req *grp)
731
+ {
732
+ for (grp = grp->grp_first; grp; grp = grp->grp_next)
733
+ eio_cancel (grp);
734
+ }
735
+
736
+ void eio_cancel (eio_req *req)
737
+ {
738
+ etp_cancel (req);
739
+ }
740
+
741
+ void eio_submit (eio_req *req)
742
+ {
743
+ etp_submit (req);
744
+ }
745
+
746
+ unsigned int eio_nreqs (void)
747
+ {
748
+ return etp_nreqs ();
749
+ }
750
+
751
+ unsigned int eio_nready (void)
752
+ {
753
+ return etp_nready ();
754
+ }
755
+
756
+ unsigned int eio_npending (void)
757
+ {
758
+ return etp_npending ();
759
+ }
760
+
761
+ unsigned int eio_nthreads (void)
762
+ {
763
+ return etp_nthreads ();
764
+ }
765
+
766
+ void eio_set_max_poll_time (double nseconds)
767
+ {
768
+ etp_set_max_poll_time (nseconds);
769
+ }
770
+
771
+ void eio_set_max_poll_reqs (unsigned int maxreqs)
772
+ {
773
+ etp_set_max_poll_reqs (maxreqs);
774
+ }
775
+
776
+ void eio_set_max_idle (unsigned int nthreads)
777
+ {
778
+ etp_set_max_idle (nthreads);
779
+ }
780
+
781
+ void eio_set_idle_timeout (unsigned int seconds)
782
+ {
783
+ etp_set_idle_timeout (seconds);
784
+ }
785
+
786
+ void eio_set_min_parallel (unsigned int nthreads)
787
+ {
788
+ etp_set_min_parallel (nthreads);
789
+ }
790
+
791
+ void eio_set_max_parallel (unsigned int nthreads)
792
+ {
793
+ etp_set_max_parallel (nthreads);
794
+ }
795
+
796
+ int eio_poll (void)
797
+ {
798
+ return etp_poll ();
799
+ }
800
+
801
+ /*****************************************************************************/
802
+ /* work around various missing functions */
803
+
804
+ #if !HAVE_PREADWRITE
805
+ # undef pread
806
+ # undef pwrite
807
+ # define pread eio__pread
808
+ # define pwrite eio__pwrite
809
+
810
+ static ssize_t
811
+ eio__pread (int fd, void *buf, size_t count, off_t offset)
812
+ {
813
+ ssize_t res;
814
+ off_t ooffset;
815
+
816
+ X_LOCK (preadwritelock);
817
+ ooffset = lseek (fd, 0, SEEK_CUR);
818
+ lseek (fd, offset, SEEK_SET);
819
+ res = read (fd, buf, count);
820
+ lseek (fd, ooffset, SEEK_SET);
821
+ X_UNLOCK (preadwritelock);
822
+
823
+ return res;
824
+ }
825
+
826
+ static ssize_t
827
+ eio__pwrite (int fd, void *buf, size_t count, off_t offset)
828
+ {
829
+ ssize_t res;
830
+ off_t ooffset;
831
+
832
+ X_LOCK (preadwritelock);
833
+ ooffset = lseek (fd, 0, SEEK_CUR);
834
+ lseek (fd, offset, SEEK_SET);
835
+ res = write (fd, buf, count);
836
+ lseek (fd, ooffset, SEEK_SET);
837
+ X_UNLOCK (preadwritelock);
838
+
839
+ return res;
840
+ }
841
+ #endif
842
+
843
+ #ifndef HAVE_UTIMES
844
+
845
+ # undef utimes
846
+ # define utimes(path,times) eio__utimes (path, times)
847
+
848
+ static int
849
+ eio__utimes (const char *filename, const struct timeval times[2])
850
+ {
851
+ if (times)
852
+ {
853
+ struct utimbuf buf;
854
+
855
+ buf.actime = times[0].tv_sec;
856
+ buf.modtime = times[1].tv_sec;
857
+
858
+ return utime (filename, &buf);
859
+ }
860
+ else
861
+ return utime (filename, 0);
862
+ }
863
+
864
+ #endif
865
+
866
+ #ifndef HAVE_FUTIMES
867
+
868
+ # undef futimes
869
+ # define futimes(fd,times) eio__futimes (fd, times)
870
+
871
+ static int eio__futimes (int fd, const struct timeval tv[2])
872
+ {
873
+ errno = ENOSYS;
874
+ return -1;
875
+ }
876
+
877
+ #endif
878
+
879
+ #if !HAVE_FDATASYNC
880
+ # undef fdatasync
881
+ # define fdatasync(fd) fsync (fd)
882
+ #endif
883
+
884
+ /* sync_file_range always needs emulation */
885
+ int
886
+ eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags)
887
+ {
888
+ #if HAVE_SYNC_FILE_RANGE
889
+ int res;
890
+
891
+ if (EIO_SYNC_FILE_RANGE_WAIT_BEFORE != SYNC_FILE_RANGE_WAIT_BEFORE
892
+ || EIO_SYNC_FILE_RANGE_WRITE != SYNC_FILE_RANGE_WRITE
893
+ || EIO_SYNC_FILE_RANGE_WAIT_AFTER != SYNC_FILE_RANGE_WAIT_AFTER)
894
+ {
895
+ flags = 0
896
+ | (flags & EIO_SYNC_FILE_RANGE_WAIT_BEFORE ? SYNC_FILE_RANGE_WAIT_BEFORE : 0)
897
+ | (flags & EIO_SYNC_FILE_RANGE_WRITE ? SYNC_FILE_RANGE_WRITE : 0)
898
+ | (flags & EIO_SYNC_FILE_RANGE_WAIT_AFTER ? SYNC_FILE_RANGE_WAIT_AFTER : 0);
899
+ }
900
+
901
+ res = sync_file_range (fd, offset, nbytes, flags);
902
+
903
+ if (!res || errno != ENOSYS)
904
+ return res;
905
+ #endif
906
+
907
+ /* even though we could play tricks with the flags, it's better to always
908
+ * call fdatasync, as that matches the expectation of its users best */
909
+ return fdatasync (fd);
910
+ }
911
+
912
+ #if !HAVE_READAHEAD
913
+ # undef readahead
914
+ # define readahead(fd,offset,count) eio__readahead (fd, offset, count, self)
915
+
916
+ static ssize_t
917
+ eio__readahead (int fd, off_t offset, size_t count, etp_worker *self)
918
+ {
919
+ size_t todo = count;
920
+ dBUF;
921
+
922
+ while (todo > 0)
923
+ {
924
+ size_t len = todo < EIO_BUFSIZE ? todo : EIO_BUFSIZE;
925
+
926
+ pread (fd, eio_buf, len, offset);
927
+ offset += len;
928
+ todo -= len;
929
+ }
930
+
931
+ errno = 0;
932
+ return count;
933
+ }
934
+
935
+ #endif
936
+
937
+ /* sendfile always needs emulation */
938
+ static ssize_t
939
+ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self)
940
+ {
941
+ ssize_t res;
942
+
943
+ if (!count)
944
+ return 0;
945
+
946
+ #if HAVE_SENDFILE
947
+ # if __linux
948
+ res = sendfile (ofd, ifd, &offset, count);
949
+
950
+ # elif __FreeBSD__
951
+ /*
952
+ * Of course, the freebsd sendfile is a dire hack with no thoughts
953
+ * wasted on making it similar to other I/O functions.
954
+ */
955
+ {
956
+ off_t sbytes;
957
+ res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0);
958
+
959
+ #if 0 /* according to the manpage, this is correct, but broken behaviour */
960
+ /* freebsd' sendfile will return 0 on success */
961
+ /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */
962
+ /* not on e.g. EIO or EPIPE - sounds broken */
963
+ if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0)
964
+ res = sbytes;
965
+ #endif
966
+
967
+ /* according to source inspection, this is correct, and useful behaviour */
968
+ if (sbytes)
969
+ res = sbytes;
970
+ }
971
+
972
+ # elif defined (__APPLE__)
973
+
974
+ {
975
+ off_t sbytes = count;
976
+ res = sendfile (ifd, ofd, offset, &sbytes, 0, 0);
977
+
978
+ /* according to the manpage, sbytes is always valid */
979
+ if (sbytes)
980
+ res = sbytes;
981
+ }
982
+
983
+ # elif __hpux
984
+ res = sendfile (ofd, ifd, offset, count, 0, 0);
985
+
986
+ # elif __solaris
987
+ {
988
+ struct sendfilevec vec;
989
+ size_t sbytes;
990
+
991
+ vec.sfv_fd = ifd;
992
+ vec.sfv_flag = 0;
993
+ vec.sfv_off = offset;
994
+ vec.sfv_len = count;
995
+
996
+ res = sendfilev (ofd, &vec, 1, &sbytes);
997
+
998
+ if (res < 0 && sbytes)
999
+ res = sbytes;
1000
+ }
1001
+
1002
+ # endif
1003
+
1004
+ #elif defined (_WIN32)
1005
+
1006
+ /* does not work, just for documentation of what would need to be done */
1007
+ {
1008
+ HANDLE h = TO_SOCKET (ifd);
1009
+ SetFilePointer (h, offset, 0, FILE_BEGIN);
1010
+ res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0);
1011
+ }
1012
+
1013
+ #else
1014
+ res = -1;
1015
+ errno = ENOSYS;
1016
+ #endif
1017
+
1018
+ if (res < 0
1019
+ && (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK
1020
+ /* BSDs */
1021
+ #ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */
1022
+ || errno == ENOTSUP
1023
+ #endif
1024
+ || errno == EOPNOTSUPP /* BSDs */
1025
+ #if __solaris
1026
+ || errno == EAFNOSUPPORT || errno == EPROTOTYPE
1027
+ #endif
1028
+ )
1029
+ )
1030
+ {
1031
+ /* emulate sendfile. this is a major pain in the ass */
1032
+ dBUF;
1033
+
1034
+ res = 0;
1035
+
1036
+ while (count)
1037
+ {
1038
+ ssize_t cnt;
1039
+
1040
+ cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset);
1041
+
1042
+ if (cnt <= 0)
1043
+ {
1044
+ if (cnt && !res) res = -1;
1045
+ break;
1046
+ }
1047
+
1048
+ cnt = write (ofd, eio_buf, cnt);
1049
+
1050
+ if (cnt <= 0)
1051
+ {
1052
+ if (cnt && !res) res = -1;
1053
+ break;
1054
+ }
1055
+
1056
+ offset += cnt;
1057
+ res += cnt;
1058
+ count -= cnt;
1059
+ }
1060
+ }
1061
+
1062
+ return res;
1063
+ }
1064
+
1065
+ static signed char
1066
+ eio_dent_cmp (const eio_dirent *a, const eio_dirent *b)
1067
+ {
1068
+ return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */
1069
+ : a->inode < b->inode ? -1
1070
+ : a->inode > b->inode ? 1
1071
+ : 0;
1072
+ }
1073
+
1074
+ #define EIO_DENT_CMP(i,op,j) eio_dent_cmp (&i, &j) op 0
1075
+
1076
+ #define EIO_SORT_CUTOFF 30 /* quite high, but performs well on many filesystems */
1077
+ #define EIO_SORT_FAST 60 /* when to only use insertion sort */
1078
+
1079
+ static void
1080
+ eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits)
1081
+ {
1082
+ unsigned char bits [9 + sizeof (ino_t) * 8];
1083
+ unsigned char *bit = bits;
1084
+
1085
+ assert (CHAR_BIT == 8);
1086
+ assert (sizeof (eio_dirent) * 8 < 256);
1087
+ assert (offsetof (eio_dirent, inode)); /* we use bit #0 as sentinel */
1088
+ assert (offsetof (eio_dirent, score)); /* we use bit #0 as sentinel */
1089
+
1090
+ if (size <= EIO_SORT_FAST)
1091
+ return;
1092
+
1093
+ /* first prepare an array of bits to test in our radix sort */
1094
+ /* try to take endianness into account, as well as differences in ino_t sizes */
1095
+ /* inode_bits must contain all inodes ORed together */
1096
+ /* which is used to skip bits that are 0 everywhere, which is very common */
1097
+ {
1098
+ ino_t endianness;
1099
+ int i, j;
1100
+
1101
+ /* we store the byte offset of byte n into byte n of "endianness" */
1102
+ for (i = 0; i < sizeof (ino_t); ++i)
1103
+ ((unsigned char *)&endianness)[i] = i;
1104
+
1105
+ *bit++ = 0;
1106
+
1107
+ for (i = 0; i < sizeof (ino_t); ++i)
1108
+ {
1109
+ /* shifting off the byte offsets out of "endianness" */
1110
+ int offs = (offsetof (eio_dirent, inode) + (endianness & 0xff)) * 8;
1111
+ endianness >>= 8;
1112
+
1113
+ for (j = 0; j < 8; ++j)
1114
+ if (inode_bits & (((ino_t)1) << (i * 8 + j)))
1115
+ *bit++ = offs + j;
1116
+ }
1117
+
1118
+ for (j = 0; j < 8; ++j)
1119
+ if (score_bits & (1 << j))
1120
+ *bit++ = offsetof (eio_dirent, score) * 8 + j;
1121
+ }
1122
+
1123
+ /* now actually do the sorting (a variant of MSD radix sort) */
1124
+ {
1125
+ eio_dirent *base_stk [9 + sizeof (ino_t) * 8], *base;
1126
+ eio_dirent *end_stk [9 + sizeof (ino_t) * 8], *end;
1127
+ unsigned char *bit_stk [9 + sizeof (ino_t) * 8];
1128
+ int stk_idx = 0;
1129
+
1130
+ base_stk [stk_idx] = dents;
1131
+ end_stk [stk_idx] = dents + size;
1132
+ bit_stk [stk_idx] = bit - 1;
1133
+
1134
+ do
1135
+ {
1136
+ base = base_stk [stk_idx];
1137
+ end = end_stk [stk_idx];
1138
+ bit = bit_stk [stk_idx];
1139
+
1140
+ for (;;)
1141
+ {
1142
+ unsigned char O = *bit >> 3;
1143
+ unsigned char M = 1 << (*bit & 7);
1144
+
1145
+ eio_dirent *a = base;
1146
+ eio_dirent *b = end;
1147
+
1148
+ if (b - a < EIO_SORT_CUTOFF)
1149
+ break;
1150
+
1151
+ /* now bit-partition the array on the bit */
1152
+ /* this ugly asymmetric loop seems to perform much better than typical */
1153
+ /* partition algos found in the literature */
1154
+ do
1155
+ if (!(((unsigned char *)a)[O] & M))
1156
+ ++a;
1157
+ else if (!(((unsigned char *)--b)[O] & M))
1158
+ {
1159
+ eio_dirent tmp = *a; *a = *b; *b = tmp;
1160
+ ++a;
1161
+ }
1162
+ while (b > a);
1163
+
1164
+ /* next bit, or stop, if no bits left in this path */
1165
+ if (!*--bit)
1166
+ break;
1167
+
1168
+ base_stk [stk_idx] = a;
1169
+ end_stk [stk_idx] = end;
1170
+ bit_stk [stk_idx] = bit;
1171
+ ++stk_idx;
1172
+
1173
+ end = a;
1174
+ }
1175
+ }
1176
+ while (stk_idx--);
1177
+ }
1178
+ }
1179
+
1180
+ static void
1181
+ eio_dent_insertion_sort (eio_dirent *dents, int size)
1182
+ {
1183
+ /* first move the smallest element to the front, to act as a sentinel */
1184
+ {
1185
+ int i;
1186
+ eio_dirent *min = dents;
1187
+
1188
+ /* the radix pre-pass ensures that the minimum element is in the first EIO_SORT_CUTOFF + 1 elements */
1189
+ for (i = size > EIO_SORT_FAST ? EIO_SORT_CUTOFF + 1 : size; --i; )
1190
+ if (EIO_DENT_CMP (dents [i], <, *min))
1191
+ min = &dents [i];
1192
+
1193
+ /* swap elements 0 and j (minimum) */
1194
+ {
1195
+ eio_dirent tmp = *dents; *dents = *min; *min = tmp;
1196
+ }
1197
+ }
1198
+
1199
+ /* then do standard insertion sort, assuming that all elements are >= dents [0] */
1200
+ {
1201
+ eio_dirent *i, *j;
1202
+
1203
+ for (i = dents + 1; i < dents + size; ++i)
1204
+ {
1205
+ eio_dirent value = *i;
1206
+
1207
+ for (j = i - 1; EIO_DENT_CMP (*j, >, value); --j)
1208
+ j [1] = j [0];
1209
+
1210
+ j [1] = value;
1211
+ }
1212
+ }
1213
+ }
1214
+
1215
+ static void
1216
+ eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits)
1217
+ {
1218
+ if (size <= 1)
1219
+ return; /* our insertion sort relies on size > 0 */
1220
+
1221
+ /* first we use a radix sort, but only for dirs >= EIO_SORT_FAST */
1222
+ /* and stop sorting when the partitions are <= EIO_SORT_CUTOFF */
1223
+ eio_dent_radix_sort (dents, size, score_bits, inode_bits);
1224
+
1225
+ /* use an insertion sort at the end, or for small arrays, */
1226
+ /* as insertion sort is more efficient for small partitions */
1227
+ eio_dent_insertion_sort (dents, size);
1228
+ }
1229
+
1230
+ /* read a full directory */
1231
+ static void
1232
+ eio__scandir (eio_req *req, etp_worker *self)
1233
+ {
1234
+ DIR *dirp;
1235
+ EIO_STRUCT_DIRENT *entp;
1236
+ char *name, *names;
1237
+ int namesalloc = 4096;
1238
+ int namesoffs = 0;
1239
+ int flags = req->int1;
1240
+ eio_dirent *dents = 0;
1241
+ int dentalloc = 128;
1242
+ int dentoffs = 0;
1243
+ ino_t inode_bits = 0;
1244
+
1245
+ req->result = -1;
1246
+
1247
+ if (!(flags & EIO_READDIR_DENTS))
1248
+ flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER);
1249
+
1250
+ X_LOCK (wrklock);
1251
+ /* the corresponding closedir is in ETP_WORKER_CLEAR */
1252
+ self->dirp = dirp = opendir (req->ptr1);
1253
+
1254
+ req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE;
1255
+ req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0;
1256
+ req->ptr2 = names = malloc (namesalloc);
1257
+ X_UNLOCK (wrklock);
1258
+
1259
+ if (dirp && names && (!flags || dents))
1260
+ for (;;)
1261
+ {
1262
+ errno = 0;
1263
+ entp = readdir (dirp);
1264
+
1265
+ if (!entp)
1266
+ {
1267
+ if (errno)
1268
+ break;
1269
+
1270
+ /* sort etc. */
1271
+ req->int1 = flags;
1272
+ req->result = dentoffs;
1273
+
1274
+ if (flags & EIO_READDIR_STAT_ORDER)
1275
+ eio_dent_sort (dents, dentoffs, flags & EIO_READDIR_DIRS_FIRST ? 7 : 0, inode_bits);
1276
+ else if (flags & EIO_READDIR_DIRS_FIRST)
1277
+ if (flags & EIO_READDIR_FOUND_UNKNOWN)
1278
+ eio_dent_sort (dents, dentoffs, 7, inode_bits); /* sort by score and inode */
1279
+ else
1280
+ {
1281
+ /* in this case, all is known, and we just put dirs first and sort them */
1282
+ eio_dirent *oth = dents + dentoffs;
1283
+ eio_dirent *dir = dents;
1284
+
1285
+ /* now partition dirs to the front, and non-dirs to the back */
1286
+ /* by walking from both sides and swapping if necessary */
1287
+ while (oth > dir)
1288
+ {
1289
+ if (dir->type == EIO_DT_DIR)
1290
+ ++dir;
1291
+ else if ((--oth)->type == EIO_DT_DIR)
1292
+ {
1293
+ eio_dirent tmp = *dir; *dir = *oth; *oth = tmp;
1294
+
1295
+ ++dir;
1296
+ }
1297
+ }
1298
+
1299
+ /* now sort the dirs only (dirs all have the same score) */
1300
+ eio_dent_sort (dents, dir - dents, 0, inode_bits);
1301
+ }
1302
+
1303
+ break;
1304
+ }
1305
+
1306
+ /* now add the entry to our list(s) */
1307
+ name = entp->d_name;
1308
+
1309
+ /* skip . and .. entries */
1310
+ if (name [0] != '.' || (name [1] && (name [1] != '.' || name [2])))
1311
+ {
1312
+ int len = D_NAMLEN (entp) + 1;
1313
+
1314
+ while (expect_false (namesoffs + len > namesalloc))
1315
+ {
1316
+ namesalloc *= 2;
1317
+ X_LOCK (wrklock);
1318
+ req->ptr2 = names = realloc (names, namesalloc);
1319
+ X_UNLOCK (wrklock);
1320
+
1321
+ if (!names)
1322
+ break;
1323
+ }
1324
+
1325
+ memcpy (names + namesoffs, name, len);
1326
+
1327
+ if (dents)
1328
+ {
1329
+ struct eio_dirent *ent;
1330
+
1331
+ if (expect_false (dentoffs == dentalloc))
1332
+ {
1333
+ dentalloc *= 2;
1334
+ X_LOCK (wrklock);
1335
+ req->ptr1 = dents = realloc (dents, dentalloc * sizeof (eio_dirent));
1336
+ X_UNLOCK (wrklock);
1337
+
1338
+ if (!dents)
1339
+ break;
1340
+ }
1341
+
1342
+ ent = dents + dentoffs;
1343
+
1344
+ ent->nameofs = namesoffs; /* rather dirtily we store the offset in the pointer */
1345
+ ent->namelen = len - 1;
1346
+ ent->inode = D_INO (entp);
1347
+
1348
+ inode_bits |= ent->inode;
1349
+
1350
+ switch (D_TYPE (entp))
1351
+ {
1352
+ default:
1353
+ ent->type = EIO_DT_UNKNOWN;
1354
+ flags |= EIO_READDIR_FOUND_UNKNOWN;
1355
+ break;
1356
+
1357
+ #ifdef DT_FIFO
1358
+ case DT_FIFO: ent->type = EIO_DT_FIFO; break;
1359
+ #endif
1360
+ #ifdef DT_CHR
1361
+ case DT_CHR: ent->type = EIO_DT_CHR; break;
1362
+ #endif
1363
+ #ifdef DT_MPC
1364
+ case DT_MPC: ent->type = EIO_DT_MPC; break;
1365
+ #endif
1366
+ #ifdef DT_DIR
1367
+ case DT_DIR: ent->type = EIO_DT_DIR; break;
1368
+ #endif
1369
+ #ifdef DT_NAM
1370
+ case DT_NAM: ent->type = EIO_DT_NAM; break;
1371
+ #endif
1372
+ #ifdef DT_BLK
1373
+ case DT_BLK: ent->type = EIO_DT_BLK; break;
1374
+ #endif
1375
+ #ifdef DT_MPB
1376
+ case DT_MPB: ent->type = EIO_DT_MPB; break;
1377
+ #endif
1378
+ #ifdef DT_REG
1379
+ case DT_REG: ent->type = EIO_DT_REG; break;
1380
+ #endif
1381
+ #ifdef DT_NWK
1382
+ case DT_NWK: ent->type = EIO_DT_NWK; break;
1383
+ #endif
1384
+ #ifdef DT_CMP
1385
+ case DT_CMP: ent->type = EIO_DT_CMP; break;
1386
+ #endif
1387
+ #ifdef DT_LNK
1388
+ case DT_LNK: ent->type = EIO_DT_LNK; break;
1389
+ #endif
1390
+ #ifdef DT_SOCK
1391
+ case DT_SOCK: ent->type = EIO_DT_SOCK; break;
1392
+ #endif
1393
+ #ifdef DT_DOOR
1394
+ case DT_DOOR: ent->type = EIO_DT_DOOR; break;
1395
+ #endif
1396
+ #ifdef DT_WHT
1397
+ case DT_WHT: ent->type = EIO_DT_WHT; break;
1398
+ #endif
1399
+ }
1400
+
1401
+ ent->score = 7;
1402
+
1403
+ if (flags & EIO_READDIR_DIRS_FIRST)
1404
+ {
1405
+ if (ent->type == EIO_DT_UNKNOWN)
1406
+ {
1407
+ if (*name == '.') /* leading dots are likely directories, and, in any case, rare */
1408
+ ent->score = 1;
1409
+ else if (!strchr (name, '.')) /* absense of dots indicate likely dirs */
1410
+ ent->score = len <= 2 ? 4 - len : len <= 4 ? 4 : len <= 7 ? 5 : 6; /* shorter == more likely dir, but avoid too many classes */
1411
+ }
1412
+ else if (ent->type == EIO_DT_DIR)
1413
+ ent->score = 0;
1414
+ }
1415
+ }
1416
+
1417
+ namesoffs += len;
1418
+ ++dentoffs;
1419
+ }
1420
+
1421
+ if (EIO_CANCELLED (req))
1422
+ {
1423
+ errno = ECANCELED;
1424
+ break;
1425
+ }
1426
+ }
1427
+ }
1428
+
1429
+ #ifdef PAGESIZE
1430
+ # define eio_pagesize() PAGESIZE
1431
+ #else
1432
+ static intptr_t
1433
+ eio_pagesize (void)
1434
+ {
1435
+ static intptr_t page;
1436
+
1437
+ if (!page)
1438
+ page = sysconf (_SC_PAGESIZE);
1439
+
1440
+ return page;
1441
+ }
1442
+ #endif
1443
+
1444
+ static void
1445
+ eio_page_align (void **addr, size_t *length)
1446
+ {
1447
+ intptr_t mask = eio_pagesize () - 1;
1448
+
1449
+ /* round down addr */
1450
+ intptr_t adj = mask & (intptr_t)*addr;
1451
+
1452
+ *addr = (void *)((intptr_t)*addr - adj);
1453
+ *length += adj;
1454
+
1455
+ /* round up length */
1456
+ *length = (*length + mask) & ~mask;
1457
+ }
1458
+
1459
+ #if !_POSIX_MEMLOCK
1460
+ # define eio__mlockall(a) ((errno = ENOSYS), -1)
1461
+ #else
1462
+
1463
+ static int
1464
+ eio__mlockall (int flags)
1465
+ {
1466
+ #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7
1467
+ extern int mallopt (int, int);
1468
+ mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */
1469
+ #endif
1470
+
1471
+ if (EIO_MCL_CURRENT != MCL_CURRENT
1472
+ || EIO_MCL_FUTURE != MCL_FUTURE)
1473
+ {
1474
+ flags = 0
1475
+ | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0)
1476
+ | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0);
1477
+ }
1478
+
1479
+ return mlockall (flags);
1480
+ }
1481
+ #endif
1482
+
1483
+ #if !_POSIX_MEMLOCK_RANGE
1484
+ # define eio__mlock(a,b) ((errno = ENOSYS), -1)
1485
+ #else
1486
+
1487
+ static int
1488
+ eio__mlock (void *addr, size_t length)
1489
+ {
1490
+ eio_page_align (&addr, &length);
1491
+
1492
+ return mlock (addr, length);
1493
+ }
1494
+
1495
+ #endif
1496
+
1497
+ #if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO)
1498
+ # define eio__msync(a,b,c) ((errno = ENOSYS), -1)
1499
+ #else
1500
+
1501
+ int
1502
+ eio__msync (void *mem, size_t len, int flags)
1503
+ {
1504
+ eio_page_align (&mem, &len);
1505
+
1506
+ if (EIO_MS_ASYNC != MS_SYNC
1507
+ || EIO_MS_INVALIDATE != MS_INVALIDATE
1508
+ || EIO_MS_SYNC != MS_SYNC)
1509
+ {
1510
+ flags = 0
1511
+ | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0)
1512
+ | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0)
1513
+ | (flags & EIO_MS_SYNC ? MS_SYNC : 0);
1514
+ }
1515
+
1516
+ return msync (mem, len, flags);
1517
+ }
1518
+
1519
+ #endif
1520
+
1521
+ int
1522
+ eio__mtouch (void *mem, size_t len, int flags)
1523
+ {
1524
+ eio_page_align (&mem, &len);
1525
+
1526
+ {
1527
+ intptr_t addr = (intptr_t)mem;
1528
+ intptr_t end = addr + len;
1529
+ intptr_t page = eio_pagesize ();
1530
+
1531
+ if (addr < end)
1532
+ if (flags & EIO_MT_MODIFY) /* modify */
1533
+ do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len);
1534
+ else
1535
+ do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len);
1536
+ }
1537
+
1538
+ return 0;
1539
+ }
1540
+
1541
+ /*****************************************************************************/
1542
+
1543
+ #define ALLOC(len) \
1544
+ if (!req->ptr2) \
1545
+ { \
1546
+ X_LOCK (wrklock); \
1547
+ req->flags |= EIO_FLAG_PTR2_FREE; \
1548
+ X_UNLOCK (wrklock); \
1549
+ req->ptr2 = malloc (len); \
1550
+ if (!req->ptr2) \
1551
+ { \
1552
+ errno = ENOMEM; \
1553
+ req->result = -1; \
1554
+ break; \
1555
+ } \
1556
+ }
1557
+
1558
+ X_THREAD_PROC (etp_proc)
1559
+ {
1560
+ ETP_REQ *req;
1561
+ struct timespec ts;
1562
+ etp_worker *self = (etp_worker *)thr_arg;
1563
+
1564
+ /* try to distribute timeouts somewhat randomly */
1565
+ ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL);
1566
+
1567
+ for (;;)
1568
+ {
1569
+ X_LOCK (reqlock);
1570
+
1571
+ for (;;)
1572
+ {
1573
+ self->req = req = reqq_shift (&req_queue);
1574
+
1575
+ if (req)
1576
+ break;
1577
+
1578
+ ++idle;
1579
+
1580
+ ts.tv_sec = time (0) + idle_timeout;
1581
+ if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT)
1582
+ {
1583
+ if (idle > max_idle)
1584
+ {
1585
+ --idle;
1586
+ X_UNLOCK (reqlock);
1587
+ X_LOCK (wrklock);
1588
+ --started;
1589
+ X_UNLOCK (wrklock);
1590
+ goto quit;
1591
+ }
1592
+
1593
+ /* we are allowed to idle, so do so without any timeout */
1594
+ X_COND_WAIT (reqwait, reqlock);
1595
+ }
1596
+
1597
+ --idle;
1598
+ }
1599
+
1600
+ --nready;
1601
+
1602
+ X_UNLOCK (reqlock);
1603
+
1604
+ if (req->type < 0)
1605
+ goto quit;
1606
+
1607
+ if (!EIO_CANCELLED (req))
1608
+ ETP_EXECUTE (self, req);
1609
+
1610
+ X_LOCK (reslock);
1611
+
1612
+ ++npending;
1613
+
1614
+ if (!reqq_push (&res_queue, req) && want_poll_cb)
1615
+ want_poll_cb ();
1616
+
1617
+ self->req = 0;
1618
+ etp_worker_clear (self);
1619
+
1620
+ X_UNLOCK (reslock);
1621
+ }
1622
+
1623
+ quit:
1624
+ X_LOCK (wrklock);
1625
+ etp_worker_free (self);
1626
+ X_UNLOCK (wrklock);
1627
+
1628
+ return 0;
1629
+ }
1630
+
1631
+ /*****************************************************************************/
1632
+
1633
+ int eio_init (void (*want_poll)(void), void (*done_poll)(void))
1634
+ {
1635
+ return etp_init (want_poll, done_poll);
1636
+ }
1637
+
1638
+ static void eio_api_destroy (eio_req *req)
1639
+ {
1640
+ free (req);
1641
+ }
1642
+
1643
+ #define REQ(rtype) \
1644
+ eio_req *req; \
1645
+ \
1646
+ req = (eio_req *)calloc (1, sizeof *req); \
1647
+ if (!req) \
1648
+ return 0; \
1649
+ \
1650
+ req->type = rtype; \
1651
+ req->pri = pri; \
1652
+ req->finish = cb; \
1653
+ req->data = data; \
1654
+ req->destroy = eio_api_destroy;
1655
+
1656
+ #define SEND eio_submit (req); return req
1657
+
1658
+ #define PATH \
1659
+ req->flags |= EIO_FLAG_PTR1_FREE; \
1660
+ req->ptr1 = strdup (path); \
1661
+ if (!req->ptr1) \
1662
+ { \
1663
+ eio_api_destroy (req); \
1664
+ return 0; \
1665
+ }
1666
+
1667
+ static void eio_execute (etp_worker *self, eio_req *req)
1668
+ {
1669
+ switch (req->type)
1670
+ {
1671
+ case EIO_READ: ALLOC (req->size);
1672
+ req->result = req->offs >= 0
1673
+ ? pread (req->int1, req->ptr2, req->size, req->offs)
1674
+ : read (req->int1, req->ptr2, req->size); break;
1675
+ case EIO_WRITE: req->result = req->offs >= 0
1676
+ ? pwrite (req->int1, req->ptr2, req->size, req->offs)
1677
+ : write (req->int1, req->ptr2, req->size); break;
1678
+
1679
+ case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break;
1680
+ case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size, self); break;
1681
+
1682
+ case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT));
1683
+ req->result = stat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break;
1684
+ case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT));
1685
+ req->result = lstat (req->ptr1, (EIO_STRUCT_STAT *)req->ptr2); break;
1686
+ case EIO_FSTAT: ALLOC (sizeof (EIO_STRUCT_STAT));
1687
+ req->result = fstat (req->int1, (EIO_STRUCT_STAT *)req->ptr2); break;
1688
+
1689
+ case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS));
1690
+ req->result = statvfs (req->ptr1, (EIO_STRUCT_STATVFS *)req->ptr2); break;
1691
+ case EIO_FSTATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS));
1692
+ req->result = fstatvfs (req->int1, (EIO_STRUCT_STATVFS *)req->ptr2); break;
1693
+
1694
+ case EIO_CHOWN: req->result = chown (req->ptr1, req->int2, req->int3); break;
1695
+ case EIO_FCHOWN: req->result = fchown (req->int1, req->int2, req->int3); break;
1696
+ case EIO_CHMOD: req->result = chmod (req->ptr1, (mode_t)req->int2); break;
1697
+ case EIO_FCHMOD: req->result = fchmod (req->int1, (mode_t)req->int2); break;
1698
+ case EIO_TRUNCATE: req->result = truncate (req->ptr1, req->offs); break;
1699
+ case EIO_FTRUNCATE: req->result = ftruncate (req->int1, req->offs); break;
1700
+
1701
+ case EIO_OPEN: req->result = open (req->ptr1, req->int1, (mode_t)req->int2); break;
1702
+ case EIO_CLOSE: req->result = close (req->int1); break;
1703
+ case EIO_DUP2: req->result = dup2 (req->int1, req->int2); break;
1704
+ case EIO_UNLINK: req->result = unlink (req->ptr1); break;
1705
+ case EIO_RMDIR: req->result = rmdir (req->ptr1); break;
1706
+ case EIO_MKDIR: req->result = mkdir (req->ptr1, (mode_t)req->int2); break;
1707
+ case EIO_RENAME: req->result = rename (req->ptr1, req->ptr2); break;
1708
+ case EIO_LINK: req->result = link (req->ptr1, req->ptr2); break;
1709
+ case EIO_SYMLINK: req->result = symlink (req->ptr1, req->ptr2); break;
1710
+ case EIO_MKNOD: req->result = mknod (req->ptr1, (mode_t)req->int2, (dev_t)req->offs); break;
1711
+
1712
+ case EIO_READLINK: ALLOC (PATH_MAX);
1713
+ req->result = readlink (req->ptr1, req->ptr2, PATH_MAX); break;
1714
+
1715
+ case EIO_SYNC: req->result = 0; sync (); break;
1716
+ case EIO_FSYNC: req->result = fsync (req->int1); break;
1717
+ case EIO_FDATASYNC: req->result = fdatasync (req->int1); break;
1718
+ case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break;
1719
+ case EIO_MTOUCH: req->result = eio__mtouch (req->ptr2, req->size, req->int1); break;
1720
+ case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break;
1721
+ case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break;
1722
+ case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break;
1723
+
1724
+ case EIO_READDIR: eio__scandir (req, self); break;
1725
+
1726
+ case EIO_BUSY:
1727
+ #ifdef _WIN32
1728
+ Sleep (req->nv1 * 1e3);
1729
+ #else
1730
+ {
1731
+ struct timeval tv;
1732
+
1733
+ tv.tv_sec = req->nv1;
1734
+ tv.tv_usec = (req->nv1 - tv.tv_sec) * 1e6;
1735
+
1736
+ req->result = select (0, 0, 0, 0, &tv);
1737
+ }
1738
+ #endif
1739
+ break;
1740
+
1741
+ case EIO_UTIME:
1742
+ case EIO_FUTIME:
1743
+ {
1744
+ struct timeval tv[2];
1745
+ struct timeval *times;
1746
+
1747
+ if (req->nv1 != -1. || req->nv2 != -1.)
1748
+ {
1749
+ tv[0].tv_sec = req->nv1;
1750
+ tv[0].tv_usec = (req->nv1 - tv[0].tv_sec) * 1000000.;
1751
+ tv[1].tv_sec = req->nv2;
1752
+ tv[1].tv_usec = (req->nv2 - tv[1].tv_sec) * 1000000.;
1753
+
1754
+ times = tv;
1755
+ }
1756
+ else
1757
+ times = 0;
1758
+
1759
+ req->result = req->type == EIO_FUTIME
1760
+ ? futimes (req->int1, times)
1761
+ : utimes (req->ptr1, times);
1762
+ }
1763
+ break;
1764
+
1765
+ case EIO_GROUP:
1766
+ abort (); /* handled in eio_request */
1767
+
1768
+ case EIO_NOP:
1769
+ req->result = 0;
1770
+ break;
1771
+
1772
+ case EIO_CUSTOM:
1773
+ ((void (*)(eio_req *))req->feed) (req);
1774
+ break;
1775
+
1776
+ default:
1777
+ errno = ENOSYS;
1778
+ req->result = -1;
1779
+ break;
1780
+ }
1781
+
1782
+ req->errorno = errno;
1783
+ }
1784
+
1785
+ #ifndef EIO_NO_WRAPPERS
1786
+
1787
+ eio_req *eio_nop (int pri, eio_cb cb, void *data)
1788
+ {
1789
+ REQ (EIO_NOP); SEND;
1790
+ }
1791
+
1792
+ eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data)
1793
+ {
1794
+ REQ (EIO_BUSY); req->nv1 = delay; SEND;
1795
+ }
1796
+
1797
+ eio_req *eio_sync (int pri, eio_cb cb, void *data)
1798
+ {
1799
+ REQ (EIO_SYNC); SEND;
1800
+ }
1801
+
1802
+ eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data)
1803
+ {
1804
+ REQ (EIO_FSYNC); req->int1 = fd; SEND;
1805
+ }
1806
+
1807
+ eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data)
1808
+ {
1809
+ REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND;
1810
+ }
1811
+
1812
+ eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data)
1813
+ {
1814
+ REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND;
1815
+ }
1816
+
1817
+ eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data)
1818
+ {
1819
+ REQ (EIO_MLOCK); req->ptr2 = addr; req->size = length; SEND;
1820
+ }
1821
+
1822
+ eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data)
1823
+ {
1824
+ REQ (EIO_MLOCKALL); req->int1 = flags; SEND;
1825
+ }
1826
+
1827
+ eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data)
1828
+ {
1829
+ REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND;
1830
+ }
1831
+
1832
+ eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data)
1833
+ {
1834
+ REQ (EIO_FDATASYNC); req->int1 = fd; SEND;
1835
+ }
1836
+
1837
+ eio_req *eio_close (int fd, int pri, eio_cb cb, void *data)
1838
+ {
1839
+ REQ (EIO_CLOSE); req->int1 = fd; SEND;
1840
+ }
1841
+
1842
+ eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data)
1843
+ {
1844
+ REQ (EIO_READAHEAD); req->int1 = fd; req->offs = offset; req->size = length; SEND;
1845
+ }
1846
+
1847
+ eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data)
1848
+ {
1849
+ REQ (EIO_READ); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND;
1850
+ }
1851
+
1852
+ eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data)
1853
+ {
1854
+ REQ (EIO_WRITE); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND;
1855
+ }
1856
+
1857
+ eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data)
1858
+ {
1859
+ REQ (EIO_FSTAT); req->int1 = fd; SEND;
1860
+ }
1861
+
1862
+ eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data)
1863
+ {
1864
+ REQ (EIO_FSTATVFS); req->int1 = fd; SEND;
1865
+ }
1866
+
1867
+ eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data)
1868
+ {
1869
+ REQ (EIO_FUTIME); req->int1 = fd; req->nv1 = atime; req->nv2 = mtime; SEND;
1870
+ }
1871
+
1872
+ eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data)
1873
+ {
1874
+ REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND;
1875
+ }
1876
+
1877
+ eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data)
1878
+ {
1879
+ REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND;
1880
+ }
1881
+
1882
+ eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data)
1883
+ {
1884
+ REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND;
1885
+ }
1886
+
1887
+ eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data)
1888
+ {
1889
+ REQ (EIO_DUP2); req->int1 = fd; req->int2 = fd2; SEND;
1890
+ }
1891
+
1892
+ eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data)
1893
+ {
1894
+ REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND;
1895
+ }
1896
+
1897
+ eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data)
1898
+ {
1899
+ REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND;
1900
+ }
1901
+
1902
+ eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data)
1903
+ {
1904
+ REQ (EIO_UTIME); PATH; req->nv1 = atime; req->nv2 = mtime; SEND;
1905
+ }
1906
+
1907
+ eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data)
1908
+ {
1909
+ REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND;
1910
+ }
1911
+
1912
+ eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data)
1913
+ {
1914
+ REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND;
1915
+ }
1916
+
1917
+ eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data)
1918
+ {
1919
+ REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND;
1920
+ }
1921
+
1922
+ eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data)
1923
+ {
1924
+ REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND;
1925
+ }
1926
+
1927
+ static eio_req *
1928
+ eio__1path (int type, const char *path, int pri, eio_cb cb, void *data)
1929
+ {
1930
+ REQ (type); PATH; SEND;
1931
+ }
1932
+
1933
+ eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data)
1934
+ {
1935
+ return eio__1path (EIO_READLINK, path, pri, cb, data);
1936
+ }
1937
+
1938
+ eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data)
1939
+ {
1940
+ return eio__1path (EIO_STAT, path, pri, cb, data);
1941
+ }
1942
+
1943
+ eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data)
1944
+ {
1945
+ return eio__1path (EIO_LSTAT, path, pri, cb, data);
1946
+ }
1947
+
1948
+ eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data)
1949
+ {
1950
+ return eio__1path (EIO_STATVFS, path, pri, cb, data);
1951
+ }
1952
+
1953
+ eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data)
1954
+ {
1955
+ return eio__1path (EIO_UNLINK, path, pri, cb, data);
1956
+ }
1957
+
1958
+ eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data)
1959
+ {
1960
+ return eio__1path (EIO_RMDIR, path, pri, cb, data);
1961
+ }
1962
+
1963
+ eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data)
1964
+ {
1965
+ REQ (EIO_READDIR); PATH; req->int1 = flags; SEND;
1966
+ }
1967
+
1968
+ eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data)
1969
+ {
1970
+ REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND;
1971
+ }
1972
+
1973
+ static eio_req *
1974
+ eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data)
1975
+ {
1976
+ REQ (type); PATH;
1977
+
1978
+ req->flags |= EIO_FLAG_PTR2_FREE;
1979
+ req->ptr2 = strdup (new_path);
1980
+ if (!req->ptr2)
1981
+ {
1982
+ eio_api_destroy (req);
1983
+ return 0;
1984
+ }
1985
+
1986
+ SEND;
1987
+ }
1988
+
1989
+ eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data)
1990
+ {
1991
+ return eio__2path (EIO_LINK, path, new_path, pri, cb, data);
1992
+ }
1993
+
1994
+ eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data)
1995
+ {
1996
+ return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data);
1997
+ }
1998
+
1999
+ eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data)
2000
+ {
2001
+ return eio__2path (EIO_RENAME, path, new_path, pri, cb, data);
2002
+ }
2003
+
2004
+ eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data)
2005
+ {
2006
+ REQ (EIO_CUSTOM); req->feed = (void (*)(eio_req *))execute; SEND;
2007
+ }
2008
+
2009
+ #endif
2010
+
2011
+ eio_req *eio_grp (eio_cb cb, void *data)
2012
+ {
2013
+ const int pri = EIO_PRI_MAX;
2014
+
2015
+ REQ (EIO_GROUP); SEND;
2016
+ }
2017
+
2018
+ #undef REQ
2019
+ #undef PATH
2020
+ #undef SEND
2021
+
2022
+ /*****************************************************************************/
2023
+ /* grp functions */
2024
+
2025
+ void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit)
2026
+ {
2027
+ grp->int2 = limit;
2028
+ grp->feed = feed;
2029
+
2030
+ grp_try_feed (grp);
2031
+ }
2032
+
2033
+ void eio_grp_limit (eio_req *grp, int limit)
2034
+ {
2035
+ grp->int2 = limit;
2036
+
2037
+ grp_try_feed (grp);
2038
+ }
2039
+
2040
+ void eio_grp_add (eio_req *grp, eio_req *req)
2041
+ {
2042
+ assert (("cannot add requests to IO::AIO::GRP after the group finished", grp->int1 != 2));
2043
+
2044
+ grp->flags |= EIO_FLAG_GROUPADD;
2045
+
2046
+ ++grp->size;
2047
+ req->grp = grp;
2048
+
2049
+ req->grp_prev = 0;
2050
+ req->grp_next = grp->grp_first;
2051
+
2052
+ if (grp->grp_first)
2053
+ grp->grp_first->grp_prev = req;
2054
+
2055
+ grp->grp_first = req;
2056
+ }
2057
+
2058
+ /*****************************************************************************/
2059
+ /* misc garbage */
2060
+
2061
+ ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count)
2062
+ {
2063
+ etp_worker wrk;
2064
+ ssize_t ret;
2065
+
2066
+ wrk.dbuf = 0;
2067
+
2068
+ ret = eio__sendfile (ofd, ifd, offset, count, &wrk);
2069
+
2070
+ if (wrk.dbuf)
2071
+ free (wrk.dbuf);
2072
+
2073
+ return ret;
2074
+ }
2075
+