The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
AIO.pm 2852
AIO.xs 224
Changes 033
MANIFEST 01
META.json 55
META.yml 22
Makefile.PL 611
README 2348
bin/treescan 22
def0.h 02
gendef0 05
libeio/ecb.h 1089
libeio/eio.c 44756
libeio/eio.h 45
libeio/etp.c 0494
libeio/libeio.m4 2424
libeio/xthread.h 28
schmorp.h 44
18 files changed (This is a version diff) 559865
@@ -70,7 +70,6 @@ call C<poll_cb> (or other C<aio_> functions) recursively.
 This is a simple example that uses the EV module and loads
 F</etc/passwd> asynchronously:
 
-   use Fcntl;
    use EV;
    use IO::AIO;
 
@@ -170,7 +169,7 @@ use common::sense;
 use base 'Exporter';
 
 BEGIN {
-   our $VERSION = '4.18';
+   our $VERSION = 4.3;
 
    our @AIO_REQ = qw(aio_sendfile aio_seek aio_read aio_write aio_open aio_close
                      aio_stat aio_lstat aio_unlink aio_rmdir aio_readdir aio_readdirx
@@ -605,8 +604,8 @@ Example: stat C</wd> and dump out the data if successful.
       fsid    => 1810
    }
 
-Here is a (likely partial) list of fsid values used by Linux - it is safe
-to hardcode these when the $^O is C<linux>:
+Here is a (likely partial - send me updates!) list of fsid values used by
+Linux - it is safe to hardcode these when C<$^O> is C<linux>:
 
    0x0000adf5 adfs
    0x0000adff affs
@@ -785,7 +784,7 @@ callback.
 =item aio_realpath $pathname, $callback->($path)
 
 Asynchronously make the path absolute and resolve any symlinks in
-C<$path>. The resulting path only consists of directories (Same as
+C<$path>. The resulting path only consists of directories (same as
 L<Cwd::realpath>).
 
 This request can be used to get the absolute path of the current working
@@ -797,6 +796,10 @@ directory by passing it a path of F<.> (a single dot).
 Asynchronously rename the object at C<$srcpath> to C<$dstpath>, just as
 rename(2) and call the callback with the result code.
 
+On systems that support the AIO::WD working directory abstraction
+natively, the case C<[$wd, "."]> as C<$srcpath> is specialcased - instead
+of failing, C<rename> is called on the absolute path of C<$wd>.
+
 
 =item aio_mkdir $pathname, $mode, $callback->($status)
 
@@ -810,6 +813,10 @@ request is executed, so do not change your umask.
 Asynchronously rmdir (delete) a directory and call the callback with the
 result code.
 
+On systems that support the AIO::WD working directory abstraction
+natively, the case C<[$wd, "."]> is specialcased - instead of failing,
+C<rmdir> is called on the absolute path of C<$wd>.
+
 
 =item aio_readdir $pathname, $callback->($entries)
 
@@ -1183,7 +1190,7 @@ sub aio_scandir($$;$) {
 =item aio_rmtree $pathname, $callback->($status)
 
 Delete a directory tree starting (and including) C<$path>, return the
-status of the final C<rmdir> only.  This is a composite request that
+status of the final C<rmdir> only. This is a composite request that
 uses C<aio_scandir> to recurse into and rmdir directories, and unlink
 everything else.
 
@@ -1313,10 +1320,10 @@ This is a rather advanced IO::AIO call, which works best on mmap(2)ed
 scalars.
 
 It touches (reads or writes) all memory pages in the specified
-range inside the scalar.  All caveats and parameters are the same
+range inside the scalar. All caveats and parameters are the same
 as for C<aio_msync>, above, except for flags, which must be either
 C<0> (which reads all pages and ensures they are instantiated) or
-C<IO::AIO::MT_MODIFY>, which modifies the memory page s(by reading and
+C<IO::AIO::MT_MODIFY>, which modifies the memory pages (by reading and
 writing an octet from it, which dirties the page).
 
 =item aio_mlock $scalar, $offset = 0, $length = undef, $callback->($status)
@@ -1527,7 +1534,7 @@ pathname will use the directory fd on newer systems, and the string on
 older systems. Some functions (such as realpath) will always rely on the
 string form of the pathname.
 
-So this fucntionality is mainly useful to get some protection against
+So this functionality is mainly useful to get some protection against
 C<chdir>, to easily get an absolute path out of a relative path for future
 reference, and to speed up doing many operations in the same directory
 (e.g. when stat'ing all files in a directory).
@@ -1550,23 +1557,29 @@ request with C<ENOENT>, there is often no need for error checking in the
 C<aio_wd> callback, as future requests using the value will fail in the
 expected way.
 
-If this call isn't available because your OS lacks it or it couldn't be
-detected, it will be emulated by calling C<fsync> instead.
-
 =item IO::AIO::CWD
 
 This is a compiletime constant (object) that represents the process
 current working directory.
 
-Specifying this object as working directory object for a pathname is as
-if the pathname would be specified directly, without a directory object,
-e.g., these calls are functionally identical:
+Specifying this object as working directory object for a pathname is as if
+the pathname would be specified directly, without a directory object. For
+example, these calls are functionally identical:
 
    aio_stat "somefile", sub { ... };
    aio_stat [IO::AIO::CWD, "somefile"], sub { ... };
 
 =back
 
+To recover the path associated with an IO::AIO::WD object, you can use
+C<aio_realpath>:
+
+   aio_realpath $wd, sub {
+      warn "path is $_[0]\n";
+   };
+
+Currently, C<aio_statvfs> always, and C<aio_rename> and C<aio_rmdir>
+sometimes, fall back to using an absolue path.
 
 =head2 IO::AIO::REQ CLASS
 
@@ -1754,16 +1767,19 @@ See C<poll_cb> for an example.
 
 =item IO::AIO::poll_cb
 
-Process some outstanding events on the result pipe. You have to call
-this regularly. Returns C<0> if all events could be processed (or there
-were no events to process), or C<-1> if it returned earlier for whatever
-reason. Returns immediately when no events are outstanding. The amount of
-events processed depends on the settings of C<IO::AIO::max_poll_req> and
-C<IO::AIO::max_poll_time>.
+Process some requests that have reached the result phase (i.e. they have
+been executed but the results are not yet reported). You have to call
+this "regularly" to finish outstanding requests.
+
+Returns C<0> if all events could be processed (or there were no
+events to process), or C<-1> if it returned earlier for whatever
+reason. Returns immediately when no events are outstanding. The amount
+of events processed depends on the settings of C<IO::AIO::max_poll_req>,
+C<IO::AIO::max_poll_time> and C<IO::AIO::max_outstanding>.
 
-If not all requests were processed for whatever reason, the filehandle
-will still be ready when C<poll_cb> returns, so normally you don't have to
-do anything special to have it called later.
+If not all requests were processed for whatever reason, the poll file
+descriptor will still be ready when C<poll_cb> returns, so normally you
+don't have to do anything special to have it called later.
 
 Apart from calling C<IO::AIO::poll_cb> when the event filehandle becomes
 ready, it can be beneficial to call this function from loops which submit
@@ -1782,10 +1798,11 @@ SYNOPSIS section, at the top of this document):
 
 =item IO::AIO::poll_wait
 
-If there are any outstanding requests and none of them in the result
-phase, wait till the result filehandle becomes ready for reading (simply
-does a C<select> on the filehandle. This is useful if you want to
-synchronously wait for some requests to finish).
+Wait until either at least one request is in the result phase or no
+requests are outstanding anymore.
+
+This is useful if you want to synchronously wait for some requests to
+become ready, without actually handling them.
 
 See C<nreqs> for an example.
 
@@ -2098,6 +2115,13 @@ See the C<splice(2)> manpage for details.
 Calls the GNU/Linux C<tee(2)> syscall, see it's manpage and the
 description for C<IO::AIO::splice> above for details.
 
+=item $actual_size = IO::AIO::pipesize $r_fh[, $new_size]
+
+Attempts to query or change the pipe buffer size. Obviously works only
+on pipes, and currently works only on GNU/Linux systems, and fails with
+C<-1>/C<ENOSYS> everywhere else. If anybody knows how to influence pipe buffer
+size on other systems, drop me a note.
+
 =back
 
 =cut
@@ -155,7 +155,7 @@ static void req_destroy  (eio_req *grp);
 # define minor(dev) ((dev) & 0xff)
 #endif
 
-#ifndef PAGESIZE
+#if PAGESIZE <= 0
 # define PAGESIZE sysconf (_SC_PAGESIZE)
 #endif
 
@@ -200,7 +200,7 @@ fiemap (eio_req *req)
     goto done;
 
   /* else we have to loop -
-   * it would be tempting (atcually I tried that first) to just query the
+   * it would be tempting (actually I tried that first) to just query the
    * number of extents needed, but linux often feels like not returning all
    * extents, without telling us it left any out. this complicates
    * this quite a bit.
@@ -223,6 +223,9 @@ fiemap (eio_req *req)
       if (ioctl (req->int1, FS_IOC_FIEMAP, incmap) < 0)
         return;
 
+      if (!incmap->fm_mapped_extents)
+        goto done;
+
       count = fiemap->fm_mapped_extents + incmap->fm_mapped_extents;
       fiemap = realloc (fiemap, sizeof (*fiemap) + sizeof (struct fiemap_extent) * count);
       errno = ENOMEM;
@@ -1615,7 +1618,10 @@ aio_group (SV *callback=&PL_sv_undef)
 
         req->type = EIO_GROUP;
 
+        PUTBACK;
         req_submit (req);
+        SPAGAIN;
+
         XPUSHs (req_sv (req, aio_grp_stash));
 }
 
@@ -1869,6 +1875,22 @@ tee (aio_rfd rfh, aio_wfd wfh, size_t length, unsigned int flags)
 	OUTPUT:
         RETVAL
 
+int
+pipesize (aio_rfd rfh, int new_size = -1)
+	PROTOTYPE: $;$
+        CODE:
+#if defined(F_SETPIPE_SZ) && defined(F_GETPIPE_SZ)
+        if (new_size >= 0)
+          RETVAL = fcntl (rfh, F_SETPIPE_SZ, new_size);
+        else
+          RETVAL = fcntl (rfh, F_GETPIPE_SZ);
+#else
+        errno = ENOSYS;
+        RETVAL = -1;
+#endif
+        OUTPUT:
+        RETVAL
+
 void _on_next_submit (SV *cb)
 	CODE:
         SvREFCNT_dec (on_next_submit);
@@ -1,5 +1,6 @@
 Revision history for IO::AIO
 
+TODO: scandir - some dirs mostly contain subdirs - invert logic?
 TODO: aio_cptree/mvtree
 TODO: reduce condvar fairness: schedule hot-cache-threads first?
 TODO: vmsplice? (http://kerneltrap.org/node/6505 http://lwn.net/Articles/178199/)
@@ -9,6 +10,38 @@ TODO: getxattr etc.?
 TODO: F_DUPFD_CLOEXEC
 TODO: emulation for splice?
 TODO: eio_mmap|mlock|munmap|splice...
+TODO: syncfs/sync windows:
+TODO: F_SETPIPE_SZ, F_GETPIPE_SZ
+http://stackoverflow.com/questions/65170/how-to-get-name-associated-with-open-handle/5286888#5286888
+http://blogs.msdn.com/b/adioltean/archive/2005/04/16/408947.aspx
+http://msdn.microsoft.com/en-us/library/aa366789%28v=vs.85%29.aspx
+http://msdn.microsoft.com/en-us/library/windows/desktop/aa366789%28v=vs.85%29.aspx
+http://msdn.microsoft.com/en-us/library/windows/desktop/aa364425%28v=vs.85%29.aspx
+http://msdn.microsoft.com/en-us/library/windows/desktop/aa364963%28v=vs.85%29.aspx
+http://msdn.microsoft.com/en-us/library/windows/desktop/aa364996%28v=vs.85%29.aspx
+http://msdn.microsoft.com/en-us/library/windows/desktop/aa364994%28v=vs.85%29.aspx
+TODO: extra socket/tcp constants &c?
+
+4.3   Fri Apr 11 06:22:38 CEST 2014
+	- perl5porters broke Async::Interrupt, BDB, EV, IO::AIO, OpenCL
+          without warning by switching the meaning of USE_SOCKETS_AS_HANDLES
+          in 5.18. What's so attractive about giving a shit about backwards
+          compatibility - I will never understand.
+
+4.2   Sat Jan 25 01:13:14 CET 2014
+	- aio_group could corrupt memory because it didn't restore
+          the stack after req_submit.
+        - be more careful on (e.g. permission) errors in bin/treescan.
+	- work around changes in ExtUtils::MakeMaker.
+	- (libeio) implement aio_realpath for win32.
+        - (xthread) work around compile time bugs in ptw32.
+        - added IO::AIO::pipesize.
+        - (libecb) insignificant update.
+
+4.19  Sun Jan  6 12:47:26 CET 2013
+	- avoid endless loop in fiemap with some XFS files.
+	- in aio_rename and aio_rmdir, specialcase the case of [$wd, "."]
+          and call rename/rmdir instead of renameat/unlinkat.
 
 4.18  Thu Oct 11 07:01:26 CEST 2012
 	- fix unintended xthread_create by intentionalising it :)
@@ -23,6 +23,7 @@ libeio/xthread.h
 libeio/ecb.h
 libeio/eio.h
 libeio/eio.c
+libeio/etp.c
 libeio/libeio.m4
 libeio/config.h.in
 
@@ -4,7 +4,7 @@
       "unknown"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150",
+   "generated_by" : "ExtUtils::MakeMaker version 6.86, CPAN::Meta::Converter version 2.133380",
    "license" : [
       "unknown"
    ],
@@ -22,20 +22,20 @@
    "prereqs" : {
       "build" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "configure" : {
          "requires" : {
-            "ExtUtils::MakeMaker" : 0
+            "ExtUtils::MakeMaker" : "0"
          }
       },
       "runtime" : {
          "requires" : {
-            "common::sense" : 0
+            "common::sense" : "0"
          }
       }
    },
    "release_status" : "stable",
-   "version" : "4.18"
+   "version" : "4.3"
 }
@@ -7,7 +7,7 @@ build_requires:
 configure_requires:
   ExtUtils::MakeMaker: 0
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.112150'
+generated_by: 'ExtUtils::MakeMaker version 6.86, CPAN::Meta::Converter version 2.133380'
 license: unknown
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -19,4 +19,4 @@ no_index:
     - inc
 requires:
   common::sense: 0
-version: 4.18
+version: 4.3
@@ -16,15 +16,20 @@ if ($^O eq "MSWin32") {
 *** also, the windows SDK is expected to be installed in /sdk
 *** and visual C is expected to be installed in /vc98
 ***
-*** Akternatively, set the INC and LIBS environment variables
-*** accordingly before running Makeifle.PL, or you can
+*** Alternatively, set the INC and LIBS environment variables
+*** accordingly before running Makefile.PL, or you can
 *** pass INC and LIBS arguments to Makefile.PL itself.
 ***
 
 EOF
 
-   $INC  = "$ENV{INC} -I/sdk/include -I/vc98/include -I/gtk/include";
-   $LIBS = ["$ENV{LIBS} -L/gtk/lib -lpthreadVC2"];
+   if ($Config{cc} =~ /(?:^|\\|\/)gcc(?:|.*\.exe)$/) {
+      $INC  = "$ENV{INC} -I/gtk/include";
+      $LIBS = ["$ENV{LIBS} -L/gtk/lib -lpthreadGC2"];
+   } else {
+      $INC  = "$ENV{INC} -I/sdk/include -I/vc98/include -I/gtk/include";
+      $LIBS = ["$ENV{LIBS} -L/gtk/lib -lpthreadVC2"];
+   }
 
    open my $fh, ">libeio/config.h"
       or die "libeio/config.h: $!";
@@ -95,7 +100,7 @@ my $mm = MM->new({
        SUFFIX	=> '.gz',
     },
     depend => {
-       "AIO.c" => "schmorp.h libeio/eio.h libeio/xthread.h libeio/eio.c libeio/config.h",
+       "AIO.c" => "schmorp.h libeio/eio.h libeio/xthread.h libeio/etp.c libeio/eio.c libeio/config.h",
     },
     NAME         => "IO::AIO",
     VERSION_FROM => "AIO.pm",
@@ -103,7 +108,7 @@ my $mm = MM->new({
     LIBS         => $LIBS,
     EXE_FILES    => ["bin/treescan"],
     PM           => {
-       'AIO.pm'		=> '$(INST_LIBDIR)/AIO.pm',
+       'AIO.pm'		=> '$(INST_LIB)/IO/AIO.pm',
     },
     PREREQ_PM => {
        "common::sense" => 0,
@@ -66,7 +66,6 @@ DESCRIPTION
     This is a simple example that uses the EV module and loads /etc/passwd
     asynchronously:
 
-       use Fcntl;
        use EV;
        use IO::AIO;
 
@@ -532,8 +531,8 @@ FUNCTIONS
               fsid    => 1810
            }
 
-        Here is a (likely partial) list of fsid values used by Linux - it is
-        safe to hardcode these when the $^O is "linux":
+        Here is a (likely partial - send me updates!) list of fsid values
+        used by Linux - it is safe to hardcode these when $^O is "linux":
 
            0x0000adf5 adfs
            0x0000adff affs
@@ -694,7 +693,7 @@ FUNCTIONS
 
     aio_realpath $pathname, $callback->($path)
         Asynchronously make the path absolute and resolve any symlinks in
-        $path. The resulting path only consists of directories (Same as
+        $path. The resulting path only consists of directories (same as
         Cwd::realpath).
 
         This request can be used to get the absolute path of the current
@@ -704,6 +703,10 @@ FUNCTIONS
         Asynchronously rename the object at $srcpath to $dstpath, just as
         rename(2) and call the callback with the result code.
 
+        On systems that support the AIO::WD working directory abstraction
+        natively, the case "[$wd, "."]" as $srcpath is specialcased -
+        instead of failing, "rename" is called on the absolute path of $wd.
+
     aio_mkdir $pathname, $mode, $callback->($status)
         Asynchronously mkdir (create) a directory and call the callback with
         the result code. $mode will be modified by the umask at the time the
@@ -713,6 +716,10 @@ FUNCTIONS
         Asynchronously rmdir (delete) a directory and call the callback with
         the result code.
 
+        On systems that support the AIO::WD working directory abstraction
+        natively, the case "[$wd, "."]" is specialcased - instead of
+        failing, "rmdir" is called on the absolute path of $wd.
+
     aio_readdir $pathname, $callback->($entries)
         Unlike the POSIX call of the same name, "aio_readdir" reads an
         entire directory (i.e. opendir + readdir + closedir). The entries
@@ -944,7 +951,7 @@ FUNCTIONS
         inside the scalar. All caveats and parameters are the same as for
         "aio_msync", above, except for flags, which must be either 0 (which
         reads all pages and ensures they are instantiated) or
-        "IO::AIO::MT_MODIFY", which modifies the memory page s(by reading
+        "IO::AIO::MT_MODIFY", which modifies the memory pages (by reading
         and writing an octet from it, which dirties the page).
 
     aio_mlock $scalar, $offset = 0, $length = undef, $callback->($status)
@@ -1155,7 +1162,7 @@ FUNCTIONS
     older systems. Some functions (such as realpath) will always rely on the
     string form of the pathname.
 
-    So this fucntionality is mainly useful to get some protection against
+    So this functionality is mainly useful to get some protection against
     "chdir", to easily get an absolute path out of a relative path for
     future reference, and to speed up doing many operations in the same
     directory (e.g. when stat'ing all files in a directory).
@@ -1175,20 +1182,27 @@ FUNCTIONS
         checking in the "aio_wd" callback, as future requests using the
         value will fail in the expected way.
 
-        If this call isn't available because your OS lacks it or it couldn't
-        be detected, it will be emulated by calling "fsync" instead.
-
     IO::AIO::CWD
         This is a compiletime constant (object) that represents the process
         current working directory.
 
         Specifying this object as working directory object for a pathname is
         as if the pathname would be specified directly, without a directory
-        object, e.g., these calls are functionally identical:
+        object. For example, these calls are functionally identical:
 
            aio_stat "somefile", sub { ... };
            aio_stat [IO::AIO::CWD, "somefile"], sub { ... };
 
+    To recover the path associated with an IO::AIO::WD object, you can use
+    "aio_realpath":
+
+       aio_realpath $wd, sub {
+          warn "path is $_[0]\n";
+       };
+
+    Currently, "aio_statvfs" always, and "aio_rename" and "aio_rmdir"
+    sometimes, fall back to using an absolue path.
+
   IO::AIO::REQ CLASS
     All non-aggregate "aio_*" functions return an object of this class when
     called in non-void context.
@@ -1349,16 +1363,20 @@ FUNCTIONS
         See "poll_cb" for an example.
 
     IO::AIO::poll_cb
-        Process some outstanding events on the result pipe. You have to call
-        this regularly. Returns 0 if all events could be processed (or there
-        were no events to process), or -1 if it returned earlier for
-        whatever reason. Returns immediately when no events are outstanding.
-        The amount of events processed depends on the settings of
-        "IO::AIO::max_poll_req" and "IO::AIO::max_poll_time".
+        Process some requests that have reached the result phase (i.e. they
+        have been executed but the results are not yet reported). You have
+        to call this "regularly" to finish outstanding requests.
 
-        If not all requests were processed for whatever reason, the
-        filehandle will still be ready when "poll_cb" returns, so normally
-        you don't have to do anything special to have it called later.
+        Returns 0 if all events could be processed (or there were no events
+        to process), or -1 if it returned earlier for whatever reason.
+        Returns immediately when no events are outstanding. The amount of
+        events processed depends on the settings of "IO::AIO::max_poll_req",
+        "IO::AIO::max_poll_time" and "IO::AIO::max_outstanding".
+
+        If not all requests were processed for whatever reason, the poll
+        file descriptor will still be ready when "poll_cb" returns, so
+        normally you don't have to do anything special to have it called
+        later.
 
         Apart from calling "IO::AIO::poll_cb" when the event filehandle
         becomes ready, it can be beneficial to call this function from loops
@@ -1376,10 +1394,11 @@ FUNCTIONS
                       cb => \&IO::AIO::poll_cb);
 
     IO::AIO::poll_wait
-        If there are any outstanding requests and none of them in the result
-        phase, wait till the result filehandle becomes ready for reading
-        (simply does a "select" on the filehandle. This is useful if you
-        want to synchronously wait for some requests to finish).
+        Wait until either at least one request is in the result phase or no
+        requests are outstanding anymore.
+
+        This is useful if you want to synchronously wait for some requests
+        to become ready, without actually handling them.
 
         See "nreqs" for an example.
 
@@ -1664,6 +1683,12 @@ FUNCTIONS
         Calls the GNU/Linux tee(2) syscall, see it's manpage and the
         description for "IO::AIO::splice" above for details.
 
+    $actual_size = IO::AIO::pipesize $r_fh[, $new_size]
+        Attempts to query or change the pipe buffer size. Obviously works
+        only on pipes, and currently works only on GNU/Linux systems, and
+        fails with -1/"ENOSYS" everywhere else. If anybody knows how to
+        influence pipe buffer size on other systems, drop me a note.
+
 EVENT LOOP INTEGRATION
     It is recommended to use AnyEvent::AIO to integrate IO::AIO
     automatically into many event loops:
@@ -3,7 +3,7 @@
 # inspired by treescan by Jamie Lokier <jamie@imbolc.ucc.ie>
 # about 40% faster than the original version (on my fs and raid :)
 
-use strict;
+use common::sense;
 use Getopt::Long;
 use Time::HiRes ();
 use IO::AIO;
@@ -67,7 +67,7 @@ sub scan {
    ++$n_dirs;
    aio_scandir $path, 8, sub {
       my ($dirs, $files) = @_
-         or warn "$path: $!\n";
+         or return warn "$path: $!\n";
 
       printfn "", [$path]   unless $opt_nodirs;
       printfn $path, $files unless $opt_nofiles;
@@ -1,3 +1,5 @@
+/* GENERATED FILE */
+/* use ./gendef0 to regenerate this file */
 #ifndef ENOSYS
 #define ENOSYS 0
 #endif
@@ -6,6 +6,11 @@ open STDIN, "<AIO.xs"
 open STDOUT, ">def0.h"
    or die "def0.h: $!";
 
+print <<EOF;
+/* GENERATED FILE */
+/* use ./gendef0 to regenerate this file */
+EOF
+
 while (<>) {
    if (/^\s*const_iv\s*\((\S+)\)\s*$/ || /^\s*const_niv\s*\([^,]+,\s*(\S+)\)\s*$/) {
       print "#ifndef $1\n",
@@ -1,7 +1,7 @@
 /*
  * libecb - http://software.schmorp.de/pkg/libecb
  *
- * Copyright (©) 2009-2012 Marc Alexander Lehmann <libecb@schmorp.de>
+ * Copyright (©) 2009-2014 Marc Alexander Lehmann <libecb@schmorp.de>
  * Copyright (©) 2011 Emanuele Giaquinta
  * All rights reserved.
  *
@@ -31,7 +31,7 @@
 #define ECB_H
 
 /* 16 bits major, 16 bits minor */
-#define ECB_VERSION 0x00010002
+#define ECB_VERSION 0x00010003
 
 #ifdef _WIN32
   typedef   signed char   int8_t;
@@ -65,6 +65,15 @@
   #endif
 #endif
 
+/* work around x32 idiocy by defining proper macros */
+#if __amd64 || __x86_64 || _M_AMD64 || _M_X64
+  #if _ILP32
+    #define ECB_AMD64_X32 1
+  #else
+    #define ECB_AMD64 1
+  #endif
+#endif
+
 /* many compilers define _GNUC_ to some versions but then only implement
  * what their idiot authors think are the "more important" extensions,
  * causing enormous grief in return for some better fake benchmark numbers.
@@ -80,13 +89,21 @@
   #endif
 #endif
 
-#define ECB_C     (__STDC__+0) /* this assumes that __STDC__ is either empty or a number */
-#define ECB_C99   (__STDC_VERSION__ >= 199901L)
-#define ECB_C11   (__STDC_VERSION__ >= 201112L)
 #define ECB_CPP   (__cplusplus+0)
 #define ECB_CPP11 (__cplusplus >= 201103L)
 
 #if ECB_CPP
+  #define ECB_C            0
+  #define ECB_STDC_VERSION 0
+#else
+  #define ECB_C            1
+  #define ECB_STDC_VERSION __STDC_VERSION__
+#endif
+
+#define ECB_C99   (ECB_STDC_VERSION >= 199901L)
+#define ECB_C11   (ECB_STDC_VERSION >= 201112L)
+
+#if ECB_CPP
   #define ECB_EXTERN_C extern "C"
   #define ECB_EXTERN_C_BEG ECB_EXTERN_C {
   #define ECB_EXTERN_C_END }
@@ -127,14 +144,18 @@
     #elif defined __ARM_ARCH_7__  || defined __ARM_ARCH_7A__  \
        || defined __ARM_ARCH_7M__ || defined __ARM_ARCH_7R__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("dmb"      : : : "memory")
-    #elif __sparc || __sparc__
+    #elif __aarch64__
+      #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("dmb ish"  : : : "memory")
+    #elif (__sparc || __sparc__) && !__sparcv8
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("membar #LoadStore | #LoadLoad | #StoreStore | #StoreLoad" : : : "memory")
       #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("membar #LoadStore | #LoadLoad"                            : : : "memory")
       #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("membar #LoadStore             | #StoreStore")
     #elif defined __s390__ || defined __s390x__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("bcr 15,0" : : : "memory")
     #elif defined __mips__
-      #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("sync"     : : : "memory")
+      /* GNU/Linux emulates sync on mips1 architectures, so we force its use */
+      /* anybody else who still uses mips1 is supposed to send in their version, with detection code. */
+      #define ECB_MEMORY_FENCE         __asm__ __volatile__ (".set mips2; sync; .set mips0" : : : "memory")
     #elif defined __alpha__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("mb"       : : : "memory")
     #elif defined __hppa__
@@ -142,6 +163,12 @@
       #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("")
     #elif defined __ia64__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("mf"       : : : "memory")
+    #elif defined __m68k__
+      #define ECB_MEMORY_FENCE         __asm__ __volatile__ (""         : : : "memory")
+    #elif defined __m88k__
+      #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("tb1 0,%%r0,128" : : : "memory")
+    #elif defined __sh__
+      #define ECB_MEMORY_FENCE         __asm__ __volatile__ (""         : : : "memory")
     #endif
   #endif
 #endif
@@ -150,6 +177,8 @@
   #if ECB_GCC_VERSION(4,7)
     /* see comment below (stdatomic.h) about the C11 memory model. */
     #define ECB_MEMORY_FENCE         __atomic_thread_fence (__ATOMIC_SEQ_CST)
+    #define ECB_MEMORY_FENCE_ACQUIRE __atomic_thread_fence (__ATOMIC_ACQUIRE)
+    #define ECB_MEMORY_FENCE_RELEASE __atomic_thread_fence (__ATOMIC_RELEASE)
 
   /* The __has_feature syntax from clang is so misdesigned that we cannot use it
    * without risking compile time errors with other compilers. We *could*
@@ -158,10 +187,18 @@
    * #elif defined __clang && __has_feature (cxx_atomic)
    *   // see comment below (stdatomic.h) about the C11 memory model.
    *   #define ECB_MEMORY_FENCE         __c11_atomic_thread_fence (__ATOMIC_SEQ_CST)
+   *   #define ECB_MEMORY_FENCE_ACQUIRE __c11_atomic_thread_fence (__ATOMIC_ACQUIRE)
+   *   #define ECB_MEMORY_FENCE_RELEASE __c11_atomic_thread_fence (__ATOMIC_RELEASE)
    */
 
   #elif ECB_GCC_VERSION(4,4) || defined __INTEL_COMPILER || defined __clang__
     #define ECB_MEMORY_FENCE         __sync_synchronize ()
+  #elif _MSC_VER >= 1500 /* VC++ 2008 */
+    /* apparently, microsoft broke all the memory barrier stuff in Visual Studio 2008... */
+    #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier)
+    #define ECB_MEMORY_FENCE         _ReadWriteBarrier (); MemoryBarrier()
+    #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier (); MemoryBarrier() /* according to msdn, _ReadBarrier is not a load fence */
+    #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier (); MemoryBarrier()
   #elif _MSC_VER >= 1400 /* VC++ 2005 */
     #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier)
     #define ECB_MEMORY_FENCE         _ReadWriteBarrier ()
@@ -191,6 +228,8 @@
     /* for most usages, or gcc and clang have a bug */
     /* I *currently* lean towards the latter, and inefficiently implement */
     /* all three of ecb's fences as a seq_cst fence */
+    /* Update, gcc-4.8 generates mfence for all c++ fences, but nothing */
+    /* for all __atomic_thread_fence's except seq_cst */
     #define ECB_MEMORY_FENCE         atomic_thread_fence (memory_order_seq_cst)
   #endif
 #endif
@@ -257,6 +296,11 @@ typedef int ecb_bool;
   #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality)
 #else
   #define ecb_attribute(attrlist)
+
+  /* possible C11 impl for integral types
+  typedef struct ecb_is_constant_struct ecb_is_constant_struct;
+  #define ecb_is_constant(expr)          _Generic ((1 ? (struct ecb_is_constant_struct *)0 : (void *)((expr) - (expr)), ecb_is_constant_struct *: 0, default: 1)) */
+
   #define ecb_is_constant(expr)          0
   #define ecb_expect(expr,value)         (expr)
   #define ecb_prefetch(addr,rw,locality)
@@ -552,22 +596,57 @@ ecb_inline ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () ==
     || __i386 || __i386__ \
     || __amd64 || __amd64__ || __x86_64 || __x86_64__ \
     || __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ \
-    || defined __arm__ && defined __ARM_EABI__ \
     || defined __s390__ || defined __s390x__ \
     || defined __mips__ \
     || defined __alpha__ \
     || defined __hppa__ \
     || defined __ia64__ \
-    || defined _M_IX86 || defined _M_AMD64 || defined _M_IA64
+    || defined __m68k__ \
+    || defined __m88k__ \
+    || defined __sh__ \
+    || defined _M_IX86 || defined _M_AMD64 || defined _M_IA64 \
+    || (defined __arm__ && (defined __ARM_EABI__ || defined __EABI__ || defined __VFP_FP__ || defined _WIN32_WCE || defined __ANDROID__)) \
+    || defined __aarch64__
   #define ECB_STDFP 1
   #include <string.h> /* for memcpy */
 #else
   #define ECB_STDFP 0
-  #include <math.h> /* for frexp*, ldexp* */
 #endif
 
 #ifndef ECB_NO_LIBM
 
+  #include <math.h> /* for frexp*, ldexp*, INFINITY, NAN */
+
+  /* only the oldest of old doesn't have this one. solaris. */
+  #ifdef INFINITY
+    #define ECB_INFINITY INFINITY
+  #else
+    #define ECB_INFINITY HUGE_VAL
+  #endif
+
+  #ifdef NAN
+    #define ECB_NAN NAN
+  #else
+    #define ECB_NAN ECB_INFINITY
+  #endif
+
+  /* converts an ieee half/binary16 to a float */
+  ecb_function_ float ecb_binary16_to_float (uint16_t x) ecb_const;
+  ecb_function_ float
+  ecb_binary16_to_float (uint16_t x)
+  {
+    int e = (x >> 10) & 0x1f;
+    int m = x & 0x3ff;
+    float r;
+
+    if      (!e     ) r = ldexpf (m        ,    -24);
+    else if (e != 31) r = ldexpf (m + 0x400, e - 25);
+    else if (m      ) r = ECB_NAN;
+    else              r = ECB_INFINITY;
+
+    return x & 0x8000 ? -r : r;
+  }
+
   /* convert a float to ieee single/binary32 */
   ecb_function_ uint32_t ecb_float_to_binary32 (float x) ecb_const;
   ecb_function_ uint32_t
@@ -1,19 +1,19 @@
 /*
  * libeio implementation
  *
- * Copyright (c) 2007,2008,2009,2010,2011,2012 Marc Alexander Lehmann <libeio@schmorp.de>
+ * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libeio@schmorp.de>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without modifica-
  * tion, are permitted provided that the following conditions are met:
- * 
+ *
  *   1.  Redistributions of source code must retain the above copyright notice,
  *       this list of conditions and the following disclaimer.
- * 
+ *
  *   2.  Redistributions in binary form must reproduce the above copyright
  *       notice, this list of conditions and the following disclaimer in the
  *       documentation and/or other materials provided with the distribution.
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
  * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
@@ -215,9 +215,9 @@ static void eio_destroy (eio_req *req);
     return EIO_ERRNO (ENOENT, -1);
   }
 
-  /* POSIX API only */
-  #define CreateHardLink(neu,old,flags) 0
-  #define CreateSymbolicLink(neu,old,flags) 0
+  /* POSIX API only, causing trouble for win32 apps */
+  #define CreateHardLink(neu,old,flags) 0 /* not really creating hardlink, still using relative paths? */
+  #define CreateSymbolicLink(neu,old,flags) 0 /* vista+ only */
 
   struct statvfs
   {
@@ -233,11 +233,19 @@ static void eio_destroy (eio_req *req);
 
   #include <sys/time.h>
   #include <sys/select.h>
-  #include <sys/statvfs.h>
   #include <unistd.h>
   #include <signal.h>
   #include <dirent.h>
 
+  #ifdef ANDROID
+    #include <sys/vfs.h>
+    #define statvfs statfs
+    #define fstatvfs fstatfs
+    #include <asm/page.h> /* supposedly limits.h does #define PAGESIZE PAGESIZE */
+  #else
+    #include <sys/statvfs.h>
+  #endif
+
   #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES
     #include <sys/mman.h>
   #endif
@@ -318,7 +326,7 @@ static void eio_destroy (eio_req *req);
 /* buffer size for various temporary buffers */
 #define EIO_BUFSIZE 65536
 
-#define dBUF	 				\
+#define dBUF					\
   char *eio_buf = malloc (EIO_BUFSIZE);		\
   errno = ENOMEM;				\
   if (!eio_buf)					\
@@ -327,8 +335,6 @@ static void eio_destroy (eio_req *req);
 #define FUBd					\
   free (eio_buf)
 
-#define EIO_TICKS ((1000000 + 1023) >> 10)
-
 /*****************************************************************************/
 
 struct tmpbuf
@@ -376,6 +382,9 @@ struct eio_pwd
 #define ETP_PRI_MIN EIO_PRI_MIN
 #define ETP_PRI_MAX EIO_PRI_MAX
 
+#define ETP_TYPE_QUIT -1
+#define ETP_TYPE_GROUP EIO_GROUP
+
 struct etp_worker;
 
 #define ETP_REQ eio_req
@@ -383,434 +392,9 @@ struct etp_worker;
 static int eio_finish (eio_req *req);
 #define ETP_FINISH(req)  eio_finish (req)
 static void eio_execute (struct etp_worker *self, eio_req *req);
-#define ETP_EXECUTE(wrk,req) eio_execute (wrk,req)
-
-/*****************************************************************************/
-
-#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1)
-
-/* calculate time difference in ~1/EIO_TICKS of a second */
-ecb_inline int
-tvdiff (struct timeval *tv1, struct timeval *tv2)
-{
-  return  (tv2->tv_sec  - tv1->tv_sec ) * EIO_TICKS
-       + ((tv2->tv_usec - tv1->tv_usec) >> 10);
-}
-
-static unsigned int started, idle, wanted = 4;
-
-static void (*want_poll_cb) (void);
-static void (*done_poll_cb) (void);
- 
-static unsigned int max_poll_time;     /* reslock */
-static unsigned int max_poll_reqs;     /* reslock */
-
-static unsigned int nreqs;    /* reqlock */
-static unsigned int nready;   /* reqlock */
-static unsigned int npending; /* reqlock */
-static unsigned int max_idle = 4;      /* maximum number of threads that can idle indefinitely */
-static unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */
-
-static xmutex_t wrklock;
-static xmutex_t reslock;
-static xmutex_t reqlock;
-static xcond_t  reqwait;
-
-typedef struct etp_worker
-{
-  struct tmpbuf tmpbuf;
-
-  /* locked by wrklock */
-  struct etp_worker *prev, *next;
-
-  xthread_t tid;
-
-#ifdef ETP_WORKER_COMMON
-  ETP_WORKER_COMMON
-#endif
-} etp_worker;
-
-static etp_worker wrk_first; /* NOT etp */
-
-#define ETP_WORKER_LOCK(wrk)   X_LOCK   (wrklock)
-#define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock)
-
-/* worker threads management */
-
-static void
-etp_worker_clear (etp_worker *wrk)
-{
-}
-
-static void ecb_cold
-etp_worker_free (etp_worker *wrk)
-{
-  free (wrk->tmpbuf.ptr);
+#define ETP_EXECUTE(wrk,req) eio_execute (wrk, req)
 
-  wrk->next->prev = wrk->prev;
-  wrk->prev->next = wrk->next;
-
-  free (wrk);
-}
-
-static unsigned int
-etp_nreqs (void)
-{
-  int retval;
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  retval = nreqs;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-  return retval;
-}
-
-static unsigned int
-etp_nready (void)
-{
-  unsigned int retval;
-
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  retval = nready;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-
-  return retval;
-}
-
-static unsigned int
-etp_npending (void)
-{
-  unsigned int retval;
-
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  retval = npending;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-
-  return retval;
-}
-
-static unsigned int
-etp_nthreads (void)
-{
-  unsigned int retval;
-
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  retval = started;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-
-  return retval;
-}
-
-/*
- * a somewhat faster data structure might be nice, but
- * with 8 priorities this actually needs <20 insns
- * per shift, the most expensive operation.
- */
-typedef struct {
-  ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */
-  int size;
-} etp_reqq;
-
-static etp_reqq req_queue;
-static etp_reqq res_queue;
-
-static void ecb_noinline ecb_cold
-reqq_init (etp_reqq *q)
-{
-  int pri;
-
-  for (pri = 0; pri < ETP_NUM_PRI; ++pri)
-    q->qs[pri] = q->qe[pri] = 0;
-
-  q->size = 0;
-}
-
-static int ecb_noinline
-reqq_push (etp_reqq *q, ETP_REQ *req)
-{
-  int pri = req->pri;
-  req->next = 0;
-
-  if (q->qe[pri])
-    {
-      q->qe[pri]->next = req;
-      q->qe[pri] = req;
-    }
-  else
-    q->qe[pri] = q->qs[pri] = req;
-
-  return q->size++;
-}
-
-static ETP_REQ * ecb_noinline
-reqq_shift (etp_reqq *q)
-{
-  int pri;
-
-  if (!q->size)
-    return 0;
-
-  --q->size;
-
-  for (pri = ETP_NUM_PRI; pri--; )
-    {
-      eio_req *req = q->qs[pri];
-
-      if (req)
-        {
-          if (!(q->qs[pri] = (eio_req *)req->next))
-            q->qe[pri] = 0;
-
-          return req;
-        }
-    }
-
-  abort ();
-}
-
-static int ecb_cold
-etp_init (void (*want_poll)(void), void (*done_poll)(void))
-{
-  X_MUTEX_CREATE (wrklock);
-  X_MUTEX_CREATE (reslock);
-  X_MUTEX_CREATE (reqlock);
-  X_COND_CREATE  (reqwait);
-
-  reqq_init (&req_queue);
-  reqq_init (&res_queue);
-
-  wrk_first.next =
-  wrk_first.prev = &wrk_first;
-
-  started  = 0;
-  idle     = 0;
-  nreqs    = 0;
-  nready   = 0;
-  npending = 0;
-
-  want_poll_cb = want_poll;
-  done_poll_cb = done_poll;
-
-  return 0;
-}
-
-X_THREAD_PROC (etp_proc);
-
-static void ecb_cold
-etp_start_thread (void)
-{
-  etp_worker *wrk = calloc (1, sizeof (etp_worker));
-
-  /*TODO*/
-  assert (("unable to allocate worker thread data", wrk));
-
-  X_LOCK (wrklock);
-
-  if (xthread_create (&wrk->tid, etp_proc, (void *)wrk))
-    {
-      wrk->prev = &wrk_first;
-      wrk->next = wrk_first.next;
-      wrk_first.next->prev = wrk;
-      wrk_first.next = wrk;
-      ++started;
-    }
-  else
-    free (wrk);
-
-  X_UNLOCK (wrklock);
-}
-
-static void
-etp_maybe_start_thread (void)
-{
-  if (ecb_expect_true (etp_nthreads () >= wanted))
-    return;
-  
-  /* todo: maybe use idle here, but might be less exact */
-  if (ecb_expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ()))
-    return;
-
-  etp_start_thread ();
-}
-
-static void ecb_cold
-etp_end_thread (void)
-{
-  eio_req *req = calloc (1, sizeof (eio_req)); /* will be freed by worker */
-
-  req->type = -1;
-  req->pri  = ETP_PRI_MAX - ETP_PRI_MIN;
-
-  X_LOCK (reqlock);
-  reqq_push (&req_queue, req);
-  X_COND_SIGNAL (reqwait);
-  X_UNLOCK (reqlock);
-
-  X_LOCK (wrklock);
-  --started;
-  X_UNLOCK (wrklock);
-}
-
-static int
-etp_poll (void)
-{
-  unsigned int maxreqs;
-  unsigned int maxtime;
-  struct timeval tv_start, tv_now;
-
-  X_LOCK (reslock);
-  maxreqs = max_poll_reqs;
-  maxtime = max_poll_time;
-  X_UNLOCK (reslock);
-
-  if (maxtime)
-    gettimeofday (&tv_start, 0);
-
-  for (;;)
-    {
-      ETP_REQ *req;
-
-      etp_maybe_start_thread ();
-
-      X_LOCK (reslock);
-      req = reqq_shift (&res_queue);
-
-      if (req)
-        {
-          --npending;
-
-          if (!res_queue.size && done_poll_cb)
-            done_poll_cb ();
-        }
-
-      X_UNLOCK (reslock);
-
-      if (!req)
-        return 0;
-
-      X_LOCK (reqlock);
-      --nreqs;
-      X_UNLOCK (reqlock);
-
-      if (ecb_expect_false (req->type == EIO_GROUP && req->size))
-        {
-          req->int1 = 1; /* mark request as delayed */
-          continue;
-        }
-      else
-        {
-          int res = ETP_FINISH (req);
-          if (ecb_expect_false (res))
-            return res;
-        }
-
-      if (ecb_expect_false (maxreqs && !--maxreqs))
-        break;
-
-      if (maxtime)
-        {
-          gettimeofday (&tv_now, 0);
-
-          if (tvdiff (&tv_start, &tv_now) >= maxtime)
-            break;
-        }
-    }
-
-  errno = EAGAIN;
-  return -1;
-}
-
-static void
-etp_cancel (ETP_REQ *req)
-{
-  req->cancelled = 1;
-
-  eio_grp_cancel (req);
-}
-
-static void
-etp_submit (ETP_REQ *req)
-{
-  req->pri -= ETP_PRI_MIN;
-
-  if (ecb_expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN;
-  if (ecb_expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN;
-
-  if (ecb_expect_false (req->type == EIO_GROUP))
-    {
-      /* I hope this is worth it :/ */
-      X_LOCK (reqlock);
-      ++nreqs;
-      X_UNLOCK (reqlock);
-
-      X_LOCK (reslock);
-
-      ++npending;
-
-      if (!reqq_push (&res_queue, req) && want_poll_cb)
-        want_poll_cb ();
-
-      X_UNLOCK (reslock);
-    }
-  else
-    {
-      X_LOCK (reqlock);
-      ++nreqs;
-      ++nready;
-      reqq_push (&req_queue, req);
-      X_COND_SIGNAL (reqwait);
-      X_UNLOCK (reqlock);
-
-      etp_maybe_start_thread ();
-    }
-}
-
-static void ecb_cold
-etp_set_max_poll_time (double nseconds)
-{
-  if (WORDACCESS_UNSAFE) X_LOCK   (reslock);
-  max_poll_time = nseconds * EIO_TICKS;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
-}
-
-static void ecb_cold
-etp_set_max_poll_reqs (unsigned int maxreqs)
-{
-  if (WORDACCESS_UNSAFE) X_LOCK   (reslock);
-  max_poll_reqs = maxreqs;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
-}
-
-static void ecb_cold
-etp_set_max_idle (unsigned int nthreads)
-{
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  max_idle = nthreads;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-}
-
-static void ecb_cold
-etp_set_idle_timeout (unsigned int seconds)
-{
-  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
-  idle_timeout = seconds;
-  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
-}
-
-static void ecb_cold
-etp_set_min_parallel (unsigned int nthreads)
-{
-  if (wanted < nthreads)
-    wanted = nthreads;
-}
-
-static void ecb_cold
-etp_set_max_parallel (unsigned int nthreads)
-{
-  if (wanted > nthreads)
-    wanted = nthreads;
-
-  while (started > wanted)
-    etp_end_thread ();
-}
+#include "etp.c"
 
 /*****************************************************************************/
 
@@ -887,8 +471,7 @@ eio_finish (eio_req *req)
 void
 eio_grp_cancel (eio_req *grp)
 {
-  for (grp = grp->grp_first; grp; grp = grp->grp_next)
-    eio_cancel (grp);
+  etp_grp_cancel (grp);
 }
 
 void
@@ -1387,8 +970,8 @@ eio__lseek (eio_req *req)
 static int
 eio__realpath (struct tmpbuf *tmpbuf, eio_wd wd, const char *path)
 {
-  const char *rel = path;
   char *res;
+  const char *rel = path;
   char *tmp1, *tmp2;
 #if SYMLOOP_MAX > 32
   int symlinks = SYMLOOP_MAX;
@@ -1405,6 +988,23 @@ eio__realpath (struct tmpbuf *tmpbuf, eio_wd wd, const char *path)
     return -1;
 
   res  = tmpbuf_get (tmpbuf, PATH_MAX * 3);
+#ifdef _WIN32
+  if (_access (rel, 4) != 0)
+    return -1;
+
+  symlinks = GetFullPathName (rel, PATH_MAX * 3, res, 0);
+
+  errno = ENAMETOOLONG;
+  if (symlinks >= PATH_MAX * 3)
+    return -1;
+
+  errno = EIO;
+  if (symlinks <= 0)
+    return -1;
+
+  return symlinks;
+
+#else
   tmp1 = res  + PATH_MAX;
   tmp2 = tmp1 + PATH_MAX;
 
@@ -1418,15 +1018,14 @@ eio__realpath (struct tmpbuf *tmpbuf, eio_wd wd, const char *path)
       {
         sprintf (tmp1, "/proc/self/fd/%d", fd);
         req->result = readlink (tmp1, res, PATH_MAX);
-        close (fd);
-
         /* here we should probably stat the open file and the disk file, to make sure they still match */
+        close (fd);
 
         if (req->result > 0)
           goto done;
       }
     else if (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EIO)
-      return;
+      return -1;
   }
 #endif
 #endif
@@ -1538,6 +1137,7 @@ eio__realpath (struct tmpbuf *tmpbuf, eio_wd wd, const char *path)
     *res++ = '/';
 
   return res - (char *)tmpbuf->ptr;
+#endif
 }
 
 static signed char
@@ -2151,6 +1751,7 @@ etp_proc_init (void)
 #endif
 }
 
+/* TODO: move somehow to etp.c */
 X_THREAD_PROC (etp_proc)
 {
   ETP_REQ *req;
@@ -2206,7 +1807,7 @@ X_THREAD_PROC (etp_proc)
 
       X_UNLOCK (reqlock);
      
-      if (req->type < 0)
+      if (req->type == ETP_TYPE_QUIT)
         goto quit;
 
       ETP_EXECUTE (self, req);
@@ -2271,6 +1872,8 @@ eio_api_destroy (eio_req *req)
       return 0;							\
     }
 
+#define SINGLEDOT(ptr) (0[(char *)(ptr)] == '.' && !1[(char *)(ptr)])
+
 static void
 eio_execute (etp_worker *self, eio_req *req)
 {
@@ -2335,9 +1938,15 @@ eio_execute (etp_worker *self, eio_req *req)
       case EIO_OPEN:      req->result = openat    (dirfd, req->ptr1, req->int1, (mode_t)req->int2); break;
 
       case EIO_UNLINK:    req->result = unlinkat  (dirfd, req->ptr1, 0); break;
-      case EIO_RMDIR:     req->result = unlinkat  (dirfd, req->ptr1, AT_REMOVEDIR); break;
+      case EIO_RMDIR:     /* complications arise because "." cannot be removed, so we might have to expand */
+                          req->result = req->wd && SINGLEDOT (req->ptr1)
+                             ? rmdir (req->wd->str)
+                             : unlinkat  (dirfd, req->ptr1, AT_REMOVEDIR); break;
       case EIO_MKDIR:     req->result = mkdirat   (dirfd, req->ptr1, (mode_t)req->int2); break;
-      case EIO_RENAME:    req->result = renameat  (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2); break;
+      case EIO_RENAME:    /* complications arise because "." cannot be renamed, so we might have to expand */
+                          req->result = req->wd && SINGLEDOT (req->ptr1)
+                             ? rename (req->wd->str, req->ptr2)
+                             : renameat (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2); break;
       case EIO_LINK:      req->result = linkat    (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2, 0); break;
       case EIO_SYMLINK:   req->result = symlinkat (req->ptr1, dirfd, req->ptr2); break;
       case EIO_MKNOD:     req->result = mknodat   (dirfd, req->ptr1, (mode_t)req->int2, (dev_t)req->offs); break;
@@ -256,16 +256,17 @@ struct eio_req
   eio_tstamp nv1;  /* utime, futime: atime; busy: sleep time */
   eio_tstamp nv2;  /* utime, futime: mtime */
 
-  int type;        /* EIO_xxx constant ETP */
   int int1;        /* all applicable requests: file descriptor; sendfile: output fd; open, msync, mlockall, readdir: flags */
   long int2;       /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, seek: whence, sync_file_range, fallocate: flags */
   long int3;       /* chown, fchown: gid; rename, link: working directory of new name */
   int errorno;     /* errno value on syscall return */
 
+  signed char type;/* EIO_xxx constant ETP */
+
 #if __i386 || __amd64
-  unsigned char cancelled;
+  unsigned char cancelled; /* ETP */
 #else
-  sig_atomic_t cancelled;
+  sig_atomic_t  cancelled; /* ETP */
 #endif
 
   unsigned char flags; /* private */
@@ -278,7 +279,7 @@ struct eio_req
 
   EIO_REQ_MEMBERS
 
-  eio_req *grp, *grp_prev, *grp_next, *grp_first; /* private */
+  eio_req *grp, *grp_prev, *grp_next, *grp_first; /* private ETP */
 };
 
 /* _private_ request flags */
@@ -0,0 +1,494 @@
+/*
+ * libetp implementation
+ *
+ * Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libetp@schmorp.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modifica-
+ * tion, are permitted provided that the following conditions are met:
+ *
+ *   1.  Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *
+ *   2.  Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER-
+ * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE-
+ * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH-
+ * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License ("GPL") version 2 or any later version,
+ * in which case the provisions of the GPL are applicable instead of
+ * the above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the BSD license, indicate your decision
+ * by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file under
+ * either the BSD or the GPL.
+ */
+
+#ifndef ETP_API_DECL
+# define ETP_API_DECL static
+#endif
+
+#ifndef ETP_PRI_MIN
+# define ETP_PRI_MIN 0
+# define ETP_PRI_MAX 0
+#endif
+
+#ifndef ETP_TYPE_QUIT
+# define ETP_TYPE_QUIT 0
+#endif
+
+#ifndef ETP_TYPE_GROUP
+# define ETP_TYPE_GROUP 1
+#endif
+
+#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1)
+
+#define ETP_TICKS ((1000000 + 1023) >> 10)
+
+/* calculate time difference in ~1/ETP_TICKS of a second */
+ecb_inline int
+etp_tvdiff (struct timeval *tv1, struct timeval *tv2)
+{
+  return  (tv2->tv_sec  - tv1->tv_sec ) * ETP_TICKS
+       + ((tv2->tv_usec - tv1->tv_usec) >> 10);
+}
+
+static unsigned int started, idle, wanted = 4;
+
+static void (*want_poll_cb) (void);
+static void (*done_poll_cb) (void);
+ 
+static unsigned int max_poll_time;     /* reslock */
+static unsigned int max_poll_reqs;     /* reslock */
+
+static unsigned int nreqs;    /* reqlock */
+static unsigned int nready;   /* reqlock */
+static unsigned int npending; /* reqlock */
+static unsigned int max_idle = 4;      /* maximum number of threads that can idle indefinitely */
+static unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */
+
+static xmutex_t wrklock;
+static xmutex_t reslock;
+static xmutex_t reqlock;
+static xcond_t  reqwait;
+
+typedef struct etp_worker
+{
+  struct tmpbuf tmpbuf;
+
+  /* locked by wrklock */
+  struct etp_worker *prev, *next;
+
+  xthread_t tid;
+
+#ifdef ETP_WORKER_COMMON
+  ETP_WORKER_COMMON
+#endif
+} etp_worker;
+
+static etp_worker wrk_first; /* NOT etp */
+
+#define ETP_WORKER_LOCK(wrk)   X_LOCK   (wrklock)
+#define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock)
+
+/* worker threads management */
+
+static void
+etp_worker_clear (etp_worker *wrk)
+{
+}
+
+static void ecb_cold
+etp_worker_free (etp_worker *wrk)
+{
+  free (wrk->tmpbuf.ptr);
+
+  wrk->next->prev = wrk->prev;
+  wrk->prev->next = wrk->next;
+
+  free (wrk);
+}
+
+ETP_API_DECL unsigned int
+etp_nreqs (void)
+{
+  int retval;
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  retval = nreqs;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+  return retval;
+}
+
+ETP_API_DECL unsigned int
+etp_nready (void)
+{
+  unsigned int retval;
+
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  retval = nready;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+
+  return retval;
+}
+
+ETP_API_DECL unsigned int
+etp_npending (void)
+{
+  unsigned int retval;
+
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  retval = npending;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+
+  return retval;
+}
+
+ETP_API_DECL unsigned int
+etp_nthreads (void)
+{
+  unsigned int retval;
+
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  retval = started;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+
+  return retval;
+}
+
+/*
+ * a somewhat faster data structure might be nice, but
+ * with 8 priorities this actually needs <20 insns
+ * per shift, the most expensive operation.
+ */
+typedef struct {
+  ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */
+  int size;
+} etp_reqq;
+
+static etp_reqq req_queue;
+static etp_reqq res_queue;
+
+static void ecb_noinline ecb_cold
+reqq_init (etp_reqq *q)
+{
+  int pri;
+
+  for (pri = 0; pri < ETP_NUM_PRI; ++pri)
+    q->qs[pri] = q->qe[pri] = 0;
+
+  q->size = 0;
+}
+
+static int ecb_noinline
+reqq_push (etp_reqq *q, ETP_REQ *req)
+{
+  int pri = req->pri;
+  req->next = 0;
+
+  if (q->qe[pri])
+    {
+      q->qe[pri]->next = req;
+      q->qe[pri] = req;
+    }
+  else
+    q->qe[pri] = q->qs[pri] = req;
+
+  return q->size++;
+}
+
+static ETP_REQ * ecb_noinline
+reqq_shift (etp_reqq *q)
+{
+  int pri;
+
+  if (!q->size)
+    return 0;
+
+  --q->size;
+
+  for (pri = ETP_NUM_PRI; pri--; )
+    {
+      eio_req *req = q->qs[pri];
+
+      if (req)
+        {
+          if (!(q->qs[pri] = (eio_req *)req->next))
+            q->qe[pri] = 0;
+
+          return req;
+        }
+    }
+
+  abort ();
+}
+
+ETP_API_DECL int ecb_cold
+etp_init (void (*want_poll)(void), void (*done_poll)(void))
+{
+  X_MUTEX_CREATE (wrklock);
+  X_MUTEX_CREATE (reslock);
+  X_MUTEX_CREATE (reqlock);
+  X_COND_CREATE  (reqwait);
+
+  reqq_init (&req_queue);
+  reqq_init (&res_queue);
+
+  wrk_first.next =
+  wrk_first.prev = &wrk_first;
+
+  started  = 0;
+  idle     = 0;
+  nreqs    = 0;
+  nready   = 0;
+  npending = 0;
+
+  want_poll_cb = want_poll;
+  done_poll_cb = done_poll;
+
+  return 0;
+}
+
+/* not yet in etp.c */
+X_THREAD_PROC (etp_proc);
+
+static void ecb_cold
+etp_start_thread (void)
+{
+  etp_worker *wrk = calloc (1, sizeof (etp_worker));
+
+  /*TODO*/
+  assert (("unable to allocate worker thread data", wrk));
+
+  X_LOCK (wrklock);
+
+  if (xthread_create (&wrk->tid, etp_proc, (void *)wrk))
+    {
+      wrk->prev = &wrk_first;
+      wrk->next = wrk_first.next;
+      wrk_first.next->prev = wrk;
+      wrk_first.next = wrk;
+      ++started;
+    }
+  else
+    free (wrk);
+
+  X_UNLOCK (wrklock);
+}
+
+static void
+etp_maybe_start_thread (void)
+{
+  if (ecb_expect_true (etp_nthreads () >= wanted))
+    return;
+  
+  /* todo: maybe use idle here, but might be less exact */
+  if (ecb_expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ()))
+    return;
+
+  etp_start_thread ();
+}
+
+static void ecb_cold
+etp_end_thread (void)
+{
+  eio_req *req = calloc (1, sizeof (eio_req)); /* will be freed by worker */
+
+  req->type = ETP_TYPE_QUIT;
+  req->pri  = ETP_PRI_MAX - ETP_PRI_MIN;
+
+  X_LOCK (reqlock);
+  reqq_push (&req_queue, req);
+  X_COND_SIGNAL (reqwait);
+  X_UNLOCK (reqlock);
+
+  X_LOCK (wrklock);
+  --started;
+  X_UNLOCK (wrklock);
+}
+
+ETP_API_DECL int
+etp_poll (void)
+{
+  unsigned int maxreqs;
+  unsigned int maxtime;
+  struct timeval tv_start, tv_now;
+
+  X_LOCK (reslock);
+  maxreqs = max_poll_reqs;
+  maxtime = max_poll_time;
+  X_UNLOCK (reslock);
+
+  if (maxtime)
+    gettimeofday (&tv_start, 0);
+
+  for (;;)
+    {
+      ETP_REQ *req;
+
+      etp_maybe_start_thread ();
+
+      X_LOCK (reslock);
+      req = reqq_shift (&res_queue);
+
+      if (req)
+        {
+          --npending;
+
+          if (!res_queue.size && done_poll_cb)
+            done_poll_cb ();
+        }
+
+      X_UNLOCK (reslock);
+
+      if (!req)
+        return 0;
+
+      X_LOCK (reqlock);
+      --nreqs;
+      X_UNLOCK (reqlock);
+
+      if (ecb_expect_false (req->type == ETP_TYPE_GROUP && req->size))
+        {
+          req->int1 = 1; /* mark request as delayed */
+          continue;
+        }
+      else
+        {
+          int res = ETP_FINISH (req);
+          if (ecb_expect_false (res))
+            return res;
+        }
+
+      if (ecb_expect_false (maxreqs && !--maxreqs))
+        break;
+
+      if (maxtime)
+        {
+          gettimeofday (&tv_now, 0);
+
+          if (etp_tvdiff (&tv_start, &tv_now) >= maxtime)
+            break;
+        }
+    }
+
+  errno = EAGAIN;
+  return -1;
+}
+
+ETP_API_DECL void
+etp_grp_cancel (ETP_REQ *grp);
+
+ETP_API_DECL void
+etp_cancel (ETP_REQ *req)
+{
+  req->cancelled = 1;
+
+  etp_grp_cancel (req);
+}
+
+ETP_API_DECL void
+etp_grp_cancel (ETP_REQ *grp)
+{
+  for (grp = grp->grp_first; grp; grp = grp->grp_next)
+    etp_cancel (grp);
+}
+
+ETP_API_DECL void
+etp_submit (ETP_REQ *req)
+{
+  req->pri -= ETP_PRI_MIN;
+
+  if (ecb_expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN;
+  if (ecb_expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN;
+
+  if (ecb_expect_false (req->type == ETP_TYPE_GROUP))
+    {
+      /* I hope this is worth it :/ */
+      X_LOCK (reqlock);
+      ++nreqs;
+      X_UNLOCK (reqlock);
+
+      X_LOCK (reslock);
+
+      ++npending;
+
+      if (!reqq_push (&res_queue, req) && want_poll_cb)
+        want_poll_cb ();
+
+      X_UNLOCK (reslock);
+    }
+  else
+    {
+      X_LOCK (reqlock);
+      ++nreqs;
+      ++nready;
+      reqq_push (&req_queue, req);
+      X_COND_SIGNAL (reqwait);
+      X_UNLOCK (reqlock);
+
+      etp_maybe_start_thread ();
+    }
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_max_poll_time (double nseconds)
+{
+  if (WORDACCESS_UNSAFE) X_LOCK   (reslock);
+  max_poll_time = nseconds * ETP_TICKS;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_max_poll_reqs (unsigned int maxreqs)
+{
+  if (WORDACCESS_UNSAFE) X_LOCK   (reslock);
+  max_poll_reqs = maxreqs;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_max_idle (unsigned int nthreads)
+{
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  max_idle = nthreads;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_idle_timeout (unsigned int seconds)
+{
+  if (WORDACCESS_UNSAFE) X_LOCK   (reqlock);
+  idle_timeout = seconds;
+  if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock);
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_min_parallel (unsigned int nthreads)
+{
+  if (wanted < nthreads)
+    wanted = nthreads;
+}
+
+ETP_API_DECL void ecb_cold
+etp_set_max_parallel (unsigned int nthreads)
+{
+  if (wanted > nthreads)
+    wanted = nthreads;
+
+  while (started > wanted)
+    etp_end_thread ();
+}
+
@@ -9,7 +9,7 @@ AC_SEARCH_LIBS(
    [AC_MSG_ERROR(pthread functions not found)]
 )
 
-AC_CACHE_CHECK(for utimes, ac_cv_utimes, [AC_LINK_IFELSE([[
+AC_CACHE_CHECK(for utimes, ac_cv_utimes, [AC_LINK_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #include <sys/time.h>
 #include <utime.h>
@@ -20,10 +20,10 @@ int main (void)
    res = utimes ("/", tv);
    return 0;
 }
-]],ac_cv_utimes=yes,ac_cv_utimes=no)])
+]])],ac_cv_utimes=yes,ac_cv_utimes=no)])
 test $ac_cv_utimes = yes && AC_DEFINE(HAVE_UTIMES, 1, utimes(2) is available)
 
-AC_CACHE_CHECK(for futimes, ac_cv_futimes, [AC_LINK_IFELSE([[
+AC_CACHE_CHECK(for futimes, ac_cv_futimes, [AC_LINK_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #include <sys/time.h>
 #include <utime.h>
@@ -35,10 +35,10 @@ int main (void)
    res = futimes (fd, tv);
    return 0;
 }
-]],ac_cv_futimes=yes,ac_cv_futimes=no)])
+]])],ac_cv_futimes=yes,ac_cv_futimes=no)])
 test $ac_cv_futimes = yes && AC_DEFINE(HAVE_FUTIMES, 1, futimes(2) is available)
 
