ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/IO-AIO/AIO.xs
Revision: 1.9
Committed: Tue Jul 12 11:02:54 2005 UTC (18 years, 10 months ago) by root
Branch: MAIN
CVS Tags: rel-0_5
Changes since 1.8: +0 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <signal.h>
10 #include <sched.h>
11
12 #include <pthread.h>
13 #include <sys/syscall.h>
14
15 typedef void *InputStream; /* hack, but 5.6.1 is simply toooo old ;) */
16 typedef void *OutputStream; /* hack, but 5.6.1 is simply toooo old ;) */
17 typedef void *InOutStream; /* hack, but 5.6.1 is simply toooo old ;) */
18
19 #if __ia64
20 # define STACKSIZE 65536
21 #else
22 # define STACKSIZE 4096
23 #endif
24
25 enum {
26 REQ_QUIT,
27 REQ_OPEN, REQ_CLOSE,
28 REQ_READ, REQ_WRITE, REQ_READAHEAD,
29 REQ_STAT, REQ_LSTAT, REQ_FSTAT, REQ_UNLINK,
30 REQ_FSYNC, REQ_FDATASYNC,
31 };
32
33 typedef struct aio_cb {
34 struct aio_cb *volatile next;
35
36 int type;
37
38 int fd;
39 off_t offset;
40 size_t length;
41 ssize_t result;
42 mode_t mode; /* open */
43 int errorno;
44 SV *data, *callback;
45 void *dataptr;
46 STRLEN dataoffset;
47
48 Stat_t *statdata;
49 } aio_cb;
50
51 typedef aio_cb *aio_req;
52
53 static int started;
54 static int nreqs;
55 static int max_outstanding = 1<<30;
56 static int respipe [2];
57
58 static pthread_mutex_t reslock = PTHREAD_MUTEX_INITIALIZER;
59 static pthread_mutex_t reqlock = PTHREAD_MUTEX_INITIALIZER;
60 static pthread_cond_t reqwait = PTHREAD_COND_INITIALIZER;
61
62 static volatile aio_req reqs, reqe; /* queue start, queue end */
63 static volatile aio_req ress, rese; /* queue start, queue end */
64
65 static void
66 poll_wait ()
67 {
68 if (!nreqs)
69 return;
70
71 fd_set rfd;
72 FD_ZERO(&rfd);
73 FD_SET(respipe [0], &rfd);
74
75 select (respipe [0] + 1, &rfd, 0, 0, 0);
76 }
77
78 static int
79 poll_cb ()
80 {
81 dSP;
82 int count = 0;
83 aio_req req;
84
85 {
86 /* read and signals sent by the worker threads */
87 char buf [32];
88 while (read (respipe [0], buf, 32) > 0)
89 ;
90 }
91
92 for (;;)
93 {
94 pthread_mutex_lock (&reslock);
95
96 req = ress;
97
98 if (ress)
99 {
100 ress = ress->next;
101 if (!ress) rese = 0;
102 }
103
104 pthread_mutex_unlock (&reslock);
105
106 if (!req)
107 break;
108
109 nreqs--;
110
111 if (req->type == REQ_QUIT)
112 started--;
113 else
114 {
115 int errorno = errno;
116 errno = req->errorno;
117
118 if (req->type == REQ_READ)
119 SvCUR_set (req->data, req->dataoffset
120 + req->result > 0 ? req->result : 0);
121
122 if (req->data)
123 SvREFCNT_dec (req->data);
124
125 if (req->type == REQ_STAT || req->type == REQ_LSTAT || req->type == REQ_FSTAT)
126 {
127 PL_laststype = req->type == REQ_LSTAT ? OP_LSTAT : OP_STAT;
128 PL_laststatval = req->result;
129 PL_statcache = *(req->statdata);
130
131 Safefree (req->statdata);
132 }
133
134 PUSHMARK (SP);
135 XPUSHs (sv_2mortal (newSViv (req->result)));
136
137 if (req->type == REQ_OPEN)
138 {
139 /* convert fd to fh */
140 SV *fh;
141
142 PUTBACK;
143 call_pv ("IO::AIO::_fd2fh", G_SCALAR | G_EVAL);
144 SPAGAIN;
145
146 fh = POPs;
147
148 PUSHMARK (SP);
149 XPUSHs (fh);
150 }
151
152 if (SvOK (req->callback))
153 {
154 PUTBACK;
155 call_sv (req->callback, G_VOID | G_EVAL);
156 SPAGAIN;
157 }
158
159 if (req->callback)
160 SvREFCNT_dec (req->callback);
161
162 errno = errorno;
163 count++;
164 }
165
166 Safefree (req);
167 }
168
169 return count;
170 }
171
172 static void *aio_proc(void *arg);
173
174 static void
175 start_thread (void)
176 {
177 sigset_t fullsigset, oldsigset;
178 pthread_t tid;
179 pthread_attr_t attr;
180
181 pthread_attr_init (&attr);
182 pthread_attr_setstacksize (&attr, STACKSIZE);
183 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
184
185 sigfillset (&fullsigset);
186 sigprocmask (SIG_SETMASK, &fullsigset, &oldsigset);
187
188 if (pthread_create (&tid, &attr, aio_proc, 0) == 0)
189 started++;
190
191 sigprocmask (SIG_SETMASK, &oldsigset, 0);
192 }
193
194 static void
195 send_req (aio_req req)
196 {
197 nreqs++;
198
199 pthread_mutex_lock (&reqlock);
200
201 req->next = 0;
202
203 if (reqe)
204 {
205 reqe->next = req;
206 reqe = req;
207 }
208 else
209 reqe = reqs = req;
210
211 pthread_cond_signal (&reqwait);
212 pthread_mutex_unlock (&reqlock);
213
214 while (nreqs > max_outstanding)
215 {
216 poll_wait ();
217 poll_cb ();
218 }
219 }
220
221 static void
222 end_thread (void)
223 {
224 aio_req req;
225 New (0, req, 1, aio_cb);
226 req->type = REQ_QUIT;
227
228 send_req (req);
229 }
230
231 static void
232 read_write (int dowrite, int fd, off_t offset, size_t length,
233 SV *data, STRLEN dataoffset, SV *callback)
234 {
235 aio_req req;
236 STRLEN svlen;
237 char *svptr = SvPV (data, svlen);
238
239 SvUPGRADE (data, SVt_PV);
240 SvPOK_on (data);
241
242 if (dataoffset < 0)
243 dataoffset += svlen;
244
245 if (dataoffset < 0 || dataoffset > svlen)
246 croak ("data offset outside of string");
247
248 if (dowrite)
249 {
250 /* write: check length and adjust. */
251 if (length < 0 || length + dataoffset > svlen)
252 length = svlen - dataoffset;
253 }
254 else
255 {
256 /* read: grow scalar as necessary */
257 svptr = SvGROW (data, length + dataoffset);
258 }
259
260 if (length < 0)
261 croak ("length must not be negative");
262
263 Newz (0, req, 1, aio_cb);
264
265 if (!req)
266 croak ("out of memory during aio_req allocation");
267
268 req->type = dowrite ? REQ_WRITE : REQ_READ;
269 req->fd = fd;
270 req->offset = offset;
271 req->length = length;
272 req->data = SvREFCNT_inc (data);
273 req->dataptr = (char *)svptr + dataoffset;
274 req->callback = SvREFCNT_inc (callback);
275
276 send_req (req);
277 }
278
279 static void *
280 aio_proc (void *thr_arg)
281 {
282 aio_req req;
283 int type;
284
285 do
286 {
287 pthread_mutex_lock (&reqlock);
288
289 for (;;)
290 {
291 req = reqs;
292
293 if (reqs)
294 {
295 reqs = reqs->next;
296 if (!reqs) reqe = 0;
297 }
298
299 if (req)
300 break;
301
302 pthread_cond_wait (&reqwait, &reqlock);
303 }
304
305 pthread_mutex_unlock (&reqlock);
306
307 errno = 0; /* strictly unnecessary */
308
309 type = req->type;
310
311 switch (type)
312 {
313 case REQ_READ: req->result = pread64 (req->fd, req->dataptr, req->length, req->offset); break;
314 case REQ_WRITE: req->result = pwrite64 (req->fd, req->dataptr, req->length, req->offset); break;
315 #if SYS_readahead
316 case REQ_READAHEAD: req->result = readahead (req->fd, req->offset, req->length); break;
317 #else
318 case REQ_READAHEAD: req->result = -1; errno = ENOSYS; break;
319 #endif
320
321 case REQ_STAT: req->result = stat (req->dataptr, req->statdata); break;
322 case REQ_LSTAT: req->result = lstat (req->dataptr, req->statdata); break;
323 case REQ_FSTAT: req->result = fstat (req->fd , req->statdata); break;
324
325 case REQ_OPEN: req->result = open (req->dataptr, req->fd, req->mode); break;
326 case REQ_CLOSE: req->result = close (req->fd); break;
327 case REQ_UNLINK: req->result = unlink (req->dataptr); break;
328
329 case REQ_FSYNC: req->result = fsync (req->fd); break;
330 case REQ_FDATASYNC: req->result = fdatasync (req->fd); break;
331
332 case REQ_QUIT:
333 break;
334
335 default:
336 req->result = ENOSYS;
337 break;
338 }
339
340 req->errorno = errno;
341
342 pthread_mutex_lock (&reslock);
343
344 req->next = 0;
345
346 if (rese)
347 {
348 rese->next = req;
349 rese = req;
350 }
351 else
352 {
353 rese = ress = req;
354
355 /* write a dummy byte to the pipe so fh becomes ready */
356 write (respipe [1], &respipe, 1);
357 }
358
359 pthread_mutex_unlock (&reslock);
360 }
361 while (type != REQ_QUIT);
362
363 return 0;
364 }
365
366 MODULE = IO::AIO PACKAGE = IO::AIO
367
368 PROTOTYPES: ENABLE
369
370 BOOT:
371 {
372 if (pipe (respipe))
373 croak ("unable to initialize result pipe");
374
375 if (fcntl (respipe [0], F_SETFL, O_NONBLOCK))
376 croak ("cannot set result pipe to nonblocking mode");
377
378 if (fcntl (respipe [1], F_SETFL, O_NONBLOCK))
379 croak ("cannot set result pipe to nonblocking mode");
380 }
381
382 void
383 min_parallel(nthreads)
384 int nthreads
385 PROTOTYPE: $
386 CODE:
387 while (nthreads > started)
388 start_thread ();
389
390 void
391 max_parallel(nthreads)
392 int nthreads
393 PROTOTYPE: $
394 CODE:
395 {
396 int cur = started;
397 while (cur > nthreads)
398 {
399 end_thread ();
400 cur--;
401 }
402
403 while (started > nthreads)
404 {
405 poll_wait ();
406 poll_cb ();
407 }
408 }
409
410 int
411 max_outstanding(nreqs)
412 int nreqs
413 PROTOTYPE: $
414 CODE:
415 RETVAL = max_outstanding;
416 max_outstanding = nreqs;
417
418 void
419 aio_open(pathname,flags,mode,callback=&PL_sv_undef)
420 SV * pathname
421 int flags
422 int mode
423 SV * callback
424 PROTOTYPE: $$$;$
425 CODE:
426 {
427 aio_req req;
428
429 Newz (0, req, 1, aio_cb);
430
431 if (!req)
432 croak ("out of memory during aio_req allocation");
433
434 req->type = REQ_OPEN;
435 req->data = newSVsv (pathname);
436 req->dataptr = SvPV_nolen (req->data);
437 req->fd = flags;
438 req->mode = mode;
439 req->callback = SvREFCNT_inc (callback);
440
441 send_req (req);
442 }
443
444 void
445 aio_close(fh,callback=&PL_sv_undef)
446 InputStream fh
447 SV * callback
448 PROTOTYPE: $;$
449 ALIAS:
450 aio_close = REQ_CLOSE
451 aio_fsync = REQ_FSYNC
452 aio_fdatasync = REQ_FDATASYNC
453 CODE:
454 {
455 aio_req req;
456
457 Newz (0, req, 1, aio_cb);
458
459 if (!req)
460 croak ("out of memory during aio_req allocation");
461
462 req->type = ix;
463 req->fd = PerlIO_fileno (fh);
464 req->callback = SvREFCNT_inc (callback);
465
466 send_req (req);
467 }
468
469 void
470 aio_read(fh,offset,length,data,dataoffset,callback=&PL_sv_undef)
471 InputStream fh
472 UV offset
473 IV length
474 SV * data
475 IV dataoffset
476 SV * callback
477 PROTOTYPE: $$$$$;$
478 CODE:
479 read_write (0, PerlIO_fileno (fh), offset, length, data, dataoffset, callback);
480
481 void
482 aio_write(fh,offset,length,data,dataoffset,callback=&PL_sv_undef)
483 OutputStream fh
484 UV offset
485 IV length
486 SV * data
487 IV dataoffset
488 SV * callback
489 PROTOTYPE: $$$$$;$
490 CODE:
491 read_write (1, PerlIO_fileno (fh), offset, length, data, dataoffset, callback);
492
493 void
494 aio_readahead(fh,offset,length,callback=&PL_sv_undef)
495 InputStream fh
496 UV offset
497 IV length
498 SV * callback
499 PROTOTYPE: $$$;$
500 CODE:
501 {
502 aio_req req;
503
504 if (length < 0)
505 croak ("length must not be negative");
506
507 Newz (0, req, 1, aio_cb);
508
509 if (!req)
510 croak ("out of memory during aio_req allocation");
511
512 req->type = REQ_READAHEAD;
513 req->fd = PerlIO_fileno (fh);
514 req->offset = offset;
515 req->length = length;
516 req->callback = SvREFCNT_inc (callback);
517
518 send_req (req);
519 }
520
521 void
522 aio_stat(fh_or_path,callback=&PL_sv_undef)
523 SV * fh_or_path
524 SV * callback
525 ALIAS:
526 aio_stat = REQ_STAT
527 aio_lstat = REQ_LSTAT
528 CODE:
529 {
530 aio_req req;
531
532 Newz (0, req, 1, aio_cb);
533
534 if (!req)
535 croak ("out of memory during aio_req allocation");
536
537 New (0, req->statdata, 1, Stat_t);
538
539 if (!req->statdata)
540 croak ("out of memory during aio_req->statdata allocation");
541
542 if (SvPOK (fh_or_path))
543 {
544 req->type = ix;
545 req->data = newSVsv (fh_or_path);
546 req->dataptr = SvPV_nolen (req->data);
547 }
548 else
549 {
550 req->type = REQ_FSTAT;
551 req->fd = PerlIO_fileno (IoIFP (sv_2io (fh_or_path)));
552 }
553
554 req->callback = SvREFCNT_inc (callback);
555
556 send_req (req);
557 }
558
559 void
560 aio_unlink(pathname,callback=&PL_sv_undef)
561 SV * pathname
562 SV * callback
563 CODE:
564 {
565 aio_req req;
566
567 Newz (0, req, 1, aio_cb);
568
569 if (!req)
570 croak ("out of memory during aio_req allocation");
571
572 req->type = REQ_UNLINK;
573 req->data = newSVsv (pathname);
574 req->dataptr = SvPV_nolen (req->data);
575 req->callback = SvREFCNT_inc (callback);
576
577 send_req (req);
578 }
579
580 void
581 flush()
582 PROTOTYPE:
583 CODE:
584 while (nreqs)
585 {
586 poll_wait ();
587 poll_cb ();
588 }
589
590 void
591 poll()
592 PROTOTYPE:
593 CODE:
594 if (nreqs)
595 {
596 poll_wait ();
597 poll_cb ();
598 }
599
600 int
601 poll_fileno()
602 PROTOTYPE:
603 CODE:
604 RETVAL = respipe [0];
605 OUTPUT:
606 RETVAL
607
608 int
609 poll_cb(...)
610 PROTOTYPE:
611 CODE:
612 RETVAL = poll_cb ();
613 OUTPUT:
614 RETVAL
615
616 void
617 poll_wait()
618 PROTOTYPE:
619 CODE:
620 if (nreqs)
621 poll_wait ();
622
623 int
624 nreqs()
625 PROTOTYPE:
626 CODE:
627 RETVAL = nreqs;
628 OUTPUT:
629 RETVAL
630