2017
[folly.git] / folly / experimental / io / AsyncIO.h
index 3c84ebb0c4f2be331669ffb88b379fdfb8fbbfc3..faa5e27fc4b4e075d36f023bba1e5d72cae15dcc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#ifndef FOLLY_IO_ASYNCIO_H_
-#define FOLLY_IO_ASYNCIO_H_
+#pragma once
 
 #include <sys/types.h>
-#include <sys/uio.h>
 #include <libaio.h>
 
+#include <atomic>
 #include <cstdint>
 #include <deque>
 #include <functional>
+#include <iosfwd>
+#include <mutex>
 #include <utility>
 #include <vector>
 
 #include <boost/noncopyable.hpp>
 
-#include "folly/Portability.h"
-#include "folly/Range.h"
+#include <folly/Portability.h>
+#include <folly/Range.h>
+#include <folly/portability/SysUio.h>
 
 namespace folly {
 
@@ -42,6 +44,7 @@ namespace folly {
  */
 class AsyncIOOp : private boost::noncopyable {
   friend class AsyncIO;
+  friend std::ostream& operator<<(std::ostream& stream, const AsyncIOOp& o);
  public:
   typedef std::function<void(AsyncIOOp*)> NotificationCallback;
 
@@ -51,7 +54,7 @@ class AsyncIOOp : private boost::noncopyable {
   // There would be a cancel() method here if Linux AIO actually implemented
   // it.  But let's not get your hopes up.
 
-  enum State {
+  enum class State {
     UNINITIALIZED,
     INITIALIZED,
     PENDING,
@@ -108,6 +111,9 @@ class AsyncIOOp : private boost::noncopyable {
   ssize_t result_;
 };
 
+std::ostream& operator<<(std::ostream& stream, const AsyncIOOp& o);
+std::ostream& operator<<(std::ostream& stream, AsyncIOOp::State state);
+
 /**
  * C++ interface around Linux Async IO.
  */
@@ -133,6 +139,12 @@ class AsyncIO : private boost::noncopyable {
    * any IOs on this AsyncIO have completed.  If you do this, you must use
    * pollCompleted() instead of wait() -- do not read from the pollFd()
    * file descriptor directly.
+   *
+   * You may use the same AsyncIO object from multiple threads, as long as
+   * there is only one concurrent caller of wait() / pollCompleted() (perhaps
+   * by always calling it from the same thread, or by providing appropriate
+   * mutual exclusion)  In this case, pending() returns a snapshot
+   * of the current number of pending requests.
    */
   explicit AsyncIO(size_t capacity, PollMode pollMode=NOT_POLLABLE);
   ~AsyncIO();
@@ -155,6 +167,12 @@ class AsyncIO : private boost::noncopyable {
    */
   size_t capacity() const { return capacity_; }
 
+  /**
+   * Return the accumulative number of submitted I/O, since this object
+   * has been created.
+   */
+  size_t totalSubmits() const { return submitted_; }
+
   /**
    * If POLLABLE, return a file descriptor that can be passed to poll / epoll
    * and will become readable when any async IO operations have completed.
@@ -175,11 +193,17 @@ class AsyncIO : private boost::noncopyable {
   void submit(Op* op);
 
  private:
+  void decrementPending();
   void initializeContext();
+
   Range<Op**> doWait(size_t minRequests, size_t maxRequests);
 
   io_context_t ctx_;
-  size_t pending_;
+  std::atomic<bool> ctxSet_;
+  std::mutex initMutex_;
+
+  std::atomic<size_t> pending_;
+  std::atomic<size_t> submitted_;
   const size_t capacity_;
   int pollFd_;
   std::vector<Op*> completed_;
@@ -215,6 +239,7 @@ class AsyncIOQueue {
    */
   typedef std::function<AsyncIOOp*()> OpFactory;
   void submit(OpFactory op);
+
  private:
   void onCompleted(AsyncIOOp* op);
   void maybeDequeue();
@@ -225,6 +250,3 @@ class AsyncIOQueue {
 };
 
 }  // namespace folly
-
-#endif /* FOLLY_IO_ASYNCIO_H_ */
-