-AC_CACHE_CHECK(for readahead, ac_cv_readahead, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for readahead, ac_cv_readahead, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <fcntl.h>
 int main (void)
 {
@@ -48,10 +48,10 @@ int main (void)
    res = readahead (fd, 0, count);
    return 0;
 }
-],ac_cv_readahead=yes,ac_cv_readahead=no)])
+])],ac_cv_readahead=yes,ac_cv_readahead=no)])
 test $ac_cv_readahead = yes && AC_DEFINE(HAVE_READAHEAD, 1, readahead(2) is available (linux))
 
-AC_CACHE_CHECK(for fdatasync, ac_cv_fdatasync, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for fdatasync, ac_cv_fdatasync, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <unistd.h>
 int main (void)
 {
@@ -59,10 +59,10 @@ int main (void)
    fdatasync (fd);
    return 0;
 }
-],ac_cv_fdatasync=yes,ac_cv_fdatasync=no)])
+])],ac_cv_fdatasync=yes,ac_cv_fdatasync=no)])
 test $ac_cv_fdatasync = yes && AC_DEFINE(HAVE_FDATASYNC, 1, fdatasync(2) is available)
 
-AC_CACHE_CHECK(for sendfile, ac_cv_sendfile, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for sendfile, ac_cv_sendfile, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 # include <sys/types.h>
 #if __linux
 # include <sys/sendfile.h>
@@ -89,10 +89,10 @@ int main (void)
 #endif
    return 0;
 }
