--- libeio/eio.pod 2011/06/05 23:07:46 1.9 +++ libeio/eio.pod 2011/07/05 20:34:42 1.18 @@ -47,19 +47,20 @@ =head2 FORK SUPPORT -Calling C is fully supported by this module. It is implemented in these steps: +Calling C is fully supported by this module - but you must not +rely on this. It is currently implemented in these steps: - 1. wait till all requests in "execute" state have been handled - (basically requests that are already handed over to the kernel). - 2. fork - 3. in the parent, continue business as usual, done - 4. in the child, destroy all ready and pending requests and free the - memory used by the worker threads. This gives you a fully empty - libeio queue. + 1. wait till all requests in "execute" state have been handled + (basically requests that are already handed over to the kernel). + 2. fork + 3. in the parent, continue business as usual, done + 4. in the child, destroy all ready and pending requests and free the + memory used by the worker threads. This gives you a fully empty + libeio queue. -Note, however, since libeio does use threads, thr above guarantee doesn't +Note, however, since libeio does use threads, the above guarantee doesn't cover your libc, for example, malloc and other libc functions are not -fork-safe, so there is very little you can do after a fork, and in fatc, +fork-safe, so there is very little you can do after a fork, and in fact, the above might crash, and thus change. =head1 INITIALISATION/INTEGRATION @@ -126,19 +127,66 @@ For libev, you would typically use an C watcher: the C callback would invoke C to wake up the event loop. Inside the callback set for the watcher, one would call C (followed by C again if C indicates that not -all requests have been handled yet). The race is taken care of because -libev resets/rearms the async watcher before calling your callback, -and therefore, before calling C. This might result in (some) -spurious wake-ups, but is generally harmless. +()>. + +If C is configured to not handle all results in one go +(i.e. it returns C<-1>) then you should start an idle watcher that calls +C until it returns something C. + +A full-featured conenctor between libeio and libev would look as follows +(if C is handling all requests, it can of course be simplified a +lot by removing the idle watcher logic): + + static struct ev_loop *loop; + static ev_idle repeat_watcher; + static ev_async ready_watcher; + + /* idle watcher callback, only used when eio_poll */ + /* didn't handle all results in one call */ + static void + repeat (EV_P_ ev_idle *w, int revents) + { + if (eio_poll () != -1) + ev_idle_stop (EV_A_ w); + } + + /* eio has some results, process them */ + static void + ready (EV_P_ ev_async *w, int revents) + { + if (eio_poll () == -1) + ev_idle_start (EV_A_ &repeat_watcher); + } + + /* wake up the event loop */ + static void + want_poll (void) + { + ev_async_send (loop, &ready_watcher) + } + + void + my_init_eio () + { + loop = EV_DEFAULT; + + ev_idle_init (&repeat_watcher, repeat); + ev_async_init (&ready_watcher, ready); + ev_async_start (loop &watcher); + + eio_init (want_poll, 0); + } For most other event loops, you would typically use a pipe - the event loop should be told to wait for read readiness on the read end. In C you would write a single byte, in C you would try to read that byte, and in the callback for the read end, you would call -C. The race is avoided here because the event loop should invoke -your callback again and again until the byte has been read (as the pipe -read callback does not read it, only C). +C. + +You don't have to take special care in the case C doesn't handle +all requests, as the done callback will not be invoked, so the event loop +will still signal readiness for the pipe until I results have been +processed. =head1 HIGH LEVEL REQUEST API @@ -155,7 +203,10 @@ required parameters, a callback of type C (called C below) and a freely usable C argument. -The return value will either be 0 +The return value will either be 0, in case something went really wrong +(which can basically only happen on very fatal errors, such as C +returning 0, which is rather unlikely), or a pointer to the newly-created +and submitted C. The callback will be called with an C which contains the results of the request. The members you can access inside that structure @@ -212,11 +263,46 @@ /* the first three arguments are passed to open(2) */ /* the remaining are priority, callback and data */ if (!eio_open ("/etc/passwd", O_RDONLY, 0, 0, file_open_done, 0)) - abort (); /* something ent wrong, we will all die!!! */ + abort (); /* something went wrong, we will all die!!! */ Note that you additionally need to call C when the C indicates that requests are ready to be processed. +=head2 CANCELLING REQUESTS + +Sometimes the need for a request goes away before the request is +finished. In that case, one can cancel the request by a call to +C: + +=over 4 + +=item eio_cancel (eio_req *req) + +Cancel the request (and all it's subrequests). If the request is currently +executing it might still continue to execute, and in other cases it might +still take a while till the request is cancelled. + +Even if cancelled, the finish callback will still be invoked - the +callbacks of all cancellable requests need to check whether the request +has been cancelled by calling C: + + static int + my_eio_cb (eio_req *req) + { + if (EIO_CANCELLED (req)) + return 0; + } + +In addition, cancelled requests will I have C<< req->result >> +set to C<-1> and C to C, or I they were +successfully executed, despite being cancelled (e.g. when they have +already been executed at the time they were cancelled). + +C is still true for requests that have successfully +executed, as long as C was called on them at some point. + +=back + =head2 AVAILABLE REQUESTS The following request functions are available. I of them return the @@ -228,14 +314,13 @@ =head3 POSIX API WRAPPERS These requests simply wrap the POSIX call of the same name, with the same -arguments: +arguments. If a function is not implemented by the OS and cannot be emulated +in some way, then all of these return C<-1> and set C to C. =over 4 =item eio_open (const char *path, int flags, mode_t mode, int pri, eio_cb cb, void *data) -=item eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) - =item eio_truncate (const char *path, off_t offset, int pri, eio_cb cb, void *data) =item eio_chown (const char *path, uid_t uid, gid_t gid, int pri, eio_cb cb, void *data) @@ -248,13 +333,7 @@ =item eio_unlink (const char *path, int pri, eio_cb cb, void *data) -=item eio_readlink (const char *path, int pri, eio_cb cb, void *data) /* result=ptr2 allocated dynamically */ - -=item eio_stat (const char *path, int pri, eio_cb cb, void *data) /* stat buffer=ptr2 allocated dynamically */ - -=item eio_lstat (const char *path, int pri, eio_cb cb, void *data) /* stat buffer=ptr2 allocated dynamically */ - -=item eio_statvfs (const char *path, int pri, eio_cb cb, void *data) /* stat buffer=ptr2 allocated dynamically */ +=item eio_utime (const char *path, eio_tstamp atime, eio_tstamp mtime, int pri, eio_cb cb, void *data) =item eio_mknod (const char *path, mode_t mode, dev_t dev, int pri, eio_cb cb, void *data) @@ -264,12 +343,8 @@ =item eio_rename (const char *path, const char *new_path, int pri, eio_cb cb, void *data) -=item eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) - =item eio_mlock (void *addr, size_t length, int pri, eio_cb cb, void *data) -=item eio_mlockall (int flags, int pri, eio_cb cb, void *data) - =item eio_close (int fd, int pri, eio_cb cb, void *data) =item eio_sync (int pri, eio_cb cb, void *data) @@ -307,27 +382,62 @@ so it is advised not to submit multiple requests on the same fd on this horrible pile of garbage. +=item eio_mlockall (int flags, int pri, eio_cb cb, void *data) + +Like C, but the flag value constants are called +C and C. + +=item eio_msync (void *addr, size_t length, int flags, int pri, eio_cb cb, void *data) + +Just like msync, except that the flag values are called C, +C and C. + +=item eio_readlink (const char *path, int pri, eio_cb cb, void *data) + +If successful, the path read by C can be accessed via C<< +req->ptr2 >> and is I null-terminated, with the length specified as +C<< req->result >>. + + if (req->result >= 0) + { + char *target = strndup ((char *)req->ptr2, req->result); + + free (target); + } + +=item eio_realpath (const char *path, int pri, eio_cb cb, void *data) + +Similar to the realpath libc function, but unlike that one, result is +C<-1> on failure and the length of the returned path in C (which is +not 0-terminated) - this is similar to readlink. + +=item eio_stat (const char *path, int pri, eio_cb cb, void *data) + +=item eio_lstat (const char *path, int pri, eio_cb cb, void *data) + =item eio_fstat (int fd, int pri, eio_cb cb, void *data) Stats a file - if C<< req->result >> indicates success, then you can access the C-like structure via C<< req->ptr2 >>: - EIO_STRUCT_STAT *statdata = (EIO_STRUCT_STAT *)req->ptr2; + EIO_STRUCT_STAT *statdata = (EIO_STRUCT_STAT *)req->ptr2; -=item eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) /* stat buffer=ptr2 allocated dynamically */ +=item eio_statvfs (const char *path, int pri, eio_cb cb, void *data) + +=item eio_fstatvfs (int fd, int pri, eio_cb cb, void *data) Stats a filesystem - if C<< req->result >> indicates success, then you can access the C-like structure via C<< req->ptr2 >>: - EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; + EIO_STRUCT_STATVFS *statdata = (EIO_STRUCT_STATVFS *)req->ptr2; =back =head3 READING DIRECTORIES Reading directories sounds simple, but can be rather demanding, especially -if you want to do stuff such as traversing a diretcory hierarchy or -processing all files in a directory. Libeio can assist thess complex tasks +if you want to do stuff such as traversing a directory hierarchy or +processing all files in a directory. Libeio can assist these complex tasks with it's C call. =over 4 @@ -340,7 +450,7 @@ argument. The C<< req->result >> indicates either the number of files found, or -C<-1> on error. On success, zero-terminated names can be found as C<< req->ptr2 >>, +C<-1> on error. On success, null-terminated names can be found as C<< req->ptr2 >>, and C, if requested by C, can be found via C<< req->ptr1 >>. @@ -369,14 +479,14 @@ also an array of C is returned, in C. A C looks like this: - struct eio_dirent - { - int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ - unsigned short namelen; /* size of filename without trailing 0 */ - unsigned char type; /* one of EIO_DT_* */ - signed char score; /* internal use */ - ino_t inode; /* the inode number, if available, otherwise unspecified */ - }; + struct eio_dirent + { + int nameofs; /* offset of null-terminated name string in (char *)req->ptr2 */ + unsigned short namelen; /* size of filename without trailing 0 */ + unsigned char type; /* one of EIO_DT_* */ + signed char score; /* internal use */ + ino_t inode; /* the inode number, if available, otherwise unspecified */ + }; The only members you normally would access are C, which is the byte-offset from C to the start of the name, C and C. @@ -429,15 +539,15 @@ all files in the given directory, then the returned order will likely be fastest. -If both this flag and C are specified, then -the likely dirs come first, resulting in a less optimal stat order. +If both this flag and C are specified, then the +likely directories come first, resulting in a less optimal stat order. =item EIO_READDIR_FOUND_UNKNOWN This flag should not be specified when calling C. Instead, it is being set by C (you can access the C via C<< req->int1 >>, when any of the C's found were C. The -absense of this flag therefore indicates that all C's are known, +absence of this flag therefore indicates that all C's are known, which can be used to speed up some algorithms. A typical use case would be to identify all subdirectories within a @@ -482,6 +592,9 @@ Calls C. If the syscall is missing, then this is the same as calling C. +Flags can be any combination of C, +C and C. + =back =head3 LIBEIO-SPECIFIC REQUESTS @@ -532,7 +645,7 @@ =item eio_busy (eio_tstamp delay, int pri, eio_cb cb, void *data) -This is a a request that takes C seconds to execute, but otherwise +This is a request that takes C seconds to execute, but otherwise does nothing - it simply puts one of the worker threads to sleep for this long. @@ -549,6 +662,40 @@ =head3 GROUPING AND LIMITING REQUESTS +There is one more rather special request, C. It is a very special +aio request: Instead of doing something, it is a container for other eio +requests. + +There are two primary use cases for this: a) bundle many requests into a +single, composite, request with a definite callback and the ability to +cancel the whole request with its subrequests and b) limiting the number +of "active" requests. + +Further below you will find more discussion of these topics - first +follows the reference section detailing the request generator and other +methods. + +=over 4 + +=item eio_req *grp = eio_grp (eio_cb cb, void *data) + +Creates, submits and returns a group request. + +=item eio_grp_add (eio_req *grp, eio_req *req) + +Adds a request to the request group. + +=item eio_grp_cancel (eio_req *grp) + +Cancels all requests I the group, but I the group request +itself. You can cancel the group request via a normal C call. + + + +=back + + + #TODO /*****************************************************************************/ @@ -557,7 +704,6 @@ eio_req *eio_grp (eio_cb cb, void *data); void eio_grp_feed (eio_req *grp, void (*feed)(eio_req *req), int limit); void eio_grp_limit (eio_req *grp, int limit); -void eio_grp_add (eio_req *grp, eio_req *req); void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the group */ @@ -574,13 +720,13 @@ A request is represented by a structure of type C. To initialise it, clear it to all zero bytes: - eio_req req; + eio_req req; - memset (&req, 0, sizeof (req)); + memset (&req, 0, sizeof (req)); A more common way to initialise a new C is to use C: - eio_req *req = calloc (1, sizeof (*req)); + eio_req *req = calloc (1, sizeof (*req)); In either case, libeio neither allocates, initialises or frees the C structure for you - it merely uses it. @@ -608,14 +754,18 @@ Note that: -a) libeio doesn't know how long your request callbacks take, so the time -spent in C is up to one callback invocation longer then this -interval. +=over 4 + +=item a) libeio doesn't know how long your request callbacks take, so the +time spent in C is up to one callback invocation longer then +this interval. -b) this is implemented by calling C after each request, -which can be costly. +=item b) this is implemented by calling C after each +request, which can be costly. -c) at least one request will be handled. +=item c) at least one request will be handled. + +=back =item eio_set_max_poll_reqs (unsigned int nreqs)