├── .gitignore ├── AUTHORS ├── MANIFEST.in ├── README.rst ├── TODO ├── eio.pxd ├── eio.pyx ├── examples ├── demo_port.py └── gevent_example.py ├── libeio ├── Changes ├── LICENSE ├── Makefile.am ├── autogen.sh ├── configure.ac ├── demo.c ├── ecb.h ├── eio.3 ├── eio.c ├── eio.h ├── eio.pod ├── libeio.m4 └── xthread.h ├── setup.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /eio.c 3 | /eio.so 4 | /libeio/* 5 | /cython_debug 6 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Travis Cline 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include eio.c 3 | recursive-include examples *py 4 | recursive-include libeio * 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | eio 2 | === 3 | 4 | A cython wrapper of libeio aimed at bringing asynchronous file IO to python. 5 | 6 | Alpha. 7 | 8 | API unstable. 9 | 10 | Currently requires Cython to build. 11 | 12 | Windows support through MinGW is.. partial. 13 | 14 | gevent and eventlet wrappers planned. 15 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | implement more complex callbacks for calls using more than one part of the eio_req 2 | check return codes and raise exceptions in all appropriate places 3 | -------------------------------------------------------------------------------- /eio.pxd: -------------------------------------------------------------------------------- 1 | #### types.h 2 | cdef extern from * nogil: 3 | ctypedef int pid_t 4 | ctypedef int dev_t 5 | ctypedef int ino_t 6 | ctypedef int mode_t 7 | ctypedef int nlink_t 8 | #ctypedef int uid_t 9 | #ctypedef int gid_t 10 | ctypedef int dev_t 11 | ctypedef int off_t 12 | ctypedef int blksize_t 13 | ctypedef int blkcnt_t 14 | ctypedef int fsfilcnt_t 15 | ctypedef int fsblkcnt_t 16 | ctypedef int time_t 17 | ctypedef int mode_t 18 | 19 | #### stat.h 20 | cdef extern from "sys/stat.h" nogil: 21 | struct stat_t "stat": 22 | dev_t st_dev # ID of device containing file 23 | ino_t st_ino # inode number 24 | mode_t st_mode # protection 25 | int st_nlink # number of hard links 26 | int st_uid # user ID of owner 27 | int st_gid # group ID of owner 28 | dev_t st_rdev # device ID (if special file) 29 | off_t st_size # total size, in bytes 30 | #blksize_t st_blksize # blocksize for filesystem I/O # not on win32 31 | #blkcnt_t st_blocks # number of blocks allocated # not on win32 32 | time_t st_atime # time of last access 33 | time_t st_mtime # time of last modification 34 | time_t st_ctime # time of last status change 35 | 36 | #### statvfs.h (but use eio.c's stuff for win compat) 37 | cdef extern from * nogil: 38 | struct statvfs_t "statvfs": 39 | unsigned long f_bsize # file system block size 40 | unsigned long f_frsize # fragment size 41 | fsblkcnt_t f_blocks # size of fs in f_frsize units 42 | fsblkcnt_t f_bfree # # free blocks 43 | fsblkcnt_t f_bavail # # free blocks for non-root 44 | fsfilcnt_t f_files # # inodes 45 | fsfilcnt_t f_ffree # # free inodes 46 | fsfilcnt_t f_favail # # free inodes for non-root 47 | unsigned long f_fsid # file system ID 48 | unsigned long f_flag # mount flags 49 | unsigned long f_namemax # maximum filename length 50 | 51 | cdef extern from "Python.h": 52 | int Py_IsInitialized () 53 | int PyEval_ThreadsInitialized () 54 | void PyEval_InitThreads () 55 | object PyString_FromStringAndSize(char *s, Py_ssize_t len) 56 | char * PyString_AsString(object) 57 | 58 | cdef extern from "libeio/eio.c": 59 | enum: EIO_PRI_MIN 60 | enum: EIO_PRI_MAX 61 | enum: EIO_PRI_DEFAULT 62 | 63 | enum: EIO_SYNC_FILE_RANGE_WAIT_BEFORE 64 | enum: EIO_SYNC_FILE_RANGE_WRITE 65 | enum: EIO_SYNC_FILE_RANGE_WAIT_AFTER 66 | 67 | enum: EIO_CUSTOM 68 | enum: EIO_OPEN 69 | enum: EIO_CLOSE 70 | enum: EIO_DUP2 71 | enum: EIO_READ 72 | enum: EIO_WRITE 73 | enum: EIO_READAHEAD 74 | enum: EIO_SENDFILE 75 | enum: EIO_STAT 76 | enum: EIO_LSTAT 77 | enum: EIO_FSTAT 78 | enum: EIO_STATVFS 79 | enum: EIO_FSTATVFS 80 | enum: EIO_TRUNCATE 81 | enum: EIO_FTRUNCATE 82 | enum: EIO_UTIME 83 | enum: EIO_FUTIME 84 | enum: EIO_CHMOD 85 | enum: EIO_FCHMOD 86 | enum: EIO_CHOWN 87 | enum: EIO_FCHOWN 88 | enum: EIO_SYNC 89 | enum: EIO_FSYNC 90 | enum: EIO_FDATASYNC 91 | enum: EIO_MSYNC 92 | enum: EIO_MTOUCH 93 | enum: EIO_SYNC_FILE_RANGE 94 | enum: EIO_MLOCK 95 | enum: EIO_MLOCKALL 96 | enum: EIO_UNLINK 97 | enum: EIO_RMDIR 98 | enum: EIO_MKDIR 99 | enum: EIO_RENAME 100 | enum: EIO_MKNOD 101 | enum: EIO_READDIR 102 | enum: EIO_LINK 103 | enum: EIO_SYMLINK 104 | enum: EIO_READLINK 105 | enum: EIO_GROUP 106 | enum: EIO_NOP 107 | enum: EIO_BUSY 108 | 109 | ctypedef int eio_uid_t 110 | ctypedef int eio_gid_t 111 | 112 | cdef struct eio_dirent 113 | cdef struct eio_req 114 | ctypedef double eio_tstamp 115 | ctypedef int (*eio_cb)(eio_req *req) 116 | 117 | cdef struct eio_req: 118 | ssize_t result # result of syscall, e.g. result = read (... 119 | int type # EIO_xxx constant ETP 120 | int int1 # all applicable requests: file descriptor sendfile: output fd open, msync, mlockall, readdir: flags 121 | long int2 # chown, fchown: uid sendfile: input fd open, chmod, mkdir, mknod: file mode, sync_file_range: flags 122 | long int3 # chown, fchown: gid 123 | int errorno # errno value on syscall return 124 | char *data 125 | void *ptr1 # all applicable requests: pathname, old name; readdir: optional eio_dirents 126 | void *ptr2 # all applicable requests: new name or memory buffer; readdir: name strings 127 | 128 | cdef void * memset (void * ptr, int value, size_t num) 129 | cdef void * calloc(size_t count, size_t size) 130 | 131 | cdef extern from "libeio/eio.c" nogil: 132 | 133 | #ctypedef void (*callback)() 134 | #int eio_init (void *want_poll, void *done_poll) 135 | #int eio_init (callback want_poll, callback done_poll) 136 | int eio_init (void (*want_poll)(), void (*done_poll)()) 137 | int eio_poll () 138 | 139 | unsigned int eio_nreqs () # number of requests in-flight 140 | unsigned int eio_nready () # number of not-yet handled requests 141 | unsigned int eio_npending () # number of finished but unhandled requests 142 | unsigned int eio_nthreads () # number of worker threads in use currently 143 | 144 | void eio_set_min_parallel (unsigned int nthreads) 145 | void eio_set_max_poll_reqs (unsigned int nreqs) 146 | 147 | 148 | eio_req *eio_nop (int pri, eio_cb cb, void *data) # does nothing except go through the whole process 149 | eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) # ties a thread for this long, simulating busyness 150 | eio_req *eio_sync (int pri, eio_cb cb, void *data) 151 | eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data) 152 | eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) 153 | eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 154 | eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 155 | eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) 156 | eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) 157 | eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) 158 | eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) 159 | eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) 160 | eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 161 | eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 162 | eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data) # stat buffer=ptr2 allocated dynamically 163 | eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) # stat buffer=ptr2 allocated dynamically 164 | eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 165 | eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) 166 | eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) 167 | eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) 168 | eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) 169 | eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) 170 | eio_req *eio_open (char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) 171 | eio_req *eio_utime (char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 172 | eio_req *eio_truncate (char *path, off_t offset, int pri, eio_cb cb, void *data) 173 | eio_req *eio_chown (char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) 174 | eio_req *eio_chmod (char *path, mode_t mode, int pri, eio_cb cb, void *data) 175 | eio_req *eio_mkdir (char *path, mode_t mode, int pri, eio_cb cb, void *data) 176 | eio_req *eio_readdir (char *path, int flags, int pri, eio_cb cb, void *data) # result=ptr2 allocated dynamically 177 | eio_req *eio_rmdir (char *path, int pri, eio_cb cb, void *data) 178 | eio_req *eio_unlink (char *path, int pri, eio_cb cb, void *data) 179 | eio_req *eio_readlink (char *path, int pri, eio_cb cb, void *data) # result=ptr2 allocated dynamically 180 | eio_req *eio_stat (char *path, int pri, eio_cb cb, void *data) # stat buffer=ptr2 allocated dynamically 181 | eio_req *eio_lstat (char *path, int pri, eio_cb cb, void *data) # stat buffer=ptr2 allocated dynamically 182 | eio_req *eio_statvfs (char *path, int pri, eio_cb cb, void *data) # stat buffer=ptr2 allocated dynamically 183 | eio_req *eio_mknod (char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) 184 | eio_req *eio_link (char *path, char *new_path, int pri, eio_cb cb, void *data) 185 | eio_req *eio_symlink (char *path, char *new_path, int pri, eio_cb cb, void *data) 186 | eio_req *eio_rename (char *path, char *new_path, int pri, eio_cb cb, void *data) 187 | eio_req *eio_custom (eio_cb execute, int pri, eio_cb cb, void *data) 188 | 189 | 190 | -------------------------------------------------------------------------------- /eio.pyx: -------------------------------------------------------------------------------- 1 | from eio cimport * 2 | from cpython.buffer cimport * 3 | 4 | #### extension types and helper functions 5 | 6 | class eio_exception(Exception): pass 7 | 8 | cdef eio_req_from_ptr(eio_req* req_ptr): 9 | r = eio_request() 10 | r._eio_req = req_ptr 11 | return r 12 | 13 | cdef class eio_request: 14 | cdef eio_req *_eio_req 15 | property type: 16 | def __get__(self): 17 | return eio_request_types[self._eio_req.type] 18 | property data: 19 | def __get__(self): 20 | return self._eio_req.data 21 | property result: 22 | def __get__(self): 23 | return self._eio_req.result 24 | property int1: 25 | def __get__(self): 26 | return self._eio_req.int1 27 | property int2: 28 | def __get__(self): 29 | return self._eio_req.int2 30 | property int3: 31 | def __get__(self): 32 | return self._eio_req.int3 33 | property ptr1: 34 | def __get__(self): 35 | return self._eio_req.ptr1 36 | property ptr2: 37 | def __get__(self): 38 | return self._eio_req.ptr2 39 | 40 | # custom properties 41 | property buf: 42 | def __get__(self): 43 | return self._eio_req.ptr2 44 | 45 | def __repr__(self): 46 | return '' % (self.type, self.int1, self.int2, self.int3) 47 | 48 | cdef stat_result_from_ptr(stat_t *stat_ptr): 49 | s = stat_result() 50 | s._statptr = stat_ptr 51 | return s 52 | 53 | cdef class stat_result: 54 | cdef stat_t *_statptr 55 | property st_dev: 56 | def __get__(self): 57 | return self._statptr.st_dev 58 | property st_ino: 59 | def __get__(self): 60 | return self._statptr.st_ino 61 | property st_mode: 62 | def __get__(self): 63 | return self._statptr.st_mode 64 | property st_nlink: 65 | def __get__(self): 66 | return self._statptr.st_nlink 67 | property st_uid: 68 | def __get__(self): 69 | return self._statptr.st_uid 70 | property st_gid: 71 | def __get__(self): 72 | return self._statptr.st_gid 73 | property st_rdev: 74 | def __get__(self): 75 | return self._statptr.st_rdev 76 | property st_size: 77 | def __get__(self): 78 | return self._statptr.st_size 79 | 80 | property st_atime: 81 | def __get__(self): 82 | return self._statptr.st_atime 83 | property st_mtime: 84 | def __get__(self): 85 | return self._statptr.st_mtime 86 | property st_ctime: 87 | def __get__(self): 88 | return self._statptr.st_ctime 89 | 90 | def __repr__(self): 91 | fields = ['st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime', 'st_ctime'] 92 | return 'eio.stat_result(%s)' % ', '.join(['%s=%s' % (f, getattr(self, f, None)) for f in fields]) 93 | 94 | 95 | eio_request_types = [ 96 | 'CUSTOM', 97 | 'OPEN', 'CLOSE', 'DUP2', 98 | 'READ', 'WRITE', 99 | 'READAHEAD', 'SENDFILE', 100 | 'STAT', 'LSTAT', 'FSTAT', 101 | 'STATVFS', 'FSTATVFS', 102 | 'TRUNCATE', 'FTRUNCATE', 103 | 'UTIME', 'FUTIME', 104 | 'CHMOD', 'FCHMOD', 105 | 'CHOWN', 'FCHOWN', 106 | 'SYNC', 'FSYNC', 'FDATASYNC', 107 | 'MSYNC', 'MTOUCH', 'SYNC_FILE_RANGE', 108 | 'MLOCK', 'MLOCKALL', 109 | 'UNLINK', 'RMDIR', 'MKDIR', 'RENAME', 110 | 'MKNOD', 'READDIR', 111 | 'LINK', 'SYMLINK', 'READLINK', 112 | 'GROUP', 'NOP', 113 | 'BUSY' 114 | ] 115 | 116 | #### references to python (want/done)_poll callbacks 117 | 118 | cdef object _py_want_poll_cb 119 | cdef object _py_done_poll_cb 120 | 121 | cdef void _want_poll() with gil: 122 | if Py_IsInitialized() == 0: 123 | return 124 | 125 | global _py_want_poll_cb 126 | if _py_want_poll_cb: 127 | _py_want_poll_cb() 128 | 129 | cdef void _done_poll() with gil: 130 | if Py_IsInitialized() == 0: 131 | return 132 | global _py_done_poll_cb 133 | if _py_done_poll_cb: 134 | _py_done_poll_cb() 135 | 136 | #### init 137 | 138 | def init(want_poll=None, done_poll=None): 139 | PyEval_InitThreads() 140 | global _py_done_poll_cb 141 | global _py_want_poll_cb 142 | _py_want_poll_cb = want_poll 143 | _py_done_poll_cb = done_poll 144 | return eio_init(_want_poll, _done_poll) 145 | 146 | #### callbacks 147 | 148 | cdef int void_callback(eio_req *req) with gil: 149 | r = eio_req_from_ptr(req) 150 | if r.data: 151 | r.data() 152 | return 0 153 | 154 | cdef int stat_callback (eio_req *req) with gil: 155 | r = eio_req_from_ptr(req) 156 | s = stat_result() 157 | #print 'stat callback', r 158 | s._statptr = r.ptr2 159 | if r.data: 160 | r.data(s) 161 | return 0 162 | 163 | cdef int result_callback (eio_req *req) with gil: 164 | r = eio_req_from_ptr(req) 165 | #print 'result callback', r 166 | if r.data: 167 | r.data(r.result) 168 | return 0 169 | 170 | cdef int rw_callback (eio_req *req) with gil: 171 | r = eio_req_from_ptr(req) 172 | #print 'rw callback', r, r.result, '"%s"' % r.buf 173 | if r.data: 174 | r.data(r) 175 | return 0 176 | 177 | #### misc 178 | def nop(): 179 | with nogil: 180 | eio_nop(0, NULL, NULL) 181 | 182 | #### posix call wrappers 183 | 184 | #eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) 185 | def open(char *path, int flags, mode_t mode=0777, callback=None): 186 | cdef eio_req *r 187 | with nogil: 188 | r = eio_open(path, flags, mode, EIO_PRI_DEFAULT, result_callback, callback) 189 | return eio_req_from_ptr(r) 190 | 191 | #eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) 192 | def truncate(char *path, off_t offset, callback=None): 193 | cdef eio_req *r 194 | with nogil: 195 | r = eio_truncate(path, offset, EIO_PRI_DEFAULT, result_callback, callback) 196 | return eio_req_from_ptr(r) 197 | 198 | #eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) 199 | def chown(char *path, eio_uid_t uid, eio_gid_t gid, callback=None): 200 | cdef eio_req *r 201 | with nogil: 202 | r = eio_chown(path, uid, gid, EIO_PRI_DEFAULT, result_callback, callback) 203 | return eio_req_from_ptr(r) 204 | 205 | #eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 206 | def chmod(char *path, mode_t mode, callback=None): 207 | cdef eio_req *r 208 | with nogil: 209 | r = eio_chmod(path, mode, EIO_PRI_DEFAULT, result_callback, callback) 210 | return eio_req_from_ptr(r) 211 | 212 | #eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 213 | def mkdir(char *path, mode_t mode=0777, callback=None): 214 | cdef eio_req *r 215 | with nogil: 216 | r = eio_mkdir(path, mode, EIO_PRI_DEFAULT, result_callback, callback) 217 | return eio_req_from_ptr(r) 218 | 219 | #eio_rmdir (const char *path, int pri, eio_cb cb, void *data) 220 | def rmdir(char *path, callback=None): 221 | cdef eio_req *r 222 | with nogil: 223 | r = eio_rmdir(path, EIO_PRI_DEFAULT, result_callback, callback) 224 | return eio_req_from_ptr(r) 225 | 226 | #eio_unlink (const char *path, int pri, eio_cb cb, void *data) 227 | def unlink(char *path, callback=None): 228 | cdef eio_req *r 229 | with nogil: 230 | r = eio_unlink(path, EIO_PRI_DEFAULT, result_callback, callback) 231 | return eio_req_from_ptr(r) 232 | 233 | #eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 234 | def utime(char *path, eio_tstamp atime, eio_tstamp mtime, callback=None): 235 | cdef eio_req *r 236 | with nogil: 237 | r = eio_utime(path, atime, mtime, EIO_PRI_DEFAULT, result_callback, callback) 238 | return eio_req_from_ptr(r) 239 | 240 | #eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) 241 | def mknod(char *path, mode_t mode=0666, dev_t dev=0, callback=None): 242 | cdef eio_req *r 243 | with nogil: 244 | r = eio_mknod(path, mode, dev, EIO_PRI_DEFAULT, result_callback, callback) 245 | return eio_req_from_ptr(r) 246 | 247 | #eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 248 | def link(char *path, char *new_path, callback=None): 249 | cdef eio_req *r 250 | with nogil: 251 | r = eio_link(path, new_path, EIO_PRI_DEFAULT, result_callback, callback) 252 | return eio_req_from_ptr(r) 253 | 254 | #eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 255 | def symlink(char *path, char *new_path, callback=None): 256 | cdef eio_req *r 257 | with nogil: 258 | r = eio_symlink(path, new_path, EIO_PRI_DEFAULT, result_callback, callback) 259 | return eio_req_from_ptr(r) 260 | 261 | #eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 262 | def rename(char *path, char *new_path, callback=None): 263 | cdef eio_req *r 264 | with nogil: 265 | r = eio_rename(path, new_path, EIO_PRI_DEFAULT, result_callback, callback) 266 | return eio_req_from_ptr(r) 267 | 268 | #eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) 269 | def mlock(addr, size_t length, callback=None): 270 | cdef eio_req *r 271 | with nogil: 272 | r = eio_mlock(addr, length, EIO_PRI_DEFAULT, result_callback, callback) 273 | return eio_req_from_ptr(r) 274 | 275 | #eio_close (int fd, int pri, eio_cb cb, void *data) 276 | def close(int fd, callback=None): 277 | cdef eio_req *r 278 | with nogil: 279 | r = eio_close(fd, EIO_PRI_DEFAULT, result_callback, callback) 280 | return eio_req_from_ptr(r) 281 | 282 | #eio_sync (int pri, eio_cb cb, void *data) 283 | def sync(callback=None): 284 | cdef eio_req *r 285 | with nogil: 286 | r = eio_sync(EIO_PRI_DEFAULT, result_callback, callback) 287 | return eio_req_from_ptr(r) 288 | 289 | #eio_fsync (int fd, int pri, eio_cb cb, void *data) 290 | def fsync(int fd, callback=None): 291 | cdef eio_req *r 292 | with nogil: 293 | r = eio_fsync(fd, EIO_PRI_DEFAULT, result_callback, callback) 294 | return eio_req_from_ptr(r) 295 | 296 | #eio_fdatasync (int fd, int pri, eio_cb cb, void *data) 297 | def fdatasync(int fd, callback=None): 298 | cdef eio_req *r 299 | with nogil: 300 | r = eio_fdatasync(fd, EIO_PRI_DEFAULT, result_callback, callback) 301 | return eio_req_from_ptr(r) 302 | 303 | #eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 304 | def futime(int fd, eio_tstamp atime, eio_tstamp mtime, callback=None): 305 | cdef eio_req *r 306 | with nogil: 307 | r = eio_futime(fd, atime, mtime, EIO_PRI_DEFAULT, result_callback, callback) 308 | return eio_req_from_ptr(r) 309 | 310 | #eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) 311 | def ftruncate(int fd, off_t offset, callback=None): 312 | cdef eio_req *r 313 | with nogil: 314 | r = eio_ftruncate(fd, offset, EIO_PRI_DEFAULT, result_callback, callback) 315 | return eio_req_from_ptr(r) 316 | 317 | #eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) 318 | def fchmod(int fd, mode_t mode, callback=None): 319 | cdef eio_req *r 320 | with nogil: 321 | r = eio_fchmod(fd, mode, EIO_PRI_DEFAULT, result_callback, callback) 322 | return eio_req_from_ptr(r) 323 | 324 | #eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) 325 | def fchown(int fd, eio_uid_t uid, eio_gid_t gid, callback=None): 326 | cdef eio_req *r 327 | with nogil: 328 | r = eio_fchown(fd, uid, gid, EIO_PRI_DEFAULT, result_callback, callback) 329 | return eio_req_from_ptr(r) 330 | 331 | #eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) 332 | def dup2(int fd, int fd2, callback=None): 333 | cdef eio_req *r 334 | with nogil: 335 | r = eio_dup2(fd, fd2, EIO_PRI_DEFAULT, result_callback, callback) 336 | return eio_req_from_ptr(r) 337 | 338 | 339 | #eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 340 | def read(int fd, size_t length, off_t offset, callback=None): 341 | cdef eio_req *r 342 | cdef char *buffer_ptr 343 | buffer = PyString_FromStringAndSize(NULL, length); 344 | buffer_ptr = PyString_AsString(buffer) 345 | 346 | with nogil: 347 | r = eio_read(fd, buffer_ptr, length, offset, EIO_PRI_DEFAULT, rw_callback, callback) 348 | return eio_req_from_ptr(r) 349 | 350 | #eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 351 | def write(int fd, buf, off_t offset=0, callback=None): 352 | cdef Py_buffer pbuf 353 | if PyObject_GetBuffer(buf, &pbuf, PyBUF_SIMPLE) == -1: 354 | raise Exception('Error creating buffer from %s' % buf) 355 | with nogil: 356 | r = eio_write(fd, pbuf.buf, pbuf.len, offset, EIO_PRI_DEFAULT, rw_callback, callback) 357 | return eio_req_from_ptr(r) 358 | 359 | 360 | #eio_mlockall (int flags, int pri, eio_cb cb, void *data) 361 | # Like mlockall, but the flag value constants are called EIO_MCL_CURRENT and EIO_MCL_FUTURE. 362 | 363 | #eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 364 | # Just like msync, except that the flag values are called EIO_MS_ASYNC, EIO_MS_INVALIDATE and EIO_MS_SYNC. 365 | 366 | #eio_readlink (const char *path, int pri, eio_cb cb, void *data) 367 | # If successful, the path read by readlink(2) can be accessed via req->ptr2 and is NOT null-terminated, with the length specified as req->result. 368 | 369 | #eio_realpath (const char *path, int pri, eio_cb cb, void *data) 370 | # Similar to the realpath libc function, but unlike that one, result is -1 on failure and the length of the returned path in ptr2 (which is not 0-terminated) - this is similar to readlink. 371 | 372 | 373 | #eio_stat (const char *path, int pri, eio_cb cb, void *data) 374 | def stat(char *path, callback=None): 375 | cdef eio_req *r 376 | with nogil: 377 | r = eio_stat(path, EIO_PRI_DEFAULT, stat_callback, callback) 378 | return eio_req_from_ptr(r) 379 | 380 | #eio_lstat (const char *path, int pri, eio_cb cb, void *data) 381 | def lstat(char *path, callback=None): 382 | cdef eio_req *r 383 | with nogil: 384 | r = eio_lstat(path, EIO_PRI_DEFAULT, stat_callback, callback) 385 | return eio_req_from_ptr(r) 386 | 387 | #eio_fstat (int fd, int pri, eio_cb cb, void *data) 388 | def fstat(int fd, callback=None): 389 | cdef eio_req *r 390 | with nogil: 391 | r = eio_fstat(fd, EIO_PRI_DEFAULT, stat_callback, callback) 392 | return eio_req_from_ptr(r) 393 | 394 | #eio_statvfs (const char *path, int pri, eio_cb cb, void *data) 395 | #eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) 396 | # Stats a filesystem - if req->result indicates success, then you can access the struct statvfs-like structure via req->ptr2: 397 | # EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; 398 | 399 | 400 | #eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) 401 | def sendfile(int out_fd, int in_fd, off_t in_offset, size_t length, callback=None): 402 | cdef eio_req *r 403 | with nogil: 404 | r = eio_sendfile(out_fd, in_fd, in_offset, length, EIO_PRI_DEFAULT, result_callback, callback) 405 | return eio_req_from_ptr(r) 406 | # Wraps the sendfile syscall. The arguments follow the Linux version, but libeio supports and will use similar calls on FreeBSD, HP/UX, Solaris and Darwin. 407 | # If the OS doesn't support some sendfile-like call, or the call fails, indicating support for the given file descriptor type (for example, Linux's sendfile might not support file to file copies), then libeio will emulate the call in userspace, so there are almost no limitations on its use. 408 | 409 | #eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) 410 | def readahead(int fd, off_t offset, size_t length, callback=None): 411 | cdef eio_req *r 412 | with nogil: 413 | r = eio_readahead(fd, offset, length, EIO_PRI_DEFAULT, result_callback, callback) 414 | return eio_req_from_ptr(r) 415 | # Calls readahead(2). If the syscall is missing, then the call is emulated by simply reading the data (currently in 64kiB chunks). 416 | 417 | #eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) 418 | def sync_file_range(int fd, off_t offset, size_t nbytes, unsigned int flags, callback=None): 419 | cdef eio_req *r 420 | with nogil: 421 | r = eio_sync_file_range(fd, offset, nbytes, flags, EIO_PRI_DEFAULT, result_callback, callback) 422 | return eio_req_from_ptr(r) 423 | # Calls sync_file_range. If the syscall is missing, then this is the same as calling fdatasync. 424 | # Flags can be any combination of EIO_SYNC_FILE_RANGE_WAIT_BEFORE, EIO_SYNC_FILE_RANGE_WRITE and EIO_SYNC_FILE_RANGE_WAIT_AFTER. 425 | 426 | #eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) 427 | def busy(eio_tstamp delay, callback=None): 428 | cdef eio_req *r 429 | with nogil: 430 | r = eio_busy(delay, EIO_PRI_DEFAULT, void_callback, callback) 431 | return eio_req_from_ptr(r) 432 | 433 | 434 | def poll(): 435 | cdef int r 436 | with nogil: 437 | r = eio_poll() 438 | return r 439 | 440 | def nreqs(): 441 | return eio_nreqs() 442 | 443 | def nready(): 444 | return eio_nready() 445 | 446 | def npending(): 447 | return eio_npending() 448 | 449 | def nthreads(): 450 | return eio_nthreads() 451 | 452 | def set_min_parallel(unsigned int nthreads): 453 | eio_set_min_parallel(nthreads) 454 | -------------------------------------------------------------------------------- /examples/demo_port.py: -------------------------------------------------------------------------------- 1 | """ 2 | porting of libeio's demo.c 3 | """ 4 | 5 | def print_stats(): 6 | print 'stats' 7 | print 'num reqs:\t', eio.nreqs() 8 | print 'num ready:\t', eio.nready() 9 | print 'num pending:\t', eio.npending() 10 | print 'num threads:\t', eio.nthreads() 11 | 12 | 13 | import os 14 | import sys 15 | import stat 16 | import time 17 | import select 18 | 19 | ##include 20 | ##include 21 | ##include 22 | ##include 23 | ##include 24 | ##include 25 | ##include 26 | ##include 27 | ##include 28 | # 29 | ##include "eio.h" 30 | import eio 31 | # 32 | #int respipe [2]; 33 | respipe = [] 34 | # 35 | #void 36 | #want_poll (void) 37 | def want_poll(): 38 | #{ 39 | # char dummy; 40 | # printf ("want_poll ()\n"); 41 | #print 'want_poll ()' 42 | os.write(respipe[1], ' ') 43 | #print 'done want_poll ()' 44 | # write (respipe [1], &dummy, 1); 45 | #} 46 | # 47 | #void 48 | #done_poll (void) 49 | def done_poll(): 50 | #{ 51 | # char dummy; 52 | # printf ("done_poll ()\n"); 53 | #print 'done_poll ()' 54 | os.read(respipe[0], 1) 55 | #print 'done done_poll ()' 56 | # read (respipe [0], &dummy, 1); 57 | #} 58 | # 59 | #void 60 | #event_loop (void) 61 | def event_loop(): 62 | #{ 63 | # // an event loop. yeah. 64 | # struct pollfd pfd; 65 | # pfd.fd = respipe [0]; 66 | # pfd.events = POLLIN; 67 | # 68 | try: 69 | p = select.poll() 70 | p.register(respipe[0], select.POLLIN) 71 | # printf ("\nentering event loop\n"); 72 | #print 'entering event loop' 73 | # while (eio_nreqs ()) 74 | while eio.nreqs(): 75 | # { 76 | # poll (&pfd, 1, -1); 77 | sys.stdout.write('.') 78 | sys.stdout.flush() 79 | p.poll(1000) 80 | # printf ("eio_poll () = %d\n", eio_poll ()); 81 | #print 'eio_poll () = %d' % eio.poll() 82 | eio.poll() 83 | except AttributeError: 84 | # no poll, stupidily wait 500ms 85 | time.sleep(0.5) 86 | while eio.nreqs(): 87 | sys.stdout.write('.') 88 | sys.stdout.flush() 89 | eio.poll() 90 | 91 | # } 92 | # printf ("leaving event loop\n"); 93 | #print 'leaving event loop' 94 | #} 95 | # 96 | #int 97 | #res_cb (eio_req *req) 98 | def res_cb(req): 99 | #{ 100 | # printf ("res_cb(%d|%s) = %d\n", req->type, req->data ? req->data : "?", EIO_RESULT (req)); 101 | print 'res_cb(%d|%s) = %d' % (req.type, req.data or '?', req.result) 102 | # 103 | # if (req->result < 0) 104 | # abort (); 105 | if req.result < 0: 106 | print 'XXX', 'bad result, aborting' 107 | os.abort() 108 | # 109 | # return 0; 110 | return 0 111 | #} 112 | # 113 | #int 114 | #readdir_cb (eio_req *req) 115 | #{ 116 | # char *buf = (char *)EIO_BUF (req); 117 | # 118 | # printf ("readdir_cb = %d\n", EIO_RESULT (req)); 119 | # 120 | # if (EIO_RESULT (req) < 0) 121 | # return 0; 122 | # 123 | # while (EIO_RESULT (req)--) 124 | # { 125 | # printf ("readdir = <%s>\n", buf); 126 | # buf += strlen (buf) + 1; 127 | # } 128 | # 129 | # return 0; 130 | #} 131 | # 132 | #int 133 | #stat_cb (eio_req *req) 134 | #{ 135 | # struct stat *buf = EIO_STAT_BUF (req); 136 | # 137 | # if (req->type == EIO_FSTAT) 138 | # printf ("fstat_cb = %d\n", EIO_RESULT (req)); 139 | # else 140 | # printf ("stat_cb(%s) = %d\n", EIO_PATH (req), EIO_RESULT (req)); 141 | # 142 | # if (!EIO_RESULT (req)) 143 | # printf ("stat size %d perm 0%o\n", buf->st_size, buf->st_mode & 0777); 144 | # 145 | # return 0; 146 | #} 147 | def stat_callback(stat_info): 148 | print 'py stat_cb', stat_info 149 | 150 | # 151 | #int 152 | #read_cb (eio_req *req) 153 | #{ 154 | # unsigned char *buf = (unsigned char *)EIO_BUF (req); 155 | # 156 | # printf ("read_cb = %d (%02x%02x%02x%02x %02x%02x%02x%02x)\n", 157 | # EIO_RESULT (req), 158 | # buf [0], buf [1], buf [2], buf [3], 159 | # buf [4], buf [5], buf [6], buf [7]); 160 | # 161 | # return 0; 162 | #} 163 | # 164 | #int last_fd; 165 | last_fd = None 166 | # 167 | #int 168 | #open_cb (eio_req *req) 169 | def open_callback(fd): 170 | global last_fd 171 | last_fd = fd 172 | print 'open_callback, fd:', fd 173 | 174 | #{ 175 | # printf ("open_cb = %d\n", EIO_RESULT (req)); 176 | # 177 | # last_fd = EIO_RESULT (req); 178 | # 179 | # return 0; 180 | #} 181 | # 182 | 183 | def read_callback(r): 184 | print 'READ CALLBACK', r, len(r.buf), r.buf 185 | 186 | 187 | #int 188 | #main (void) 189 | if __name__ == '__main__': 190 | #{ 191 | # printf ("pipe ()\n"); 192 | print 'pipe ()' 193 | # if (pipe (respipe)) abort (); 194 | respipe = os.pipe() 195 | # 196 | # printf ("eio_init ()\n"); 197 | print 'eio_init ()' 198 | 199 | # if (eio_init (want_poll, done_poll)) abort (); 200 | if eio.init(want_poll, done_poll): 201 | # if eio.init(): 202 | print 'init failed, aborting' 203 | os.abort() 204 | # 205 | # do 206 | # { 207 | # /* avoid relative paths yourself(!) */ 208 | # eio_mkdir ("eio-test-dir", 0777, 0, res_cb, "mkdir"); 209 | r = eio.mkdir('eio-test-dir', 0777) 210 | # eio_nop (0, res_cb, "nop"); 211 | eio.nop() 212 | # eio_stat ("eio-test-dir", 0, stat_cb, "stat"); 213 | eio.stat('eio-test-dir', stat_callback) 214 | # eio_lstat ("eio-test-dir", 0, stat_cb, "stat"); 215 | eio.lstat('eio-test-dir') 216 | # eio_open ("eio-test-dir/eio-test-file", O_RDWR | O_CREAT, 0777, 0, open_cb, "open"); 217 | eio.open('eio-test-dir/eio-test-file', os.O_RDWR | os.O_CREAT, 0777, open_callback) 218 | # eio_symlink ("test", "eio-test-dir/eio-symlink", 0, res_cb, "symlink"); 219 | eio.symlink('test', 'eio-test-dir/eio-symlink') 220 | # eio_mknod ("eio-test-dir/eio-fifo", S_IFIFO, 0, 0, res_cb, "mknod"); 221 | eio.mknod('eio-test-dir/eio-fifo', stat.S_IFIFO, 0) 222 | # event_loop (); 223 | event_loop() 224 | # 225 | # eio_utime ("eio-test-dir", 12345.678, 23456.789, 0, res_cb, "utime"); 226 | eio.utime('eio-test-dir', 12345.678, 23456.789) 227 | # eio_futime (last_fd, 92345.678, 93456.789, 0, res_cb, "futime"); 228 | eio.futime(last_fd, 92345.678, 93456.789) 229 | # eio_chown ("eio-test-dir", getuid (), getgid (), 0, res_cb, "chown"); 230 | if hasattr(os, 'getuid'): 231 | eio.chown('eio-test-dir', os.getuid(), os.getgid()) 232 | # eio_fchown (last_fd, getuid (), getgid (), 0, res_cb, "fchown"); 233 | eio.fchown(last_fd, os.getuid(), os.getgid()) 234 | # eio_fchmod (last_fd, 0723, 0, res_cb, "fchmod"); 235 | eio.fchmod(last_fd, 0723, 0) 236 | # eio_readdir ("eio-test-dir", 0, 0, readdir_cb, "readdir"); 237 | # eio_readdir ("/nonexistant", 0, 0, readdir_cb, "readdir"); 238 | # eio_fstat (last_fd, 0, stat_cb, "stat"); 239 | # eio_write (last_fd, "test\nfail\n", 10, 4, 0, res_cb, "write"); 240 | eio.write(last_fd, 'test\nfail\n', 4) 241 | # event_loop (); 242 | event_loop() 243 | # 244 | # eio_read (last_fd, 0, 8, 0, EIO_PRI_DEFAULT, read_cb, "read"); 245 | eio.read(last_fd, 8, 0, read_callback) 246 | # eio_readlink ("eio-test-dir/eio-symlink", 0, res_cb, "readlink"); 247 | event_loop() 248 | # 249 | # eio_dup2 (1, 2, EIO_PRI_DEFAULT, res_cb, "dup"); // dup stdout to stderr 250 | eio.dup2 (1, 2) # dup stdout to stderr 251 | # eio_chmod ("eio-test-dir", 0765, 0, res_cb, "chmod"); 252 | eio.chmod('eio-test-dir', 0765) 253 | # eio_ftruncate (last_fd, 9, 0, res_cb, "ftruncate"); 254 | eio.ftruncate(last_fd, 9) 255 | # eio_fdatasync (last_fd, 0, res_cb, "fdatasync"); 256 | eio.fdatasync(last_fd) 257 | # eio_fsync (last_fd, 0, res_cb, "fsync"); 258 | eio.fsync(last_fd) 259 | # eio_sync (0, res_cb, "sync"); 260 | eio.sync() 261 | # eio_busy (0.5, 0, res_cb, "busy"); 262 | eio.busy(0.5) 263 | # event_loop (); 264 | event_loop() 265 | # 266 | # eio_sendfile (1, last_fd, 4, 5, 0, res_cb, "sendfile"); // write "test\n" to stdout 267 | eio.sendfile(1, last_fd, 4, 5) # write "test\n" to stdout 268 | # eio_fstat (last_fd, 0, stat_cb, "stat"); 269 | eio.fstat(last_fd) 270 | # event_loop (); 271 | event_loop() 272 | # 273 | # eio_truncate ("eio-test-dir/eio-test-file", 6, 0, res_cb, "truncate"); 274 | eio.truncate('eio-test-dir/eio-test-file', 6) 275 | # eio_readahead (last_fd, 0, 64, 0, res_cb, "readahead"); 276 | eio.readahead(last_fd, 0, 64) 277 | # event_loop (); 278 | event_loop() 279 | # 280 | # eio_close (last_fd, 0, res_cb, "close"); 281 | eio.close(last_fd) 282 | # eio_link ("eio-test-dir/eio-test-file", "eio-test-dir/eio-test-file-2", 0, res_cb, "link"); 283 | eio.link('eio-test-dir/eio-test-file', 'eio-test-dir/eio-test-file-2') 284 | # event_loop (); 285 | event_loop() 286 | # 287 | # eio_rename ("eio-test-dir/eio-test-file", "eio-test-dir/eio-test-file-renamed", 0, res_cb, "rename"); 288 | eio.rename('eio-test-dir/eio-test-file', 'eio-test-dir/eio-test-file-renamed') 289 | # event_loop (); 290 | event_loop() 291 | # 292 | # eio_unlink ("eio-test-dir/eio-fifo", 0, res_cb, "unlink"); 293 | eio.unlink('eio-test-dir/eio-fifo') 294 | # eio_unlink ("eio-test-dir/eio-symlink", 0, res_cb, "unlink"); 295 | eio.unlink('eio-test-dir/eio-symlink') 296 | # eio_unlink ("eio-test-dir/eio-test-file-2", 0, res_cb, "unlink"); 297 | eio.unlink('eio-test-dir/eio-test-file-2') 298 | # eio_unlink ("eio-test-dir/eio-test-file-renamed", 0, res_cb, "unlink"); 299 | eio.unlink('eio-test-dir/eio-test-file-renamed') 300 | # event_loop (); 301 | event_loop() 302 | # 303 | # eio_rmdir ("eio-test-dir", 0, res_cb, "rmdir"); 304 | eio.rmdir('eio-test-dir') 305 | # event_loop (); 306 | event_loop() 307 | # } 308 | # while (0); 309 | # 310 | # return 0; 311 | #} 312 | # 313 | print 'normally exiting\n' 314 | -------------------------------------------------------------------------------- /examples/gevent_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example showing eio io lib works in a greened (gevent) environment. 3 | """ 4 | import os 5 | import eio 6 | import gevent 7 | from gevent.event import Event 8 | from gevent.socket import wait_read 9 | 10 | 11 | def show_nonblocking(): 12 | while True: 13 | gevent.sleep(0.1) 14 | print 'not blocking!' 15 | 16 | ## eio utilities 17 | 18 | # pipe used for cross thread signalling 19 | respipe = os.pipe() 20 | 21 | def poll_eio(): 22 | while True: 23 | wait_read(respipe[0]) 24 | while eio.nreqs(): 25 | eio.poll() 26 | gevent.sleep() 27 | 28 | def want_poll(): 29 | os.write(respipe[1], ' ') 30 | 31 | def done_poll(): 32 | os.read(respipe[0], 1) 33 | 34 | def do_io(n_megs, use_eio=False): 35 | 36 | r = os.open('/dev/urandom', os.O_RDONLY) 37 | 38 | print '\nreading %s megs from /dev/urandom' % n_megs 39 | 40 | if use_eio: 41 | eio.init(want_poll, done_poll) 42 | 43 | r_event = Event() 44 | def set_r_event(result): 45 | r_event.set() 46 | 47 | buf = eio.read(r, 1024*1024*n_megs, 0, set_r_event) 48 | 49 | print 'waiting' 50 | r_event.wait() 51 | else: 52 | buf = os.read(r, 1024*1024*n_megs) 53 | print '\ndone.' 54 | 55 | gevent.sleep(0.5) 56 | 57 | 58 | if __name__ == '__main__': 59 | import sys 60 | num_megs = 50 61 | use_eio = False 62 | 63 | if len(sys.argv) > 1: 64 | use_eio = sys.argv[1] == 'eio' 65 | if len(sys.argv) > 2: 66 | try: 67 | num_megs = int(sys.argv[2]) 68 | except ValueError: 69 | pass 70 | 71 | if use_eio: 72 | print 'using eio calls' 73 | else: 74 | print 'using traditional stdlib os calls, run as `%s eio` to use eio calls' % sys.argv[0] 75 | 76 | gevent.spawn(show_nonblocking) 77 | gevent.spawn(poll_eio) 78 | gevent.spawn(do_io, num_megs, use_eio).join() 79 | 80 | -------------------------------------------------------------------------------- /libeio/Changes: -------------------------------------------------------------------------------- 1 | Revision history for libeio 2 | 3 | TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd 4 | TODO: openbsd requires stdint.h for intptr_t - why posix? 5 | 6 | TODO: make mtouch/readdir maybe others cancellable in-request 7 | TODO: fadvise request 8 | TODO: fdopendir/utimensat 9 | 1.0 10 | - fix a deadlock where a wakeup signal could be missed when 11 | a timeout occured at the same time. 12 | - use nonstandard but maybe-working-on-bsd fork technique. 13 | - use fewer time() syscalls when waiting for new requests. 14 | - fix a path-memory-leak in readdir when using the wrappers 15 | (reported by Thomas L. Shinnick). 16 | - support a max_idle value of 0. 17 | - support setting of idle timeout value (eio_set_idle_timeout). 18 | - readdir: correctly handle malloc failures. 19 | - readdir: new flags argument, can return inode 20 | and possibly filetype, can sort in various ways. 21 | - readdir: stop immediately when cancelled, do 22 | not continue reading the directory. 23 | - fix return value of eio_sendfile_sync. 24 | - include sys/mman.h for msync. 25 | - added EIO_STACKSIZE. 26 | - added msync, mtouch support (untested). 27 | - added sync_file_range (untested). 28 | - fixed custom support. 29 | - use a more robust feed-add detection method. 30 | - "outbundled" from IO::AIO. 31 | - eio_set_max_polltime did not properly convert time to ticks. 32 | - tentatively support darwin in sendfile. 33 | - fix freebsd/darwin sendfile. 34 | - also use sendfile emulation for ENOTSUP and EOPNOTSUPP 35 | error codes. 36 | - add OS-independent EIO_MT_* and EIO_MS_* flag enums. 37 | - add eio_statvfs/eio_fstatvfs. 38 | - add eio_mlock/eio_mlockall and OS-independent MCL_* flag enums. 39 | - no longer set errno to 0 before making syscalls, this only lures 40 | people into the trap of believing errno shows success or failure. 41 | - "fix" demo.c so that it works as non-root. 42 | - suppoert utimes seperately from futimes, as some systems have 43 | utimes but not futimes. 44 | - use _POSIX_MEMLOCK_RANGE for mlock. 45 | - do not (errornously) overwrite CFLAGS in configure.ac. 46 | - mknod used int3 for dev_t (§2 bit), not offs (64 bit). 47 | - fix memory corruption in eio_readdirx for the flags 48 | combination EIO_READDIR_STAT_ORDER | EIO_READDIR_DIRS_FIRST. 49 | - port to openbsd (another blatantly broken non-UNIX/POSIX platform). 50 | - fix eio_custom prototype. 51 | - work around a Linux (and likely FreeBSD and other kernels) bug 52 | where sendfile would not transfer all the requested bytes on 53 | large transfers, using a heuristic. 54 | - use libecb, and apply lots of minor space optimisations. 55 | - disable sendfile on darwin, broken as everything else. 56 | - add realpath request and implementation. 57 | - cancelled requests will still invoke their request callbacks. 58 | - add fallocate. 59 | - do not acquire any locks when forking. 60 | - incorporated some mingw32 changes by traviscline. 61 | - added syncfs support, using direct syscall. 62 | - set thread name on linux (ps -L/Hcx, top, gdb). 63 | - remove useless use of volatile variables. 64 | - fix memory leak when reaping threads. 65 | - use utime now uses nanosecond resolution on posix 2008 systems. 66 | - allow taking advantage of posix 2008 xxxat functions and fdopendir 67 | by implementing a working directory abstraction. 68 | 69 | -------------------------------------------------------------------------------- /libeio/LICENSE: -------------------------------------------------------------------------------- 1 | All files in libeio are Copyright (C)2007,2008 Marc Alexander Lehmann. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | Alternatively, the contents of this package may be used under the terms 28 | of the GNU General Public License ("GPL") version 2 or any later version, 29 | in which case the provisions of the GPL are applicable instead of the 30 | above. If you wish to allow the use of your version of this package only 31 | under the terms of the GPL and not to allow others to use your version of 32 | this file under the BSD license, indicate your decision by deleting the 33 | provisions above and replace them with the notice and other provisions 34 | required by the GPL in this and the other files of this package. If you do 35 | not delete the provisions above, a recipient may use your version of this 36 | file under either the BSD or the GPL. 37 | -------------------------------------------------------------------------------- /libeio/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign no-dependencies 2 | 3 | VERSION_INFO = 1:0 4 | 5 | EXTRA_DIST = LICENSE Changes autogen.sh 6 | 7 | #man_MANS = ev.3 8 | 9 | include_HEADERS = eio.h 10 | 11 | lib_LTLIBRARIES = libeio.la 12 | 13 | libeio_la_SOURCES = eio.c ecb.h xthread.h config.h 14 | libeio_la_LDFLAGS = -version-info $(VERSION_INFO) 15 | 16 | -------------------------------------------------------------------------------- /libeio/autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install --symlink --force 4 | -------------------------------------------------------------------------------- /libeio/configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.59) 2 | AC_INIT 3 | AC_CONFIG_SRCDIR([eio.h]) 4 | AC_CONFIG_HEADERS([config.h]) 5 | 6 | AM_INIT_AUTOMAKE(libeio,1.0) 7 | AM_MAINTAINER_MODE 8 | 9 | AC_GNU_SOURCE 10 | 11 | AC_PROG_LIBTOOL 12 | 13 | AC_PROG_CC 14 | 15 | if test "x$GCC" = xyes ; then 16 | CFLAGS="-O3 $CFLAGS" 17 | fi 18 | 19 | m4_include([libeio.m4]) 20 | 21 | AC_CONFIG_FILES([Makefile]) 22 | AC_OUTPUT 23 | -------------------------------------------------------------------------------- /libeio/demo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "eio.h" 12 | 13 | int respipe [2]; 14 | 15 | void 16 | want_poll (void) 17 | { 18 | char dummy; 19 | printf ("want_poll ()\n"); 20 | write (respipe [1], &dummy, 1); 21 | } 22 | 23 | void 24 | done_poll (void) 25 | { 26 | char dummy; 27 | printf ("done_poll ()\n"); 28 | read (respipe [0], &dummy, 1); 29 | } 30 | 31 | void 32 | event_loop (void) 33 | { 34 | // an event loop. yeah. 35 | struct pollfd pfd; 36 | pfd.fd = respipe [0]; 37 | pfd.events = POLLIN; 38 | 39 | printf ("\nentering event loop\n"); 40 | while (eio_nreqs ()) 41 | { 42 | poll (&pfd, 1, -1); 43 | printf ("eio_poll () = %d\n", eio_poll ()); 44 | } 45 | printf ("leaving event loop\n"); 46 | } 47 | 48 | int 49 | res_cb (eio_req *req) 50 | { 51 | printf ("res_cb(%d|%s) = %d\n", req->type, req->data ? req->data : "?", EIO_RESULT (req)); 52 | 53 | if (req->result < 0) 54 | abort (); 55 | 56 | return 0; 57 | } 58 | 59 | int 60 | readdir_cb (eio_req *req) 61 | { 62 | char *buf = (char *)EIO_BUF (req); 63 | 64 | printf ("readdir_cb = %d\n", EIO_RESULT (req)); 65 | 66 | if (EIO_RESULT (req) < 0) 67 | return 0; 68 | 69 | while (EIO_RESULT (req)--) 70 | { 71 | printf ("readdir = <%s>\n", buf); 72 | buf += strlen (buf) + 1; 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | int 79 | stat_cb (eio_req *req) 80 | { 81 | struct stat *buf = EIO_STAT_BUF (req); 82 | 83 | if (req->type == EIO_FSTAT) 84 | printf ("fstat_cb = %d\n", EIO_RESULT (req)); 85 | else 86 | printf ("stat_cb(%s) = %d\n", EIO_PATH (req), EIO_RESULT (req)); 87 | 88 | if (!EIO_RESULT (req)) 89 | printf ("stat size %d perm 0%o\n", buf->st_size, buf->st_mode & 0777); 90 | 91 | return 0; 92 | } 93 | 94 | int 95 | read_cb (eio_req *req) 96 | { 97 | unsigned char *buf = (unsigned char *)EIO_BUF (req); 98 | 99 | printf ("read_cb = %d (%02x%02x%02x%02x %02x%02x%02x%02x)\n", 100 | EIO_RESULT (req), 101 | buf [0], buf [1], buf [2], buf [3], 102 | buf [4], buf [5], buf [6], buf [7]); 103 | 104 | return 0; 105 | } 106 | 107 | int last_fd; 108 | 109 | int 110 | open_cb (eio_req *req) 111 | { 112 | printf ("open_cb = %d\n", EIO_RESULT (req)); 113 | 114 | last_fd = EIO_RESULT (req); 115 | 116 | return 0; 117 | } 118 | 119 | int 120 | main (void) 121 | { 122 | printf ("pipe ()\n"); 123 | if (pipe (respipe)) abort (); 124 | 125 | printf ("eio_init ()\n"); 126 | if (eio_init (want_poll, done_poll)) abort (); 127 | 128 | do 129 | { 130 | /* avoid relative paths yourself(!) */ 131 | eio_mkdir ("eio-test-dir", 0777, 0, res_cb, "mkdir"); 132 | eio_nop (0, res_cb, "nop"); 133 | event_loop (); 134 | 135 | eio_stat ("eio-test-dir", 0, stat_cb, "stat"); 136 | eio_lstat ("eio-test-dir", 0, stat_cb, "stat"); 137 | eio_open ("eio-test-dir/eio-test-file", O_RDWR | O_CREAT, 0777, 0, open_cb, "open"); 138 | eio_symlink ("test", "eio-test-dir/eio-symlink", 0, res_cb, "symlink"); 139 | eio_mknod ("eio-test-dir/eio-fifo", S_IFIFO, 0, 0, res_cb, "mknod"); 140 | event_loop (); 141 | 142 | eio_utime ("eio-test-dir", 12345.678, 23456.789, 0, res_cb, "utime"); 143 | eio_futime (last_fd, 92345.678, 93456.789, 0, res_cb, "futime"); 144 | eio_chown ("eio-test-dir", getuid (), getgid (), 0, res_cb, "chown"); 145 | eio_fchown (last_fd, getuid (), getgid (), 0, res_cb, "fchown"); 146 | eio_fchmod (last_fd, 0723, 0, res_cb, "fchmod"); 147 | eio_readdir ("eio-test-dir", 0, 0, readdir_cb, "readdir"); 148 | eio_readdir ("/nonexistant", 0, 0, readdir_cb, "readdir"); 149 | eio_fstat (last_fd, 0, stat_cb, "stat"); 150 | eio_write (last_fd, "test\nfail\n", 10, 4, 0, res_cb, "write"); 151 | event_loop (); 152 | 153 | eio_read (last_fd, 0, 8, 0, EIO_PRI_DEFAULT, read_cb, "read"); 154 | eio_readlink ("eio-test-dir/eio-symlink", 0, res_cb, "readlink"); 155 | event_loop (); 156 | 157 | eio_dup2 (1, 2, EIO_PRI_DEFAULT, res_cb, "dup"); // dup stdout to stderr 158 | eio_chmod ("eio-test-dir", 0765, 0, res_cb, "chmod"); 159 | eio_ftruncate (last_fd, 9, 0, res_cb, "ftruncate"); 160 | eio_fdatasync (last_fd, 0, res_cb, "fdatasync"); 161 | eio_fsync (last_fd, 0, res_cb, "fsync"); 162 | eio_sync (0, res_cb, "sync"); 163 | eio_busy (0.5, 0, res_cb, "busy"); 164 | event_loop (); 165 | 166 | eio_sendfile (1, last_fd, 4, 5, 0, res_cb, "sendfile"); // write "test\n" to stdout 167 | eio_fstat (last_fd, 0, stat_cb, "stat"); 168 | event_loop (); 169 | 170 | eio_truncate ("eio-test-dir/eio-test-file", 6, 0, res_cb, "truncate"); 171 | eio_readahead (last_fd, 0, 64, 0, res_cb, "readahead"); 172 | event_loop (); 173 | 174 | eio_close (last_fd, 0, res_cb, "close"); 175 | eio_link ("eio-test-dir/eio-test-file", "eio-test-dir/eio-test-file-2", 0, res_cb, "link"); 176 | event_loop (); 177 | 178 | eio_rename ("eio-test-dir/eio-test-file", "eio-test-dir/eio-test-file-renamed", 0, res_cb, "rename"); 179 | event_loop (); 180 | 181 | eio_unlink ("eio-test-dir/eio-fifo", 0, res_cb, "unlink"); 182 | eio_unlink ("eio-test-dir/eio-symlink", 0, res_cb, "unlink"); 183 | eio_unlink ("eio-test-dir/eio-test-file-2", 0, res_cb, "unlink"); 184 | eio_unlink ("eio-test-dir/eio-test-file-renamed", 0, res_cb, "unlink"); 185 | event_loop (); 186 | 187 | eio_rmdir ("eio-test-dir", 0, res_cb, "rmdir"); 188 | event_loop (); 189 | } 190 | while (0); 191 | 192 | return 0; 193 | } 194 | 195 | -------------------------------------------------------------------------------- /libeio/ecb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libecb - http://software.schmorp.de/pkg/libecb 3 | * 4 | * Copyright (©) 2009-2011 Marc Alexander Lehmann 5 | * Copyright (©) 2011 Emanuele Giaquinta 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without modifica- 9 | * tion, are permitted provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 19 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 20 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 21 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 22 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 26 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 27 | * OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef ECB_H 31 | #define ECB_H 32 | 33 | #ifdef _WIN32 34 | typedef signed char int8_t; 35 | typedef unsigned char uint8_t; 36 | typedef signed short int16_t; 37 | typedef unsigned short uint16_t; 38 | typedef signed int int32_t; 39 | typedef unsigned int uint32_t; 40 | #if __GNUC__ 41 | typedef signed long long int64_t; 42 | typedef unsigned long long uint64_t; 43 | #else /* _MSC_VER || __BORLANDC__ */ 44 | typedef signed __int64 int64_t; 45 | typedef unsigned __int64 uint64_t; 46 | #endif 47 | #else 48 | #include 49 | #endif 50 | 51 | /* many compilers define _GNUC_ to some versions but then only implement 52 | * what their idiot authors think are the "more important" extensions, 53 | * causing enormous grief in return for some better fake benchmark numbers. 54 | * or so. 55 | * we try to detect these and simply assume they are not gcc - if they have 56 | * an issue with that they should have done it right in the first place. 57 | */ 58 | #ifndef ECB_GCC_VERSION 59 | #if !defined(__GNUC_MINOR__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) || defined(__llvm__) || defined(__clang__) 60 | #define ECB_GCC_VERSION(major,minor) 0 61 | #else 62 | #define ECB_GCC_VERSION(major,minor) (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) 63 | #endif 64 | #endif 65 | 66 | /*****************************************************************************/ 67 | 68 | /* ECB_NO_THREADS - ecb is not used by multiple threads, ever */ 69 | /* ECB_NO_SMP - ecb might be used in multiple threads, but only on a single cpu */ 70 | 71 | #if ECB_NO_THREADS || ECB_NO_SMP 72 | #define ECB_MEMORY_FENCE do { } while (0) 73 | #endif 74 | 75 | #ifndef ECB_MEMORY_FENCE 76 | #if ECB_GCC_VERSION(2,5) || defined(__INTEL_COMPILER) || defined(__clang__) 77 | #if __i386__ 78 | #define ECB_MEMORY_FENCE __asm__ __volatile__ ("lock; orb $0, -1(%%esp)" : : : "memory") 79 | #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE /* non-lock xchg might be enough */ 80 | #define ECB_MEMORY_FENCE_RELEASE do { } while (0) /* unlikely to change in future cpus */ 81 | #elif __amd64 82 | #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mfence" : : : "memory") 83 | #define ECB_MEMORY_FENCE_ACQUIRE __asm__ __volatile__ ("lfence" : : : "memory") 84 | #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("sfence") /* play safe - not needed in any current cpu */ 85 | #elif __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__ 86 | #define ECB_MEMORY_FENCE __asm__ __volatile__ ("sync" : : : "memory") 87 | #elif defined(__ARM_ARCH_6__ ) || defined(__ARM_ARCH_6J__ ) \ 88 | || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) 89 | #define ECB_MEMORY_FENCE __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,5" : : "r" (0) : "memory") 90 | #elif defined(__ARM_ARCH_7__ ) || defined(__ARM_ARCH_7A__ ) \ 91 | || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7R__ ) 92 | #define ECB_MEMORY_FENCE __asm__ __volatile__ ("dmb" : : : "memory") 93 | #endif 94 | #endif 95 | #endif 96 | 97 | #ifndef ECB_MEMORY_FENCE 98 | #if ECB_GCC_VERSION(4,4) || defined(__INTEL_COMPILER) || defined(__clang__) 99 | #define ECB_MEMORY_FENCE __sync_synchronize () 100 | /*#define ECB_MEMORY_FENCE_ACQUIRE ({ char dummy = 0; __sync_lock_test_and_set (&dummy, 1); }) */ 101 | /*#define ECB_MEMORY_FENCE_RELEASE ({ char dummy = 1; __sync_lock_release (&dummy ); }) */ 102 | #elif _MSC_VER >= 1400 /* VC++ 2005 */ 103 | #pragma intrinsic(_ReadBarrier,_WriteBarrier,_ReadWriteBarrier) 104 | #define ECB_MEMORY_FENCE _ReadWriteBarrier () 105 | #define ECB_MEMORY_FENCE_ACQUIRE _ReadWriteBarrier () /* according to msdn, _ReadBarrier is not a load fence */ 106 | #define ECB_MEMORY_FENCE_RELEASE _WriteBarrier () 107 | #elif defined(_WIN32) 108 | #include 109 | #define ECB_MEMORY_FENCE MemoryBarrier () /* actually just xchg on x86... scary */ 110 | #endif 111 | #endif 112 | 113 | #ifndef ECB_MEMORY_FENCE 114 | #if !ECB_AVOID_PTHREADS 115 | /* 116 | * if you get undefined symbol references to pthread_mutex_lock, 117 | * or failure to find pthread.h, then you should implement 118 | * the ECB_MEMORY_FENCE operations for your cpu/compiler 119 | * OR provide pthread.h and link against the posix thread library 120 | * of your system. 121 | */ 122 | #include 123 | #define ECB_NEEDS_PTHREADS 1 124 | #define ECB_MEMORY_FENCE_NEEDS_PTHREADS 1 125 | 126 | static pthread_mutex_t ecb_mf_lock = PTHREAD_MUTEX_INITIALIZER; 127 | #define ECB_MEMORY_FENCE do { pthread_mutex_lock (&ecb_mf_lock); pthread_mutex_unlock (&ecb_mf_lock); } while (0) 128 | #endif 129 | #endif 130 | 131 | #if !defined(ECB_MEMORY_FENCE_ACQUIRE) && defined(ECB_MEMORY_FENCE) 132 | #define ECB_MEMORY_FENCE_ACQUIRE ECB_MEMORY_FENCE 133 | #endif 134 | 135 | #if !defined(ECB_MEMORY_FENCE_RELEASE) && defined(ECB_MEMORY_FENCE) 136 | #define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE 137 | #endif 138 | 139 | /*****************************************************************************/ 140 | 141 | #define ECB_C99 (__STDC_VERSION__ >= 199901L) 142 | 143 | #if __cplusplus 144 | #define ecb_inline static inline 145 | #elif ECB_GCC_VERSION(2,5) 146 | #define ecb_inline static __inline__ 147 | #elif ECB_C99 148 | #define ecb_inline static inline 149 | #else 150 | #define ecb_inline static 151 | #endif 152 | 153 | #if ECB_GCC_VERSION(3,3) 154 | #define ecb_restrict __restrict__ 155 | #elif ECB_C99 156 | #define ecb_restrict restrict 157 | #else 158 | #define ecb_restrict 159 | #endif 160 | 161 | typedef int ecb_bool; 162 | 163 | #define ECB_CONCAT_(a, b) a ## b 164 | #define ECB_CONCAT(a, b) ECB_CONCAT_(a, b) 165 | #define ECB_STRINGIFY_(a) # a 166 | #define ECB_STRINGIFY(a) ECB_STRINGIFY_(a) 167 | 168 | #define ecb_function_ ecb_inline 169 | 170 | #if ECB_GCC_VERSION(3,1) 171 | #define ecb_attribute(attrlist) __attribute__(attrlist) 172 | #define ecb_is_constant(expr) __builtin_constant_p (expr) 173 | #define ecb_expect(expr,value) __builtin_expect ((expr),(value)) 174 | #define ecb_prefetch(addr,rw,locality) __builtin_prefetch (addr, rw, locality) 175 | #else 176 | #define ecb_attribute(attrlist) 177 | #define ecb_is_constant(expr) 0 178 | #define ecb_expect(expr,value) (expr) 179 | #define ecb_prefetch(addr,rw,locality) 180 | #endif 181 | 182 | /* no emulation for ecb_decltype */ 183 | #if ECB_GCC_VERSION(4,5) 184 | #define ecb_decltype(x) __decltype(x) 185 | #elif ECB_GCC_VERSION(3,0) 186 | #define ecb_decltype(x) __typeof(x) 187 | #endif 188 | 189 | #define ecb_noinline ecb_attribute ((__noinline__)) 190 | #define ecb_noreturn ecb_attribute ((__noreturn__)) 191 | #define ecb_unused ecb_attribute ((__unused__)) 192 | #define ecb_const ecb_attribute ((__const__)) 193 | #define ecb_pure ecb_attribute ((__pure__)) 194 | 195 | #if ECB_GCC_VERSION(4,3) 196 | #define ecb_artificial ecb_attribute ((__artificial__)) 197 | #define ecb_hot ecb_attribute ((__hot__)) 198 | #define ecb_cold ecb_attribute ((__cold__)) 199 | #else 200 | #define ecb_artificial 201 | #define ecb_hot 202 | #define ecb_cold 203 | #endif 204 | 205 | /* put around conditional expressions if you are very sure that the */ 206 | /* expression is mostly true or mostly false. note that these return */ 207 | /* booleans, not the expression. */ 208 | #define ecb_expect_false(expr) ecb_expect (!!(expr), 0) 209 | #define ecb_expect_true(expr) ecb_expect (!!(expr), 1) 210 | /* for compatibility to the rest of the world */ 211 | #define ecb_likely(expr) ecb_expect_true (expr) 212 | #define ecb_unlikely(expr) ecb_expect_false (expr) 213 | 214 | /* count trailing zero bits and count # of one bits */ 215 | #if ECB_GCC_VERSION(3,4) 216 | /* we assume int == 32 bit, long == 32 or 64 bit and long long == 64 bit */ 217 | #define ecb_ld32(x) (__builtin_clz (x) ^ 31) 218 | #define ecb_ld64(x) (__builtin_clzll (x) ^ 63) 219 | #define ecb_ctz32(x) __builtin_ctz (x) 220 | #define ecb_ctz64(x) __builtin_ctzll (x) 221 | #define ecb_popcount32(x) __builtin_popcount (x) 222 | /* no popcountll */ 223 | #else 224 | ecb_function_ int ecb_ctz32 (uint32_t x) ecb_const; 225 | ecb_function_ int 226 | ecb_ctz32 (uint32_t x) 227 | { 228 | int r = 0; 229 | 230 | x &= ~x + 1; /* this isolates the lowest bit */ 231 | 232 | #if ECB_branchless_on_i386 233 | r += !!(x & 0xaaaaaaaa) << 0; 234 | r += !!(x & 0xcccccccc) << 1; 235 | r += !!(x & 0xf0f0f0f0) << 2; 236 | r += !!(x & 0xff00ff00) << 3; 237 | r += !!(x & 0xffff0000) << 4; 238 | #else 239 | if (x & 0xaaaaaaaa) r += 1; 240 | if (x & 0xcccccccc) r += 2; 241 | if (x & 0xf0f0f0f0) r += 4; 242 | if (x & 0xff00ff00) r += 8; 243 | if (x & 0xffff0000) r += 16; 244 | #endif 245 | 246 | return r; 247 | } 248 | 249 | ecb_function_ int ecb_ctz64 (uint64_t x) ecb_const; 250 | ecb_function_ int 251 | ecb_ctz64 (uint64_t x) 252 | { 253 | int shift = x & 0xffffffffU ? 0 : 32; 254 | return ecb_ctz32 (x >> shift) + shift; 255 | } 256 | 257 | ecb_function_ int ecb_popcount32 (uint32_t x) ecb_const; 258 | ecb_function_ int 259 | ecb_popcount32 (uint32_t x) 260 | { 261 | x -= (x >> 1) & 0x55555555; 262 | x = ((x >> 2) & 0x33333333) + (x & 0x33333333); 263 | x = ((x >> 4) + x) & 0x0f0f0f0f; 264 | x *= 0x01010101; 265 | 266 | return x >> 24; 267 | } 268 | 269 | ecb_function_ int ecb_ld32 (uint32_t x) ecb_const; 270 | ecb_function_ int ecb_ld32 (uint32_t x) 271 | { 272 | int r = 0; 273 | 274 | if (x >> 16) { x >>= 16; r += 16; } 275 | if (x >> 8) { x >>= 8; r += 8; } 276 | if (x >> 4) { x >>= 4; r += 4; } 277 | if (x >> 2) { x >>= 2; r += 2; } 278 | if (x >> 1) { r += 1; } 279 | 280 | return r; 281 | } 282 | 283 | ecb_function_ int ecb_ld64 (uint64_t x) ecb_const; 284 | ecb_function_ int ecb_ld64 (uint64_t x) 285 | { 286 | int r = 0; 287 | 288 | if (x >> 32) { x >>= 32; r += 32; } 289 | 290 | return r + ecb_ld32 (x); 291 | } 292 | #endif 293 | 294 | /* popcount64 is only available on 64 bit cpus as gcc builtin */ 295 | /* so for this version we are lazy */ 296 | ecb_function_ int ecb_popcount64 (uint64_t x) ecb_const; 297 | ecb_function_ int 298 | ecb_popcount64 (uint64_t x) 299 | { 300 | return ecb_popcount32 (x) + ecb_popcount32 (x >> 32); 301 | } 302 | 303 | ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) ecb_const; 304 | ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) ecb_const; 305 | ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) ecb_const; 306 | ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) ecb_const; 307 | ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) ecb_const; 308 | ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) ecb_const; 309 | ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) ecb_const; 310 | ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) ecb_const; 311 | 312 | ecb_inline uint8_t ecb_rotl8 (uint8_t x, unsigned int count) { return (x >> ( 8 - count)) | (x << count); } 313 | ecb_inline uint8_t ecb_rotr8 (uint8_t x, unsigned int count) { return (x << ( 8 - count)) | (x >> count); } 314 | ecb_inline uint16_t ecb_rotl16 (uint16_t x, unsigned int count) { return (x >> (16 - count)) | (x << count); } 315 | ecb_inline uint16_t ecb_rotr16 (uint16_t x, unsigned int count) { return (x << (16 - count)) | (x >> count); } 316 | ecb_inline uint32_t ecb_rotl32 (uint32_t x, unsigned int count) { return (x >> (32 - count)) | (x << count); } 317 | ecb_inline uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { return (x << (32 - count)) | (x >> count); } 318 | ecb_inline uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); } 319 | ecb_inline uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); } 320 | 321 | #if ECB_GCC_VERSION(4,3) 322 | #define ecb_bswap16(x) (__builtin_bswap32 (x) >> 16) 323 | #define ecb_bswap32(x) __builtin_bswap32 (x) 324 | #define ecb_bswap64(x) __builtin_bswap64 (x) 325 | #else 326 | ecb_function_ uint16_t ecb_bswap16 (uint16_t x) ecb_const; 327 | ecb_function_ uint16_t 328 | ecb_bswap16 (uint16_t x) 329 | { 330 | return ecb_rotl16 (x, 8); 331 | } 332 | 333 | ecb_function_ uint32_t ecb_bswap32 (uint32_t x) ecb_const; 334 | ecb_function_ uint32_t 335 | ecb_bswap32 (uint32_t x) 336 | { 337 | return (((uint32_t)ecb_bswap16 (x)) << 16) | ecb_bswap16 (x >> 16); 338 | } 339 | 340 | ecb_function_ uint64_t ecb_bswap64 (uint64_t x) ecb_const; 341 | ecb_function_ uint64_t 342 | ecb_bswap64 (uint64_t x) 343 | { 344 | return (((uint64_t)ecb_bswap32 (x)) << 32) | ecb_bswap32 (x >> 32); 345 | } 346 | #endif 347 | 348 | #if ECB_GCC_VERSION(4,5) 349 | #define ecb_unreachable() __builtin_unreachable () 350 | #else 351 | /* this seems to work fine, but gcc always emits a warning for it :/ */ 352 | ecb_function_ void ecb_unreachable (void) ecb_noreturn; 353 | ecb_function_ void ecb_unreachable (void) { } 354 | #endif 355 | 356 | /* try to tell the compiler that some condition is definitely true */ 357 | #define ecb_assume(cond) do { if (!(cond)) ecb_unreachable (); } while (0) 358 | 359 | ecb_function_ unsigned char ecb_byteorder_helper (void) ecb_const; 360 | ecb_function_ unsigned char 361 | ecb_byteorder_helper (void) 362 | { 363 | const uint32_t u = 0x11223344; 364 | return *(unsigned char *)&u; 365 | } 366 | 367 | ecb_function_ ecb_bool ecb_big_endian (void) ecb_const; 368 | ecb_function_ ecb_bool ecb_big_endian (void) { return ecb_byteorder_helper () == 0x11; } 369 | ecb_function_ ecb_bool ecb_little_endian (void) ecb_const; 370 | ecb_function_ ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; } 371 | 372 | #if ECB_GCC_VERSION(3,0) || ECB_C99 373 | #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0)) 374 | #else 375 | #define ecb_mod(m,n) ((m) < 0 ? ((n) - 1 - ((-1 - (m)) % (n))) : ((m) % (n))) 376 | #endif 377 | 378 | #if __cplusplus 379 | template 380 | static inline T ecb_div_rd (T val, T div) 381 | { 382 | return val < 0 ? - ((-val + div - 1) / div) : (val ) / div; 383 | } 384 | template 385 | static inline T ecb_div_ru (T val, T div) 386 | { 387 | return val < 0 ? - ((-val ) / div) : (val + div - 1) / div; 388 | } 389 | #else 390 | #define ecb_div_rd(val,div) ((val) < 0 ? - ((-(val) + (div) - 1) / (div)) : ((val) ) / (div)) 391 | #define ecb_div_ru(val,div) ((val) < 0 ? - ((-(val) ) / (div)) : ((val) + (div) - 1) / (div)) 392 | #endif 393 | 394 | #if ecb_cplusplus_does_not_suck 395 | /* does not work for local types (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2657.htm) */ 396 | template 397 | static inline int ecb_array_length (const T (&arr)[N]) 398 | { 399 | return N; 400 | } 401 | #else 402 | #define ecb_array_length(name) (sizeof (name) / sizeof (name [0])) 403 | #endif 404 | 405 | #endif 406 | 407 | -------------------------------------------------------------------------------- /libeio/eio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libeio implementation 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef _WIN32 41 | # include "config.h" 42 | #endif 43 | 44 | #include "eio.h" 45 | #include "ecb.h" 46 | 47 | #ifdef EIO_STACKSIZE 48 | # define X_STACKSIZE EIO_STACKSIZE 49 | #endif 50 | #include "xthread.h" 51 | 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | /* intptr_t comes from unistd.h, says POSIX/UNIX/tradition */ 64 | /* intptr_t only comes from stdint.h, says idiot openbsd coder */ 65 | #if HAVE_STDINT_H 66 | # include 67 | #endif 68 | 69 | #ifndef ECANCELED 70 | # define ECANCELED EDOM 71 | #endif 72 | #ifndef ELOOP 73 | # define ELOOP EDOM 74 | #endif 75 | 76 | #if !defined(ENOTSOCK) && defined(WSAENOTSOCK) 77 | # define ENOTSOCK WSAENOTSOCK 78 | #endif 79 | 80 | static void eio_destroy (eio_req *req); 81 | 82 | #ifndef EIO_FINISH 83 | # define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0 84 | #endif 85 | 86 | #ifndef EIO_DESTROY 87 | # define EIO_DESTROY(req) do { if ((req)->destroy) (req)->destroy (req); } while (0) 88 | #endif 89 | 90 | #ifndef EIO_FEED 91 | # define EIO_FEED(req) do { if ((req)->feed ) (req)->feed (req); } while (0) 92 | #endif 93 | 94 | #ifndef EIO_FD_TO_WIN32_HANDLE 95 | # define EIO_FD_TO_WIN32_HANDLE(fd) _get_osfhandle (fd) 96 | #endif 97 | #ifndef EIO_WIN32_HANDLE_TO_FD 98 | # define EIO_WIN32_HANDLE_TO_FD(handle) _open_osfhandle (handle, 0) 99 | #endif 100 | 101 | #define EIO_ERRNO(errval,retval) ((errno = errval), retval) 102 | 103 | #define EIO_ENOSYS() EIO_ERRNO (ENOSYS, -1) 104 | 105 | #ifdef _WIN32 106 | 107 | #undef PAGESIZE 108 | #define PAGESIZE 4096 /* GetSystemInfo? */ 109 | 110 | /* TODO: look at how perl does stat (non-sloppy), unlink (ro-files), utime, link */ 111 | 112 | #ifdef EIO_STRUCT_STATI64 113 | /* look at perl's non-sloppy stat */ 114 | #define stat(path,buf) _stati64 (path,buf) 115 | #define fstat(fd,buf) _fstati64 (fd,buf) 116 | #endif 117 | #define lstat(path,buf) stat (path,buf) 118 | #define fsync(fd) (FlushFileBuffers ((HANDLE)EIO_FD_TO_WIN32_HANDLE (fd)) ? 0 : EIO_ERRNO (EBADF, -1)) 119 | #define mkdir(path,mode) _mkdir (path) 120 | #define link(old,neu) (CreateHardLink (neu, old, 0) ? 0 : EIO_ERRNO (ENOENT, -1)) 121 | 122 | #define chmod(path,mode) _chmod (path, mode) 123 | #define dup(fd) _dup (fd) 124 | #define dup2(fd1,fd2) _dup2 (fd1, fd2) 125 | 126 | #define fchmod(fd,mode) EIO_ENOSYS () 127 | #define chown(path,uid,gid) EIO_ENOSYS () 128 | #define fchown(fd,uid,gid) EIO_ENOSYS () 129 | #define truncate(path,offs) EIO_ENOSYS () /* far-miss: SetEndOfFile */ 130 | #define ftruncate(fd,offs) EIO_ENOSYS () /* near-miss: SetEndOfFile */ 131 | #define mknod(path,mode,dev) EIO_ENOSYS () 132 | #define sync() EIO_ENOSYS () 133 | #define readlink(path,buf,s) EIO_ENOSYS () 134 | #define statvfs(path,buf) EIO_ENOSYS () 135 | #define fstatvfs(fd,buf) EIO_ENOSYS () 136 | 137 | /* rename() uses MoveFile, which fails to overwrite */ 138 | #define rename(old,neu) eio__rename (old, neu) 139 | 140 | static int 141 | eio__rename (const char *old, const char *neu) 142 | { 143 | if (MoveFileEx (old, neu, MOVEFILE_REPLACE_EXISTING)) 144 | return 0; 145 | 146 | /* should steal _dosmaperr */ 147 | switch (GetLastError ()) 148 | { 149 | case ERROR_FILE_NOT_FOUND: 150 | case ERROR_PATH_NOT_FOUND: 151 | case ERROR_INVALID_DRIVE: 152 | case ERROR_NO_MORE_FILES: 153 | case ERROR_BAD_NETPATH: 154 | case ERROR_BAD_NET_NAME: 155 | case ERROR_BAD_PATHNAME: 156 | case ERROR_FILENAME_EXCED_RANGE: 157 | errno = ENOENT; 158 | break; 159 | 160 | default: 161 | errno = EACCES; 162 | break; 163 | } 164 | 165 | return -1; 166 | } 167 | 168 | /* we could even stat and see if it exists */ 169 | static int 170 | symlink (const char *old, const char *neu) 171 | { 172 | #if WINVER >= 0x0600 173 | if (CreateSymbolicLink (neu, old, 1)) 174 | return 0; 175 | 176 | if (CreateSymbolicLink (neu, old, 0)) 177 | return 0; 178 | #endif 179 | 180 | return EIO_ERRNO (ENOENT, -1); 181 | } 182 | 183 | /* POSIX API only */ 184 | #define CreateHardLink(neu,old,flags) 0 185 | #define CreateSymbolicLink(neu,old,flags) 0 186 | 187 | struct statvfs 188 | { 189 | int dummy; 190 | }; 191 | 192 | #define DT_DIR EIO_DT_DIR 193 | #define DT_REG EIO_DT_REG 194 | #define D_NAME(entp) entp.cFileName 195 | #define D_TYPE(entp) (entp.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG) 196 | 197 | #else 198 | 199 | #include 200 | #include 201 | #include 202 | #include 203 | #include 204 | #include 205 | 206 | #if _POSIX_MEMLOCK || _POSIX_MEMLOCK_RANGE || _POSIX_MAPPED_FILES 207 | #include 208 | #endif 209 | 210 | #define D_NAME(entp) entp->d_name 211 | 212 | /* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */ 213 | #if __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ 214 | #define _DIRENT_HAVE_D_TYPE /* sigh */ 215 | #define D_INO(de) (de)->d_fileno 216 | #define D_NAMLEN(de) (de)->d_namlen 217 | #elif __linux || defined d_ino || _XOPEN_SOURCE >= 600 218 | #define D_INO(de) (de)->d_ino 219 | #endif 220 | 221 | #ifdef _D_EXACT_NAMLEN 222 | #undef D_NAMLEN 223 | #define D_NAMLEN(de) _D_EXACT_NAMLEN (de) 224 | #endif 225 | 226 | #ifdef _DIRENT_HAVE_D_TYPE 227 | #define D_TYPE(de) (de)->d_type 228 | #endif 229 | 230 | #ifndef EIO_STRUCT_DIRENT 231 | #define EIO_STRUCT_DIRENT struct dirent 232 | #endif 233 | 234 | #endif 235 | 236 | #if HAVE_UTIMES 237 | # include 238 | #endif 239 | 240 | #if HAVE_SYS_SYSCALL_H 241 | # include 242 | #endif 243 | 244 | #if HAVE_SYS_PRCTL_H 245 | # include 246 | #endif 247 | 248 | #if HAVE_SENDFILE 249 | # if __linux 250 | # include 251 | # elif __FreeBSD__ || defined __APPLE__ 252 | # include 253 | # include 254 | # elif __hpux 255 | # include 256 | # elif __solaris 257 | # include 258 | # else 259 | # error sendfile support requested but not available 260 | # endif 261 | #endif 262 | 263 | #ifndef D_TYPE 264 | # define D_TYPE(de) 0 265 | #endif 266 | #ifndef D_INO 267 | # define D_INO(de) 0 268 | #endif 269 | #ifndef D_NAMLEN 270 | # define D_NAMLEN(entp) strlen (D_NAME (entp)) 271 | #endif 272 | 273 | /* used for struct dirent, AIX doesn't provide it */ 274 | #ifndef NAME_MAX 275 | # define NAME_MAX 4096 276 | #endif 277 | 278 | /* used for readlink etc. */ 279 | #ifndef PATH_MAX 280 | # define PATH_MAX 4096 281 | #endif 282 | 283 | /* buffer size for various temporary buffers */ 284 | #define EIO_BUFSIZE 65536 285 | 286 | #define dBUF \ 287 | char *eio_buf = malloc (EIO_BUFSIZE); \ 288 | errno = ENOMEM; \ 289 | if (!eio_buf) \ 290 | return -1 291 | 292 | #define FUBd \ 293 | free (eio_buf) 294 | 295 | #define EIO_TICKS ((1000000 + 1023) >> 10) 296 | 297 | /*****************************************************************************/ 298 | 299 | struct tmpbuf 300 | { 301 | void *ptr; 302 | int len; 303 | }; 304 | 305 | static void * 306 | tmpbuf_get (struct tmpbuf *buf, int len) 307 | { 308 | if (buf->len < len) 309 | { 310 | free (buf->ptr); 311 | buf->ptr = malloc (buf->len = len); 312 | } 313 | 314 | return buf->ptr; 315 | } 316 | 317 | struct tmpbuf; 318 | 319 | #if _POSIX_VERSION >= 200809L 320 | #define HAVE_AT 1 321 | #define WD2FD(wd) ((wd) ? (wd)->fd : AT_FDCWD) 322 | #ifndef O_SEARCH 323 | #define O_SEARCH O_RDONLY 324 | #endif 325 | #else 326 | #define HAVE_AT 0 327 | static const char *wd_expand (struct tmpbuf *tmpbuf, eio_wd wd, const char *path); 328 | #endif 329 | 330 | struct eio_pwd 331 | { 332 | #if HAVE_AT 333 | int fd; 334 | #endif 335 | int len; 336 | char str[1]; /* actually, a 0-terminated canonical path */ 337 | }; 338 | 339 | /*****************************************************************************/ 340 | 341 | #define ETP_PRI_MIN EIO_PRI_MIN 342 | #define ETP_PRI_MAX EIO_PRI_MAX 343 | 344 | struct etp_worker; 345 | 346 | #define ETP_REQ eio_req 347 | #define ETP_DESTROY(req) eio_destroy (req) 348 | static int eio_finish (eio_req *req); 349 | #define ETP_FINISH(req) eio_finish (req) 350 | static void eio_execute (struct etp_worker *self, eio_req *req); 351 | #define ETP_EXECUTE(wrk,req) eio_execute (wrk,req) 352 | 353 | /*****************************************************************************/ 354 | 355 | #define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1) 356 | 357 | /* calculate time difference in ~1/EIO_TICKS of a second */ 358 | ecb_inline int 359 | tvdiff (struct timeval *tv1, struct timeval *tv2) 360 | { 361 | return (tv2->tv_sec - tv1->tv_sec ) * EIO_TICKS 362 | + ((tv2->tv_usec - tv1->tv_usec) >> 10); 363 | } 364 | 365 | static unsigned int started, idle, wanted = 4; 366 | 367 | static void (*want_poll_cb) (void); 368 | static void (*done_poll_cb) (void); 369 | 370 | static unsigned int max_poll_time; /* reslock */ 371 | static unsigned int max_poll_reqs; /* reslock */ 372 | 373 | static unsigned int nreqs; /* reqlock */ 374 | static unsigned int nready; /* reqlock */ 375 | static unsigned int npending; /* reqlock */ 376 | static unsigned int max_idle = 4; /* maximum number of threads that can idle indefinitely */ 377 | static unsigned int idle_timeout = 10; /* number of seconds after which an idle threads exit */ 378 | 379 | static xmutex_t wrklock; 380 | static xmutex_t reslock; 381 | static xmutex_t reqlock; 382 | static xcond_t reqwait; 383 | 384 | #if !HAVE_PREADWRITE 385 | /* 386 | * make our pread/pwrite emulation safe against themselves, but not against 387 | * normal read/write by using a mutex. slows down execution a lot, 388 | * but that's your problem, not mine. 389 | */ 390 | static xmutex_t preadwritelock; 391 | #endif 392 | 393 | typedef struct etp_worker 394 | { 395 | struct tmpbuf tmpbuf; 396 | 397 | /* locked by wrklock */ 398 | struct etp_worker *prev, *next; 399 | 400 | xthread_t tid; 401 | 402 | #ifdef ETP_WORKER_COMMON 403 | ETP_WORKER_COMMON 404 | #endif 405 | } etp_worker; 406 | 407 | static etp_worker wrk_first; /* NOT etp */ 408 | 409 | #define ETP_WORKER_LOCK(wrk) X_LOCK (wrklock) 410 | #define ETP_WORKER_UNLOCK(wrk) X_UNLOCK (wrklock) 411 | 412 | /* worker threads management */ 413 | 414 | static void 415 | etp_worker_clear (etp_worker *wrk) 416 | { 417 | } 418 | 419 | static void ecb_cold 420 | etp_worker_free (etp_worker *wrk) 421 | { 422 | free (wrk->tmpbuf.ptr); 423 | 424 | wrk->next->prev = wrk->prev; 425 | wrk->prev->next = wrk->next; 426 | 427 | free (wrk); 428 | } 429 | 430 | static unsigned int 431 | etp_nreqs (void) 432 | { 433 | int retval; 434 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 435 | retval = nreqs; 436 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 437 | return retval; 438 | } 439 | 440 | static unsigned int 441 | etp_nready (void) 442 | { 443 | unsigned int retval; 444 | 445 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 446 | retval = nready; 447 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 448 | 449 | return retval; 450 | } 451 | 452 | static unsigned int 453 | etp_npending (void) 454 | { 455 | unsigned int retval; 456 | 457 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 458 | retval = npending; 459 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 460 | 461 | return retval; 462 | } 463 | 464 | static unsigned int 465 | etp_nthreads (void) 466 | { 467 | unsigned int retval; 468 | 469 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 470 | retval = started; 471 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 472 | 473 | return retval; 474 | } 475 | 476 | /* 477 | * a somewhat faster data structure might be nice, but 478 | * with 8 priorities this actually needs <20 insns 479 | * per shift, the most expensive operation. 480 | */ 481 | typedef struct { 482 | ETP_REQ *qs[ETP_NUM_PRI], *qe[ETP_NUM_PRI]; /* qstart, qend */ 483 | int size; 484 | } etp_reqq; 485 | 486 | static etp_reqq req_queue; 487 | static etp_reqq res_queue; 488 | 489 | static void ecb_noinline ecb_cold 490 | reqq_init (etp_reqq *q) 491 | { 492 | int pri; 493 | 494 | for (pri = 0; pri < ETP_NUM_PRI; ++pri) 495 | q->qs[pri] = q->qe[pri] = 0; 496 | 497 | q->size = 0; 498 | } 499 | 500 | static int ecb_noinline 501 | reqq_push (etp_reqq *q, ETP_REQ *req) 502 | { 503 | int pri = req->pri; 504 | req->next = 0; 505 | 506 | if (q->qe[pri]) 507 | { 508 | q->qe[pri]->next = req; 509 | q->qe[pri] = req; 510 | } 511 | else 512 | q->qe[pri] = q->qs[pri] = req; 513 | 514 | return q->size++; 515 | } 516 | 517 | static ETP_REQ * ecb_noinline 518 | reqq_shift (etp_reqq *q) 519 | { 520 | int pri; 521 | 522 | if (!q->size) 523 | return 0; 524 | 525 | --q->size; 526 | 527 | for (pri = ETP_NUM_PRI; pri--; ) 528 | { 529 | eio_req *req = q->qs[pri]; 530 | 531 | if (req) 532 | { 533 | if (!(q->qs[pri] = (eio_req *)req->next)) 534 | q->qe[pri] = 0; 535 | 536 | return req; 537 | } 538 | } 539 | 540 | abort (); 541 | } 542 | 543 | static int ecb_cold 544 | etp_init (void (*want_poll)(void), void (*done_poll)(void)) 545 | { 546 | X_MUTEX_CREATE (wrklock); 547 | X_MUTEX_CREATE (reslock); 548 | X_MUTEX_CREATE (reqlock); 549 | X_COND_CREATE (reqwait); 550 | 551 | reqq_init (&req_queue); 552 | reqq_init (&res_queue); 553 | 554 | wrk_first.next = 555 | wrk_first.prev = &wrk_first; 556 | 557 | started = 0; 558 | idle = 0; 559 | nreqs = 0; 560 | nready = 0; 561 | npending = 0; 562 | 563 | want_poll_cb = want_poll; 564 | done_poll_cb = done_poll; 565 | 566 | return 0; 567 | } 568 | 569 | X_THREAD_PROC (etp_proc); 570 | 571 | static void ecb_cold 572 | etp_start_thread (void) 573 | { 574 | etp_worker *wrk = calloc (1, sizeof (etp_worker)); 575 | 576 | /*TODO*/ 577 | assert (("unable to allocate worker thread data", wrk)); 578 | 579 | X_LOCK (wrklock); 580 | 581 | if (thread_create (&wrk->tid, etp_proc, (void *)wrk)) 582 | { 583 | wrk->prev = &wrk_first; 584 | wrk->next = wrk_first.next; 585 | wrk_first.next->prev = wrk; 586 | wrk_first.next = wrk; 587 | ++started; 588 | } 589 | else 590 | free (wrk); 591 | 592 | X_UNLOCK (wrklock); 593 | } 594 | 595 | static void 596 | etp_maybe_start_thread (void) 597 | { 598 | if (ecb_expect_true (etp_nthreads () >= wanted)) 599 | return; 600 | 601 | /* todo: maybe use idle here, but might be less exact */ 602 | if (ecb_expect_true (0 <= (int)etp_nthreads () + (int)etp_npending () - (int)etp_nreqs ())) 603 | return; 604 | 605 | etp_start_thread (); 606 | } 607 | 608 | static void ecb_cold 609 | etp_end_thread (void) 610 | { 611 | eio_req *req = calloc (1, sizeof (eio_req)); /* will be freed by worker */ 612 | 613 | req->type = -1; 614 | req->pri = ETP_PRI_MAX - ETP_PRI_MIN; 615 | 616 | X_LOCK (reqlock); 617 | reqq_push (&req_queue, req); 618 | X_COND_SIGNAL (reqwait); 619 | X_UNLOCK (reqlock); 620 | 621 | X_LOCK (wrklock); 622 | --started; 623 | X_UNLOCK (wrklock); 624 | } 625 | 626 | static int 627 | etp_poll (void) 628 | { 629 | unsigned int maxreqs; 630 | unsigned int maxtime; 631 | struct timeval tv_start, tv_now; 632 | 633 | X_LOCK (reslock); 634 | maxreqs = max_poll_reqs; 635 | maxtime = max_poll_time; 636 | X_UNLOCK (reslock); 637 | 638 | if (maxtime) 639 | gettimeofday (&tv_start, 0); 640 | 641 | for (;;) 642 | { 643 | ETP_REQ *req; 644 | 645 | etp_maybe_start_thread (); 646 | 647 | X_LOCK (reslock); 648 | req = reqq_shift (&res_queue); 649 | 650 | if (req) 651 | { 652 | --npending; 653 | 654 | if (!res_queue.size && done_poll_cb) 655 | done_poll_cb (); 656 | } 657 | 658 | X_UNLOCK (reslock); 659 | 660 | if (!req) 661 | return 0; 662 | 663 | X_LOCK (reqlock); 664 | --nreqs; 665 | X_UNLOCK (reqlock); 666 | 667 | if (ecb_expect_false (req->type == EIO_GROUP && req->size)) 668 | { 669 | req->int1 = 1; /* mark request as delayed */ 670 | continue; 671 | } 672 | else 673 | { 674 | int res = ETP_FINISH (req); 675 | if (ecb_expect_false (res)) 676 | return res; 677 | } 678 | 679 | if (ecb_expect_false (maxreqs && !--maxreqs)) 680 | break; 681 | 682 | if (maxtime) 683 | { 684 | gettimeofday (&tv_now, 0); 685 | 686 | if (tvdiff (&tv_start, &tv_now) >= maxtime) 687 | break; 688 | } 689 | } 690 | 691 | errno = EAGAIN; 692 | return -1; 693 | } 694 | 695 | static void 696 | etp_cancel (ETP_REQ *req) 697 | { 698 | req->cancelled = 1; 699 | 700 | eio_grp_cancel (req); 701 | } 702 | 703 | static void 704 | etp_submit (ETP_REQ *req) 705 | { 706 | req->pri -= ETP_PRI_MIN; 707 | 708 | if (ecb_expect_false (req->pri < ETP_PRI_MIN - ETP_PRI_MIN)) req->pri = ETP_PRI_MIN - ETP_PRI_MIN; 709 | if (ecb_expect_false (req->pri > ETP_PRI_MAX - ETP_PRI_MIN)) req->pri = ETP_PRI_MAX - ETP_PRI_MIN; 710 | 711 | if (ecb_expect_false (req->type == EIO_GROUP)) 712 | { 713 | /* I hope this is worth it :/ */ 714 | X_LOCK (reqlock); 715 | ++nreqs; 716 | X_UNLOCK (reqlock); 717 | 718 | X_LOCK (reslock); 719 | 720 | ++npending; 721 | 722 | if (!reqq_push (&res_queue, req) && want_poll_cb) 723 | want_poll_cb (); 724 | 725 | X_UNLOCK (reslock); 726 | } 727 | else 728 | { 729 | X_LOCK (reqlock); 730 | ++nreqs; 731 | ++nready; 732 | reqq_push (&req_queue, req); 733 | X_COND_SIGNAL (reqwait); 734 | X_UNLOCK (reqlock); 735 | 736 | etp_maybe_start_thread (); 737 | } 738 | } 739 | 740 | static void ecb_cold 741 | etp_set_max_poll_time (double nseconds) 742 | { 743 | if (WORDACCESS_UNSAFE) X_LOCK (reslock); 744 | max_poll_time = nseconds * EIO_TICKS; 745 | if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); 746 | } 747 | 748 | static void ecb_cold 749 | etp_set_max_poll_reqs (unsigned int maxreqs) 750 | { 751 | if (WORDACCESS_UNSAFE) X_LOCK (reslock); 752 | max_poll_reqs = maxreqs; 753 | if (WORDACCESS_UNSAFE) X_UNLOCK (reslock); 754 | } 755 | 756 | static void ecb_cold 757 | etp_set_max_idle (unsigned int nthreads) 758 | { 759 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 760 | max_idle = nthreads; 761 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 762 | } 763 | 764 | static void ecb_cold 765 | etp_set_idle_timeout (unsigned int seconds) 766 | { 767 | if (WORDACCESS_UNSAFE) X_LOCK (reqlock); 768 | idle_timeout = seconds; 769 | if (WORDACCESS_UNSAFE) X_UNLOCK (reqlock); 770 | } 771 | 772 | static void ecb_cold 773 | etp_set_min_parallel (unsigned int nthreads) 774 | { 775 | if (wanted < nthreads) 776 | wanted = nthreads; 777 | } 778 | 779 | static void ecb_cold 780 | etp_set_max_parallel (unsigned int nthreads) 781 | { 782 | if (wanted > nthreads) 783 | wanted = nthreads; 784 | 785 | while (started > wanted) 786 | etp_end_thread (); 787 | } 788 | 789 | /*****************************************************************************/ 790 | 791 | static void 792 | grp_try_feed (eio_req *grp) 793 | { 794 | while (grp->size < grp->int2 && !EIO_CANCELLED (grp)) 795 | { 796 | grp->flags &= ~EIO_FLAG_GROUPADD; 797 | 798 | EIO_FEED (grp); 799 | 800 | /* stop if no progress has been made */ 801 | if (!(grp->flags & EIO_FLAG_GROUPADD)) 802 | { 803 | grp->feed = 0; 804 | break; 805 | } 806 | } 807 | } 808 | 809 | static int 810 | grp_dec (eio_req *grp) 811 | { 812 | --grp->size; 813 | 814 | /* call feeder, if applicable */ 815 | grp_try_feed (grp); 816 | 817 | /* finish, if done */ 818 | if (!grp->size && grp->int1) 819 | return eio_finish (grp); 820 | else 821 | return 0; 822 | } 823 | 824 | static void 825 | eio_destroy (eio_req *req) 826 | { 827 | if ((req)->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1); 828 | if ((req)->flags & EIO_FLAG_PTR2_FREE) free (req->ptr2); 829 | 830 | EIO_DESTROY (req); 831 | } 832 | 833 | static int 834 | eio_finish (eio_req *req) 835 | { 836 | int res = EIO_FINISH (req); 837 | 838 | if (req->grp) 839 | { 840 | int res2; 841 | eio_req *grp = req->grp; 842 | 843 | /* unlink request */ 844 | if (req->grp_next) req->grp_next->grp_prev = req->grp_prev; 845 | if (req->grp_prev) req->grp_prev->grp_next = req->grp_next; 846 | 847 | if (grp->grp_first == req) 848 | grp->grp_first = req->grp_next; 849 | 850 | res2 = grp_dec (grp); 851 | 852 | if (!res) 853 | res = res2; 854 | } 855 | 856 | eio_destroy (req); 857 | 858 | return res; 859 | } 860 | 861 | void 862 | eio_grp_cancel (eio_req *grp) 863 | { 864 | for (grp = grp->grp_first; grp; grp = grp->grp_next) 865 | eio_cancel (grp); 866 | } 867 | 868 | void 869 | eio_cancel (eio_req *req) 870 | { 871 | etp_cancel (req); 872 | } 873 | 874 | void 875 | eio_submit (eio_req *req) 876 | { 877 | etp_submit (req); 878 | } 879 | 880 | unsigned int 881 | eio_nreqs (void) 882 | { 883 | return etp_nreqs (); 884 | } 885 | 886 | unsigned int 887 | eio_nready (void) 888 | { 889 | return etp_nready (); 890 | } 891 | 892 | unsigned int 893 | eio_npending (void) 894 | { 895 | return etp_npending (); 896 | } 897 | 898 | unsigned int ecb_cold 899 | eio_nthreads (void) 900 | { 901 | return etp_nthreads (); 902 | } 903 | 904 | void ecb_cold 905 | eio_set_max_poll_time (double nseconds) 906 | { 907 | etp_set_max_poll_time (nseconds); 908 | } 909 | 910 | void ecb_cold 911 | eio_set_max_poll_reqs (unsigned int maxreqs) 912 | { 913 | etp_set_max_poll_reqs (maxreqs); 914 | } 915 | 916 | void ecb_cold 917 | eio_set_max_idle (unsigned int nthreads) 918 | { 919 | etp_set_max_idle (nthreads); 920 | } 921 | 922 | void ecb_cold 923 | eio_set_idle_timeout (unsigned int seconds) 924 | { 925 | etp_set_idle_timeout (seconds); 926 | } 927 | 928 | void ecb_cold 929 | eio_set_min_parallel (unsigned int nthreads) 930 | { 931 | etp_set_min_parallel (nthreads); 932 | } 933 | 934 | void ecb_cold 935 | eio_set_max_parallel (unsigned int nthreads) 936 | { 937 | etp_set_max_parallel (nthreads); 938 | } 939 | 940 | int eio_poll (void) 941 | { 942 | return etp_poll (); 943 | } 944 | 945 | /*****************************************************************************/ 946 | /* work around various missing functions */ 947 | 948 | #if !HAVE_PREADWRITE 949 | # undef pread 950 | # undef pwrite 951 | # define pread eio__pread 952 | # define pwrite eio__pwrite 953 | 954 | static eio_ssize_t 955 | eio__pread (int fd, void *buf, size_t count, off_t offset) 956 | { 957 | eio_ssize_t res; 958 | off_t ooffset; 959 | 960 | X_LOCK (preadwritelock); 961 | ooffset = lseek (fd, 0, SEEK_CUR); 962 | lseek (fd, offset, SEEK_SET); 963 | res = read (fd, buf, count); 964 | lseek (fd, ooffset, SEEK_SET); 965 | X_UNLOCK (preadwritelock); 966 | 967 | return res; 968 | } 969 | 970 | static eio_ssize_t 971 | eio__pwrite (int fd, void *buf, size_t count, off_t offset) 972 | { 973 | eio_ssize_t res; 974 | off_t ooffset; 975 | 976 | X_LOCK (preadwritelock); 977 | ooffset = lseek (fd, 0, SEEK_CUR); 978 | lseek (fd, offset, SEEK_SET); 979 | res = write (fd, buf, count); 980 | lseek (fd, ooffset, SEEK_SET); 981 | X_UNLOCK (preadwritelock); 982 | 983 | return res; 984 | } 985 | #endif 986 | 987 | #ifndef HAVE_UTIMES 988 | 989 | # undef utimes 990 | # define utimes(path,times) eio__utimes (path, times) 991 | 992 | static int 993 | eio__utimes (const char *filename, const struct timeval times[2]) 994 | { 995 | if (times) 996 | { 997 | struct utimbuf buf; 998 | 999 | buf.actime = times[0].tv_sec; 1000 | buf.modtime = times[1].tv_sec; 1001 | 1002 | return utime (filename, &buf); 1003 | } 1004 | else 1005 | return utime (filename, 0); 1006 | } 1007 | 1008 | #endif 1009 | 1010 | #ifndef HAVE_FUTIMES 1011 | 1012 | # undef futimes 1013 | # define futimes(fd,times) eio__futimes (fd, times) 1014 | 1015 | static int 1016 | eio__futimes (int fd, const struct timeval tv[2]) 1017 | { 1018 | errno = ENOSYS; 1019 | return -1; 1020 | } 1021 | 1022 | #endif 1023 | 1024 | #if !HAVE_FDATASYNC 1025 | # undef fdatasync 1026 | # define fdatasync(fd) fsync (fd) 1027 | #endif 1028 | 1029 | static int 1030 | eio__syncfs (int fd) 1031 | { 1032 | int res; 1033 | 1034 | #if HAVE_SYS_SYNCFS 1035 | res = (int)syscall (__NR_syncfs, (int)(fd)); 1036 | #else 1037 | res = -1; 1038 | errno = ENOSYS; 1039 | #endif 1040 | 1041 | if (res < 0 && errno == ENOSYS && fd >= 0) 1042 | sync (); 1043 | 1044 | return res; 1045 | } 1046 | 1047 | /* sync_file_range always needs emulation */ 1048 | static int 1049 | eio__sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags) 1050 | { 1051 | #if HAVE_SYNC_FILE_RANGE 1052 | int res; 1053 | 1054 | if (EIO_SYNC_FILE_RANGE_WAIT_BEFORE != SYNC_FILE_RANGE_WAIT_BEFORE 1055 | || EIO_SYNC_FILE_RANGE_WRITE != SYNC_FILE_RANGE_WRITE 1056 | || EIO_SYNC_FILE_RANGE_WAIT_AFTER != SYNC_FILE_RANGE_WAIT_AFTER) 1057 | { 1058 | flags = 0 1059 | | (flags & EIO_SYNC_FILE_RANGE_WAIT_BEFORE ? SYNC_FILE_RANGE_WAIT_BEFORE : 0) 1060 | | (flags & EIO_SYNC_FILE_RANGE_WRITE ? SYNC_FILE_RANGE_WRITE : 0) 1061 | | (flags & EIO_SYNC_FILE_RANGE_WAIT_AFTER ? SYNC_FILE_RANGE_WAIT_AFTER : 0); 1062 | } 1063 | 1064 | res = sync_file_range (fd, offset, nbytes, flags); 1065 | 1066 | if (!res || errno != ENOSYS) 1067 | return res; 1068 | #endif 1069 | 1070 | /* even though we could play tricks with the flags, it's better to always 1071 | * call fdatasync, as that matches the expectation of its users best */ 1072 | return fdatasync (fd); 1073 | } 1074 | 1075 | static int 1076 | eio__fallocate (int fd, int mode, off_t offset, size_t len) 1077 | { 1078 | #if HAVE_FALLOCATE 1079 | return fallocate (fd, mode, offset, len); 1080 | #else 1081 | errno = ENOSYS; 1082 | return -1; 1083 | #endif 1084 | } 1085 | 1086 | #if !HAVE_READAHEAD 1087 | # undef readahead 1088 | # define readahead(fd,offset,count) eio__readahead (fd, offset, count, self) 1089 | 1090 | static eio_ssize_t 1091 | eio__readahead (int fd, off_t offset, size_t count, etp_worker *self) 1092 | { 1093 | size_t todo = count; 1094 | dBUF; 1095 | 1096 | while (todo > 0) 1097 | { 1098 | size_t len = todo < EIO_BUFSIZE ? todo : EIO_BUFSIZE; 1099 | 1100 | pread (fd, eio_buf, len, offset); 1101 | offset += len; 1102 | todo -= len; 1103 | } 1104 | 1105 | FUBd; 1106 | 1107 | errno = 0; 1108 | return count; 1109 | } 1110 | 1111 | #endif 1112 | 1113 | /* sendfile always needs emulation */ 1114 | static eio_ssize_t 1115 | eio__sendfile (int ofd, int ifd, off_t offset, size_t count) 1116 | { 1117 | eio_ssize_t written = 0; 1118 | eio_ssize_t res; 1119 | 1120 | if (!count) 1121 | return 0; 1122 | 1123 | for (;;) 1124 | { 1125 | #ifdef __APPLE__ 1126 | # undef HAVE_SENDFILE /* broken, as everything on os x */ 1127 | #endif 1128 | #if HAVE_SENDFILE 1129 | # if __linux 1130 | off_t soffset = offset; 1131 | res = sendfile (ofd, ifd, &soffset, count); 1132 | 1133 | # elif __FreeBSD__ 1134 | /* 1135 | * Of course, the freebsd sendfile is a dire hack with no thoughts 1136 | * wasted on making it similar to other I/O functions. 1137 | */ 1138 | off_t sbytes; 1139 | res = sendfile (ifd, ofd, offset, count, 0, &sbytes, 0); 1140 | 1141 | #if 0 /* according to the manpage, this is correct, but broken behaviour */ 1142 | /* freebsd' sendfile will return 0 on success */ 1143 | /* freebsd 8 documents it as only setting *sbytes on EINTR and EAGAIN, but */ 1144 | /* not on e.g. EIO or EPIPE - sounds broken */ 1145 | if ((res < 0 && (errno == EAGAIN || errno == EINTR) && sbytes) || res == 0) 1146 | res = sbytes; 1147 | #endif 1148 | 1149 | /* according to source inspection, this is correct, and useful behaviour */ 1150 | if (sbytes) 1151 | res = sbytes; 1152 | 1153 | # elif defined (__APPLE__) 1154 | off_t sbytes = count; 1155 | res = sendfile (ifd, ofd, offset, &sbytes, 0, 0); 1156 | 1157 | /* according to the manpage, sbytes is always valid */ 1158 | if (sbytes) 1159 | res = sbytes; 1160 | 1161 | # elif __hpux 1162 | res = sendfile (ofd, ifd, offset, count, 0, 0); 1163 | 1164 | # elif __solaris 1165 | struct sendfilevec vec; 1166 | size_t sbytes; 1167 | 1168 | vec.sfv_fd = ifd; 1169 | vec.sfv_flag = 0; 1170 | vec.sfv_off = offset; 1171 | vec.sfv_len = count; 1172 | 1173 | res = sendfilev (ofd, &vec, 1, &sbytes); 1174 | 1175 | if (res < 0 && sbytes) 1176 | res = sbytes; 1177 | 1178 | # endif 1179 | 1180 | #elif defined (_WIN32) && 0 1181 | /* does not work, just for documentation of what would need to be done */ 1182 | /* actually, cannot be done like this, as TransmitFile changes the file offset, */ 1183 | /* libeio guarantees that the file offset does not change, and windows */ 1184 | /* has no way to get an independent handle to the same file description */ 1185 | HANDLE h = TO_SOCKET (ifd); 1186 | SetFilePointer (h, offset, 0, FILE_BEGIN); 1187 | res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0); 1188 | 1189 | #else 1190 | res = -1; 1191 | errno = ENOSYS; 1192 | #endif 1193 | 1194 | /* we assume sendfile can copy at least 128mb in one go */ 1195 | if (res <= 128 * 1024 * 1024) 1196 | { 1197 | if (res > 0) 1198 | written += res; 1199 | 1200 | if (written) 1201 | return written; 1202 | 1203 | break; 1204 | } 1205 | else 1206 | { 1207 | /* if we requested more, then probably the kernel was lazy */ 1208 | written += res; 1209 | offset += res; 1210 | count -= res; 1211 | 1212 | if (!count) 1213 | return written; 1214 | } 1215 | } 1216 | 1217 | if (res < 0 1218 | && (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK 1219 | /* BSDs */ 1220 | #ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */ 1221 | || errno == ENOTSUP 1222 | #endif 1223 | #ifdef EOPNOTSUPP /* windows */ 1224 | || errno == EOPNOTSUPP /* BSDs */ 1225 | #endif 1226 | #if __solaris 1227 | || errno == EAFNOSUPPORT || errno == EPROTOTYPE 1228 | #endif 1229 | ) 1230 | ) 1231 | { 1232 | /* emulate sendfile. this is a major pain in the ass */ 1233 | dBUF; 1234 | 1235 | res = 0; 1236 | 1237 | while (count) 1238 | { 1239 | eio_ssize_t cnt; 1240 | 1241 | cnt = pread (ifd, eio_buf, count > EIO_BUFSIZE ? EIO_BUFSIZE : count, offset); 1242 | 1243 | if (cnt <= 0) 1244 | { 1245 | if (cnt && !res) res = -1; 1246 | break; 1247 | } 1248 | 1249 | cnt = write (ofd, eio_buf, cnt); 1250 | 1251 | if (cnt <= 0) 1252 | { 1253 | if (cnt && !res) res = -1; 1254 | break; 1255 | } 1256 | 1257 | offset += cnt; 1258 | res += cnt; 1259 | count -= cnt; 1260 | } 1261 | 1262 | FUBd; 1263 | } 1264 | 1265 | return res; 1266 | } 1267 | 1268 | #ifdef PAGESIZE 1269 | # define eio_pagesize() PAGESIZE 1270 | #else 1271 | static intptr_t 1272 | eio_pagesize (void) 1273 | { 1274 | static intptr_t page; 1275 | 1276 | if (!page) 1277 | page = sysconf (_SC_PAGESIZE); 1278 | 1279 | return page; 1280 | } 1281 | #endif 1282 | 1283 | static void 1284 | eio_page_align (void **addr, size_t *length) 1285 | { 1286 | intptr_t mask = eio_pagesize () - 1; 1287 | 1288 | /* round down addr */ 1289 | intptr_t adj = mask & (intptr_t)*addr; 1290 | 1291 | *addr = (void *)((intptr_t)*addr - adj); 1292 | *length += adj; 1293 | 1294 | /* round up length */ 1295 | *length = (*length + mask) & ~mask; 1296 | } 1297 | 1298 | #if !_POSIX_MEMLOCK 1299 | # define eio__mlockall(a) EIO_ENOSYS () 1300 | #else 1301 | 1302 | static int 1303 | eio__mlockall (int flags) 1304 | { 1305 | #if __GLIBC__ == 2 && __GLIBC_MINOR__ <= 7 1306 | extern int mallopt (int, int); 1307 | mallopt (-6, 238); /* http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=473812 */ 1308 | #endif 1309 | 1310 | if (EIO_MCL_CURRENT != MCL_CURRENT 1311 | || EIO_MCL_FUTURE != MCL_FUTURE) 1312 | { 1313 | flags = 0 1314 | | (flags & EIO_MCL_CURRENT ? MCL_CURRENT : 0) 1315 | | (flags & EIO_MCL_FUTURE ? MCL_FUTURE : 0); 1316 | } 1317 | 1318 | return mlockall (flags); 1319 | } 1320 | #endif 1321 | 1322 | #if !_POSIX_MEMLOCK_RANGE 1323 | # define eio__mlock(a,b) EIO_ENOSYS () 1324 | #else 1325 | 1326 | static int 1327 | eio__mlock (void *addr, size_t length) 1328 | { 1329 | eio_page_align (&addr, &length); 1330 | 1331 | return mlock (addr, length); 1332 | } 1333 | 1334 | #endif 1335 | 1336 | #if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO) 1337 | # define eio__msync(a,b,c) EIO_ENOSYS () 1338 | #else 1339 | 1340 | static int 1341 | eio__msync (void *mem, size_t len, int flags) 1342 | { 1343 | eio_page_align (&mem, &len); 1344 | 1345 | if (EIO_MS_ASYNC != MS_SYNC 1346 | || EIO_MS_INVALIDATE != MS_INVALIDATE 1347 | || EIO_MS_SYNC != MS_SYNC) 1348 | { 1349 | flags = 0 1350 | | (flags & EIO_MS_ASYNC ? MS_ASYNC : 0) 1351 | | (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0) 1352 | | (flags & EIO_MS_SYNC ? MS_SYNC : 0); 1353 | } 1354 | 1355 | return msync (mem, len, flags); 1356 | } 1357 | 1358 | #endif 1359 | 1360 | static int 1361 | eio__mtouch (eio_req *req) 1362 | { 1363 | void *mem = req->ptr2; 1364 | size_t len = req->size; 1365 | int flags = req->int1; 1366 | 1367 | eio_page_align (&mem, &len); 1368 | 1369 | { 1370 | intptr_t addr = (intptr_t)mem; 1371 | intptr_t end = addr + len; 1372 | intptr_t page = eio_pagesize (); 1373 | 1374 | if (addr < end) 1375 | if (flags & EIO_MT_MODIFY) /* modify */ 1376 | do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len && !EIO_CANCELLED (req)); 1377 | else 1378 | do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len && !EIO_CANCELLED (req)); 1379 | } 1380 | 1381 | return 0; 1382 | } 1383 | 1384 | /*****************************************************************************/ 1385 | /* requests implemented outside eio_execute, because they are so large */ 1386 | 1387 | /* result will always end up in tmpbuf, there is always space for adding a 0-byte */ 1388 | static int 1389 | eio__realpath (struct tmpbuf *tmpbuf, eio_wd wd, const char *path) 1390 | { 1391 | const char *rel = path; 1392 | char *res; 1393 | char *tmp1, *tmp2; 1394 | #if SYMLOOP_MAX > 32 1395 | int symlinks = SYMLOOP_MAX; 1396 | #else 1397 | int symlinks = 32; 1398 | #endif 1399 | 1400 | errno = EINVAL; 1401 | if (!rel) 1402 | return -1; 1403 | 1404 | errno = ENOENT; 1405 | if (!*rel) 1406 | return -1; 1407 | 1408 | res = tmpbuf_get (tmpbuf, PATH_MAX * 3); 1409 | tmp1 = res + PATH_MAX; 1410 | tmp2 = tmp1 + PATH_MAX; 1411 | 1412 | #if 0 /* disabled, the musl way to do things is just too racy */ 1413 | #if __linux && defined(O_NONBLOCK) && defined(O_NOATIME) 1414 | /* on linux we may be able to ask the kernel */ 1415 | { 1416 | int fd = open (rel, O_RDONLY | O_NONBLOCK | O_NOCTTY | O_NOATIME); 1417 | 1418 | if (fd >= 0) 1419 | { 1420 | sprintf (tmp1, "/proc/self/fd/%d", fd); 1421 | req->result = readlink (tmp1, res, PATH_MAX); 1422 | close (fd); 1423 | 1424 | /* here we should probably stat the open file and the disk file, to make sure they still match */ 1425 | 1426 | if (req->result > 0) 1427 | goto done; 1428 | } 1429 | else if (errno == ELOOP || errno == ENAMETOOLONG || errno == ENOENT || errno == ENOTDIR || errno == EIO) 1430 | return; 1431 | } 1432 | #endif 1433 | #endif 1434 | 1435 | if (*rel != '/') 1436 | { 1437 | int len; 1438 | 1439 | errno = ENOENT; 1440 | if (wd == EIO_INVALID_WD) 1441 | return -1; 1442 | 1443 | if (wd == EIO_CWD) 1444 | { 1445 | if (!getcwd (res, PATH_MAX)) 1446 | return -1; 1447 | 1448 | len = strlen (res); 1449 | } 1450 | else 1451 | memcpy (res, wd->str, len = wd->len); 1452 | 1453 | if (res [1]) /* only use if not / */ 1454 | res += len; 1455 | } 1456 | 1457 | while (*rel) 1458 | { 1459 | eio_ssize_t len, linklen; 1460 | const char *beg = rel; 1461 | 1462 | while (*rel && *rel != '/') 1463 | ++rel; 1464 | 1465 | len = rel - beg; 1466 | 1467 | if (!len) /* skip slashes */ 1468 | { 1469 | ++rel; 1470 | continue; 1471 | } 1472 | 1473 | if (beg [0] == '.') 1474 | { 1475 | if (len == 1) 1476 | continue; /* . - nop */ 1477 | 1478 | if (beg [1] == '.' && len == 2) 1479 | { 1480 | /* .. - back up one component, if possible */ 1481 | 1482 | while (res != tmpbuf->ptr) 1483 | if (*--res == '/') 1484 | break; 1485 | 1486 | continue; 1487 | } 1488 | } 1489 | 1490 | errno = ENAMETOOLONG; 1491 | if (res + 1 + len + 1 >= tmp1) 1492 | return; 1493 | 1494 | /* copy one component */ 1495 | *res = '/'; 1496 | memcpy (res + 1, beg, len); 1497 | 1498 | /* zero-terminate, for readlink */ 1499 | res [len + 1] = 0; 1500 | 1501 | /* now check if it's a symlink */ 1502 | linklen = readlink (tmpbuf->ptr, tmp1, PATH_MAX); 1503 | 1504 | if (linklen < 0) 1505 | { 1506 | if (errno != EINVAL) 1507 | return -1; 1508 | 1509 | /* it's a normal directory. hopefully */ 1510 | res += len + 1; 1511 | } 1512 | else 1513 | { 1514 | /* yay, it was a symlink - build new path in tmp2 */ 1515 | int rellen = strlen (rel); 1516 | 1517 | errno = ENAMETOOLONG; 1518 | if (linklen + 1 + rellen >= PATH_MAX) 1519 | return -1; 1520 | 1521 | errno = ELOOP; 1522 | if (!--symlinks) 1523 | return -1; 1524 | 1525 | if (*tmp1 == '/') 1526 | res = tmpbuf->ptr; /* symlink resolves to an absolute path */ 1527 | 1528 | /* we need to be careful, as rel might point into tmp2 already */ 1529 | memmove (tmp2 + linklen + 1, rel, rellen + 1); 1530 | tmp2 [linklen] = '/'; 1531 | memcpy (tmp2, tmp1, linklen); 1532 | 1533 | rel = tmp2; 1534 | } 1535 | } 1536 | 1537 | /* special case for the lone root path */ 1538 | if (res == tmpbuf->ptr) 1539 | *res++ = '/'; 1540 | 1541 | return res - (char *)tmpbuf->ptr; 1542 | } 1543 | 1544 | static signed char 1545 | eio_dent_cmp (const eio_dirent *a, const eio_dirent *b) 1546 | { 1547 | return a->score - b->score ? a->score - b->score /* works because our signed char is always 0..100 */ 1548 | : a->inode < b->inode ? -1 1549 | : a->inode > b->inode ? 1 1550 | : 0; 1551 | } 1552 | 1553 | #define EIO_DENT_CMP(i,op,j) eio_dent_cmp (&i, &j) op 0 1554 | 1555 | #define EIO_SORT_CUTOFF 30 /* quite high, but performs well on many filesystems */ 1556 | #define EIO_SORT_FAST 60 /* when to only use insertion sort */ 1557 | 1558 | static void 1559 | eio_dent_radix_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) 1560 | { 1561 | unsigned char bits [9 + sizeof (eio_ino_t) * 8]; 1562 | unsigned char *bit = bits; 1563 | 1564 | assert (CHAR_BIT == 8); 1565 | assert (sizeof (eio_dirent) * 8 < 256); 1566 | assert (offsetof (eio_dirent, inode)); /* we use bit #0 as sentinel */ 1567 | assert (offsetof (eio_dirent, score)); /* we use bit #0 as sentinel */ 1568 | 1569 | if (size <= EIO_SORT_FAST) 1570 | return; 1571 | 1572 | /* first prepare an array of bits to test in our radix sort */ 1573 | /* try to take endianness into account, as well as differences in eio_ino_t sizes */ 1574 | /* inode_bits must contain all inodes ORed together */ 1575 | /* which is used to skip bits that are 0 everywhere, which is very common */ 1576 | { 1577 | eio_ino_t endianness; 1578 | int i, j; 1579 | 1580 | /* we store the byte offset of byte n into byte n of "endianness" */ 1581 | for (i = 0; i < sizeof (eio_ino_t); ++i) 1582 | ((unsigned char *)&endianness)[i] = i; 1583 | 1584 | *bit++ = 0; 1585 | 1586 | for (i = 0; i < sizeof (eio_ino_t); ++i) 1587 | { 1588 | /* shifting off the byte offsets out of "endianness" */ 1589 | int offs = (offsetof (eio_dirent, inode) + (endianness & 0xff)) * 8; 1590 | endianness >>= 8; 1591 | 1592 | for (j = 0; j < 8; ++j) 1593 | if (inode_bits & (((eio_ino_t)1) << (i * 8 + j))) 1594 | *bit++ = offs + j; 1595 | } 1596 | 1597 | for (j = 0; j < 8; ++j) 1598 | if (score_bits & (1 << j)) 1599 | *bit++ = offsetof (eio_dirent, score) * 8 + j; 1600 | } 1601 | 1602 | /* now actually do the sorting (a variant of MSD radix sort) */ 1603 | { 1604 | eio_dirent *base_stk [9 + sizeof (eio_ino_t) * 8], *base; 1605 | eio_dirent *end_stk [9 + sizeof (eio_ino_t) * 8], *end; 1606 | unsigned char *bit_stk [9 + sizeof (eio_ino_t) * 8]; 1607 | int stk_idx = 0; 1608 | 1609 | base_stk [stk_idx] = dents; 1610 | end_stk [stk_idx] = dents + size; 1611 | bit_stk [stk_idx] = bit - 1; 1612 | 1613 | do 1614 | { 1615 | base = base_stk [stk_idx]; 1616 | end = end_stk [stk_idx]; 1617 | bit = bit_stk [stk_idx]; 1618 | 1619 | for (;;) 1620 | { 1621 | unsigned char O = *bit >> 3; 1622 | unsigned char M = 1 << (*bit & 7); 1623 | 1624 | eio_dirent *a = base; 1625 | eio_dirent *b = end; 1626 | 1627 | if (b - a < EIO_SORT_CUTOFF) 1628 | break; 1629 | 1630 | /* now bit-partition the array on the bit */ 1631 | /* this ugly asymmetric loop seems to perform much better than typical */ 1632 | /* partition algos found in the literature */ 1633 | do 1634 | if (!(((unsigned char *)a)[O] & M)) 1635 | ++a; 1636 | else if (!(((unsigned char *)--b)[O] & M)) 1637 | { 1638 | eio_dirent tmp = *a; *a = *b; *b = tmp; 1639 | ++a; 1640 | } 1641 | while (b > a); 1642 | 1643 | /* next bit, or stop, if no bits left in this path */ 1644 | if (!*--bit) 1645 | break; 1646 | 1647 | base_stk [stk_idx] = a; 1648 | end_stk [stk_idx] = end; 1649 | bit_stk [stk_idx] = bit; 1650 | ++stk_idx; 1651 | 1652 | end = a; 1653 | } 1654 | } 1655 | while (stk_idx--); 1656 | } 1657 | } 1658 | 1659 | static void 1660 | eio_dent_insertion_sort (eio_dirent *dents, int size) 1661 | { 1662 | /* first move the smallest element to the front, to act as a sentinel */ 1663 | { 1664 | int i; 1665 | eio_dirent *min = dents; 1666 | 1667 | /* the radix pre-pass ensures that the minimum element is in the first EIO_SORT_CUTOFF + 1 elements */ 1668 | for (i = size > EIO_SORT_FAST ? EIO_SORT_CUTOFF + 1 : size; --i; ) 1669 | if (EIO_DENT_CMP (dents [i], <, *min)) 1670 | min = &dents [i]; 1671 | 1672 | /* swap elements 0 and j (minimum) */ 1673 | { 1674 | eio_dirent tmp = *dents; *dents = *min; *min = tmp; 1675 | } 1676 | } 1677 | 1678 | /* then do standard insertion sort, assuming that all elements are >= dents [0] */ 1679 | { 1680 | eio_dirent *i, *j; 1681 | 1682 | for (i = dents + 1; i < dents + size; ++i) 1683 | { 1684 | eio_dirent value = *i; 1685 | 1686 | for (j = i - 1; EIO_DENT_CMP (*j, >, value); --j) 1687 | j [1] = j [0]; 1688 | 1689 | j [1] = value; 1690 | } 1691 | } 1692 | } 1693 | 1694 | static void 1695 | eio_dent_sort (eio_dirent *dents, int size, signed char score_bits, eio_ino_t inode_bits) 1696 | { 1697 | if (size <= 1) 1698 | return; /* our insertion sort relies on size > 0 */ 1699 | 1700 | /* first we use a radix sort, but only for dirs >= EIO_SORT_FAST */ 1701 | /* and stop sorting when the partitions are <= EIO_SORT_CUTOFF */ 1702 | eio_dent_radix_sort (dents, size, score_bits, inode_bits); 1703 | 1704 | /* use an insertion sort at the end, or for small arrays, */ 1705 | /* as insertion sort is more efficient for small partitions */ 1706 | eio_dent_insertion_sort (dents, size); 1707 | } 1708 | 1709 | /* read a full directory */ 1710 | static void 1711 | eio__scandir (eio_req *req, etp_worker *self) 1712 | { 1713 | char *name, *names; 1714 | int namesalloc = 4096 - sizeof (void *) * 4; 1715 | int namesoffs = 0; 1716 | int flags = req->int1; 1717 | eio_dirent *dents = 0; 1718 | int dentalloc = 128; 1719 | int dentoffs = 0; 1720 | eio_ino_t inode_bits = 0; 1721 | #ifdef _WIN32 1722 | HANDLE dirp; 1723 | WIN32_FIND_DATA entp; 1724 | #else 1725 | DIR *dirp; 1726 | EIO_STRUCT_DIRENT *entp; 1727 | #endif 1728 | 1729 | req->result = -1; 1730 | 1731 | if (!(flags & EIO_READDIR_DENTS)) 1732 | flags &= ~(EIO_READDIR_DIRS_FIRST | EIO_READDIR_STAT_ORDER); 1733 | 1734 | #ifdef _WIN32 1735 | { 1736 | int len = strlen ((const char *)req->ptr1); 1737 | char *path = malloc (MAX_PATH); 1738 | const char *fmt; 1739 | const char *reqpath = wd_expand (&self->tmpbuf, req->wd, req->ptr1); 1740 | 1741 | if (!len) 1742 | fmt = "./*"; 1743 | else if (reqpath[len - 1] == '/' || reqpath[len - 1] == '\\') 1744 | fmt = "%s*"; 1745 | else 1746 | fmt = "%s/*"; 1747 | 1748 | _snprintf (path, MAX_PATH, fmt, reqpath); 1749 | dirp = FindFirstFile (path, &entp); 1750 | free (path); 1751 | 1752 | if (dirp == INVALID_HANDLE_VALUE) 1753 | { 1754 | /* should steal _dosmaperr */ 1755 | switch (GetLastError ()) 1756 | { 1757 | case ERROR_FILE_NOT_FOUND: 1758 | req->result = 0; 1759 | break; 1760 | 1761 | case ERROR_INVALID_NAME: 1762 | case ERROR_PATH_NOT_FOUND: 1763 | case ERROR_NO_MORE_FILES: 1764 | errno = ENOENT; 1765 | break; 1766 | 1767 | case ERROR_NOT_ENOUGH_MEMORY: 1768 | errno = ENOMEM; 1769 | break; 1770 | 1771 | default: 1772 | errno = EINVAL; 1773 | break; 1774 | } 1775 | 1776 | return; 1777 | } 1778 | } 1779 | #else 1780 | #if HAVE_AT 1781 | if (req->wd) 1782 | { 1783 | int fd = openat (WD2FD (req->wd), req->ptr1, O_CLOEXEC | O_SEARCH | O_DIRECTORY); 1784 | 1785 | if (fd < 0) 1786 | return; 1787 | 1788 | dirp = fdopendir (fd); 1789 | 1790 | if (!dirp) 1791 | close (fd); 1792 | } 1793 | else 1794 | dirp = opendir (req->ptr1); 1795 | #else 1796 | dirp = opendir (wd_expand (&self->tmpbuf, req->wd, req->ptr1)); 1797 | #endif 1798 | 1799 | if (!dirp) 1800 | return; 1801 | #endif 1802 | 1803 | if (req->flags & EIO_FLAG_PTR1_FREE) 1804 | free (req->ptr1); 1805 | 1806 | req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; 1807 | req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; 1808 | req->ptr2 = names = malloc (namesalloc); 1809 | 1810 | if (!names || (flags && !dents)) 1811 | return; 1812 | 1813 | for (;;) 1814 | { 1815 | int done; 1816 | 1817 | #ifdef _WIN32 1818 | done = !dirp; 1819 | #else 1820 | errno = 0; 1821 | entp = readdir (dirp); 1822 | done = !entp; 1823 | #endif 1824 | 1825 | if (done) 1826 | { 1827 | #ifndef _WIN32 1828 | int old_errno = errno; 1829 | closedir (dirp); 1830 | errno = old_errno; 1831 | 1832 | if (errno) 1833 | break; 1834 | #endif 1835 | 1836 | /* sort etc. */ 1837 | req->int1 = flags; 1838 | req->result = dentoffs; 1839 | 1840 | if (flags & EIO_READDIR_STAT_ORDER) 1841 | eio_dent_sort (dents, dentoffs, flags & EIO_READDIR_DIRS_FIRST ? 7 : 0, inode_bits); 1842 | else if (flags & EIO_READDIR_DIRS_FIRST) 1843 | if (flags & EIO_READDIR_FOUND_UNKNOWN) 1844 | eio_dent_sort (dents, dentoffs, 7, inode_bits); /* sort by score and inode */ 1845 | else 1846 | { 1847 | /* in this case, all is known, and we just put dirs first and sort them */ 1848 | eio_dirent *oth = dents + dentoffs; 1849 | eio_dirent *dir = dents; 1850 | 1851 | /* now partition dirs to the front, and non-dirs to the back */ 1852 | /* by walking from both sides and swapping if necessary */ 1853 | while (oth > dir) 1854 | { 1855 | if (dir->type == EIO_DT_DIR) 1856 | ++dir; 1857 | else if ((--oth)->type == EIO_DT_DIR) 1858 | { 1859 | eio_dirent tmp = *dir; *dir = *oth; *oth = tmp; 1860 | 1861 | ++dir; 1862 | } 1863 | } 1864 | 1865 | /* now sort the dirs only (dirs all have the same score) */ 1866 | eio_dent_sort (dents, dir - dents, 0, inode_bits); 1867 | } 1868 | 1869 | break; 1870 | } 1871 | 1872 | /* now add the entry to our list(s) */ 1873 | name = D_NAME (entp); 1874 | 1875 | /* skip . and .. entries */ 1876 | if (name [0] != '.' || (name [1] && (name [1] != '.' || name [2]))) 1877 | { 1878 | int len = D_NAMLEN (entp) + 1; 1879 | 1880 | while (ecb_expect_false (namesoffs + len > namesalloc)) 1881 | { 1882 | namesalloc *= 2; 1883 | req->ptr2 = names = realloc (names, namesalloc); 1884 | 1885 | if (!names) 1886 | break; 1887 | } 1888 | 1889 | memcpy (names + namesoffs, name, len); 1890 | 1891 | if (dents) 1892 | { 1893 | struct eio_dirent *ent; 1894 | 1895 | if (ecb_expect_false (dentoffs == dentalloc)) 1896 | { 1897 | dentalloc *= 2; 1898 | req->ptr1 = dents = realloc (dents, dentalloc * sizeof (eio_dirent)); 1899 | 1900 | if (!dents) 1901 | break; 1902 | } 1903 | 1904 | ent = dents + dentoffs; 1905 | 1906 | ent->nameofs = namesoffs; /* rather dirtily we store the offset in the pointer */ 1907 | ent->namelen = len - 1; 1908 | ent->inode = D_INO (entp); 1909 | 1910 | inode_bits |= ent->inode; 1911 | 1912 | switch (D_TYPE (entp)) 1913 | { 1914 | default: 1915 | ent->type = EIO_DT_UNKNOWN; 1916 | flags |= EIO_READDIR_FOUND_UNKNOWN; 1917 | break; 1918 | 1919 | #ifdef DT_FIFO 1920 | case DT_FIFO: ent->type = EIO_DT_FIFO; break; 1921 | #endif 1922 | #ifdef DT_CHR 1923 | case DT_CHR: ent->type = EIO_DT_CHR; break; 1924 | #endif 1925 | #ifdef DT_MPC 1926 | case DT_MPC: ent->type = EIO_DT_MPC; break; 1927 | #endif 1928 | #ifdef DT_DIR 1929 | case DT_DIR: ent->type = EIO_DT_DIR; break; 1930 | #endif 1931 | #ifdef DT_NAM 1932 | case DT_NAM: ent->type = EIO_DT_NAM; break; 1933 | #endif 1934 | #ifdef DT_BLK 1935 | case DT_BLK: ent->type = EIO_DT_BLK; break; 1936 | #endif 1937 | #ifdef DT_MPB 1938 | case DT_MPB: ent->type = EIO_DT_MPB; break; 1939 | #endif 1940 | #ifdef DT_REG 1941 | case DT_REG: ent->type = EIO_DT_REG; break; 1942 | #endif 1943 | #ifdef DT_NWK 1944 | case DT_NWK: ent->type = EIO_DT_NWK; break; 1945 | #endif 1946 | #ifdef DT_CMP 1947 | case DT_CMP: ent->type = EIO_DT_CMP; break; 1948 | #endif 1949 | #ifdef DT_LNK 1950 | case DT_LNK: ent->type = EIO_DT_LNK; break; 1951 | #endif 1952 | #ifdef DT_SOCK 1953 | case DT_SOCK: ent->type = EIO_DT_SOCK; break; 1954 | #endif 1955 | #ifdef DT_DOOR 1956 | case DT_DOOR: ent->type = EIO_DT_DOOR; break; 1957 | #endif 1958 | #ifdef DT_WHT 1959 | case DT_WHT: ent->type = EIO_DT_WHT; break; 1960 | #endif 1961 | } 1962 | 1963 | ent->score = 7; 1964 | 1965 | if (flags & EIO_READDIR_DIRS_FIRST) 1966 | { 1967 | if (ent->type == EIO_DT_UNKNOWN) 1968 | { 1969 | if (*name == '.') /* leading dots are likely directories, and, in any case, rare */ 1970 | ent->score = 1; 1971 | else if (!strchr (name, '.')) /* absense of dots indicate likely dirs */ 1972 | ent->score = len <= 2 ? 4 - len : len <= 4 ? 4 : len <= 7 ? 5 : 6; /* shorter == more likely dir, but avoid too many classes */ 1973 | } 1974 | else if (ent->type == EIO_DT_DIR) 1975 | ent->score = 0; 1976 | } 1977 | } 1978 | 1979 | namesoffs += len; 1980 | ++dentoffs; 1981 | } 1982 | 1983 | if (EIO_CANCELLED (req)) 1984 | { 1985 | errno = ECANCELED; 1986 | break; 1987 | } 1988 | 1989 | #ifdef _WIN32 1990 | if (!FindNextFile (dirp, &entp)) 1991 | { 1992 | FindClose (dirp); 1993 | dirp = 0; 1994 | } 1995 | #endif 1996 | } 1997 | } 1998 | 1999 | /*****************************************************************************/ 2000 | /* working directory stuff */ 2001 | /* various deficiencies in the posix 2008 api force us to */ 2002 | /* keep the absolute path in string form at all times */ 2003 | /* fuck yeah. */ 2004 | 2005 | #if !HAVE_AT 2006 | 2007 | /* a bit like realpath, but usually faster because it doesn'T have to return */ 2008 | /* an absolute or canonical path */ 2009 | static const char * 2010 | wd_expand (struct tmpbuf *tmpbuf, eio_wd wd, const char *path) 2011 | { 2012 | if (!wd || *path == '/') 2013 | return path; 2014 | 2015 | if (path [0] == '.' && !path [1]) 2016 | return wd->str; 2017 | 2018 | { 2019 | int l1 = wd->len; 2020 | int l2 = strlen (path); 2021 | 2022 | char *res = tmpbuf_get (tmpbuf, l1 + l2 + 2); 2023 | 2024 | memcpy (res, wd->str, l1); 2025 | res [l1] = '/'; 2026 | memcpy (res + l1 + 1, path, l2 + 1); 2027 | 2028 | return res; 2029 | } 2030 | } 2031 | 2032 | #endif 2033 | 2034 | static eio_wd 2035 | eio__wd_open_sync (struct tmpbuf *tmpbuf, eio_wd wd, const char *path) 2036 | { 2037 | int fd; 2038 | eio_wd res; 2039 | int len = eio__realpath (tmpbuf, wd, path); 2040 | 2041 | if (len < 0) 2042 | return EIO_INVALID_WD; 2043 | 2044 | #if HAVE_AT 2045 | fd = openat (WD2FD (wd), path, O_CLOEXEC | O_SEARCH | O_DIRECTORY); 2046 | 2047 | if (fd < 0) 2048 | return EIO_INVALID_WD; 2049 | #endif 2050 | 2051 | res = malloc (sizeof (*res) + len); /* one extra 0-byte */ 2052 | 2053 | #if HAVE_AT 2054 | res->fd = fd; 2055 | #endif 2056 | 2057 | res->len = len; 2058 | memcpy (res->str, tmpbuf->ptr, len); 2059 | res->str [len] = 0; 2060 | 2061 | return res; 2062 | } 2063 | 2064 | eio_wd 2065 | eio_wd_open_sync (eio_wd wd, const char *path) 2066 | { 2067 | struct tmpbuf tmpbuf = { 0 }; 2068 | wd = eio__wd_open_sync (&tmpbuf, wd, path); 2069 | free (tmpbuf.ptr); 2070 | 2071 | return wd; 2072 | } 2073 | 2074 | void 2075 | eio_wd_close_sync (eio_wd wd) 2076 | { 2077 | if (wd != EIO_INVALID_WD && wd != EIO_CWD) 2078 | { 2079 | #if HAVE_AT 2080 | close (wd->fd); 2081 | #endif 2082 | free (wd); 2083 | } 2084 | } 2085 | 2086 | #if HAVE_AT 2087 | 2088 | /* they forgot these */ 2089 | 2090 | static int 2091 | eio__truncateat (int dirfd, const char *path, off_t length) 2092 | { 2093 | int fd = openat (dirfd, path, O_WRONLY | O_CLOEXEC); 2094 | int res; 2095 | 2096 | if (fd < 0) 2097 | return fd; 2098 | 2099 | res = ftruncate (fd, length); 2100 | close (fd); 2101 | return res; 2102 | } 2103 | 2104 | static int 2105 | eio__statvfsat (int dirfd, const char *path, struct statvfs *buf) 2106 | { 2107 | int fd = openat (dirfd, path, O_SEARCH | O_CLOEXEC); 2108 | int res; 2109 | 2110 | if (fd < 0) 2111 | return fd; 2112 | 2113 | res = fstatvfs (fd, buf); 2114 | close (fd); 2115 | return res; 2116 | 2117 | } 2118 | 2119 | #endif 2120 | 2121 | /*****************************************************************************/ 2122 | 2123 | #define ALLOC(len) \ 2124 | if (!req->ptr2) \ 2125 | { \ 2126 | X_LOCK (wrklock); \ 2127 | req->flags |= EIO_FLAG_PTR2_FREE; \ 2128 | X_UNLOCK (wrklock); \ 2129 | req->ptr2 = malloc (len); \ 2130 | if (!req->ptr2) \ 2131 | { \ 2132 | errno = ENOMEM; \ 2133 | req->result = -1; \ 2134 | break; \ 2135 | } \ 2136 | } 2137 | 2138 | static void ecb_noinline ecb_cold 2139 | etp_proc_init (void) 2140 | { 2141 | #if HAVE_PRCTL_SET_NAME 2142 | /* provide a more sensible "thread name" */ 2143 | char name[16 + 1]; 2144 | const int namelen = sizeof (name) - 1; 2145 | int len; 2146 | 2147 | prctl (PR_GET_NAME, (unsigned long)name, 0, 0, 0); 2148 | name [namelen] = 0; 2149 | len = strlen (name); 2150 | strcpy (name + (len <= namelen - 4 ? len : namelen - 4), "/eio"); 2151 | prctl (PR_SET_NAME, (unsigned long)name, 0, 0, 0); 2152 | #endif 2153 | } 2154 | 2155 | X_THREAD_PROC (etp_proc) 2156 | { 2157 | ETP_REQ *req; 2158 | struct timespec ts; 2159 | etp_worker *self = (etp_worker *)thr_arg; 2160 | 2161 | etp_proc_init (); 2162 | 2163 | /* try to distribute timeouts somewhat evenly */ 2164 | ts.tv_nsec = ((unsigned long)self & 1023UL) * (1000000000UL / 1024UL); 2165 | 2166 | for (;;) 2167 | { 2168 | ts.tv_sec = 0; 2169 | 2170 | X_LOCK (reqlock); 2171 | 2172 | for (;;) 2173 | { 2174 | req = reqq_shift (&req_queue); 2175 | 2176 | if (req) 2177 | break; 2178 | 2179 | if (ts.tv_sec == 1) /* no request, but timeout detected, let's quit */ 2180 | { 2181 | X_UNLOCK (reqlock); 2182 | X_LOCK (wrklock); 2183 | --started; 2184 | X_UNLOCK (wrklock); 2185 | goto quit; 2186 | } 2187 | 2188 | ++idle; 2189 | 2190 | if (idle <= max_idle) 2191 | /* we are allowed to idle, so do so without any timeout */ 2192 | X_COND_WAIT (reqwait, reqlock); 2193 | else 2194 | { 2195 | /* initialise timeout once */ 2196 | if (!ts.tv_sec) 2197 | ts.tv_sec = time (0) + idle_timeout; 2198 | 2199 | if (X_COND_TIMEDWAIT (reqwait, reqlock, ts) == ETIMEDOUT) 2200 | ts.tv_sec = 1; /* assuming this is not a value computed above.,.. */ 2201 | } 2202 | 2203 | --idle; 2204 | } 2205 | 2206 | --nready; 2207 | 2208 | X_UNLOCK (reqlock); 2209 | 2210 | if (req->type < 0) 2211 | goto quit; 2212 | 2213 | ETP_EXECUTE (self, req); 2214 | 2215 | X_LOCK (reslock); 2216 | 2217 | ++npending; 2218 | 2219 | if (!reqq_push (&res_queue, req) && want_poll_cb) 2220 | want_poll_cb (); 2221 | 2222 | etp_worker_clear (self); 2223 | 2224 | X_UNLOCK (reslock); 2225 | } 2226 | 2227 | quit: 2228 | free (req); 2229 | 2230 | X_LOCK (wrklock); 2231 | etp_worker_free (self); 2232 | X_UNLOCK (wrklock); 2233 | 2234 | return 0; 2235 | } 2236 | 2237 | /*****************************************************************************/ 2238 | 2239 | int ecb_cold 2240 | eio_init (void (*want_poll)(void), void (*done_poll)(void)) 2241 | { 2242 | #if !HAVE_PREADWRITE 2243 | X_MUTEX_CREATE (preadwritelock); 2244 | #endif 2245 | 2246 | return etp_init (want_poll, done_poll); 2247 | } 2248 | 2249 | ecb_inline void 2250 | eio_api_destroy (eio_req *req) 2251 | { 2252 | free (req); 2253 | } 2254 | 2255 | #define REQ(rtype) \ 2256 | eio_req *req; \ 2257 | \ 2258 | req = (eio_req *)calloc (1, sizeof *req); \ 2259 | if (!req) \ 2260 | return 0; \ 2261 | \ 2262 | req->type = rtype; \ 2263 | req->pri = pri; \ 2264 | req->finish = cb; \ 2265 | req->data = data; \ 2266 | req->destroy = eio_api_destroy; 2267 | 2268 | #define SEND eio_submit (req); return req 2269 | 2270 | #define PATH \ 2271 | req->flags |= EIO_FLAG_PTR1_FREE; \ 2272 | req->ptr1 = strdup (path); \ 2273 | if (!req->ptr1) \ 2274 | { \ 2275 | eio_api_destroy (req); \ 2276 | return 0; \ 2277 | } 2278 | 2279 | static void 2280 | eio_execute (etp_worker *self, eio_req *req) 2281 | { 2282 | #if HAVE_AT 2283 | int dirfd; 2284 | #else 2285 | const char *path; 2286 | #endif 2287 | 2288 | if (ecb_expect_false (EIO_CANCELLED (req))) 2289 | { 2290 | req->result = -1; 2291 | req->errorno = ECANCELED; 2292 | return; 2293 | } 2294 | 2295 | if (ecb_expect_false (req->wd == EIO_INVALID_WD)) 2296 | { 2297 | req->result = -1; 2298 | req->errorno = ENOENT; 2299 | return; 2300 | } 2301 | 2302 | if (req->type >= EIO_OPEN) 2303 | { 2304 | #if HAVE_AT 2305 | dirfd = WD2FD (req->wd); 2306 | #else 2307 | path = wd_expand (&self->tmpbuf, req->wd, req->ptr1); 2308 | #endif 2309 | } 2310 | 2311 | switch (req->type) 2312 | { 2313 | case EIO_WD_OPEN: req->wd = eio__wd_open_sync (&self->tmpbuf, req->wd, req->ptr1); 2314 | req->result = req->wd == EIO_INVALID_WD ? -1 : 0; 2315 | break; 2316 | case EIO_WD_CLOSE: req->result = 0; 2317 | eio_wd_close_sync (req->wd); break; 2318 | 2319 | case EIO_READ: ALLOC (req->size); 2320 | req->result = req->offs >= 0 2321 | ? pread (req->int1, req->ptr2, req->size, req->offs) 2322 | : read (req->int1, req->ptr2, req->size); break; 2323 | case EIO_WRITE: req->result = req->offs >= 0 2324 | ? pwrite (req->int1, req->ptr2, req->size, req->offs) 2325 | : write (req->int1, req->ptr2, req->size); break; 2326 | 2327 | case EIO_READAHEAD: req->result = readahead (req->int1, req->offs, req->size); break; 2328 | case EIO_SENDFILE: req->result = eio__sendfile (req->int1, req->int2, req->offs, req->size); break; 2329 | 2330 | #if HAVE_AT 2331 | 2332 | case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2333 | req->result = fstatat (dirfd, req->ptr1, (EIO_STRUCT_STAT *)req->ptr2, 0); break; 2334 | case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2335 | req->result = fstatat (dirfd, req->ptr1, (EIO_STRUCT_STAT *)req->ptr2, AT_SYMLINK_NOFOLLOW); break; 2336 | case EIO_CHOWN: req->result = fchownat (dirfd, req->ptr1, req->int2, req->int3, 0); break; 2337 | case EIO_CHMOD: req->result = fchmodat (dirfd, req->ptr1, (mode_t)req->int2, 0); break; 2338 | case EIO_TRUNCATE: req->result = eio__truncateat (dirfd, req->ptr1, req->offs); break; 2339 | case EIO_OPEN: req->result = openat (dirfd, req->ptr1, req->int1, (mode_t)req->int2); break; 2340 | 2341 | case EIO_UNLINK: req->result = unlinkat (dirfd, req->ptr1, 0); break; 2342 | case EIO_RMDIR: req->result = unlinkat (dirfd, req->ptr1, AT_REMOVEDIR); break; 2343 | case EIO_MKDIR: req->result = mkdirat (dirfd, req->ptr1, (mode_t)req->int2); break; 2344 | case EIO_RENAME: req->result = renameat (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2); break; 2345 | case EIO_LINK: req->result = linkat (dirfd, req->ptr1, WD2FD ((eio_wd)req->int3), req->ptr2, 0); break; 2346 | case EIO_SYMLINK: req->result = symlinkat (req->ptr1, dirfd, req->ptr2); break; 2347 | case EIO_MKNOD: req->result = mknodat (dirfd, req->ptr1, (mode_t)req->int2, (dev_t)req->offs); break; 2348 | case EIO_READLINK: ALLOC (PATH_MAX); 2349 | req->result = readlinkat (dirfd, req->ptr1, req->ptr2, PATH_MAX); break; 2350 | case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); 2351 | req->result = eio__statvfsat (dirfd, req->ptr1, (EIO_STRUCT_STATVFS *)req->ptr2); break; 2352 | case EIO_UTIME: 2353 | case EIO_FUTIME: 2354 | { 2355 | struct timespec ts[2]; 2356 | struct timespec *times; 2357 | 2358 | if (req->nv1 != -1. || req->nv2 != -1.) 2359 | { 2360 | ts[0].tv_sec = req->nv1; 2361 | ts[0].tv_nsec = (req->nv1 - ts[0].tv_sec) * 1e9; 2362 | ts[1].tv_sec = req->nv2; 2363 | ts[1].tv_nsec = (req->nv2 - ts[1].tv_sec) * 1e9; 2364 | 2365 | times = ts; 2366 | } 2367 | else 2368 | times = 0; 2369 | 2370 | req->result = req->type == EIO_FUTIME 2371 | ? futimens (req->int1, times) 2372 | : utimensat (dirfd, req->ptr1, times, 0); 2373 | } 2374 | break; 2375 | 2376 | #else 2377 | 2378 | case EIO_STAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2379 | req->result = stat (path , (EIO_STRUCT_STAT *)req->ptr2); break; 2380 | case EIO_LSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2381 | req->result = lstat (path , (EIO_STRUCT_STAT *)req->ptr2); break; 2382 | case EIO_CHOWN: req->result = chown (path , req->int2, req->int3); break; 2383 | case EIO_CHMOD: req->result = chmod (path , (mode_t)req->int2); break; 2384 | case EIO_TRUNCATE: req->result = truncate (path , req->offs); break; 2385 | case EIO_OPEN: req->result = open (path , req->int1, (mode_t)req->int2); break; 2386 | 2387 | case EIO_UNLINK: req->result = unlink (path ); break; 2388 | case EIO_RMDIR: req->result = rmdir (path ); break; 2389 | case EIO_MKDIR: req->result = mkdir (path , (mode_t)req->int2); break; 2390 | case EIO_RENAME: req->result = rename (path , req->ptr2); break; 2391 | case EIO_LINK: req->result = link (path , req->ptr2); break; 2392 | case EIO_SYMLINK: req->result = symlink (path , req->ptr2); break; 2393 | case EIO_MKNOD: req->result = mknod (path , (mode_t)req->int2, (dev_t)req->offs); break; 2394 | case EIO_READLINK: ALLOC (PATH_MAX); 2395 | req->result = readlink (path, req->ptr2, PATH_MAX); break; 2396 | case EIO_STATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); 2397 | req->result = statvfs (path , (EIO_STRUCT_STATVFS *)req->ptr2); break; 2398 | 2399 | case EIO_UTIME: 2400 | case EIO_FUTIME: 2401 | { 2402 | struct timeval tv[2]; 2403 | struct timeval *times; 2404 | 2405 | if (req->nv1 != -1. || req->nv2 != -1.) 2406 | { 2407 | tv[0].tv_sec = req->nv1; 2408 | tv[0].tv_usec = (req->nv1 - tv[0].tv_sec) * 1e6; 2409 | tv[1].tv_sec = req->nv2; 2410 | tv[1].tv_usec = (req->nv2 - tv[1].tv_sec) * 1e6; 2411 | 2412 | times = tv; 2413 | } 2414 | else 2415 | times = 0; 2416 | 2417 | req->result = req->type == EIO_FUTIME 2418 | ? futimes (req->int1, times) 2419 | : utimes (req->ptr1, times); 2420 | } 2421 | break; 2422 | 2423 | #endif 2424 | 2425 | case EIO_REALPATH: if (0 <= (req->result = eio__realpath (&self->tmpbuf, req->wd, req->ptr1))) 2426 | { 2427 | ALLOC (req->result); 2428 | memcpy (req->ptr2, self->tmpbuf.ptr, req->result); 2429 | } 2430 | break; 2431 | 2432 | case EIO_FSTAT: ALLOC (sizeof (EIO_STRUCT_STAT)); 2433 | req->result = fstat (req->int1, (EIO_STRUCT_STAT *)req->ptr2); break; 2434 | 2435 | case EIO_FSTATVFS: ALLOC (sizeof (EIO_STRUCT_STATVFS)); 2436 | req->result = fstatvfs (req->int1, (EIO_STRUCT_STATVFS *)req->ptr2); break; 2437 | 2438 | case EIO_FCHOWN: req->result = fchown (req->int1, req->int2, req->int3); break; 2439 | case EIO_FCHMOD: req->result = fchmod (req->int1, (mode_t)req->int2); break; 2440 | case EIO_FTRUNCATE: req->result = ftruncate (req->int1, req->offs); break; 2441 | 2442 | case EIO_CLOSE: req->result = close (req->int1); break; 2443 | case EIO_DUP2: req->result = dup2 (req->int1, req->int2); break; 2444 | case EIO_SYNC: req->result = 0; sync (); break; 2445 | case EIO_FSYNC: req->result = fsync (req->int1); break; 2446 | case EIO_FDATASYNC: req->result = fdatasync (req->int1); break; 2447 | case EIO_SYNCFS: req->result = eio__syncfs (req->int1); break; 2448 | case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break; 2449 | case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break; 2450 | case EIO_MTOUCH: req->result = eio__mtouch (req); break; 2451 | case EIO_MLOCK: req->result = eio__mlock (req->ptr2, req->size); break; 2452 | case EIO_MLOCKALL: req->result = eio__mlockall (req->int1); break; 2453 | case EIO_FALLOCATE: req->result = eio__fallocate (req->int1, req->int2, req->offs, req->size); break; 2454 | 2455 | case EIO_READDIR: eio__scandir (req, self); break; 2456 | 2457 | case EIO_BUSY: 2458 | #ifdef _WIN32 2459 | Sleep (req->nv1 * 1e3); 2460 | #else 2461 | { 2462 | struct timeval tv; 2463 | 2464 | tv.tv_sec = req->nv1; 2465 | tv.tv_usec = (req->nv1 - tv.tv_sec) * 1e6; 2466 | 2467 | req->result = select (0, 0, 0, 0, &tv); 2468 | } 2469 | #endif 2470 | break; 2471 | 2472 | case EIO_GROUP: 2473 | abort (); /* handled in eio_request */ 2474 | 2475 | case EIO_NOP: 2476 | req->result = 0; 2477 | break; 2478 | 2479 | case EIO_CUSTOM: 2480 | req->feed (req); 2481 | break; 2482 | 2483 | default: 2484 | errno = ENOSYS; 2485 | req->result = -1; 2486 | break; 2487 | } 2488 | 2489 | req->errorno = errno; 2490 | } 2491 | 2492 | #ifndef EIO_NO_WRAPPERS 2493 | 2494 | eio_req *eio_wd_open (const char *path, int pri, eio_cb cb, void *data) 2495 | { 2496 | REQ (EIO_WD_OPEN); PATH; SEND; 2497 | } 2498 | 2499 | eio_req *eio_wd_close (eio_wd wd, int pri, eio_cb cb, void *data) 2500 | { 2501 | REQ (EIO_WD_CLOSE); req->wd = wd; SEND; 2502 | } 2503 | 2504 | eio_req *eio_nop (int pri, eio_cb cb, void *data) 2505 | { 2506 | REQ (EIO_NOP); SEND; 2507 | } 2508 | 2509 | eio_req *eio_busy (double delay, int pri, eio_cb cb, void *data) 2510 | { 2511 | REQ (EIO_BUSY); req->nv1 = delay; SEND; 2512 | } 2513 | 2514 | eio_req *eio_sync (int pri, eio_cb cb, void *data) 2515 | { 2516 | REQ (EIO_SYNC); SEND; 2517 | } 2518 | 2519 | eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data) 2520 | { 2521 | REQ (EIO_FSYNC); req->int1 = fd; SEND; 2522 | } 2523 | 2524 | eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 2525 | { 2526 | REQ (EIO_MSYNC); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; 2527 | } 2528 | 2529 | eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data) 2530 | { 2531 | REQ (EIO_FDATASYNC); req->int1 = fd; SEND; 2532 | } 2533 | 2534 | eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data) 2535 | { 2536 | REQ (EIO_SYNCFS); req->int1 = fd; SEND; 2537 | } 2538 | 2539 | eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) 2540 | { 2541 | REQ (EIO_SYNC_FILE_RANGE); req->int1 = fd; req->offs = offset; req->size = nbytes; req->int2 = flags; SEND; 2542 | } 2543 | 2544 | eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 2545 | { 2546 | REQ (EIO_MTOUCH); req->ptr2 = addr; req->size = length; req->int1 = flags; SEND; 2547 | } 2548 | 2549 | eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) 2550 | { 2551 | REQ (EIO_MLOCK); req->ptr2 = addr; req->size = length; SEND; 2552 | } 2553 | 2554 | eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data) 2555 | { 2556 | REQ (EIO_MLOCKALL); req->int1 = flags; SEND; 2557 | } 2558 | 2559 | eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data) 2560 | { 2561 | REQ (EIO_FALLOCATE); req->int1 = fd; req->int2 = mode; req->offs = offset; req->size = len; SEND; 2562 | } 2563 | 2564 | eio_req *eio_close (int fd, int pri, eio_cb cb, void *data) 2565 | { 2566 | REQ (EIO_CLOSE); req->int1 = fd; SEND; 2567 | } 2568 | 2569 | eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) 2570 | { 2571 | REQ (EIO_READAHEAD); req->int1 = fd; req->offs = offset; req->size = length; SEND; 2572 | } 2573 | 2574 | eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 2575 | { 2576 | REQ (EIO_READ); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; 2577 | } 2578 | 2579 | eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 2580 | { 2581 | REQ (EIO_WRITE); req->int1 = fd; req->offs = offset; req->size = length; req->ptr2 = buf; SEND; 2582 | } 2583 | 2584 | eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data) 2585 | { 2586 | REQ (EIO_FSTAT); req->int1 = fd; SEND; 2587 | } 2588 | 2589 | eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) 2590 | { 2591 | REQ (EIO_FSTATVFS); req->int1 = fd; SEND; 2592 | } 2593 | 2594 | eio_req *eio_futime (int fd, double atime, double mtime, int pri, eio_cb cb, void *data) 2595 | { 2596 | REQ (EIO_FUTIME); req->int1 = fd; req->nv1 = atime; req->nv2 = mtime; SEND; 2597 | } 2598 | 2599 | eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) 2600 | { 2601 | REQ (EIO_FTRUNCATE); req->int1 = fd; req->offs = offset; SEND; 2602 | } 2603 | 2604 | eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) 2605 | { 2606 | REQ (EIO_FCHMOD); req->int1 = fd; req->int2 = (long)mode; SEND; 2607 | } 2608 | 2609 | eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) 2610 | { 2611 | REQ (EIO_FCHOWN); req->int1 = fd; req->int2 = (long)uid; req->int3 = (long)gid; SEND; 2612 | } 2613 | 2614 | eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) 2615 | { 2616 | REQ (EIO_DUP2); req->int1 = fd; req->int2 = fd2; SEND; 2617 | } 2618 | 2619 | eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) 2620 | { 2621 | REQ (EIO_SENDFILE); req->int1 = out_fd; req->int2 = in_fd; req->offs = in_offset; req->size = length; SEND; 2622 | } 2623 | 2624 | eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) 2625 | { 2626 | REQ (EIO_OPEN); PATH; req->int1 = flags; req->int2 = (long)mode; SEND; 2627 | } 2628 | 2629 | eio_req *eio_utime (const char *path, double atime, double mtime, int pri, eio_cb cb, void *data) 2630 | { 2631 | REQ (EIO_UTIME); PATH; req->nv1 = atime; req->nv2 = mtime; SEND; 2632 | } 2633 | 2634 | eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) 2635 | { 2636 | REQ (EIO_TRUNCATE); PATH; req->offs = offset; SEND; 2637 | } 2638 | 2639 | eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data) 2640 | { 2641 | REQ (EIO_CHOWN); PATH; req->int2 = (long)uid; req->int3 = (long)gid; SEND; 2642 | } 2643 | 2644 | eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 2645 | { 2646 | REQ (EIO_CHMOD); PATH; req->int2 = (long)mode; SEND; 2647 | } 2648 | 2649 | eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 2650 | { 2651 | REQ (EIO_MKDIR); PATH; req->int2 = (long)mode; SEND; 2652 | } 2653 | 2654 | static eio_req * 2655 | eio__1path (int type, const char *path, int pri, eio_cb cb, void *data) 2656 | { 2657 | REQ (type); PATH; SEND; 2658 | } 2659 | 2660 | eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data) 2661 | { 2662 | return eio__1path (EIO_READLINK, path, pri, cb, data); 2663 | } 2664 | 2665 | eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data) 2666 | { 2667 | return eio__1path (EIO_REALPATH, path, pri, cb, data); 2668 | } 2669 | 2670 | eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data) 2671 | { 2672 | return eio__1path (EIO_STAT, path, pri, cb, data); 2673 | } 2674 | 2675 | eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data) 2676 | { 2677 | return eio__1path (EIO_LSTAT, path, pri, cb, data); 2678 | } 2679 | 2680 | eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data) 2681 | { 2682 | return eio__1path (EIO_STATVFS, path, pri, cb, data); 2683 | } 2684 | 2685 | eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data) 2686 | { 2687 | return eio__1path (EIO_UNLINK, path, pri, cb, data); 2688 | } 2689 | 2690 | eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data) 2691 | { 2692 | return eio__1path (EIO_RMDIR, path, pri, cb, data); 2693 | } 2694 | 2695 | eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) 2696 | { 2697 | REQ (EIO_READDIR); PATH; req->int1 = flags; SEND; 2698 | } 2699 | 2700 | eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) 2701 | { 2702 | REQ (EIO_MKNOD); PATH; req->int2 = (long)mode; req->offs = (off_t)dev; SEND; 2703 | } 2704 | 2705 | static eio_req * 2706 | eio__2path (int type, const char *path, const char *new_path, int pri, eio_cb cb, void *data) 2707 | { 2708 | REQ (type); PATH; 2709 | 2710 | req->flags |= EIO_FLAG_PTR2_FREE; 2711 | req->ptr2 = strdup (new_path); 2712 | if (!req->ptr2) 2713 | { 2714 | eio_api_destroy (req); 2715 | return 0; 2716 | } 2717 | 2718 | SEND; 2719 | } 2720 | 2721 | eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 2722 | { 2723 | return eio__2path (EIO_LINK, path, new_path, pri, cb, data); 2724 | } 2725 | 2726 | eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 2727 | { 2728 | return eio__2path (EIO_SYMLINK, path, new_path, pri, cb, data); 2729 | } 2730 | 2731 | eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 2732 | { 2733 | return eio__2path (EIO_RENAME, path, new_path, pri, cb, data); 2734 | } 2735 | 2736 | eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data) 2737 | { 2738 | REQ (EIO_CUSTOM); req->feed = execute; SEND; 2739 | } 2740 | 2741 | #endif 2742 | 2743 | eio_req *eio_grp (eio_cb cb, void *data) 2744 | { 2745 | const int pri = EIO_PRI_MAX; 2746 | 2747 | REQ (EIO_GROUP); SEND; 2748 | } 2749 | 2750 | #undef REQ 2751 | #undef PATH 2752 | #undef SEND 2753 | 2754 | /*****************************************************************************/ 2755 | /* grp functions */ 2756 | 2757 | void 2758 | eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit) 2759 | { 2760 | grp->int2 = limit; 2761 | grp->feed = feed; 2762 | 2763 | grp_try_feed (grp); 2764 | } 2765 | 2766 | void 2767 | eio_grp_limit (eio_req *grp, int limit) 2768 | { 2769 | grp->int2 = limit; 2770 | 2771 | grp_try_feed (grp); 2772 | } 2773 | 2774 | void 2775 | eio_grp_add (eio_req *grp, eio_req *req) 2776 | { 2777 | assert (("cannot add requests to IO::AIO::GRP after the group finished", grp->int1 != 2)); 2778 | 2779 | grp->flags |= EIO_FLAG_GROUPADD; 2780 | 2781 | ++grp->size; 2782 | req->grp = grp; 2783 | 2784 | req->grp_prev = 0; 2785 | req->grp_next = grp->grp_first; 2786 | 2787 | if (grp->grp_first) 2788 | grp->grp_first->grp_prev = req; 2789 | 2790 | grp->grp_first = req; 2791 | } 2792 | 2793 | /*****************************************************************************/ 2794 | /* misc garbage */ 2795 | 2796 | eio_ssize_t 2797 | eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count) 2798 | { 2799 | return eio__sendfile (ofd, ifd, offset, count); 2800 | } 2801 | 2802 | -------------------------------------------------------------------------------- /libeio/eio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libeio API header 3 | * 4 | * Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without modifica- 8 | * tion, are permitted provided that the following conditions are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright notice, 11 | * this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 18 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- 19 | * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 20 | * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- 21 | * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- 25 | * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 26 | * OF THE POSSIBILITY OF SUCH DAMAGE. 27 | * 28 | * Alternatively, the contents of this file may be used under the terms of 29 | * the GNU General Public License ("GPL") version 2 or any later version, 30 | * in which case the provisions of the GPL are applicable instead of 31 | * the above. If you wish to allow the use of your version of this file 32 | * only under the terms of the GPL and not to allow others to use your 33 | * version of this file under the BSD license, indicate your decision 34 | * by deleting the provisions above and replace them with the notice 35 | * and other provisions required by the GPL. If you do not delete the 36 | * provisions above, a recipient may use your version of this file under 37 | * either the BSD or the GPL. 38 | */ 39 | 40 | #ifndef EIO_H_ 41 | #define EIO_H_ 42 | 43 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | typedef struct eio_req eio_req; 52 | typedef struct eio_dirent eio_dirent; 53 | 54 | typedef int (*eio_cb)(eio_req *req); 55 | 56 | #ifndef EIO_REQ_MEMBERS 57 | # define EIO_REQ_MEMBERS 58 | #endif 59 | 60 | #ifndef EIO_STRUCT_STAT 61 | # ifdef _WIN32 62 | # define EIO_STRUCT_STAT struct _stati64 63 | # define EIO_STRUCT_STATI64 64 | # else 65 | # define EIO_STRUCT_STAT struct stat 66 | # endif 67 | #endif 68 | 69 | #ifdef _WIN32 70 | typedef int eio_uid_t; 71 | typedef int eio_gid_t; 72 | #ifdef __MINGW32__ /* no intptr_t */ 73 | typedef ssize_t eio_ssize_t; 74 | #else 75 | typedef intptr_t eio_ssize_t; /* or SSIZE_T */ 76 | #endif 77 | #if __GNUC__ 78 | typedef long long eio_ino_t; /* signed for compatibility to msvc */ 79 | #else 80 | typedef __int64 eio_ino_t; /* unsigned not supported by msvc */ 81 | #endif 82 | #else 83 | typedef uid_t eio_uid_t; 84 | typedef gid_t eio_gid_t; 85 | typedef ssize_t eio_ssize_t; 86 | typedef ino_t eio_ino_t; 87 | #endif 88 | 89 | #ifndef EIO_STRUCT_STATVFS 90 | # define EIO_STRUCT_STATVFS struct statvfs 91 | #endif 92 | 93 | /* managing working directories */ 94 | 95 | typedef struct eio_pwd *eio_wd; 96 | 97 | #define EIO_CWD 0 /* the current working directory of the process, guaranteed to be a null pointer */ 98 | #define EIO_INVALID_WD ((eio_wd)(int)-1) /* failure return for eio_wd_open */ 99 | 100 | eio_wd eio_wd_open_sync (eio_wd wd, const char *path); 101 | void eio_wd_close_sync (eio_wd wd); 102 | 103 | /* for readdir */ 104 | 105 | /* eio_readdir flags */ 106 | enum 107 | { 108 | EIO_READDIR_DENTS = 0x01, /* ptr2 contains eio_dirents, not just the (unsorted) names */ 109 | EIO_READDIR_DIRS_FIRST = 0x02, /* dirents gets sorted into a good stat() ing order to find directories first */ 110 | EIO_READDIR_STAT_ORDER = 0x04, /* dirents gets sorted into a good stat() ing order to quickly stat all files */ 111 | EIO_READDIR_FOUND_UNKNOWN = 0x80, /* set by eio_readdir when *_ARRAY was set and any TYPE=UNKNOWN's were found */ 112 | 113 | EIO_READDIR_CUSTOM1 = 0x100, /* for use by apps */ 114 | EIO_READDIR_CUSTOM2 = 0x200 /* for use by apps */ 115 | }; 116 | 117 | /* using "typical" values in the hope that the compiler will do something sensible */ 118 | enum eio_dtype 119 | { 120 | EIO_DT_UNKNOWN = 0, 121 | EIO_DT_FIFO = 1, 122 | EIO_DT_CHR = 2, 123 | EIO_DT_MPC = 3, /* multiplexed char device (v7+coherent) */ 124 | EIO_DT_DIR = 4, 125 | EIO_DT_NAM = 5, /* xenix special named file */ 126 | EIO_DT_BLK = 6, 127 | EIO_DT_MPB = 7, /* multiplexed block device (v7+coherent) */ 128 | EIO_DT_REG = 8, 129 | EIO_DT_NWK = 9, /* HP-UX network special */ 130 | EIO_DT_CMP = 9, /* VxFS compressed */ 131 | EIO_DT_LNK = 10, 132 | /* DT_SHAD = 11,*/ 133 | EIO_DT_SOCK = 12, 134 | EIO_DT_DOOR = 13, /* solaris door */ 135 | EIO_DT_WHT = 14, 136 | EIO_DT_MAX = 15 /* highest DT_VALUE ever, hopefully */ 137 | }; 138 | 139 | struct eio_dirent 140 | { 141 | int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ 142 | unsigned short namelen; /* size of filename without trailing 0 */ 143 | unsigned char type; /* one of EIO_DT_* */ 144 | signed char score; /* internal use */ 145 | eio_ino_t inode; /* the inode number, if available, otherwise unspecified */ 146 | }; 147 | 148 | /* eio_msync flags */ 149 | enum 150 | { 151 | EIO_MS_ASYNC = 1, 152 | EIO_MS_INVALIDATE = 2, 153 | EIO_MS_SYNC = 4 154 | }; 155 | 156 | /* eio_mtouch flags */ 157 | enum 158 | { 159 | EIO_MT_MODIFY = 1 160 | }; 161 | 162 | /* eio_sync_file_range flags */ 163 | enum 164 | { 165 | EIO_SYNC_FILE_RANGE_WAIT_BEFORE = 1, 166 | EIO_SYNC_FILE_RANGE_WRITE = 2, 167 | EIO_SYNC_FILE_RANGE_WAIT_AFTER = 4 168 | }; 169 | 170 | /* eio_fallocate flags */ 171 | enum 172 | { 173 | EIO_FALLOC_FL_KEEP_SIZE = 1 /* MUST match the value in linux/falloc.h */ 174 | }; 175 | 176 | /* timestamps and differences - feel free to use double in your code directly */ 177 | typedef double eio_tstamp; 178 | 179 | /* the eio request structure */ 180 | enum 181 | { 182 | EIO_CUSTOM, 183 | EIO_WD_OPEN, EIO_WD_CLOSE, 184 | 185 | EIO_CLOSE, EIO_DUP2, 186 | EIO_READ, EIO_WRITE, 187 | EIO_READAHEAD, EIO_SENDFILE, 188 | EIO_FSTAT, EIO_FSTATVFS, 189 | EIO_FTRUNCATE, EIO_FUTIME, EIO_FCHMOD, EIO_FCHOWN, 190 | EIO_SYNC, EIO_FSYNC, EIO_FDATASYNC, EIO_SYNCFS, 191 | EIO_MSYNC, EIO_MTOUCH, EIO_SYNC_FILE_RANGE, EIO_FALLOCATE, 192 | EIO_MLOCK, EIO_MLOCKALL, 193 | EIO_GROUP, EIO_NOP, 194 | EIO_BUSY, 195 | 196 | /* these use wd + ptr1, but are emulated */ 197 | EIO_REALPATH, 198 | EIO_STATVFS, 199 | EIO_READDIR, 200 | 201 | /* all the following requests use wd + ptr1 as path in xxxat functions */ 202 | EIO_OPEN, 203 | EIO_STAT, EIO_LSTAT, 204 | EIO_TRUNCATE, 205 | EIO_UTIME, 206 | EIO_CHMOD, 207 | EIO_CHOWN, 208 | EIO_UNLINK, EIO_RMDIR, EIO_MKDIR, EIO_RENAME, 209 | EIO_MKNOD, 210 | EIO_LINK, EIO_SYMLINK, EIO_READLINK, 211 | 212 | EIO_REQ_TYPE_NUM 213 | }; 214 | 215 | /* mlockall constants */ 216 | enum 217 | { 218 | EIO_MCL_CURRENT = 1, 219 | EIO_MCL_FUTURE = 2, 220 | }; 221 | 222 | /* request priorities */ 223 | 224 | enum { 225 | EIO_PRI_MIN = -4, 226 | EIO_PRI_MAX = 4, 227 | EIO_PRI_DEFAULT = 0, 228 | }; 229 | 230 | /* eio request structure */ 231 | /* this structure is mostly read-only */ 232 | /* when initialising it, all members must be zero-initialised */ 233 | struct eio_req 234 | { 235 | eio_req volatile *next; /* private ETP */ 236 | 237 | eio_wd wd; /* all applicable requests: working directory of pathname, old name; wd_open: return wd */ 238 | 239 | eio_ssize_t result; /* result of syscall, e.g. result = read (... */ 240 | off_t offs; /* read, write, truncate, readahead, sync_file_range, fallocate: file offset, mknod: dev_t */ 241 | size_t size; /* read, write, readahead, sendfile, msync, mlock, sync_file_range, fallocate: length */ 242 | void *ptr1; /* all applicable requests: pathname, old name; readdir: optional eio_dirents */ 243 | void *ptr2; /* all applicable requests: new name or memory buffer; readdir: name strings */ 244 | eio_tstamp nv1; /* utime, futime: atime; busy: sleep time */ 245 | eio_tstamp nv2; /* utime, futime: mtime */ 246 | 247 | int type; /* EIO_xxx constant ETP */ 248 | int int1; /* all applicable requests: file descriptor; sendfile: output fd; open, msync, mlockall, readdir: flags */ 249 | long int2; /* chown, fchown: uid; sendfile: input fd; open, chmod, mkdir, mknod: file mode, sync_file_range, fallocate: flags */ 250 | long int3; /* chown, fchown: gid; rename, link: working directory of new name */ 251 | int errorno; /* errno value on syscall return */ 252 | 253 | #if __i386 || __amd64 254 | unsigned char cancelled; 255 | #else 256 | sig_atomic_t cancelled; 257 | #endif 258 | 259 | unsigned char flags; /* private */ 260 | signed char pri; /* the priority */ 261 | 262 | void *data; 263 | eio_cb finish; 264 | void (*destroy)(eio_req *req); /* called when request no longer needed */ 265 | void (*feed)(eio_req *req); /* only used for group requests */ 266 | 267 | EIO_REQ_MEMBERS 268 | 269 | eio_req *grp, *grp_prev, *grp_next, *grp_first; /* private */ 270 | }; 271 | 272 | /* _private_ request flags */ 273 | enum { 274 | EIO_FLAG_PTR1_FREE = 0x01, /* need to free(ptr1) */ 275 | EIO_FLAG_PTR2_FREE = 0x02, /* need to free(ptr2) */ 276 | EIO_FLAG_GROUPADD = 0x04 /* some request was added to the group */ 277 | }; 278 | 279 | /* undocumented/unsupported/private helper */ 280 | /*void eio_page_align (void **addr, size_t *length);*/ 281 | 282 | /* returns < 0 on error, errno set 283 | * need_poll, if non-zero, will be called when results are available 284 | * and eio_poll_cb needs to be invoked (it MUST NOT call eio_poll_cb itself). 285 | * done_poll is called when the need to poll is gone. 286 | */ 287 | int eio_init (void (*want_poll)(void), void (*done_poll)(void)); 288 | 289 | /* must be called regularly to handle pending requests */ 290 | /* returns 0 if all requests were handled, -1 if not, or the value of EIO_FINISH if != 0 */ 291 | int eio_poll (void); 292 | 293 | /* stop polling if poll took longer than duration seconds */ 294 | void eio_set_max_poll_time (eio_tstamp nseconds); 295 | /* do not handle more then count requests in one call to eio_poll_cb */ 296 | void eio_set_max_poll_reqs (unsigned int nreqs); 297 | 298 | /* set minimum required number 299 | * maximum wanted number 300 | * or maximum idle number of threads */ 301 | void eio_set_min_parallel (unsigned int nthreads); 302 | void eio_set_max_parallel (unsigned int nthreads); 303 | void eio_set_max_idle (unsigned int nthreads); 304 | void eio_set_idle_timeout (unsigned int seconds); 305 | 306 | unsigned int eio_nreqs (void); /* number of requests in-flight */ 307 | unsigned int eio_nready (void); /* number of not-yet handled requests */ 308 | unsigned int eio_npending (void); /* number of finished but unhandled requests */ 309 | unsigned int eio_nthreads (void); /* number of worker threads in use currently */ 310 | 311 | /*****************************************************************************/ 312 | /* convenience wrappers */ 313 | 314 | #ifndef EIO_NO_WRAPPERS 315 | eio_req *eio_wd_open (const char *path, int pri, eio_cb cb, void *data); /* result=wd */ 316 | eio_req *eio_wd_close (eio_wd wd, int pri, eio_cb cb, void *data); 317 | eio_req *eio_nop (int pri, eio_cb cb, void *data); /* does nothing except go through the whole process */ 318 | eio_req *eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data); /* ties a thread for this long, simulating busyness */ 319 | eio_req *eio_sync (int pri, eio_cb cb, void *data); 320 | eio_req *eio_fsync (int fd, int pri, eio_cb cb, void *data); 321 | eio_req *eio_fdatasync (int fd, int pri, eio_cb cb, void *data); 322 | eio_req *eio_syncfs (int fd, int pri, eio_cb cb, void *data); 323 | eio_req *eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); 324 | eio_req *eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data); 325 | eio_req *eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data); 326 | eio_req *eio_mlockall (int flags, int pri, eio_cb cb, void *data); 327 | eio_req *eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data); 328 | eio_req *eio_fallocate (int fd, int mode, off_t offset, size_t len, int pri, eio_cb cb, void *data); 329 | eio_req *eio_close (int fd, int pri, eio_cb cb, void *data); 330 | eio_req *eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data); 331 | eio_req *eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); 332 | eio_req *eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data); 333 | eio_req *eio_fstat (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ 334 | eio_req *eio_fstatvfs (int fd, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ 335 | eio_req *eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); 336 | eio_req *eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data); 337 | eio_req *eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data); 338 | eio_req *eio_fchown (int fd, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); 339 | eio_req *eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data); 340 | eio_req *eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data); 341 | eio_req *eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data); 342 | eio_req *eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data); 343 | eio_req *eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data); 344 | eio_req *eio_chown (const char *path, eio_uid_t uid, eio_gid_t gid, int pri, eio_cb cb, void *data); 345 | eio_req *eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data); 346 | eio_req *eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data); 347 | eio_req *eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ 348 | eio_req *eio_rmdir (const char *path, int pri, eio_cb cb, void *data); 349 | eio_req *eio_unlink (const char *path, int pri, eio_cb cb, void *data); 350 | eio_req *eio_readlink (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ 351 | eio_req *eio_realpath (const char *path, int pri, eio_cb cb, void *data); /* result=ptr2 allocated dynamically */ 352 | eio_req *eio_stat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ 353 | eio_req *eio_lstat (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ 354 | eio_req *eio_statvfs (const char *path, int pri, eio_cb cb, void *data); /* stat buffer=ptr2 allocated dynamically */ 355 | eio_req *eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data); 356 | eio_req *eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data); 357 | eio_req *eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data); 358 | eio_req *eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data); 359 | eio_req *eio_custom (void (*execute)(eio_req *), int pri, eio_cb cb, void *data); 360 | #endif 361 | 362 | /*****************************************************************************/ 363 | /* groups */ 364 | 365 | eio_req *eio_grp (eio_cb cb, void *data); 366 | void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit); 367 | void eio_grp_limit (eio_req *grp, int limit); 368 | void eio_grp_add (eio_req *grp, eio_req *req); 369 | void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the group */ 370 | 371 | /*****************************************************************************/ 372 | /* request api */ 373 | 374 | /* true if the request was cancelled, useful in the invoke callback */ 375 | #define EIO_CANCELLED(req) ((req)->cancelled) 376 | 377 | #define EIO_RESULT(req) ((req)->result) 378 | /* returns a pointer to the result buffer allocated by eio */ 379 | #define EIO_BUF(req) ((req)->ptr2) 380 | #define EIO_STAT_BUF(req) ((EIO_STRUCT_STAT *)EIO_BUF(req)) 381 | #define EIO_STATVFS_BUF(req) ((EIO_STRUCT_STATVFS *)EIO_BUF(req)) 382 | #define EIO_PATH(req) ((char *)(req)->ptr1) 383 | 384 | /* submit a request for execution */ 385 | void eio_submit (eio_req *req); 386 | /* cancel a request as soon fast as possible, if possible */ 387 | void eio_cancel (eio_req *req); 388 | 389 | /*****************************************************************************/ 390 | /* convenience functions */ 391 | 392 | eio_ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count); 393 | 394 | #ifdef __cplusplus 395 | } 396 | #endif 397 | 398 | #endif 399 | 400 | -------------------------------------------------------------------------------- /libeio/eio.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | libeio - truly asynchronous POSIX I/O 4 | 5 | =head1 SYNOPSIS 6 | 7 | #include 8 | 9 | =head1 DESCRIPTION 10 | 11 | The newest version of this document is also available as an html-formatted 12 | web page you might find easier to navigate when reading it for the first 13 | time: L. 14 | 15 | Note that this library is a by-product of the C perl 16 | module, and many of the subtler points regarding requests lifetime 17 | and so on are only documented in its documentation at the 18 | moment: L. 19 | 20 | =head2 FEATURES 21 | 22 | This library provides fully asynchronous versions of most POSIX functions 23 | dealing with I/O. Unlike most asynchronous libraries, this not only 24 | includes C and C, but also C, C, C and 25 | similar functions, as well as less rarely ones such as C, C 26 | or C. 27 | 28 | It also offers wrappers around C (Solaris, Linux, HP-UX and 29 | FreeBSD, with emulation on other platforms) and C (Linux, with 30 | emulation elsewhere>). 31 | 32 | The goal is to enable you to write fully non-blocking programs. For 33 | example, in a game server, you would not want to freeze for a few seconds 34 | just because the server is running a backup and you happen to call 35 | C. 36 | 37 | =head2 TIME REPRESENTATION 38 | 39 | Libeio represents time as a single floating point number, representing the 40 | (fractional) number of seconds since the (POSIX) epoch (somewhere near 41 | the beginning of 1970, details are complicated, don't ask). This type is 42 | called C, but it is guaranteed to be of type C (or 43 | better), so you can freely use C yourself. 44 | 45 | Unlike the name component C might indicate, it is also used for 46 | time differences throughout libeio. 47 | 48 | =head2 FORK SUPPORT 49 | 50 | Usage of pthreads in a program changes the semantics of fork 51 | considerably. Specifically, only async-safe functions can be called after 52 | fork. Libeio uses pthreads, so this applies, and makes using fork hard for 53 | anything but relatively fork + exec uses. 54 | 55 | This library only works in the process that initialised it: Forking is 56 | fully supported, but using libeio in any other process than the one that 57 | called C is not. 58 | 59 | You might get around by not I libeio before (or after) forking in 60 | the parent, and using it in the child afterwards. You could also try to 61 | call the L function again in the child, which will brutally 62 | reinitialise all data structures, which isn't POSIX conformant, but 63 | typically works. 64 | 65 | Otherwise, the only recommendation you should follow is: treat fork code 66 | the same way you treat signal handlers, and only ever call C in 67 | the process that uses it, and only once ever. 68 | 69 | =head1 INITIALISATION/INTEGRATION 70 | 71 | Before you can call any eio functions you first have to initialise the 72 | library. The library integrates into any event loop, but can also be used 73 | without one, including in polling mode. 74 | 75 | You have to provide the necessary glue yourself, however. 76 | 77 | =over 4 78 | 79 | =item int eio_init (void (*want_poll)(void), void (*done_poll)(void)) 80 | 81 | This function initialises the library. On success it returns C<0>, on 82 | failure it returns C<-1> and sets C appropriately. 83 | 84 | It accepts two function pointers specifying callbacks as argument, both of 85 | which can be C<0>, in which case the callback isn't called. 86 | 87 | There is currently no way to change these callbacks later, or to 88 | "uninitialise" the library again. 89 | 90 | =item want_poll callback 91 | 92 | The C callback is invoked whenever libeio wants attention (i.e. 93 | it wants to be polled by calling C). It is "edge-triggered", 94 | that is, it will only be called once when eio wants attention, until all 95 | pending requests have been handled. 96 | 97 | This callback is called while locks are being held, so I. That includes 99 | C. What you should do is notify some other thread, or wake up 100 | your event loop, and then call C. 101 | 102 | =item done_poll callback 103 | 104 | This callback is invoked when libeio detects that all pending requests 105 | have been handled. It is "edge-triggered", that is, it will only be 106 | called once after C. To put it differently, C and 107 | C are invoked in pairs: after C you have to call 108 | C until either C indicates that everything has been 109 | handled or C has been called, which signals the same. 110 | 111 | Note that C might return after C and C 112 | have been called again, so watch out for races in your code. 113 | 114 | As with C, this callback is called while locks are being held, 115 | so you I. 116 | 117 | =item int eio_poll () 118 | 119 | This function has to be called whenever there are pending requests that 120 | need finishing. You usually call this after C has indicated 121 | that you should do so, but you can also call this function regularly to 122 | poll for new results. 123 | 124 | If any request invocation returns a non-zero value, then C 125 | immediately returns with that value as return value. 126 | 127 | Otherwise, if all requests could be handled, it returns C<0>. If for some 128 | reason not all requests have been handled, i.e. some are still pending, it 129 | returns C<-1>. 130 | 131 | =back 132 | 133 | For libev, you would typically use an C watcher: the 134 | C callback would invoke C to wake up the event 135 | loop. Inside the callback set for the watcher, one would call C. 137 | 138 | If C is configured to not handle all results in one go 139 | (i.e. it returns C<-1>) then you should start an idle watcher that calls 140 | C until it returns something C. 141 | 142 | A full-featured connector between libeio and libev would look as follows 143 | (if C is handling all requests, it can of course be simplified a 144 | lot by removing the idle watcher logic): 145 | 146 | static struct ev_loop *loop; 147 | static ev_idle repeat_watcher; 148 | static ev_async ready_watcher; 149 | 150 | /* idle watcher callback, only used when eio_poll */ 151 | /* didn't handle all results in one call */ 152 | static void 153 | repeat (EV_P_ ev_idle *w, int revents) 154 | { 155 | if (eio_poll () != -1) 156 | ev_idle_stop (EV_A_ w); 157 | } 158 | 159 | /* eio has some results, process them */ 160 | static void 161 | ready (EV_P_ ev_async *w, int revents) 162 | { 163 | if (eio_poll () == -1) 164 | ev_idle_start (EV_A_ &repeat_watcher); 165 | } 166 | 167 | /* wake up the event loop */ 168 | static void 169 | want_poll (void) 170 | { 171 | ev_async_send (loop, &ready_watcher) 172 | } 173 | 174 | void 175 | my_init_eio () 176 | { 177 | loop = EV_DEFAULT; 178 | 179 | ev_idle_init (&repeat_watcher, repeat); 180 | ev_async_init (&ready_watcher, ready); 181 | ev_async_start (loop &watcher); 182 | 183 | eio_init (want_poll, 0); 184 | } 185 | 186 | For most other event loops, you would typically use a pipe - the event 187 | loop should be told to wait for read readiness on the read end. In 188 | C you would write a single byte, in C you would try 189 | to read that byte, and in the callback for the read end, you would call 190 | C. 191 | 192 | You don't have to take special care in the case C doesn't handle 193 | all requests, as the done callback will not be invoked, so the event loop 194 | will still signal readiness for the pipe until I results have been 195 | processed. 196 | 197 | 198 | =head1 HIGH LEVEL REQUEST API 199 | 200 | Libeio has both a high-level API, which consists of calling a request 201 | function with a callback to be called on completion, and a low-level API 202 | where you fill out request structures and submit them. 203 | 204 | This section describes the high-level API. 205 | 206 | =head2 REQUEST SUBMISSION AND RESULT PROCESSING 207 | 208 | You submit a request by calling the relevant C function with the 209 | required parameters, a callback of type C 210 | (called C below) and a freely usable C argument. 211 | 212 | The return value will either be 0, in case something went really wrong 213 | (which can basically only happen on very fatal errors, such as C 214 | returning 0, which is rather unlikely), or a pointer to the newly-created 215 | and submitted C. 216 | 217 | The callback will be called with an C which contains the 218 | results of the request. The members you can access inside that structure 219 | vary from request to request, except for: 220 | 221 | =over 4 222 | 223 | =item C 224 | 225 | This contains the result value from the call (usually the same as the 226 | syscall of the same name). 227 | 228 | =item C 229 | 230 | This contains the value of C after the call. 231 | 232 | =item C 233 | 234 | The C member simply stores the value of the C argument. 235 | 236 | =back 237 | 238 | Members not explicitly described as accessible must not be 239 | accessed. Specifically, there is no guarantee that any members will still 240 | have the value they had when the request was submitted. 241 | 242 | The return value of the callback is normally C<0>, which tells libeio to 243 | continue normally. If a callback returns a nonzero value, libeio will 244 | stop processing results (in C) and will return the value to its 245 | caller. 246 | 247 | Memory areas passed to libeio wrappers must stay valid as long as a 248 | request executes, with the exception of paths, which are being copied 249 | internally. Any memory libeio itself allocates will be freed after the 250 | finish callback has been called. If you want to manage all memory passed 251 | to libeio yourself you can use the low-level API. 252 | 253 | For example, to open a file, you could do this: 254 | 255 | static int 256 | file_open_done (eio_req *req) 257 | { 258 | if (req->result < 0) 259 | { 260 | /* open() returned -1 */ 261 | errno = req->errorno; 262 | perror ("open"); 263 | } 264 | else 265 | { 266 | int fd = req->result; 267 | /* now we have the new fd in fd */ 268 | } 269 | 270 | return 0; 271 | } 272 | 273 | /* the first three arguments are passed to open(2) */ 274 | /* the remaining are priority, callback and data */ 275 | if (!eio_open ("/etc/passwd", O_RDONLY, 0, 0, file_open_done, 0)) 276 | abort (); /* something went wrong, we will all die!!! */ 277 | 278 | Note that you additionally need to call C when the C 279 | indicates that requests are ready to be processed. 280 | 281 | =head2 CANCELLING REQUESTS 282 | 283 | Sometimes the need for a request goes away before the request is 284 | finished. In that case, one can cancel the request by a call to 285 | C: 286 | 287 | =over 4 288 | 289 | =item eio_cancel (eio_req *req) 290 | 291 | Cancel the request (and all its subrequests). If the request is currently 292 | executing it might still continue to execute, and in other cases it might 293 | still take a while till the request is cancelled. 294 | 295 | Even if cancelled, the finish callback will still be invoked - the 296 | callbacks of all cancellable requests need to check whether the request 297 | has been cancelled by calling C: 298 | 299 | static int 300 | my_eio_cb (eio_req *req) 301 | { 302 | if (EIO_CANCELLED (req)) 303 | return 0; 304 | } 305 | 306 | In addition, cancelled requests will I have C<< req->result >> 307 | set to C<-1> and C to C, or I they were 308 | successfully executed, despite being cancelled (e.g. when they have 309 | already been executed at the time they were cancelled). 310 | 311 | C is still true for requests that have successfully 312 | executed, as long as C was called on them at some point. 313 | 314 | =back 315 | 316 | =head2 AVAILABLE REQUESTS 317 | 318 | The following request functions are available. I of them return the 319 | C on success and C<0> on failure, and I of them have the 320 | same three trailing arguments: C, C and C. The C is 321 | mandatory, but in most cases, you pass in C<0> as C and C<0> or some 322 | custom data value as C. 323 | 324 | =head3 POSIX API WRAPPERS 325 | 326 | These requests simply wrap the POSIX call of the same name, with the same 327 | arguments. If a function is not implemented by the OS and cannot be emulated 328 | in some way, then all of these return C<-1> and set C to C. 329 | 330 | =over 4 331 | 332 | =item eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) 333 | 334 | =item eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) 335 | 336 | =item eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) 337 | 338 | =item eio_chmod (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 339 | 340 | =item eio_mkdir (const char *path, mode_t mode, int pri, eio_cb cb, void *data) 341 | 342 | =item eio_rmdir (const char *path, int pri, eio_cb cb, void *data) 343 | 344 | =item eio_unlink (const char *path, int pri, eio_cb cb, void *data) 345 | 346 | =item eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 347 | 348 | =item eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) 349 | 350 | =item eio_link (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 351 | 352 | =item eio_symlink (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 353 | 354 | =item eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) 355 | 356 | =item eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) 357 | 358 | =item eio_close (int fd, int pri, eio_cb cb, void *data) 359 | 360 | =item eio_sync (int pri, eio_cb cb, void *data) 361 | 362 | =item eio_fsync (int fd, int pri, eio_cb cb, void *data) 363 | 364 | =item eio_fdatasync (int fd, int pri, eio_cb cb, void *data) 365 | 366 | =item eio_futime (int fd, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) 367 | 368 | =item eio_ftruncate (int fd, off_t offset, int pri, eio_cb cb, void *data) 369 | 370 | =item eio_fchmod (int fd, mode_t mode, int pri, eio_cb cb, void *data) 371 | 372 | =item eio_fchown (int fd, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) 373 | 374 | =item eio_dup2 (int fd, int fd2, int pri, eio_cb cb, void *data) 375 | 376 | These have the same semantics as the syscall of the same name, their 377 | return value is available as C<< req->result >> later. 378 | 379 | =item eio_read (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 380 | 381 | =item eio_write (int fd, void *buf, size_t length, off_t offset, int pri, eio_cb cb, void *data) 382 | 383 | These two requests are called C and C, but actually wrap 384 | C and C. On systems that lack these calls (such as cygwin), 385 | libeio uses lseek/read_or_write/lseek and a mutex to serialise the 386 | requests, so all these requests run serially and do not disturb each 387 | other. However, they still disturb the file offset while they run, so it's 388 | not safe to call these functions concurrently with non-libeio functions on 389 | the same fd on these systems. 390 | 391 | Not surprisingly, pread and pwrite are not thread-safe on Darwin (OS/X), 392 | so it is advised not to submit multiple requests on the same fd on this 393 | horrible pile of garbage. 394 | 395 | =item eio_mlockall (int flags, int pri, eio_cb cb, void *data) 396 | 397 | Like C, but the flag value constants are called 398 | C and C. 399 | 400 | =item eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 401 | 402 | Just like msync, except that the flag values are called C, 403 | C and C. 404 | 405 | =item eio_readlink (const char *path, int pri, eio_cb cb, void *data) 406 | 407 | If successful, the path read by C can be accessed via C<< 408 | req->ptr2 >> and is I null-terminated, with the length specified as 409 | C<< req->result >>. 410 | 411 | if (req->result >= 0) 412 | { 413 | char *target = strndup ((char *)req->ptr2, req->result); 414 | 415 | free (target); 416 | } 417 | 418 | =item eio_realpath (const char *path, int pri, eio_cb cb, void *data) 419 | 420 | Similar to the realpath libc function, but unlike that one, C<< 421 | req->result >> is C<-1> on failure. On success, the result is the length 422 | of the returned path in C (which is I 0-terminated) - this is 423 | similar to readlink. 424 | 425 | =item eio_stat (const char *path, int pri, eio_cb cb, void *data) 426 | 427 | =item eio_lstat (const char *path, int pri, eio_cb cb, void *data) 428 | 429 | =item eio_fstat (int fd, int pri, eio_cb cb, void *data) 430 | 431 | Stats a file - if C<< req->result >> indicates success, then you can 432 | access the C-like structure via C<< req->ptr2 >>: 433 | 434 | EIO_STRUCT_STAT *statdata = (EIO_STRUCT_STAT *)req->ptr2; 435 | 436 | =item eio_statvfs (const char *path, int pri, eio_cb cb, void *data) 437 | 438 | =item eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) 439 | 440 | Stats a filesystem - if C<< req->result >> indicates success, then you can 441 | access the C-like structure via C<< req->ptr2 >>: 442 | 443 | EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; 444 | 445 | =back 446 | 447 | =head3 READING DIRECTORIES 448 | 449 | Reading directories sounds simple, but can be rather demanding, especially 450 | if you want to do stuff such as traversing a directory hierarchy or 451 | processing all files in a directory. Libeio can assist these complex tasks 452 | with it's C call. 453 | 454 | =over 4 455 | 456 | =item eio_readdir (const char *path, int flags, int pri, eio_cb cb, void *data) 457 | 458 | This is a very complex call. It basically reads through a whole directory 459 | (via the C, C and C calls) and returns either 460 | the names or an array of C, depending on the C 461 | argument. 462 | 463 | The C<< req->result >> indicates either the number of files found, or 464 | C<-1> on error. On success, null-terminated names can be found as C<< req->ptr2 >>, 465 | and C, if requested by C, can be found via C<< 466 | req->ptr1 >>. 467 | 468 | Here is an example that prints all the names: 469 | 470 | int i; 471 | char *names = (char *)req->ptr2; 472 | 473 | for (i = 0; i < req->result; ++i) 474 | { 475 | printf ("name #%d: %s\n", i, names); 476 | 477 | /* move to next name */ 478 | names += strlen (names) + 1; 479 | } 480 | 481 | Pseudo-entries such as F<.> and F<..> are never returned by C. 482 | 483 | C can be any combination of: 484 | 485 | =over 4 486 | 487 | =item EIO_READDIR_DENTS 488 | 489 | If this flag is specified, then, in addition to the names in C, 490 | also an array of C is returned, in C. A C looks like this: 492 | 493 | struct eio_dirent 494 | { 495 | int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ 496 | unsigned short namelen; /* size of filename without trailing 0 */ 497 | unsigned char type; /* one of EIO_DT_* */ 498 | signed char score; /* internal use */ 499 | ino_t inode; /* the inode number, if available, otherwise unspecified */ 500 | }; 501 | 502 | The only members you normally would access are C, which is the 503 | byte-offset from C to the start of the name, C and C. 504 | 505 | C can be one of: 506 | 507 | C - if the type is not known (very common) and you have to C 508 | the name yourself if you need to know, 509 | one of the "standard" POSIX file types (C, C, C, 510 | C, C, C, C) 511 | or some OS-specific type (currently 512 | C - multiplexed char device (v7+coherent), 513 | C - xenix special named file, 514 | C - multiplexed block device (v7+coherent), 515 | C - HP-UX network special, 516 | C - VxFS compressed, 517 | C - solaris door, or 518 | C). 519 | 520 | This example prints all names and their type: 521 | 522 | int i; 523 | struct eio_dirent *ents = (struct eio_dirent *)req->ptr1; 524 | char *names = (char *)req->ptr2; 525 | 526 | for (i = 0; i < req->result; ++i) 527 | { 528 | struct eio_dirent *ent = ents + i; 529 | char *name = names + ent->nameofs; 530 | 531 | printf ("name #%d: %s (type %d)\n", i, name, ent->type); 532 | } 533 | 534 | =item EIO_READDIR_DIRS_FIRST 535 | 536 | When this flag is specified, then the names will be returned in an order 537 | where likely directories come first, in optimal C order. This is 538 | useful when you need to quickly find directories, or you want to find all 539 | directories while avoiding to stat() each entry. 540 | 541 | If the system returns type information in readdir, then this is used 542 | to find directories directly. Otherwise, likely directories are names 543 | beginning with ".", or otherwise names with no dots, of which names with 544 | short names are tried first. 545 | 546 | =item EIO_READDIR_STAT_ORDER 547 | 548 | When this flag is specified, then the names will be returned in an order 549 | suitable for stat()'ing each one. That is, when you plan to stat() 550 | all files in the given directory, then the returned order will likely 551 | be fastest. 552 | 553 | If both this flag and C are specified, then the 554 | likely directories come first, resulting in a less optimal stat order. 555 | 556 | =item EIO_READDIR_FOUND_UNKNOWN 557 | 558 | This flag should not be specified when calling C. Instead, 559 | it is being set by C (you can access the C via C<< 560 | req->int1 >>, when any of the C's found were C. The 561 | absence of this flag therefore indicates that all C's are known, 562 | which can be used to speed up some algorithms. 563 | 564 | A typical use case would be to identify all subdirectories within a 565 | directory - you would ask C for C. If 566 | then this flag is I set, then all the entries at the beginning of the 567 | returned array of type C are the directories. Otherwise, you 568 | should start C'ing the entries starting at the beginning of the 569 | array, stopping as soon as you found all directories (the count can be 570 | deduced by the link count of the directory). 571 | 572 | =back 573 | 574 | =back 575 | 576 | =head3 OS-SPECIFIC CALL WRAPPERS 577 | 578 | These wrap OS-specific calls (usually Linux ones), and might or might not 579 | be emulated on other operating systems. Calls that are not emulated will 580 | return C<-1> and set C to C. 581 | 582 | =over 4 583 | 584 | =item eio_sendfile (int out_fd, int in_fd, off_t in_offset, size_t length, int pri, eio_cb cb, void *data) 585 | 586 | Wraps the C syscall. The arguments follow the Linux version, but 587 | libeio supports and will use similar calls on FreeBSD, HP/UX, Solaris and 588 | Darwin. 589 | 590 | If the OS doesn't support some sendfile-like call, or the call fails, 591 | indicating support for the given file descriptor type (for example, 592 | Linux's sendfile might not support file to file copies), then libeio will 593 | emulate the call in userspace, so there are almost no limitations on its 594 | use. 595 | 596 | =item eio_readahead (int fd, off_t offset, size_t length, int pri, eio_cb cb, void *data) 597 | 598 | Calls C. If the syscall is missing, then the call is 599 | emulated by simply reading the data (currently in 64kiB chunks). 600 | 601 | =item eio_syncfs (int fd, int pri, eio_cb cb, void *data) 602 | 603 | Calls Linux' C syscall, if available. Returns C<-1> and sets 604 | C to C if the call is missing I, 605 | if the C is C<< >= 0 >>, so you can probe for the availability of the 606 | syscall with a negative C argument and checking for C<-1/ENOSYS>. 607 | 608 | =item eio_sync_file_range (int fd, off_t offset, size_t nbytes, unsigned int flags, int pri, eio_cb cb, void *data) 609 | 610 | Calls C. If the syscall is missing, then this is the same 611 | as calling C. 612 | 613 | Flags can be any combination of C, 614 | C and C. 615 | 616 | =item eio_fallocate (int fd, int mode, off_t offset, off_t len, int pri, eio_cb cb, void *data) 617 | 618 | Calls C (note: I C!). If the syscall is 619 | missing, then it returns failure and sets C to C. 620 | 621 | The C argument can be C<0> (for behaviour similar to 622 | C), or C, which keeps the size 623 | of the file unchanged (but still preallocates space beyond end of file). 624 | 625 | =back 626 | 627 | =head3 LIBEIO-SPECIFIC REQUESTS 628 | 629 | These requests are specific to libeio and do not correspond to any OS call. 630 | 631 | =over 4 632 | 633 | =item eio_mtouch (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) 634 | 635 | Reads (C) or modifies (C as parameter and is expected to read 648 | and modify any request-specific members. Specifically, it should set C<< 649 | req->result >> to the result value, just like other requests. 650 | 651 | Here is an example that simply calls C, like C, but it 652 | uses the C member as filename and uses a hardcoded C. If 653 | you want to pass more/other parameters, you either need to pass some 654 | struct or so via C or provide your own wrapper using the low-level 655 | API. 656 | 657 | static int 658 | my_open_done (eio_req *req) 659 | { 660 | int fd = req->result; 661 | 662 | return 0; 663 | } 664 | 665 | static void 666 | my_open (eio_req *req) 667 | { 668 | req->result = open (req->data, O_RDONLY); 669 | } 670 | 671 | eio_custom (my_open, 0, my_open_done, "/etc/passwd"); 672 | 673 | =item eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) 674 | 675 | This is a request that takes C seconds to execute, but otherwise 676 | does nothing - it simply puts one of the worker threads to sleep for this 677 | long. 678 | 679 | This request can be used to artificially increase load, e.g. for debugging 680 | or benchmarking reasons. 681 | 682 | =item eio_nop (int pri, eio_cb cb, void *data) 683 | 684 | This request does nothing, except go through the whole request cycle. This 685 | can be used to measure latency or in some cases to simplify code, but is 686 | not really of much use. 687 | 688 | =back 689 | 690 | =head3 GROUPING AND LIMITING REQUESTS 691 | 692 | There is one more rather special request, C. It is a very special 693 | aio request: Instead of doing something, it is a container for other eio 694 | requests. 695 | 696 | There are two primary use cases for this: a) bundle many requests into a 697 | single, composite, request with a definite callback and the ability to 698 | cancel the whole request with its subrequests and b) limiting the number 699 | of "active" requests. 700 | 701 | Further below you will find more discussion of these topics - first 702 | follows the reference section detailing the request generator and other 703 | methods. 704 | 705 | =over 4 706 | 707 | =item eio_req *grp = eio_grp (eio_cb cb, void *data) 708 | 709 | Creates, submits and returns a group request. Note that it doesn't have a 710 | priority, unlike all other requests. 711 | 712 | =item eio_grp_add (eio_req *grp, eio_req *req) 713 | 714 | Adds a request to the request group. 715 | 716 | =item eio_grp_cancel (eio_req *grp) 717 | 718 | Cancels all requests I the group, but I the group request 719 | itself. You can cancel the group request I all subrequests via a 720 | normal C call. 721 | 722 | =back 723 | 724 | =head4 GROUP REQUEST LIFETIME 725 | 726 | Left alone, a group request will instantly move to the pending state and 727 | will be finished at the next call of C. 728 | 729 | The usefulness stems from the fact that, if a subrequest is added to a 730 | group I a call to C, via C, then the group 731 | will not finish until all the subrequests have finished. 732 | 733 | So the usage cycle of a group request is like this: after it is created, 734 | you normally instantly add a subrequest. If none is added, the group 735 | request will finish on it's own. As long as subrequests are added before 736 | the group request is finished it will be kept from finishing, that is the 737 | callbacks of any subrequests can, in turn, add more requests to the group, 738 | and as long as any requests are active, the group request itself will not 739 | finish. 740 | 741 | =head4 CREATING COMPOSITE REQUESTS 742 | 743 | Imagine you wanted to create an C request that opens a file, 744 | reads it and closes it. This means it has to execute at least three eio 745 | requests, but for various reasons it might be nice if that request looked 746 | like any other eio request. 747 | 748 | This can be done with groups: 749 | 750 | =over 4 751 | 752 | =item 1) create the request object 753 | 754 | Create a group that contains all further requests. This is the request you 755 | can return as "the load request". 756 | 757 | =item 2) open the file, maybe 758 | 759 | Next, open the file with C and add the request to the group 760 | request and you are finished setting up the request. 761 | 762 | If, for some reason, you cannot C (path is a null ptr?) you 763 | can set C<< grp->result >> to C<-1> to signal an error and let the group 764 | request finish on its own. 765 | 766 | =item 3) open callback adds more requests 767 | 768 | In the open callback, if the open was not successful, copy C<< 769 | req->errorno >> to C<< grp->errorno >> and set C<< grp->errorno >> to 770 | C<-1> to signal an error. 771 | 772 | Otherwise, malloc some memory or so and issue a read request, adding the 773 | read request to the group. 774 | 775 | =item 4) continue issuing requests till finished 776 | 777 | In the real callback, check for errors and possibly continue with 778 | C or any other eio request in the same way. 779 | 780 | As soon as no new requests are added the group request will finish. Make 781 | sure you I set C<< grp->result >> to some sensible value. 782 | 783 | =back 784 | 785 | =head4 REQUEST LIMITING 786 | 787 | 788 | #TODO 789 | 790 | void eio_grp_limit (eio_req *grp, int limit); 791 | 792 | 793 | =back 794 | 795 | 796 | =head1 LOW LEVEL REQUEST API 797 | 798 | #TODO 799 | 800 | 801 | =head1 ANATOMY AND LIFETIME OF AN EIO REQUEST 802 | 803 | A request is represented by a structure of type C. To initialise 804 | it, clear it to all zero bytes: 805 | 806 | eio_req req; 807 | 808 | memset (&req, 0, sizeof (req)); 809 | 810 | A more common way to initialise a new C is to use C: 811 | 812 | eio_req *req = calloc (1, sizeof (*req)); 813 | 814 | In either case, libeio neither allocates, initialises or frees the 815 | C structure for you - it merely uses it. 816 | 817 | zero 818 | 819 | #TODO 820 | 821 | =head2 CONFIGURATION 822 | 823 | The functions in this section can sometimes be useful, but the default 824 | configuration will do in most case, so you should skip this section on 825 | first reading. 826 | 827 | =over 4 828 | 829 | =item eio_set_max_poll_time (eio_tstamp nseconds) 830 | 831 | This causes C to return after it has detected that it was 832 | running for C seconds or longer (this number can be fractional). 833 | 834 | This can be used to limit the amount of time spent handling eio requests, 835 | for example, in interactive programs, you might want to limit this time to 836 | C<0.01> seconds or so. 837 | 838 | Note that: 839 | 840 | =over 4 841 | 842 | =item a) libeio doesn't know how long your request callbacks take, so the 843 | time spent in C is up to one callback invocation longer then 844 | this interval. 845 | 846 | =item b) this is implemented by calling C after each 847 | request, which can be costly. 848 | 849 | =item c) at least one request will be handled. 850 | 851 | =back 852 | 853 | =item eio_set_max_poll_reqs (unsigned int nreqs) 854 | 855 | When C is non-zero, then C will not handle more than 856 | C requests per invocation. This is a less costly way to limit the 857 | amount of work done by C then setting a time limit. 858 | 859 | If you know your callbacks are generally fast, you could use this to 860 | encourage interactiveness in your programs by setting it to C<10>, C<100> 861 | or even C<1000>. 862 | 863 | =item eio_set_min_parallel (unsigned int nthreads) 864 | 865 | Make sure libeio can handle at least this many requests in parallel. It 866 | might be able handle more. 867 | 868 | =item eio_set_max_parallel (unsigned int nthreads) 869 | 870 | Set the maximum number of threads that libeio will spawn. 871 | 872 | =item eio_set_max_idle (unsigned int nthreads) 873 | 874 | Libeio uses threads internally to handle most requests, and will start and stop threads on demand. 875 | 876 | This call can be used to limit the number of idle threads (threads without 877 | work to do): libeio will keep some threads idle in preparation for more 878 | requests, but never longer than C threads. 879 | 880 | In addition to this, libeio will also stop threads when they are idle for 881 | a few seconds, regardless of this setting. 882 | 883 | =item unsigned int eio_nthreads () 884 | 885 | Return the number of worker threads currently running. 886 | 887 | =item unsigned int eio_nreqs () 888 | 889 | Return the number of requests currently handled by libeio. This is the 890 | total number of requests that have been submitted to libeio, but not yet 891 | destroyed. 892 | 893 | =item unsigned int eio_nready () 894 | 895 | Returns the number of ready requests, i.e. requests that have been 896 | submitted but have not yet entered the execution phase. 897 | 898 | =item unsigned int eio_npending () 899 | 900 | Returns the number of pending requests, i.e. requests that have been 901 | executed and have results, but have not been finished yet by a call to 902 | C). 903 | 904 | =back 905 | 906 | =head1 EMBEDDING 907 | 908 | Libeio can be embedded directly into programs. This functionality is not 909 | documented and not (yet) officially supported. 910 | 911 | Note that, when including C, you are responsible for defining 912 | the compilation environment (C<_LARGEFILE_SOURCE>, C<_GNU_SOURCE> etc.). 913 | 914 | If you need to know how, check the C perl module, which does 915 | exactly that. 916 | 917 | 918 | =head1 COMPILETIME CONFIGURATION 919 | 920 | These symbols, if used, must be defined when compiling F. 921 | 922 | =over 4 923 | 924 | =item EIO_STACKSIZE 925 | 926 | This symbol governs the stack size for each eio thread. Libeio itself 927 | was written to use very little stackspace, but when using C 928 | requests, you might want to increase this. 929 | 930 | If this symbol is undefined (the default) then libeio will use its default 931 | stack size (C currently). If it is defined, but 932 | C<0>, then the default operating system stack size will be used. In all 933 | other cases, the value must be an expression that evaluates to the desired 934 | stack size. 935 | 936 | =back 937 | 938 | 939 | =head1 PORTABILITY REQUIREMENTS 940 | 941 | In addition to a working ISO-C implementation, libeio relies on a few 942 | additional extensions: 943 | 944 | =over 4 945 | 946 | =item POSIX threads 947 | 948 | To be portable, this module uses threads, specifically, the POSIX threads 949 | library must be available (and working, which partially excludes many xBSD 950 | systems, where C is buggy). 951 | 952 | =item POSIX-compatible filesystem API 953 | 954 | This is actually a harder portability requirement: The libeio API is quite 955 | demanding regarding POSIX API calls (symlinks, user/group management 956 | etc.). 957 | 958 | =item C must hold a time value in seconds with enough accuracy 959 | 960 | The type C is used to represent timestamps. It is required to 961 | have at least 51 bits of mantissa (and 9 bits of exponent), which is good 962 | enough for at least into the year 4000. This requirement is fulfilled by 963 | implementations implementing IEEE 754 (basically all existing ones). 964 | 965 | =back 966 | 967 | If you know of other additional requirements drop me a note. 968 | 969 | 970 | =head1 AUTHOR 971 | 972 | Marc Lehmann . 973 | 974 | -------------------------------------------------------------------------------- /libeio/libeio.m4: -------------------------------------------------------------------------------- 1 | dnl openbsd in it's neverending brokenness requires stdint.h for intptr_t, 2 | dnl but that header isn't very portable... 3 | AC_CHECK_HEADERS([stdint.h sys/syscall.h sys/prctl.h]) 4 | 5 | AC_SEARCH_LIBS( 6 | pthread_create, 7 | [pthread pthreads pthreadVC2], 8 | , 9 | [AC_MSG_ERROR(pthread functions not found)] 10 | ) 11 | 12 | AC_CACHE_CHECK(for utimes, ac_cv_utimes, [AC_LINK_IFELSE([[ 13 | #include 14 | #include 15 | #include 16 | struct timeval tv[2]; 17 | int res; 18 | int main (void) 19 | { 20 | res = utimes ("/", tv); 21 | return 0; 22 | } 23 | ]],ac_cv_utimes=yes,ac_cv_utimes=no)]) 24 | test $ac_cv_utimes = yes && AC_DEFINE(HAVE_UTIMES, 1, utimes(2) is available) 25 | 26 | AC_CACHE_CHECK(for futimes, ac_cv_futimes, [AC_LINK_IFELSE([[ 27 | #include 28 | #include 29 | #include 30 | struct timeval tv[2]; 31 | int res; 32 | int fd; 33 | int main (void) 34 | { 35 | res = futimes (fd, tv); 36 | return 0; 37 | } 38 | ]],ac_cv_futimes=yes,ac_cv_futimes=no)]) 39 | test $ac_cv_futimes = yes && AC_DEFINE(HAVE_FUTIMES, 1, futimes(2) is available) 40 | 41 | AC_CACHE_CHECK(for readahead, ac_cv_readahead, [AC_LINK_IFELSE([ 42 | #include 43 | int main (void) 44 | { 45 | int fd = 0; 46 | size_t count = 2; 47 | ssize_t res; 48 | res = readahead (fd, 0, count); 49 | return 0; 50 | } 51 | ],ac_cv_readahead=yes,ac_cv_readahead=no)]) 52 | test $ac_cv_readahead = yes && AC_DEFINE(HAVE_READAHEAD, 1, readahead(2) is available (linux)) 53 | 54 | AC_CACHE_CHECK(for fdatasync, ac_cv_fdatasync, [AC_LINK_IFELSE([ 55 | #include 56 | int main (void) 57 | { 58 | int fd = 0; 59 | fdatasync (fd); 60 | return 0; 61 | } 62 | ],ac_cv_fdatasync=yes,ac_cv_fdatasync=no)]) 63 | test $ac_cv_fdatasync = yes && AC_DEFINE(HAVE_FDATASYNC, 1, fdatasync(2) is available) 64 | 65 | AC_CACHE_CHECK(for pread and pwrite, ac_cv_preadwrite, [AC_LINK_IFELSE([ 66 | #include 67 | int main (void) 68 | { 69 | int fd = 0; 70 | size_t count = 1; 71 | char buf; 72 | off_t offset = 1; 73 | ssize_t res; 74 | res = pread (fd, &buf, count, offset); 75 | res = pwrite (fd, &buf, count, offset); 76 | return 0; 77 | } 78 | ],ac_cv_preadwrite=yes,ac_cv_preadwrite=no)]) 79 | test $ac_cv_preadwrite = yes && AC_DEFINE(HAVE_PREADWRITE, 1, pread(2) and pwrite(2) are available) 80 | 81 | AC_CACHE_CHECK(for sendfile, ac_cv_sendfile, [AC_LINK_IFELSE([ 82 | # include 83 | #if __linux 84 | # include 85 | #elif __FreeBSD__ || defined __APPLE__ 86 | # include 87 | # include 88 | #elif __hpux 89 | # include 90 | #else 91 | # error unsupported architecture 92 | #endif 93 | int main (void) 94 | { 95 | int fd = 0; 96 | off_t offset = 1; 97 | size_t count = 2; 98 | ssize_t res; 99 | #if __linux 100 | res = sendfile (fd, fd, offset, count); 101 | #elif __FreeBSD__ 102 | res = sendfile (fd, fd, offset, count, 0, &offset, 0); 103 | #elif __hpux 104 | res = sendfile (fd, fd, offset, count, 0, 0); 105 | #endif 106 | return 0; 107 | } 108 | ],ac_cv_sendfile=yes,ac_cv_sendfile=no)]) 109 | test $ac_cv_sendfile = yes && AC_DEFINE(HAVE_SENDFILE, 1, sendfile(2) is available and supported) 110 | 111 | AC_CACHE_CHECK(for sync_file_range, ac_cv_sync_file_range, [AC_LINK_IFELSE([ 112 | #include 113 | int main (void) 114 | { 115 | int fd = 0; 116 | off64_t offset = 1; 117 | off64_t nbytes = 1; 118 | unsigned int flags = SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER; 119 | ssize_t res; 120 | res = sync_file_range (fd, offset, nbytes, flags); 121 | return 0; 122 | } 123 | ],ac_cv_sync_file_range=yes,ac_cv_sync_file_range=no)]) 124 | test $ac_cv_sync_file_range = yes && AC_DEFINE(HAVE_SYNC_FILE_RANGE, 1, sync_file_range(2) is available) 125 | 126 | AC_CACHE_CHECK(for fallocate, ac_cv_fallocate, [AC_LINK_IFELSE([ 127 | #include 128 | int main (void) 129 | { 130 | int fd = 0; 131 | int mode = 0; 132 | off_t offset = 1; 133 | off_t len = 1; 134 | int res; 135 | res = fallocate (fd, mode, offset, len); 136 | return 0; 137 | } 138 | ],ac_cv_fallocate=yes,ac_cv_fallocate=no)]) 139 | test $ac_cv_fallocate = yes && AC_DEFINE(HAVE_FALLOCATE, 1, fallocate(2) is available) 140 | 141 | AC_CACHE_CHECK(for sys_syncfs, ac_cv_sys_syncfs, [AC_LINK_IFELSE([ 142 | #include 143 | #include 144 | int main (void) 145 | { 146 | int res = syscall (__NR_syncfs, (int)0); 147 | } 148 | ],ac_cv_sys_syncfs=yes,ac_cv_sys_syncfs=no)]) 149 | test $ac_cv_sys_syncfs = yes && AC_DEFINE(HAVE_SYS_SYNCFS, 1, syscall(__NR_syncfs) is available) 150 | 151 | AC_CACHE_CHECK(for prctl_set_name, ac_cv_prctl_set_name, [AC_LINK_IFELSE([ 152 | #include 153 | int main (void) 154 | { 155 | char name[] = "test123"; 156 | int res = prctl (PR_SET_NAME, (unsigned long)name, 0, 0, 0); 157 | } 158 | ],ac_cv_prctl_set_name=yes,ac_cv_prctl_set_name=no)]) 159 | test $ac_cv_prctl_set_name = yes && AC_DEFINE(HAVE_PRCTL_SET_NAME, 1, prctl(PR_SET_NAME) is available) 160 | 161 | dnl ############################################################################# 162 | dnl # these checks exist for the benefit of IO::AIO 163 | 164 | dnl at least uclibc defines _POSIX_ADVISORY_INFO without *any* of the required 165 | dnl functionality actually being present. ugh. 166 | AC_CACHE_CHECK(for posix_madvise, ac_cv_posix_madvise, [AC_LINK_IFELSE([ 167 | #include 168 | int main (void) 169 | { 170 | int res = posix_madvise ((void *)0, (size_t)0, POSIX_MADV_NORMAL); 171 | int a = POSIX_MADV_SEQUENTIAL; 172 | int b = POSIX_MADV_RANDOM; 173 | int c = POSIX_MADV_WILLNEED; 174 | int d = POSIX_MADV_DONTNEED; 175 | return 0; 176 | } 177 | ],ac_cv_posix_madvise=yes,ac_cv_posix_madvise=no)]) 178 | test $ac_cv_posix_madvise = yes && AC_DEFINE(HAVE_POSIX_MADVISE, 1, posix_madvise(2) is available) 179 | 180 | AC_CACHE_CHECK(for posix_fadvise, ac_cv_posix_fadvise, [AC_LINK_IFELSE([ 181 | #define _XOPEN_SOURCE 600 182 | #include 183 | int main (void) 184 | { 185 | int res = posix_fadvise ((int)0, (off_t)0, (off_t)0, POSIX_FADV_NORMAL); 186 | int a = POSIX_FADV_SEQUENTIAL; 187 | int b = POSIX_FADV_NOREUSE; 188 | int c = POSIX_FADV_RANDOM; 189 | int d = POSIX_FADV_WILLNEED; 190 | int e = POSIX_FADV_DONTNEED; 191 | return 0; 192 | } 193 | ],ac_cv_posix_fadvise=yes,ac_cv_posix_fadvise=no)]) 194 | test $ac_cv_posix_fadvise = yes && AC_DEFINE(HAVE_POSIX_FADVISE, 1, posix_fadvise(2) is available) 195 | 196 | -------------------------------------------------------------------------------- /libeio/xthread.h: -------------------------------------------------------------------------------- 1 | #ifndef XTHREAD_H_ 2 | #define XTHREAD_H_ 3 | 4 | /* whether word reads are potentially non-atomic. 5 | * this is conservative, likely most arches this runs 6 | * on have atomic word read/writes. 7 | */ 8 | #ifndef WORDACCESS_UNSAFE 9 | # if __i386 || __x86_64 10 | # define WORDACCESS_UNSAFE 0 11 | # else 12 | # define WORDACCESS_UNSAFE 1 13 | # endif 14 | #endif 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | 18 | #ifdef _WIN32 19 | 20 | #define NTDDI_VERSION NTDDI_WIN2K // needed to get win2000 api calls 21 | #define _WIN32_WINNT 0x400 22 | #include //D 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define sigset_t int 31 | #define sigfillset(a) 32 | #define pthread_sigmask(a,b,c) 33 | #define sigaddset(a,b) 34 | #define sigemptyset(s) 35 | 36 | typedef pthread_mutex_t xmutex_t; 37 | #define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER 38 | #define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) 39 | #define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) 40 | #define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) 41 | 42 | typedef pthread_cond_t xcond_t; 43 | #define X_COND_INIT PTHREAD_COND_INITIALIZER 44 | #define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) 45 | #define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) 46 | #define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) 47 | #define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) 48 | 49 | typedef pthread_t xthread_t; 50 | #define X_THREAD_PROC(name) void *name (void *thr_arg) 51 | #define X_THREAD_ATFORK(a,b,c) 52 | 53 | static int 54 | thread_create (xthread_t *tid, void *(*proc)(void *), void *arg) 55 | { 56 | int retval; 57 | pthread_attr_t attr; 58 | 59 | pthread_attr_init (&attr); 60 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 61 | 62 | retval = pthread_create (tid, &attr, proc, arg) == 0; 63 | 64 | pthread_attr_destroy (&attr); 65 | 66 | return retval; 67 | } 68 | 69 | #define respipe_read(a,b,c) PerlSock_recv ((a), (b), (c), 0) 70 | #define respipe_write(a,b,c) send ((a), (b), (c), 0) 71 | #define respipe_close(a) PerlSock_closesocket ((a)) 72 | 73 | #else 74 | ///////////////////////////////////////////////////////////////////////////// 75 | 76 | #if __linux && !defined(_GNU_SOURCE) 77 | # define _GNU_SOURCE 78 | #endif 79 | 80 | /* just in case */ 81 | #define _REENTRANT 1 82 | 83 | #if __solaris 84 | # define _POSIX_PTHREAD_SEMANTICS 1 85 | /* try to bribe solaris headers into providing a current pthread API 86 | * despite environment being configured for an older version. 87 | */ 88 | # define __EXTENSIONS__ 1 89 | #endif 90 | 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | 97 | typedef pthread_mutex_t xmutex_t; 98 | #if __linux && defined (PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) 99 | # define X_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP 100 | # define X_MUTEX_CREATE(mutex) \ 101 | do { \ 102 | pthread_mutexattr_t attr; \ 103 | pthread_mutexattr_init (&attr); \ 104 | pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ADAPTIVE_NP); \ 105 | pthread_mutex_init (&(mutex), &attr); \ 106 | } while (0) 107 | #else 108 | # define X_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER 109 | # define X_MUTEX_CREATE(mutex) pthread_mutex_init (&(mutex), 0) 110 | #endif 111 | #define X_LOCK(mutex) pthread_mutex_lock (&(mutex)) 112 | #define X_UNLOCK(mutex) pthread_mutex_unlock (&(mutex)) 113 | 114 | typedef pthread_cond_t xcond_t; 115 | #define X_COND_INIT PTHREAD_COND_INITIALIZER 116 | #define X_COND_CREATE(cond) pthread_cond_init (&(cond), 0) 117 | #define X_COND_SIGNAL(cond) pthread_cond_signal (&(cond)) 118 | #define X_COND_WAIT(cond,mutex) pthread_cond_wait (&(cond), &(mutex)) 119 | #define X_COND_TIMEDWAIT(cond,mutex,to) pthread_cond_timedwait (&(cond), &(mutex), &(to)) 120 | 121 | typedef pthread_t xthread_t; 122 | #define X_THREAD_PROC(name) static void *name (void *thr_arg) 123 | #define X_THREAD_ATFORK(prepare,parent,child) pthread_atfork (prepare, parent, child) 124 | 125 | // the broken bsd's once more 126 | #ifndef PTHREAD_STACK_MIN 127 | # define PTHREAD_STACK_MIN 0 128 | #endif 129 | 130 | #ifndef X_STACKSIZE 131 | # define X_STACKSIZE sizeof (void *) * 4096 132 | #endif 133 | 134 | static int 135 | thread_create (xthread_t *tid, void *(*proc)(void *), void *arg) 136 | { 137 | int retval; 138 | sigset_t fullsigset, oldsigset; 139 | pthread_attr_t attr; 140 | 141 | pthread_attr_init (&attr); 142 | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); 143 | pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN < X_STACKSIZE ? X_STACKSIZE : PTHREAD_STACK_MIN); 144 | #ifdef PTHREAD_SCOPE_PROCESS 145 | pthread_attr_setscope (&attr, PTHREAD_SCOPE_PROCESS); 146 | #endif 147 | 148 | sigfillset (&fullsigset); 149 | 150 | pthread_sigmask (SIG_SETMASK, &fullsigset, &oldsigset); 151 | retval = pthread_create (tid, &attr, proc, arg) == 0; 152 | pthread_sigmask (SIG_SETMASK, &oldsigset, 0); 153 | 154 | pthread_attr_destroy (&attr); 155 | 156 | return retval; 157 | } 158 | 159 | #define respipe_read(a,b,c) read ((a), (b), (c)) 160 | #define respipe_write(a,b,c) write ((a), (b), (c)) 161 | #define respipe_close(a) close ((a)) 162 | 163 | #endif 164 | 165 | #endif 166 | 167 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | from distutils.core import Extension, setup 4 | from distutils.command.build_ext import build_ext 5 | 6 | cython_available = False 7 | try: 8 | from Cython.Distutils import build_ext 9 | from Cython.Distutils.extension import Extension 10 | cython_available = True 11 | except: 12 | pass 13 | 14 | def get_ext_modules(): 15 | if not cython_available: 16 | print 'nocy' 17 | eio_extension = Extension('eio', ['eio.c'], libraries=['pthread']) 18 | else: 19 | print 'cy' 20 | eio_extension = Extension('eio', ['eio.pyx'], libraries=['pthread']) 21 | 22 | eio_extension.configure = configure_eio 23 | return [eio_extension] 24 | 25 | def configure_eio(): 26 | if os.path.exists('libeio/config.h'): 27 | print 'libeio/config.h found, skipping libeio configuration' 28 | else: 29 | print 'libeio/config.h not found, configuring libeio' 30 | subprocess.call('sh -c ./autogen.sh', shell=True, cwd='libeio') 31 | subprocess.call('sh -c ./configure', shell=True, cwd='libeio') 32 | 33 | 34 | class my_build_ext(build_ext): 35 | 36 | def build_extension(self, ext): 37 | if getattr(ext, 'configure', None): 38 | ext.configure() 39 | self.compiler.dll_libraries = [] # remove msvcr dll for mingw compilation 40 | return build_ext.build_extension(self, ext) 41 | 42 | __version__ = (0, 0, 1, 'a') 43 | 44 | setup( 45 | name = 'eio', 46 | #packages = ['eio'], 47 | version = '.'.join([str(x) for x in __version__]), 48 | cmdclass = {'build_ext': my_build_ext}, 49 | ext_modules = get_ext_modules(), 50 | author = 'Travis Cline', 51 | author_email = 'travis.cline@gmail.com', 52 | url = 'http://github.com/traviscline/eio', 53 | description = 'gevent compatibility layer for pyzmq', 54 | long_description=open('README.rst').read(), 55 | license = 'New BSD', 56 | ) 57 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #import pyximport; pyximport.install() 2 | 3 | import os 4 | import eio 5 | import time 6 | import unittest 7 | 8 | def print_stats(): 9 | print '\tstats:' 10 | print '\t\tnum reqs:\t', eio.nreqs() 11 | print '\t\tnum ready:\t', eio.nready() 12 | print '\t\tnum pending:\t', eio.npending() 13 | print '\t\tnum threads:\t', eio.nthreads() 14 | 15 | def want_callback(): 16 | print 'Want Called!' 17 | 18 | def done_callback(): 19 | print 'Done Called!' 20 | 21 | print eio.init(want_callback, done_callback) 22 | 23 | class TestDirectoryOperations(unittest.TestCase): 24 | 25 | def setUp(self): 26 | print 'setup' 27 | print_stats() 28 | 29 | def tearDown(self): 30 | print 'setup' 31 | print_stats() 32 | 33 | def test_mkdir(self): 34 | def mkdir_cb(foo): 35 | print 'mkdir callback', foo 36 | 37 | d1 = 'test' 38 | d2 = 'test/eio-test-dir' 39 | 40 | # ensure dirs don't exist: 41 | if os.path.exists(d2): 42 | os.rmdir(d2) 43 | if os.path.exists(d1): 44 | os.rmdir(d1) 45 | 46 | self.assertFalse(os.path.exists(d1)) 47 | eio.mkdir(d1, 0777, mkdir_cb) 48 | time.sleep(0.1) 49 | print 'poll', eio.poll() 50 | self.assertTrue(os.path.exists(d1)) 51 | 52 | self.assertFalse(os.path.exists(d2)) 53 | eio.mkdir(d2, 0777, mkdir_cb) 54 | time.sleep(2.1) 55 | print 'poll', eio.poll() 56 | self.assertTrue(os.path.exists(d2)) 57 | 58 | eio.rmdir(d2) 59 | self.assertFalse(os.path.exists(d2)) 60 | eio.rmdir(d1) 61 | self.assertFalse(os.path.exists(d1)) 62 | 63 | 64 | 65 | print 'poll', eio.poll() 66 | time.sleep(0.1) 67 | print 'exiting' --------------------------------------------------------------------------------