-],ac_cv_sendfile=yes,ac_cv_sendfile=no)])
+])],ac_cv_sendfile=yes,ac_cv_sendfile=no)])
 test $ac_cv_sendfile = yes && AC_DEFINE(HAVE_SENDFILE, 1, sendfile(2) is available and supported)
 
-AC_CACHE_CHECK(for sync_file_range, ac_cv_sync_file_range, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for sync_file_range, ac_cv_sync_file_range, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <fcntl.h>
 int main (void)
 {
@@ -104,10 +104,10 @@ int main (void)
    res = sync_file_range (fd, offset, nbytes, flags);
    return 0;
 }
-],ac_cv_sync_file_range=yes,ac_cv_sync_file_range=no)])
+])],ac_cv_sync_file_range=yes,ac_cv_sync_file_range=no)])
 test $ac_cv_sync_file_range = yes && AC_DEFINE(HAVE_SYNC_FILE_RANGE, 1, sync_file_range(2) is available)
 
-AC_CACHE_CHECK(for fallocate, ac_cv_linux_fallocate, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for fallocate, ac_cv_linux_fallocate, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <fcntl.h>
 int main (void)
 {
@@ -119,27 +119,27 @@ int main (void)
    res = fallocate (fd, mode, offset, len);
    return 0;
 }
-],ac_cv_linux_fallocate=yes,ac_cv_linux_fallocate=no)])
+])],ac_cv_linux_fallocate=yes,ac_cv_linux_fallocate=no)])
 test $ac_cv_linux_fallocate = yes && AC_DEFINE(HAVE_LINUX_FALLOCATE, 1, fallocate(2) is available)
 
