summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes1
-rw-r--r--eio.c20
-rw-r--r--eio.h4
-rw-r--r--eio.pod177
4 files changed, 128 insertions, 74 deletions
diff --git a/Changes b/Changes
index ba519f9..0cd1985 100644
--- a/Changes
+++ b/Changes
@@ -50,4 +50,5 @@ TODO: fadvise request
- use libecb, and apply lots of minor space optimisations.
- disable sendfile on darwin, broken as everything else.
- add realpath request and implementation.
+ - cancelled requests will still invoke their request callbacks.
diff --git a/eio.c b/eio.c
index 2d97d91..58a8c84 100644
--- a/eio.c
+++ b/eio.c
@@ -62,11 +62,17 @@
#include <assert.h>
/* intptr_t comes from unistd.h, says POSIX/UNIX/tradition */
-/* intptr_t only comes form stdint.h, says idiot openbsd coder */
+/* intptr_t only comes from stdint.h, says idiot openbsd coder */
#if HAVE_STDINT_H
# include <stdint.h>
#endif
+#ifndef ECANCELED
+# define ECANCELED EDOM
+#endif
+
+static void eio_destroy (eio_req *req);
+
#ifndef EIO_FINISH
# define EIO_FINISH(req) ((req)->finish) && !EIO_CANCELLED (req) ? (req)->finish (req) : 0
#endif
@@ -713,7 +719,7 @@ grp_dec (eio_req *grp)
return 0;
}
-void
+static void
eio_destroy (eio_req *req)
{
if ((req)->flags & EIO_FLAG_PTR1_FREE) free (req->ptr1);
@@ -1831,8 +1837,7 @@ X_THREAD_PROC (etp_proc)
if (req->type < 0)
goto quit;
- if (!EIO_CANCELLED (req))
- ETP_EXECUTE (self, req);
+ ETP_EXECUTE (self, req);
X_LOCK (reslock);
@@ -1896,6 +1901,13 @@ eio_api_destroy (eio_req *req)
static void
eio_execute (etp_worker *self, eio_req *req)
{
+ if (ecb_expect_false (EIO_CANCELLED (req)))
+ {
+ req->result = -1;
+ req->errorno = ECANCELED;
+ return;
+ }
+
switch (req->type)
{
case EIO_READ: ALLOC (req->size);
diff --git a/eio.h b/eio.h
index 2f96681..2f8f029 100644
--- a/eio.h
+++ b/eio.h
@@ -321,11 +321,9 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g
void eio_submit (eio_req *req);
/* cancel a request as soon fast as possible, if possible */
void eio_cancel (eio_req *req);
-/* destroy a request that has never been submitted */
-void eio_destroy (eio_req *req);
/*****************************************************************************/
-/* convinience functions */
+/* convenience functions */
ssize_t eio_sendfile_sync (int ofd, int ifd, off_t offset, size_t count);
diff --git a/eio.pod b/eio.pod
index 31933fe..d178c2c 100644
--- a/eio.pod
+++ b/eio.pod
@@ -47,19 +47,20 @@ time differences throughout libeio.
=head2 FORK SUPPORT
-Calling C<fork ()> is fully supported by this module. It is 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.
-
-Note, however, since libeio does use threads, thr above guarantee doesn't
+Calling C<fork ()> 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.
+
+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
@@ -136,45 +137,45 @@ A full-featured conenctor between libeio and libev would look as follows
(if C<eio_poll> 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);
- }
+ 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
@@ -267,6 +268,38 @@ For example, to open a file, you could do this:
Note that you additionally need to call C<eio_poll> when the C<want_cb>
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 reqiest by a call to
+C<eio_cancel>:
+
+=over 4
+
+=item eio_cancel (eio_req *req)
+
+Cancel the request. 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<EIO_CANCELLED (req)>:
+
+ static int
+ my_eio_cb (eio_req *req)
+ {
+ if (EIO_CANCELLED (req))
+ return 0;
+ }
+
+In addition, cancelled requests will either have C<< req->result >> set to
+C<-1> and C<errno> to C<ECANCELED>, or otherwise they were successfully
+executed despite being cancelled (e.g. when they have already been
+executed at the time they were cancelled).
+
+=back
+
=head2 AVAILABLE REQUESTS
The following request functions are available. I<All> of them return the
@@ -384,7 +417,7 @@ not 0-terminated) - this is similar to readlink.
Stats a file - if C<< req->result >> indicates success, then you can
access the C<struct stat>-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_statvfs (const char *path, int pri, eio_cb cb, void *data)
@@ -393,7 +426,7 @@ access the C<struct stat>-like structure via C<< req->ptr2 >>:
Stats a filesystem - if C<< req->result >> indicates success, then you can
access the C<struct statvfs>-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
@@ -443,14 +476,14 @@ If this flag is specified, then, in addition to the names in C<ptr2>,
also an array of C<struct eio_dirent> is returned, in C<ptr1>. A C<struct
eio_dirent> 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<nameofs>, which is the
byte-offset from C<ptr2> to the start of the name, C<namelen> and C<type>.
@@ -640,9 +673,20 @@ the reference section detailing the request generator and other methods.
=over 4
-=item eio_grp (eio_cb cb, void *data)
+=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<in> the group, but I<not> the group request
+itself. You can cancel the group request via a normal C<eio_cancel> call.
+
-Creates and submits a group request.
=back
@@ -656,7 +700,6 @@ Creates and submits a group request.
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 */
@@ -673,13 +716,13 @@ void eio_grp_cancel (eio_req *grp); /* cancels all sub requests but not the g
A request is represented by a structure of type C<eio_req>. 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<eio_req> is to use C<calloc>:
- 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<eio_req> structure for you - it merely uses it.