fix AsyncIO::doWait
authorPhilip Pronin <philipp@fb.com>
Sun, 29 Jun 2014 11:49:47 +0000 (04:49 -0700)
committerTudor Bosman <tudorb@fb.com>
Tue, 1 Jul 2014 23:42:43 +0000 (16:42 -0700)
Summary:
As it turns out, `io_getevents` may actually return less than
`min_nr` events.  According to the aio logic
(https://github.com/torvalds/linux/blob/10b5b5361a3c2a7fff9dbfa0f127adc2531e7732/fs/aio.c#L1634),
there may be a couple of rounds required to get at least `nr_min` events, and
if interrupted after the first one, incomplete results would be returned

Test Plan:
fbconfig -r folly/experimental/io/test && fbmake runtests_opt -32

and was no longer able to repro #4609062

Reviewed By: soren@fb.com

FB internal diff: D1410389

Tasks: 4609062

folly/experimental/io/AsyncIO.cpp
folly/experimental/io/AsyncIO.h

index 633a30bf2df171952c8584134e8332b7a86da507..1a4273df0a8a8577eb8d61a54c1e59d1cbff661e 100644 (file)
@@ -19,9 +19,9 @@
 #include <sys/eventfd.h>
 #include <unistd.h>
 #include <cerrno>
+#include <ostream>
 #include <stdexcept>
 #include <string>
-#include <fstream>
 
 #include <boost/intrusive/parent_from_member.hpp>
 #include <glog/logging.h>
@@ -220,13 +220,25 @@ Range<AsyncIO::Op**> AsyncIO::pollCompleted() {
 
 Range<AsyncIO::Op**> AsyncIO::doWait(size_t minRequests, size_t maxRequests) {
   io_event events[maxRequests];
-  int count;
+
+  size_t count = 0;
   do {
-    // Wait forever
-    count = io_getevents(ctx_, minRequests, maxRequests, events, nullptr);
-  } while (count == -EINTR);
-  checkKernelError(count, "AsyncIO: io_getevents failed");
-  DCHECK_GE(count, minRequests);  // the man page says so
+    int ret;
+    do {
+      // GOTCHA: io_getevents() may returns less than min_nr results if
+      // interrupted after some events have been read (if before, -EINTR
+      // is returned).
+      ret = io_getevents(ctx_,
+                         minRequests - count,
+                         maxRequests - count,
+                         events + count,
+                         /* timeout */ nullptr);  // wait forever
+    } while (ret == -EINTR);
+    // Check as may not be able to recover without leaking events.
+    CHECK_GE(ret, 0)
+      << "AsyncIO: io_getevents failed with error " << errnoStr(-ret);
+    count += ret;
+  } while (count < minRequests);
   DCHECK_LE(count, maxRequests);
 
   completed_.clear();
index 6c9da8d258325d2b1db6c9b71c7631ff476c5b7f..b595a325f64ab6617b6678827d6cea0704ea9999 100644 (file)
@@ -25,8 +25,8 @@
 #include <cstdint>
 #include <deque>
 #include <functional>
+#include <iosfwd>
 #include <mutex>
-#include <ostream>
 #include <utility>
 #include <vector>
 
@@ -233,6 +233,7 @@ class AsyncIOQueue {
    */
   typedef std::function<AsyncIOOp*()> OpFactory;
   void submit(OpFactory op);
+
  private:
   void onCompleted(AsyncIOOp* op);
   void maybeDequeue();