-AC_CACHE_CHECK(for sys_syncfs, ac_cv_sys_syncfs, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for sys_syncfs, ac_cv_sys_syncfs, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <unistd.h>
 #include <sys/syscall.h>
 int main (void)
 {
   int res = syscall (__NR_syncfs, (int)0);
 }
-],ac_cv_sys_syncfs=yes,ac_cv_sys_syncfs=no)])
+])],ac_cv_sys_syncfs=yes,ac_cv_sys_syncfs=no)])
 test $ac_cv_sys_syncfs = yes && AC_DEFINE(HAVE_SYS_SYNCFS, 1, syscall(__NR_syncfs) is available)
 
-AC_CACHE_CHECK(for prctl_set_name, ac_cv_prctl_set_name, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for prctl_set_name, ac_cv_prctl_set_name, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <sys/prctl.h>
 int main (void)
 {
   char name[] = "test123";
   int res = prctl (PR_SET_NAME, (unsigned long)name, 0, 0, 0);
 }
-],ac_cv_prctl_set_name=yes,ac_cv_prctl_set_name=no)])
+])],ac_cv_prctl_set_name=yes,ac_cv_prctl_set_name=no)])
 test $ac_cv_prctl_set_name = yes && AC_DEFINE(HAVE_PRCTL_SET_NAME, 1, prctl(PR_SET_NAME) is available)
 
 dnl #############################################################################
