--- libeio/eio.c 2011/07/05 18:59:28 1.80 +++ libeio/eio.c 2011/07/17 18:27:17 1.92 @@ -56,7 +56,6 @@ #include #include #include -#include #include #include #include @@ -70,6 +69,9 @@ #ifndef ECANCELED # define ECANCELED EDOM #endif +#ifndef ELOOP +# define ELOOP EDOM +#endif static void eio_destroy (eio_req *req); @@ -85,43 +87,112 @@ # define EIO_FEED(req) do { if ((req)->feed ) (req)->feed (req); } while (0) #endif +#ifndef EIO_FD_TO_WIN32_HANDLE +# define EIO_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd) +#endif +#ifndef EIO_WIN32_HANDLE_TO_FD +# define EIO_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0) +#endif + +#define EIO_ERRNO(errval,retval) ((errno = errval), retval) + +#define EIO_ENOSYS() EIO_ERRNO (ENOSYS, -1) + #ifdef _WIN32 - /*doh*/ + #undef PAGESIZE + #define PAGESIZE 4096 /* GetSystemInfo? */ + + #ifdef EIO_STRUCT_STATI64 + #define stat(path,buf) _stati64 (path,buf) + #define fstat(fd,buf) _fstati64 (path,buf) + #endif + #define lstat(path,buf) stat (path,buf) + #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1)) + #define mkdir(path,mode) _mkdir (path) + #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1)) + + #define chmod(path,mode) _chmod (path, mode) + #define dup(fd) _dup (fd) + #define dup2(fd1,fd2) _dup2 (fd1, fd2) + + #define fchmod(fd,mode) EIO_ENOSYS () + #define chown(path,uid,gid) EIO_ENOSYS () + #define fchown(fd,uid,gid) EIO_ENOSYS () + #define truncate(path,offs) EIO_ENOSYS () /* far-miss: SetEndOfFile */ + #define ftruncate(fd,offs) EIO_ENOSYS () /* near-miss: SetEndOfFile */ + #define mknod(path,mode,dev) EIO_ENOSYS () + #define sync() EIO_ENOSYS () + #define readlink(path,buf,s) EIO_ENOSYS () + #define statvfs(path,buf) EIO_ENOSYS () + #define fstatvfs(fd,buf) EIO_ENOSYS () + + /* we could even stat and see if it exists */ + static int + symlink (const char *old, const char *neu) + { + #if WINVER >= 0x0600 + if (CreateSymbolicLink (neu, old, 1)) + return 0; + + if (CreateSymbolicLink (neu, old, 0)) + return 0; + #endif + + return EIO_ERRNO (ENOENT, -1); + } + + /* POSIX API only */ + #define CreateHardLink(neu,old,flags) 0 + #define CreateSymbolicLink(neu,old,flags) 0 + + struct statvfs + { + int dummy; + }; + + #define DT_DIR EIO_DT_DIR + #define DT_REG EIO_DT_REG + #define D_NAME(entp) entp.cFileName + #define D_TYPE(entp) (entp.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG) + #else -# include -# include -# include -# include -# include -# include - -#if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES -# include -#endif - -/* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ -# if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ -# define _DIRENT_HAVE_D_TYPE /* sigh */ -# define D_INO(de) (de)->d_fileno -# define D_NAMLEN(de) (de)->d_namlen -# elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 -# define D_INO(de) (de)->d_ino -# endif + #include + #include + #include + #include + #include + #include + #include -#ifdef _D_EXACT_NAMLEN -# undef D_NAMLEN -# define D_NAMLEN(de) _D_EXACT_NAMLEN (de) -#endif + #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES + #include + #endif -# ifdef _DIRENT_HAVE_D_TYPE -# define D_TYPE(de) (de)->d_type -# endif + #define D_NAME(entp) entp->d_name -# ifndef EIO_STRUCT_DIRENT -# define EIO_STRUCT_DIRENT struct dirent -# endif + /* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ + #if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ + #define _DIRENT_HAVE_D_TYPE /* sigh */ + #define D_INO(de) (de)->d_fileno + #define D_NAMLEN(de) (de)->d_namlen + #elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 + #define D_INO(de) (de)->d_ino + #endif + + #ifdef _D_EXACT_NAMLEN + #undef D_NAMLEN + #define D_NAMLEN(de) _D_EXACT_NAMLEN (de) + #endif + + #ifdef _DIRENT_HAVE_D_TYPE + #define D_TYPE(de) (de)->d_type + #endif + + #ifndef EIO_STRUCT_DIRENT + #define EIO_STRUCT_DIRENT struct dirent + #endif #endif @@ -147,7 +218,7 @@ # define D_INO(de) 0 #endif #ifndef D_NAMLEN -# define D_NAMLEN(de) strlen ((de)->d_name) +# define D_NAMLEN(entp) strlen (D_NAME (entp)) #endif /* used for struct dirent, AIX doesn't provide it */ @@ -191,17 +262,10 @@ { \ free (wrk->dbuf); \ wrk->dbuf = 0; \ - } \ - \ - if (wrk->dirp) \ - { \ - closedir (wrk->dirp); \ - wrk->dirp = 0; \ } #define ETP_WORKER_COMMON \ - void *dbuf; \ - DIR *dirp; + void *dbuf; /*****************************************************************************/ @@ -337,6 +401,17 @@ 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) { @@ -383,6 +458,9 @@ static void ecb_cold etp_thread_init (void) { +#if !HAVE_PREADWRITE + X_MUTEX_CREATE (preadwritelock); +#endif X_MUTEX_CREATE (wrklock); X_MUTEX_CREATE (reslock); X_MUTEX_CREATE (reqlock); @@ -390,48 +468,13 @@ } static void ecb_cold -etp_atfork_prepare (void) -{ - X_LOCK (wrklock); - X_LOCK (reqlock); - X_LOCK (reslock); -#if !HAVE_PREADWRITE - X_LOCK (preadwritelock); -#endif -} - -static void ecb_cold -etp_atfork_parent (void) -{ -#if !HAVE_PREADWRITE - X_UNLOCK (preadwritelock); -#endif - X_UNLOCK (reslock); - X_UNLOCK (reqlock); - X_UNLOCK (wrklock); -} - -static void ecb_cold etp_atfork_child (void) { - ETP_REQ *prv; - - while ((prv = reqq_shift (&req_queue))) - ETP_DESTROY (prv); + reqq_init (&req_queue); + reqq_init (&res_queue); - while ((prv = reqq_shift (&res_queue))) - ETP_DESTROY (prv); - - while (wrk_first.next != &wrk_first) - { - etp_worker *wrk = wrk_first.next; - - if (wrk->req) - ETP_DESTROY (wrk->req); - - etp_worker_clear (wrk); - etp_worker_free (wrk); - } + wrk_first.next = + wrk_first.prev = &wrk_first; started = 0; idle = 0; @@ -446,7 +489,7 @@ etp_once_init (void) { etp_thread_init (); - X_THREAD_ATFORK (etp_atfork_prepare, etp_atfork_parent, etp_atfork_child); + X_THREAD_ATFORK (0, 0, etp_atfork_child); } static int ecb_cold @@ -591,9 +634,7 @@ static void etp_cancel (ETP_REQ *req) { - X_LOCK (wrklock); - req->flags |= EIO_FLAG_CANCELLED; - X_UNLOCK (wrklock); + req->cancelled = 1; eio_grp_cancel (req); } @@ -849,10 +890,10 @@ # define pread eio__pread # define pwrite eio__pwrite -static ssize_t +static eio_ssize_t eio__pread (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -865,10 +906,10 @@ return res; } -static ssize_t +static eio_ssize_t eio__pwrite (int fd, void *buf, size_t count, off_t offset) { - ssize_t res; + eio_ssize_t res; off_t ooffset; X_LOCK (preadwritelock); @@ -952,11 +993,22 @@ return fdatasync (fd); } +static int +eio__fallocate (int fd, int mode, off_t offset, size_t len) +{ +#if HAVE_FALLOCATE + return fallocate (fd, mode, offset, len); +#else + errno = ENOSYS; + return -1; +#endif +} + #if !HAVE_READAHEAD # undef readahead # define readahead(fd,offset,count) eio__readahead (fd, offset, count, self) -static ssize_t +static eio_ssize_t eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) { size_t todo = count; @@ -978,17 +1030,20 @@ #endif /* sendfile always needs emulation */ -static ssize_t +static eio_ssize_t eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self) { - ssize_t written = 0; - ssize_t res; + eio_ssize_t written = 0; + eio_ssize_t res; if (!count) return 0; for (;;) { +#ifdef __APPLE__ +# undef HAVE_SENDFILE /* broken, as everything on os x */ +#endif #if HAVE_SENDFILE # if __linux off_t soffset = offset; @@ -1014,7 +1069,7 @@ if (sbytes) res = sbytes; -# elif defined (__APPLE__) && 0 /* broken, as everything on os x */ +# elif defined (__APPLE__) off_t sbytes = count; res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); @@ -1041,7 +1096,7 @@ # endif -#elif defined (_WIN32) +#elif defined (_WIN32) && 0 /* does not work, just for documentation of what would need to be done */ /* actually, cannot be done like this, as TransmitFile changes the file offset, */ /* libeio guarantees that the file offset does not change, and windows */ @@ -1084,7 +1139,9 @@ #ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */ || errno == ENOTSUP #endif +#ifdef EOPNOTSUPP /* windows */ || errno == EOPNOTSUPP /* BSDs */ +#endif #if __solaris || errno == EAFNOSUPPORT || errno == EPROTOTYPE #endif @@ -1098,7 +1155,7 @@ while (count) { - ssize_t cnt; + eio_ssize_t cnt; cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset); @@ -1156,7 +1213,7 @@ } #if !_POSIX_MEMLOCK -# define eio__mlockall(a) ((errno = ENOSYS), -1) +# define eio__mlockall(a) EIO_ENOSYS () #else static int @@ -1180,7 +1237,7 @@ #endif #if !_POSIX_MEMLOCK_RANGE -# define eio__mlock(a,b) ((errno = ENOSYS), -1) +# define eio__mlock(a,b) EIO_ENOSYS () #else static int @@ -1194,7 +1251,7 @@ #endif #if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) -# define eio__msync(a,b,c) ((errno = ENOSYS), -1) +# define eio__msync(a,b,c) EIO_ENOSYS () #else static int @@ -1316,7 +1373,7 @@ while (*rel) { - ssize_t len, linklen; + eio_ssize_t len, linklen; char *beg = rel; while (*rel && *rel != '/') @@ -1419,9 +1476,9 @@ #define EIO_SORT_FAST 60 /* when to only use insertion sort */ static void -eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { - unsigned char bits [9 + sizeof (ino_t) * 8]; + unsigned char bits [9 + sizeof (eio_ino_t) * 8]; unsigned char *bit = bits; assert (CHAR_BIT == 8); @@ -1433,27 +1490,27 @@ return; /* first prepare an array of bits to test in our radix sort */ - /* try to take endianness into account, as well as differences in ino_t sizes */ + /* try to take endianness into account, as well as differences in eio_ino_t sizes */ /* inode_bits must contain all inodes ORed together */ /* which is used to skip bits that are 0 everywhere, which is very common */ { - ino_t endianness; + eio_ino_t endianness; int i, j; /* we store the byte offset of byte n into byte n of "endianness" */ - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) ((unsigned char *)&endianness)[i] = i; *bit++ = 0; - for (i = 0; i < sizeof (ino_t); ++i) + for (i = 0; i < sizeof (eio_ino_t); ++i) { /* shifting off the byte offsets out of "endianness" */ int offs = (offsetof (eio_dirent, inode) + (endianness & 0xff)) * 8; endianness >>= 8; for (j = 0; j < 8; ++j) - if (inode_bits & (((ino_t)1) << (i * 8 + j))) + if (inode_bits & (((eio_ino_t)1) << (i * 8 + j))) *bit++ = offs + j; } @@ -1464,9 +1521,9 @@ /* now actually do the sorting (a variant of MSD radix sort) */ { - eio_dirent *base_stk [9 + sizeof (ino_t) * 8], *base; - eio_dirent *end_stk [9 + sizeof (ino_t) * 8], *end; - unsigned char *bit_stk [9 + sizeof (ino_t) * 8]; + eio_dirent *base_stk [9 + sizeof (eio_ino_t) * 8], *base; + eio_dirent *end_stk [9 + sizeof (eio_ino_t) * 8], *end; + unsigned char *bit_stk [9 + sizeof (eio_ino_t) * 8]; int stk_idx = 0; base_stk [stk_idx] = dents; @@ -1555,7 +1612,7 @@ } static void -eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, ino_t inode_bits) +eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) { if (size <= 1) return; /* our insertion sort relies on size > 0 */ @@ -1573,25 +1630,73 @@ static void eio__scandir (eio_req *req, etp_worker *self) { - DIR *dirp; - EIO_STRUCT_DIRENT *entp; char *name, *names; - int namesalloc = 4096; + int namesalloc = 4096 - sizeof (void *) * 4; int namesoffs = 0; int flags = req->int1; eio_dirent *dents = 0; int dentalloc = 128; int dentoffs = 0; - ino_t inode_bits = 0; + eio_ino_t inode_bits = 0; +#ifdef _WIN32 + HANDLE dirp; + WIN32_FIND_DATA entp; +#else + DIR *dirp; + EIO_STRUCT_DIRENT *entp; +#endif req->result = -1; if (!(flags & EIO_READDIR_DENTS)) flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER); - X_LOCK (wrklock); - /* the corresponding closedir is in ETP_WORKER_CLEAR */ - self->dirp = dirp = opendir (req->ptr1); +#ifdef _WIN32 + { + int len = strlen ((const char *)req->ptr1); + char *path = malloc (MAX_PATH); + const char *fmt; + + if (!len) + fmt = "./*"; + else if (((const char *)req->ptr1)[len - 1] == '/' || ((const char *)req->ptr1)[len - 1] == '\\') + fmt = "%s*"; + else + fmt = "%s/*"; + + _snprintf (path, MAX_PATH, fmt, (const char *)req->ptr1); + dirp = FindFirstFile (path, &entp); + free (path); + + if (dirp == INVALID_HANDLE_VALUE) + { + dirp = 0; + + switch (GetLastError ()) + { + case ERROR_FILE_NOT_FOUND: + req->result = 0; + break; + + case ERROR_INVALID_NAME: + case ERROR_PATH_NOT_FOUND: + case ERROR_NO_MORE_FILES: + errno = ENOENT; + break; + + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + + default: + errno = EINVAL; + break; + } + } + } +#else + dirp = opendir (req->ptr1); +#endif if (req->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1); @@ -1599,18 +1704,30 @@ req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; req->ptr2 = names = malloc (namesalloc); - X_UNLOCK (wrklock); if (dirp && names && (!flags || dents)) for (;;) { + int done; + +#ifdef _WIN32 + done = !dirp; +#else errno = 0; entp = readdir (dirp); + done = !entp; +#endif - if (!entp) + if (done) { +#ifndef _WIN32 + int old_errno = errno; + closedir (dirp); + errno = old_errno; + if (errno) break; +#endif /* sort etc. */ req->int1 = flags; @@ -1649,7 +1766,7 @@ } /* now add the entry to our list(s) */ - name = entp->d_name; + name = D_NAME (entp); /* skip . and .. entries */ if (name [0] != '.' || (name [1] && (name [1] != '.' || name [2]))) @@ -1659,9 +1776,7 @@ while (ecb_expect_false (namesoffs + len > namesalloc)) { namesalloc *= 2; - X_LOCK (wrklock); req->ptr2 = names = realloc (names, namesalloc); - X_UNLOCK (wrklock); if (!names) break; @@ -1676,9 +1791,7 @@ if (ecb_expect_false (dentoffs == dentalloc)) { dentalloc *= 2; - X_LOCK (wrklock); req->ptr1 = dents = realloc (dents, dentalloc * sizeof (eio_dirent)); - X_UNLOCK (wrklock); if (!dents) break; @@ -1768,6 +1881,14 @@ errno = ECANCELED; break; } + +#ifdef _WIN32 + if (!FindNextFile (dirp, &entp)) + { + FindClose (dirp); + dirp = 0; + } +#endif } } @@ -1794,11 +1915,13 @@ struct timespec ts; etp_worker *self = (etp_worker *)thr_arg; - /* try to distribute timeouts somewhat randomly */ + /* try to distribute timeouts somewhat evenly */ ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL); for (;;) { + ts.tv_sec = 0; + X_LOCK (reqlock); for (;;) @@ -1808,23 +1931,28 @@ if (req) break; + if (ts.tv_sec == 1) /* no request, but timeout detected, let's quit */ + { + X_UNLOCK (reqlock); + X_LOCK (wrklock); + --started; + X_UNLOCK (wrklock); + goto quit; + } + ++idle; - ts.tv_sec = time (0) + idle_timeout; - if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + if (idle <= max_idle) + /* we are allowed to idle, so do so without any timeout */ + X_COND_WAIT (reqwait, reqlock); + else { - if (idle > max_idle) - { - --idle; - X_UNLOCK (reqlock); - X_LOCK (wrklock); - --started; - X_UNLOCK (wrklock); - goto quit; - } + /* initialise timeout once */ + if (!ts.tv_sec) + ts.tv_sec = time (0) + idle_timeout; - /* we are allowed to idle, so do so without any timeout */ - X_COND_WAIT (reqwait, reqlock); + if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) + ts.tv_sec = 1; /* assuming this is not a value computed above.,.. */ } --idle; @@ -1964,6 +2092,7 @@ case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break; case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break; case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; + case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break; case EIO_READDIR: eio__scandir (req, self); break; @@ -2073,6 +2202,11 @@ REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; } +eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) +{ + REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; +} + eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) { REQ (EIO_FDATASYNC); req->int1 = fd; SEND; @@ -2123,7 +2257,7 @@ REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; } -eio_req *eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } @@ -2153,7 +2287,7 @@ REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; } -eio_req *eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) +eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) { REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; } @@ -2310,11 +2444,11 @@ /*****************************************************************************/ /* misc garbage */ -ssize_t +eio_ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) { etp_worker wrk; - ssize_t ret; + eio_ssize_t ret; wrk.dbuf = 0;