ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/IO-AIO/AIO.xs
Revision: 1.29
Committed: Wed Aug 17 06:12:10 2005 UTC (18 years, 9 months ago) by root
Branch: MAIN
CVS Tags: rel-1_4
Changes since 1.28: +4 -0 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #define _REENTRANT 1
2 #include <errno.h>
3
4 #include "EXTERN.h"
5 #include "perl.h"
6 #include "XSUB.h"
7
8 #include "autoconf/config.h"
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <sched.h>
17
18 #include <pthread.h>
19
20 typedef void *InputStream; /* hack, but 5.6.1 is simply toooo old ;) */
21 typedef void *OutputStream; /* hack, but 5.6.1 is simply toooo old ;) */
22 typedef void *InOutStream; /* hack, but 5.6.1 is simply toooo old ;) */
23
24 #if __ia64
25 # define STACKSIZE 65536
26 #else
27 # define STACKSIZE 4096
28 #endif
29
30 enum {
31 REQ_QUIT,
32 REQ_OPEN, REQ_CLOSE,
33 REQ_READ, REQ_WRITE, REQ_READAHEAD,
34 REQ_STAT, REQ_LSTAT, REQ_FSTAT,
35 REQ_FSYNC, REQ_FDATASYNC,
36 REQ_UNLINK, REQ_RMDIR,
37 REQ_SYMLINK,
38 };
39
40 typedef struct aio_cb {
41 struct aio_cb *volatile next;
42
43 int type;
44
45 int fd;
46 off_t offset;
47 size_t length;
48 ssize_t result;
49 mode_t mode; /* open */
50 int errorno;
51 SV *data, *callback, *fh;
52 void *dataptr, *data2ptr;
53 STRLEN dataoffset;
54
55 Stat_t *statdata;
56 } aio_cb;
57
58 typedef aio_cb *aio_req;
59
60 static int started;
61 static volatile int nreqs;
62 static int max_outstanding = 1<<30;
63 static int respipe [2];
64
65 static pthread_mutex_t reslock = PTHREAD_MUTEX_INITIALIZER;
66 static pthread_mutex_t reqlock = PTHREAD_MUTEX_INITIALIZER;
67 static pthread_cond_t reqwait = PTHREAD_COND_INITIALIZER;
68
69 static volatile aio_req reqs, reqe; /* queue start, queue end */
70 static volatile aio_req ress, rese; /* queue start, queue end */
71
72 static void free_req (aio_req req)
73 {
74 if (req->data)
75 SvREFCNT_dec (req->data);
76
77 if (req->fh)
78 SvREFCNT_dec (req->fh);
79
80 if (req->statdata)
81 Safefree (req->statdata);
82
83 if (req->callback)
84 SvREFCNT_dec (req->callback);
85
86 Safefree (req);
87 }
88
89 static void
90 poll_wait ()
91 {
92 if (nreqs && !ress)
93 {
94 fd_set rfd;
95 FD_ZERO(&rfd);
96 FD_SET(respipe [0], &rfd);
97
98 select (respipe [0] + 1, &rfd, 0, 0, 0);
99 }
100 }
101
102 static int
103 poll_cb ()
104 {
105 dSP;
106 int count = 0;
107 int do_croak = 0;
108 aio_req req;
109
110 for (;;)
111 {
112 pthread_mutex_lock (&reslock);
113 req = ress;
114
115 if (req)
116 {
117 ress = req->next;
118
119 if (!ress)
120 {
121 rese = 0;
122
123 /* read any signals sent by the worker threads */
124 char buf [32];
125 while (read (respipe [0], buf, 32) == 32)
126 ;
127 }
128 }
129
130 pthread_mutex_unlock (&reslock);
131
132 if (!req)
133 break;
134
135 nreqs--;
136
137 if (req->type == REQ_QUIT)
138 started--;
139 else
140 {
141 int errorno = errno;
142 errno = req->errorno;
143
144 if (req->type == REQ_READ)
145 SvCUR_set (req->data, req->dataoffset + (req->result > 0 ? req->result : 0));
146
147 if (req->data2ptr && (req->type == REQ_READ || req->type == REQ_WRITE))
148 SvREADONLY_off (req->data);
149
150 if (req->statdata)
151 {
152 PL_laststype = req->type == REQ_LSTAT ? OP_LSTAT : OP_STAT;
153 PL_laststatval = req->result;
154 PL_statcache = *(req->statdata);
155 }
156
157 ENTER;
158 PUSHMARK (SP);
159 XPUSHs (sv_2mortal (newSViv (req->result)));
160
161 if (req->type == REQ_OPEN)
162 {
163 /* convert fd to fh */
164 SV *fh;
165
166 PUTBACK;
167 call_pv ("IO::AIO::_fd2fh", G_SCALAR | G_EVAL);
168 SPAGAIN;
169
170 fh = SvREFCNT_inc (POPs);
171
172 PUSHMARK (SP);
173 XPUSHs (sv_2mortal (fh));
174 }
175
176 if (SvOK (req->callback))
177 {
178 PUTBACK;
179 call_sv (req->callback, G_VOID | G_EVAL);
180 SPAGAIN;
181
182 if (SvTRUE (ERRSV))
183 {
184 free_req (req);
185 croak (0);
186 }
187 }
188
189 LEAVE;
190
191 errno = errorno;
192 count++;
193 }
194
195 free_req (req);
196 }
197
198 return count;
199 }
200
201 static void *aio_proc(void *arg);
202
203 static void
204 start_thread (void)
205 {
206 sigset_t fullsigset, oldsigset;
207 pthread_t tid;
208 pthread_attr_t attr;
209
210 pthread_attr_init (&attr);
211 pthread_attr_setstacksize (&attr, STACKSIZE);
212 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
213
214 sigfillset (&fullsigset);
215 sigprocmask (SIG_SETMASK, &fullsigset, &oldsigset);
216
217 if (pthread_create (&tid, &attr, aio_proc, 0) == 0)
218 started++;
219
220 sigprocmask (SIG_SETMASK, &oldsigset, 0);
221 }
222
223 static void
224 send_req (aio_req req)
225 {
226 nreqs++;
227
228 pthread_mutex_lock (&reqlock);
229
230 req->next = 0;
231
232 if (reqe)
233 {
234 reqe->next = req;
235 reqe = req;
236 }
237 else
238 reqe = reqs = req;
239
240 pthread_cond_signal (&reqwait);
241 pthread_mutex_unlock (&reqlock);
242
243 if (nreqs > max_outstanding)
244 for (;;)
245 {
246 poll_cb ();
247
248 if (nreqs <= max_outstanding)
249 break;
250
251 poll_wait ();
252 }
253 }
254
255 static void
256 end_thread (void)
257 {
258 aio_req req;
259 Newz (0, req, 1, aio_cb);
260 req->type = REQ_QUIT;
261
262 send_req (req);
263 }
264
265 static void min_parallel (int nthreads)
266 {
267 while (nthreads > started)
268 start_thread ();
269 }
270
271 static void max_parallel (int nthreads)
272 {
273 int cur = started;
274
275 while (cur > nthreads)
276 {
277 end_thread ();
278 cur--;
279 }
280
281 while (started > nthreads)
282 {
283 poll_wait ();
284 poll_cb ();
285 }
286 }
287
288 static void create_pipe ()
289 {
290 if (pipe (respipe))
291 croak ("unable to initialize result pipe");
292
293 if (fcntl (respipe [0], F_SETFL, O_NONBLOCK))
294 croak ("cannot set result pipe to nonblocking mode");
295
296 if (fcntl (respipe [1], F_SETFL, O_NONBLOCK))
297 croak ("cannot set result pipe to nonblocking mode");
298 }
299
300 static void atfork_prepare (void)
301 {
302 pthread_mutex_lock (&reqlock);
303 pthread_mutex_lock (&reslock);
304 }
305
306 static void atfork_parent (void)
307 {
308 pthread_mutex_unlock (&reslock);
309 pthread_mutex_unlock (&reqlock);
310 }
311
312 static void atfork_child (void)
313 {
314 aio_req prv;
315
316 int restart = started;
317 started = 0;
318
319 while (reqs)
320 {
321 prv = reqs;
322 reqs = prv->next;
323 free_req (prv);
324 }
325
326 reqs = reqe = 0;
327
328 while (ress)
329 {
330 prv = ress;
331 ress = prv->next;
332 free_req (prv);
333 }
334
335 ress = rese = 0;
336
337 close (respipe [0]);
338 close (respipe [1]);
339 create_pipe ();
340
341 atfork_parent ();
342
343 min_parallel (restart);
344 }
345
346 /*****************************************************************************/
347 /* work around various missing functions */
348
349 #if !HAVE_PREADWRITE
350 # define pread aio_pread
351 # define pwrite aio_pwrite
352
353 /*
354 * make our pread/pwrite safe against themselves, but not against
355 * normal read/write by using a mutex. slows down execution a lot,
356 * but that's your problem, not mine.
357 */
358 static pthread_mutex_t iolock = PTHREAD_MUTEX_INITIALIZER;
359
360 static ssize_t
361 pread (int fd, void *buf, size_t count, off_t offset)
362 {
363 ssize_t res;
364 off_t ooffset;
365
366 pthread_mutex_lock (&iolock);
367 ooffset = lseek (fd, 0, SEEK_CUR);
368 lseek (fd, offset, SEEK_SET);
369 res = read (fd, buf, count);
370 lseek (fd, ooffset, SEEK_SET);
371 pthread_mutex_unlock (&iolock);
372
373 return res;
374 }
375
376 static ssize_t
377 pwrite (int fd, void *buf, size_t count, off_t offset)
378 {
379 ssize_t res;
380 off_t ooffset;
381
382 pthread_mutex_lock (&iolock);
383 ooffset = lseek (fd, 0, SEEK_CUR);
384 lseek (fd, offset, SEEK_SET);
385 res = write (fd, buf, count);
386 lseek (fd, offset, SEEK_SET);
387 pthread_mutex_unlock (&iolock);
388
389 return res;
390 }
391 #endif
392
393 #if !HAVE_FDATASYNC
394 # define fdatasync fsync
395 #endif
396
397 #if !HAVE_READAHEAD
398 # define readahead aio_readahead
399
400 static char readahead_buf[4096];
401
402 static ssize_t
403 readahead (int fd, off_t offset, size_t count)
404 {
405 while (count > 0)
406 {
407 size_t len = count < sizeof (readahead_buf) ? count : sizeof (readahead_buf);
408
409 pread (fd, readahead_buf, len, offset);
410 offset += len;
411 count -= len;
412 }
413
414 errno = 0;
415 }
416 #endif
417
418 /*****************************************************************************/
419
420 static void *
421 aio_proc (void *thr_arg)
422 {
423 aio_req req;
424 int type;
425
426 do
427 {
428 pthread_mutex_lock (&reqlock);
429
430 for (;;)
431 {
432 req = reqs;
433
434 if (reqs)
435 {
436 reqs = reqs->next;
437 if (!reqs) reqe = 0;
438 }
439
440 if (req)
441 break;
442
443 pthread_cond_wait (&reqwait, &reqlock);
444 }
445
446 pthread_mutex_unlock (&reqlock);
447
448 errno = 0; /* strictly unnecessary */
449
450 type = req->type;
451
452 switch (type)
453 {
454 case REQ_READ: req->result = pread (req->fd, req->dataptr, req->length, req->offset); break;
455 case REQ_WRITE: req->result = pwrite (req->fd, req->dataptr, req->length, req->offset); break;
456
457 case REQ_READAHEAD: req->result = readahead (req->fd, req->offset, req->length); break;
458
459 case REQ_STAT: req->result = stat (req->dataptr, req->statdata); break;
460 case REQ_LSTAT: req->result = lstat (req->dataptr, req->statdata); break;
461 case REQ_FSTAT: req->result = fstat (req->fd , req->statdata); break;
462
463 case REQ_OPEN: req->result = open (req->dataptr, req->fd, req->mode); break;
464 case REQ_CLOSE: req->result = close (req->fd); break;
465 case REQ_UNLINK: req->result = unlink (req->dataptr); break;
466 case REQ_RMDIR: req->result = rmdir (req->dataptr); break;
467 case REQ_SYMLINK: req->result = symlink (req->data2ptr, req->dataptr); break;
468
469 case REQ_FDATASYNC: req->result = fdatasync (req->fd); break;
470 case REQ_FSYNC: req->result = fsync (req->fd); break;
471
472 case REQ_QUIT:
473 break;
474
475 default:
476 req->result = ENOSYS;
477 break;
478 }
479
480 req->errorno = errno;
481
482 pthread_mutex_lock (&reslock);
483
484 req->next = 0;
485
486 if (rese)
487 {
488 rese->next = req;
489 rese = req;
490 }
491 else
492 {
493 rese = ress = req;
494
495 /* write a dummy byte to the pipe so fh becomes ready */
496 write (respipe [1], &respipe, 1);
497 }
498
499 pthread_mutex_unlock (&reslock);
500 }
501 while (type != REQ_QUIT);
502
503 return 0;
504 }
505
506 #define dREQ \
507 aio_req req; \
508 \
509 if (SvOK (callback) && !SvROK (callback)) \
510 croak ("clalback must be undef or of reference type"); \
511 \
512 Newz (0, req, 1, aio_cb); \
513 if (!req) \
514 croak ("out of memory during aio_req allocation"); \
515 \
516 req->callback = newSVsv (callback);
517
518 MODULE = IO::AIO PACKAGE = IO::AIO
519
520 PROTOTYPES: ENABLE
521
522 BOOT:
523 {
524 create_pipe ();
525 pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
526 }
527
528 void
529 min_parallel(nthreads)
530 int nthreads
531 PROTOTYPE: $
532
533 void
534 max_parallel(nthreads)
535 int nthreads
536 PROTOTYPE: $
537
538 int
539 max_outstanding(nreqs)
540 int nreqs
541 PROTOTYPE: $
542 CODE:
543 RETVAL = max_outstanding;
544 max_outstanding = nreqs;
545
546 void
547 aio_open(pathname,flags,mode,callback=&PL_sv_undef)
548 SV * pathname
549 int flags
550 int mode
551 SV * callback
552 PROTOTYPE: $$$;$
553 CODE:
554 {
555 dREQ;
556
557 req->type = REQ_OPEN;
558 req->data = newSVsv (pathname);
559 req->dataptr = SvPVbyte_nolen (req->data);
560 req->fd = flags;
561 req->mode = mode;
562
563 send_req (req);
564 }
565
566 void
567 aio_close(fh,callback=&PL_sv_undef)
568 SV * fh
569 SV * callback
570 PROTOTYPE: $;$
571 ALIAS:
572 aio_close = REQ_CLOSE
573 aio_fsync = REQ_FSYNC
574 aio_fdatasync = REQ_FDATASYNC
575 CODE:
576 {
577 dREQ;
578
579 req->type = ix;
580 req->fh = newSVsv (fh);
581 req->fd = PerlIO_fileno (IoIFP (sv_2io (fh)));
582
583 send_req (req);
584 }
585
586 void
587 aio_read(fh,offset,length,data,dataoffset,callback=&PL_sv_undef)
588 SV * fh
589 UV offset
590 IV length
591 SV * data
592 IV dataoffset
593 SV * callback
594 ALIAS:
595 aio_read = REQ_READ
596 aio_write = REQ_WRITE
597 PROTOTYPE: $$$$$;$
598 CODE:
599 {
600 aio_req req;
601 STRLEN svlen;
602 char *svptr = SvPVbyte (data, svlen);
603
604 SvUPGRADE (data, SVt_PV);
605 SvPOK_on (data);
606
607 if (dataoffset < 0)
608 dataoffset += svlen;
609
610 if (dataoffset < 0 || dataoffset > svlen)
611 croak ("data offset outside of string");
612
613 if (ix == REQ_WRITE)
614 {
615 /* write: check length and adjust. */
616 if (length < 0 || length + dataoffset > svlen)
617 length = svlen - dataoffset;
618 }
619 else
620 {
621 /* read: grow scalar as necessary */
622 svptr = SvGROW (data, length + dataoffset);
623 }
624
625 if (length < 0)
626 croak ("length must not be negative");
627
628 {
629 dREQ;
630
631 req->type = ix;
632 req->fh = newSVsv (fh);
633 req->fd = PerlIO_fileno (ix == REQ_READ ? IoIFP (sv_2io (fh))
634 : IoOFP (sv_2io (fh)));
635 req->offset = offset;
636 req->length = length;
637 req->data = SvREFCNT_inc (data);
638 req->dataptr = (char *)svptr + dataoffset;
639
640 if (!SvREADONLY (data))
641 {
642 SvREADONLY_on (data);
643 req->data2ptr = (void *)data;
644 }
645
646 send_req (req);
647 }
648 }
649
650 void
651 aio_readahead(fh,offset,length,callback=&PL_sv_undef)
652 SV * fh
653 UV offset
654 IV length
655 SV * callback
656 PROTOTYPE: $$$;$
657 CODE:
658 {
659 dREQ;
660
661 req->type = REQ_READAHEAD;
662 req->fh = newSVsv (fh);
663 req->fd = PerlIO_fileno (IoIFP (sv_2io (fh)));
664 req->offset = offset;
665 req->length = length;
666
667 send_req (req);
668 }
669
670 void
671 aio_stat(fh_or_path,callback=&PL_sv_undef)
672 SV * fh_or_path
673 SV * callback
674 ALIAS:
675 aio_stat = REQ_STAT
676 aio_lstat = REQ_LSTAT
677 CODE:
678 {
679 dREQ;
680
681 New (0, req->statdata, 1, Stat_t);
682 if (!req->statdata)
683 {
684 free_req (req);
685 croak ("out of memory during aio_req->statdata allocation");
686 }
687
688 if (SvPOK (fh_or_path))
689 {
690 req->type = ix;
691 req->data = newSVsv (fh_or_path);
692 req->dataptr = SvPVbyte_nolen (req->data);
693 }
694 else
695 {
696 req->type = REQ_FSTAT;
697 req->fh = newSVsv (fh_or_path);
698 req->fd = PerlIO_fileno (IoIFP (sv_2io (fh_or_path)));
699 }
700
701 send_req (req);
702 }
703
704 void
705 aio_unlink(pathname,callback=&PL_sv_undef)
706 SV * pathname
707 SV * callback
708 ALIAS:
709 aio_unlink = REQ_UNLINK
710 aio_rmdir = REQ_RMDIR
711 CODE:
712 {
713 dREQ;
714
715 req->type = ix;
716 req->data = newSVsv (pathname);
717 req->dataptr = SvPVbyte_nolen (req->data);
718
719 send_req (req);
720 }
721
722 void
723 aio_symlink(oldpath,newpath,callback=&PL_sv_undef)
724 SV * oldpath
725 SV * newpath
726 SV * callback
727 CODE:
728 {
729 dREQ;
730
731 req->type = REQ_SYMLINK;
732 req->fh = newSVsv (oldpath);
733 req->data2ptr = SvPVbyte_nolen (req->fh);
734 req->data = newSVsv (newpath);
735 req->dataptr = SvPVbyte_nolen (req->data);
736
737 send_req (req);
738 }
739
740 void
741 flush()
742 PROTOTYPE:
743 CODE:
744 while (nreqs)
745 {
746 poll_wait ();
747 poll_cb ();
748 }
749
750 void
751 poll()
752 PROTOTYPE:
753 CODE:
754 if (nreqs)
755 {
756 poll_wait ();
757 poll_cb ();
758 }
759
760 int
761 poll_fileno()
762 PROTOTYPE:
763 CODE:
764 RETVAL = respipe [0];
765 OUTPUT:
766 RETVAL
767
768 int
769 poll_cb(...)
770 PROTOTYPE:
771 CODE:
772 RETVAL = poll_cb ();
773 OUTPUT:
774 RETVAL
775
776 void
777 poll_wait()
778 PROTOTYPE:
779 CODE:
780 if (nreqs)
781 poll_wait ();
782
783 int
784 nreqs()
785 PROTOTYPE:
786 CODE:
787 RETVAL = nreqs;
788 OUTPUT:
789 RETVAL
790