@@ -147,7 +147,7 @@ dnl # these checks exist for the benefit of IO::AIO
 
 dnl at least uclibc defines _POSIX_ADVISORY_INFO without *any* of the required
 dnl functionality actually being present. ugh.
-AC_CACHE_CHECK(for posix_madvise, ac_cv_posix_madvise, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for posix_madvise, ac_cv_posix_madvise, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <sys/mman.h>
 int main (void)
 {
@@ -158,10 +158,10 @@ int main (void)
    int d = POSIX_MADV_DONTNEED;
    return 0;
 }
-],ac_cv_posix_madvise=yes,ac_cv_posix_madvise=no)])
+])],ac_cv_posix_madvise=yes,ac_cv_posix_madvise=no)])
 test $ac_cv_posix_madvise = yes && AC_DEFINE(HAVE_POSIX_MADVISE, 1, posix_madvise(2) is available)
 
-AC_CACHE_CHECK(for posix_fadvise, ac_cv_posix_fadvise, [AC_LINK_IFELSE([
+AC_CACHE_CHECK(for posix_fadvise, ac_cv_posix_fadvise, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #define _XOPEN_SOURCE 600
 #include <fcntl.h>
 int main (void)
@@ -174,13 +174,13 @@ int main (void)
    int e = POSIX_FADV_DONTNEED;
    return 0;
 }
-],ac_cv_posix_fadvise=yes,ac_cv_posix_fadvise=no)])
+])],ac_cv_posix_fadvise=yes,ac_cv_posix_fadvise=no)])
 test $ac_cv_posix_fadvise = yes && AC_DEFINE(HAVE_POSIX_FADVISE, 1, posix_fadvise(2) is available)
 
 dnl lots of linux specifics
 AC_CHECK_HEADERS([linux/fs.h linux/fiemap.h])
 
