--- IO-AIO/AIO.xs 2006/10/31 00:11:52 1.89 +++ IO-AIO/AIO.xs 2007/05/09 06:45:12 1.98 @@ -1,12 +1,4 @@ -/* solaris */ -#define _POSIX_PTHREAD_SEMANTICS 1 - -#if __linux && !defined(_GNU_SOURCE) -# define _GNU_SOURCE -#endif - -/* just in case */ -#define _REENTRANT 1 +#include "xthread.h" #include @@ -16,9 +8,8 @@ #include "autoconf/config.h" -#include - #include +#include #include #include #include @@ -53,31 +44,6 @@ # define NAME_MAX 4096 #endif -#ifndef PTHREAD_STACK_MIN -/* care for broken platforms, e.g. windows */ -# define PTHREAD_STACK_MIN 16384 -#endif - -#if __ia64 -# define STACKSIZE 65536 -#elif __i386 || __x86_64 /* 16k is unreasonably high :( */ -# define STACKSIZE PTHREAD_STACK_MIN -#else -# define STACKSIZE 16384 -#endif - -/* wether word reads are potentially non-atomic. - * this is conservatice, likely most arches this runs - * on have atomic word read/writes. - */ -#ifndef WORDACCESS_UNSAFE -# if __i386 || __x86_64 -# define WORDACCESS_UNSAFE 0 -# else -# define WORDACCESS_UNSAFE 1 -# endif -#endif - /* buffer size for various temporary buffers */ #define AIO_BUFSIZE 65536 @@ -89,6 +55,8 @@ if (!aio_buf) \ return -1; +typedef SV SV8; /* byte-sv, used for argument-checking */ + enum { REQ_QUIT, REQ_OPEN, REQ_CLOSE, @@ -96,7 +64,7 @@ REQ_SENDFILE, REQ_STAT, REQ_LSTAT, REQ_FSTAT, REQ_FSYNC, REQ_FDATASYNC, - REQ_UNLINK, REQ_RMDIR, REQ_RENAME, + REQ_UNLINK, REQ_RMDIR, REQ_MKDIR, REQ_RENAME, REQ_MKNOD, REQ_READDIR, REQ_LINK, REQ_SYMLINK, REQ_READLINK, REQ_GROUP, REQ_NOP, @@ -160,27 +128,52 @@ + ((tv2->tv_usec - tv1->tv_usec) >> 10); } -static int next_pri = DEFAULT_PRI + PRI_BIAS; +static thread_t main_tid; +static int main_sig; +static int block_sig_level; -static unsigned int started, idle, wanted; +void block_sig () +{ + sigset_t ss; -#if __linux && defined (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) -# define AIO_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP -#else -# define AIO_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER -#endif + if (block_sig_level++) + return; + + if (!main_sig) + return; -#define LOCK(mutex) pthread_mutex_lock (&(mutex)) -#define UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) + sigemptyset (&ss); + sigaddset (&ss, main_sig); + pthread_sigmask (SIG_BLOCK, &ss, 0); +} + +void unblock_sig () +{ + sigset_t ss; + + if (--block_sig_level) + return; + + if (!main_sig) + return; + + sigemptyset (&ss); + sigaddset (&ss, main_sig); + pthread_sigmask (SIG_UNBLOCK, &ss, 0); +} + +static int next_pri = DEFAULT_PRI + PRI_BIAS; + +static unsigned int started, idle, wanted; /* worker threads management */ -static pthread_mutex_t wrklock = AIO_MUTEX_INIT; +static mutex_t wrklock = MUTEX_INIT; typedef struct worker { /* locked by wrklock */ struct worker *prev, *next; - pthread_t tid; + thread_t tid; /* locked by reslock, reqlock or wrklock */ aio_req req; /* currently processed request */ @@ -218,9 +211,9 @@ static volatile unsigned int max_outstanding = 0xffffffff; static int respipe [2]; -static pthread_mutex_t reslock = AIO_MUTEX_INIT; -static pthread_mutex_t reqlock = AIO_MUTEX_INIT; -static pthread_cond_t reqwait = PTHREAD_COND_INITIALIZER; +static mutex_t reslock = MUTEX_INIT; +static mutex_t reqlock = MUTEX_INIT; +static cond_t reqwait = COND_INIT; #if WORDACCESS_UNSAFE @@ -320,7 +313,7 @@ } static int poll_cb (); -static void req_invoke (aio_req req); +static int req_invoke (aio_req req); static void req_free (aio_req req); static void req_cancel (aio_req req); @@ -350,6 +343,8 @@ static void aio_grp_feed (aio_req grp) { + block_sig (); + while (grp->size < grp->int2 && !(grp->flags & FLAG_CANCELLED)) { int old_len = grp->size; @@ -377,6 +372,8 @@ break; } } + + unblock_sig (); } static void aio_grp_dec (aio_req grp) @@ -389,12 +386,21 @@ /* finish, if done */ if (!grp->size && grp->int1) { - req_invoke (grp); + block_sig (); + + if (!req_invoke (grp)) + { + req_free (grp); + unblock_sig (); + croak (0); + } + req_free (grp); + unblock_sig (); } } -static void req_invoke (aio_req req) +static int req_invoke (aio_req req) { dSP; @@ -447,10 +453,9 @@ call_pv ("IO::AIO::_fd2fh", G_SCALAR | G_EVAL); SPAGAIN; - fh = SvREFCNT_inc (POPs); - + fh = POPs; PUSHMARK (SP); - XPUSHs (sv_2mortal (fh)); + XPUSHs (fh); } break; @@ -525,11 +530,7 @@ aio_grp_dec (grp); } - if (SvTRUE (ERRSV)) - { - req_free (req); - croak (0); - } + return !SvTRUE (ERRSV); } static void req_free (aio_req req) @@ -576,27 +577,14 @@ static void start_thread (void) { - sigset_t fullsigset, oldsigset; - pthread_attr_t attr; - worker *wrk = calloc (1, sizeof (worker)); if (!wrk) croak ("unable to allocate worker thread data"); - pthread_attr_init (&attr); - pthread_attr_setstacksize (&attr, STACKSIZE); - pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); -#ifdef PTHREAD_SCOPE_PROCESS - pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); -#endif - - sigfillset (&fullsigset); - LOCK (wrklock); - sigprocmask (SIG_SETMASK, &fullsigset, &oldsigset); - if (pthread_create (&wrk->tid, &attr, aio_proc, (void *)wrk) == 0) + if (thread_create (&wrk->tid, aio_proc, (void *)wrk)) { wrk->prev = &wrk_first; wrk->next = wrk_first.next; @@ -607,7 +595,6 @@ else free (wrk); - sigprocmask (SIG_SETMASK, &oldsigset, 0); UNLOCK (wrklock); } @@ -625,14 +612,18 @@ static void req_send (aio_req req) { + block_sig (); + ++nreqs; LOCK (reqlock); ++nready; reqq_push (&req_queue, req); - pthread_cond_signal (&reqwait); + COND_SIGNAL (reqwait); UNLOCK (reqlock); + unblock_sig (); + maybe_start_thread (); } @@ -647,7 +638,7 @@ LOCK (reqlock); reqq_push (&req_queue, req); - pthread_cond_signal (&reqwait); + COND_SIGNAL (reqwait); UNLOCK (reqlock); LOCK (wrklock); @@ -712,6 +703,8 @@ if (max_poll_time) gettimeofday (&tv_start, 0); + block_sig (); + for (;;) { for (;;) @@ -728,8 +721,8 @@ if (!res_queue.size) { /* read any signals sent by the worker threads */ - char buf [32]; - while (read (respipe [0], buf, 32) == 32) + char buf [4]; + while (read (respipe [0], buf, 4) == 4) ; } } @@ -748,7 +741,12 @@ } else { - req_invoke (req); + if (!req_invoke (req)) + { + req_free (req); + unblock_sig (); + croak (0); + } count++; } @@ -775,6 +773,7 @@ ++maxreqs; } + unblock_sig (); return count; } @@ -802,7 +801,7 @@ * normal read/write by using a mutex. slows down execution a lot, * but that's your problem, not mine. */ -static pthread_mutex_t preadwritelock = PTHREAD_MUTEX_INITIALIZER; +static mutex_t preadwritelock = MUTEX_INIT; static ssize_t pread (int fd, void *buf, size_t count, off_t offset) { @@ -863,7 +862,7 @@ #if !HAVE_READDIR_R # define readdir_r aio_readdir_r -static pthread_mutex_t readdirlock = PTHREAD_MUTEX_INITIALIZER; +static mutex_t readdirlock = MUTEX_INIT; static int readdir_r (DIR *dirp, struct dirent *ent, struct dirent **res) { @@ -1072,7 +1071,7 @@ ++idle; - if (pthread_cond_timedwait (&reqwait, &reqlock, &ts) + if (COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) { if (idle > max_idle) @@ -1086,7 +1085,7 @@ } /* we are allowed to idle, so do so without any timeout */ - pthread_cond_wait (&reqwait, &reqlock); + COND_WAIT (reqwait, reqlock); ts.tv_sec = time (0) + IDLE_TIMEOUT; } @@ -1116,6 +1115,7 @@ case REQ_CLOSE: req->result = close (req->int1); break; case REQ_UNLINK: req->result = unlink (req->ptr1); break; case REQ_RMDIR: req->result = rmdir (req->ptr1); break; + case REQ_MKDIR: req->result = mkdir (req->ptr1, req->mode); break; case REQ_RENAME: req->result = rename (req->ptr2, req->ptr1); break; case REQ_LINK: req->result = link (req->ptr2, req->ptr1); break; case REQ_SYMLINK: req->result = symlink (req->ptr2, req->ptr1); break; @@ -1155,8 +1155,14 @@ ++npending; if (!reqq_push (&res_queue, req)) - /* write a dummy byte to the pipe so fh becomes ready */ - write (respipe [1], &respipe, 1); + { + /* write a dummy byte to the pipe so fh becomes ready */ + write (respipe [1], &respipe, 1); + + /* optionally signal the main thread asynchronously */ + if (main_sig) + pthread_kill (main_tid, main_sig); + } self->req = 0; worker_clear (self); @@ -1269,9 +1275,10 @@ newCONSTSUB (stash, "O_CREAT", newSViv (O_CREAT)); newCONSTSUB (stash, "O_TRUNC", newSViv (O_TRUNC)); newCONSTSUB (stash, "S_IFIFO", newSViv (S_IFIFO)); + newCONSTSUB (stash, "SIGIO", newSViv (SIGIO)); create_pipe (); - pthread_atfork (atfork_prepare, atfork_parent, atfork_child); + ATFORK (atfork_prepare, atfork_parent, atfork_child); } void @@ -1311,7 +1318,7 @@ void aio_open (pathname,flags,mode,callback=&PL_sv_undef) - SV * pathname + SV8 * pathname int flags int mode SV * callback @@ -1354,7 +1361,7 @@ SV * fh UV offset UV length - SV * data + SV8 * data UV dataoffset SV * callback ALIAS: @@ -1384,7 +1391,7 @@ else { /* read: grow scalar as necessary */ - svptr = SvGROW (data, length + dataoffset); + svptr = SvGROW (data, length + dataoffset + 1); } if (length < 0) @@ -1415,7 +1422,7 @@ void aio_readlink (path,callback=&PL_sv_undef) - SV * path + SV8 * path SV * callback PROTOTYPE: $$;$ PPCODE: @@ -1480,7 +1487,7 @@ void aio_stat (fh_or_path,callback=&PL_sv_undef) - SV * fh_or_path + SV8 * fh_or_path SV * callback ALIAS: aio_stat = REQ_STAT @@ -1516,7 +1523,7 @@ void aio_unlink (pathname,callback=&PL_sv_undef) - SV * pathname + SV8 * pathname SV * callback ALIAS: aio_unlink = REQ_UNLINK @@ -1534,9 +1541,26 @@ } void +aio_mkdir (pathname,mode,callback=&PL_sv_undef) + SV8 * pathname + UV mode + SV * callback + PPCODE: +{ + dREQ; + + req->type = REQ_MKDIR; + req->sv1 = newSVsv (pathname); + req->ptr1 = SvPVbyte_nolen (req->sv1); + req->mode = mode; + + REQ_SEND; +} + +void aio_link (oldpath,newpath,callback=&PL_sv_undef) - SV * oldpath - SV * newpath + SV8 * oldpath + SV8 * newpath SV * callback ALIAS: aio_link = REQ_LINK @@ -1557,10 +1581,10 @@ void aio_mknod (pathname,mode,dev,callback=&PL_sv_undef) - SV * pathname - SV * callback + SV8 * pathname UV mode UV dev + SV * callback PPCODE: { dREQ; @@ -1644,18 +1668,17 @@ while (nreqs) { poll_wait (); - poll_cb (0); + poll_cb (); } -void +int poll() PROTOTYPE: CODE: - if (nreqs) - { - poll_wait (); - poll_cb (0); - } + poll_wait (); + RETVAL = poll_cb (); + OUTPUT: + RETVAL int poll_fileno() @@ -1677,8 +1700,44 @@ poll_wait() PROTOTYPE: CODE: - if (nreqs) - poll_wait (); + poll_wait (); + +void +setsig (int signum = SIGIO) + PROTOTYPE: ;$ + CODE: +{ + if (block_sig_level) + croak ("cannot call IO::AIO::setsig from within aio_block/callback"); + + LOCK (reslock); + main_tid = pthread_self (); + main_sig = signum; + UNLOCK (reslock); + + if (main_sig && npending) + pthread_kill (main_tid, main_sig); +} + +void +aio_block (SV *cb) + PROTOTYPE: & + PPCODE: +{ + int count; + + block_sig (); + PUSHMARK (SP); + PUTBACK; + count = call_sv (cb, GIMME_V | G_NOARGS | G_EVAL); + SPAGAIN; + unblock_sig (); + + if (SvTRUE (ERRSV)) + croak (0); + + XSRETURN (count); +} int nreqs() @@ -1738,6 +1797,9 @@ int i; aio_req req; + if (main_sig && !block_sig_level) + croak ("aio_group->add called outside aio_block/callback context while IO::AIO::setsig is in use"); + if (grp->int1 == 2) croak ("cannot add requests to IO::AIO::GRP after the group finished");