@@ -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))