-AC_CACHE_CHECK([for splice, vmsplice and tee], ac_cv_linux_splice, [AC_LINK_IFELSE([
+AC_CACHE_CHECK([for splice, vmsplice and tee], ac_cv_linux_splice, [AC_LINK_IFELSE([AC_LANG_SOURCE([
 #include <fcntl.h>
 int main (void)
 {
@@ -190,6 +190,6 @@ int main (void)
    res = vmsplice ((int)0, (struct iovec *)0, 0, SPLICE_F_NONBLOCK | SPLICE_F_GIFT);
    return 0;
 }
-],ac_cv_linux_splice=yes,ac_cv_linux_splice=no)])
+])],ac_cv_linux_splice=yes,ac_cv_linux_splice=no)])
 test $ac_cv_linux_splice = yes && AC_DEFINE(HAVE_LINUX_SPLICE, 1, splice/vmsplice/tee(2) are available)
 
@@ -17,8 +17,8 @@
 
 #ifdef _WIN32
 
-#define NTDDI_VERSION NTDDI_WIN2K // needed to get win2000 api calls
-#define _WIN32_WINNT 0x400
+//#define NTDDI_VERSION NTDDI_WIN2K // needed to get win2000 api calls, fails with mingw
+#define _WIN32_WINNT 0x400 // maybe working alternative for mingw
 #include <stdio.h>//D
 #include <fcntl.h>
 #include <io.h>
@@ -26,6 +26,12 @@
 #include <winsock2.h>
 #include <process.h>
 #include <windows.h>
+
+/* work around some bugs in ptw32 */
+#if defined(__MINGW32__) && defined(_TIMESPEC_DEFINED)
+#define HAVE_STRUCT_TIMESPEC 1
+#endif
+
 #include <pthread.h>
 #define sigset_t int
 #define sigfillset(a)
@@ -190,7 +190,7 @@ s_get_cv (SV *cb_sv)
   dTHX;
   HV *st;
   GV *gvp;
-
+  
   return (SV *)sv_2cv (cb_sv, &st, &gvp, 0);
 }
 
@@ -234,7 +234,7 @@ s_gensub (pTHX_ void (*xsub)(pTHX_ CV *), void *arg)
 /*****************************************************************************/
 /* portable pipe/socketpair */
 
-#ifdef USE_SOCKETS_AS_HANDLES
+#if defined(USE_SOCKETS_AS_HANDLES) || PERL_VERSION_ATLEAST(5,18,0)
 # define S_TO_HANDLE(x) ((HANDLE)win32_get_osfhandle (x))
 #else
 # define S_TO_HANDLE(x) ((HANDLE)x)
@@ -255,7 +255,7 @@ s_pipe (int filedes [2])
   SOCKET listener;
   SOCKET sock [2] = { -1, -1 };
 
-  if ((listener = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 
+  if ((listener = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
     return -1;
 
   addr.sin_family = AF_INET;
@@ -271,7 +271,7 @@ s_pipe (int filedes [2])
   if (listen (listener, 1))
     goto fail;
 
-  if ((sock [0] = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 
+  if ((sock [0] = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
     goto fail;
 
   if (connect (sock [0], (struct sockaddr *)&addr, addr_size))