Use Baton (again) in EventBase::runInEventBaseThreadAndWait
[folly.git] / folly / io / async / AsyncTimeout.h
index d1d2ba87eb16fb48e0abf66594191435ccae0ef7..2f626d840c015e33c1a490f9fc1f29825154b840 100644 (file)
@@ -1,28 +1,28 @@
 /*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
+ * Copyright 2004-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
+
 #pragma once
 
-#include "folly/io/async/TimeoutManager.h"
+#include <folly/io/async/TimeoutManager.h>
+
+#include <folly/portability/Event.h>
 
 #include <boost/noncopyable.hpp>
-#include <event.h>
 #include <memory>
+#include <utility>
 
 namespace folly {
 
@@ -96,7 +96,7 @@ class AsyncTimeout : private boost::noncopyable {
    *         rescheduling an existing timeout.
    */
   bool scheduleTimeout(uint32_t milliseconds);
-  bool scheduleTimeout(std::chrono::milliseconds timeout);
+  bool scheduleTimeout(TimeoutManager::timeout_type timeout);
 
   /**
    * Cancel the timeout, if it is running.
@@ -138,6 +138,10 @@ class AsyncTimeout : private boost::noncopyable {
   void detachTimeoutManager();
   void detachEventBase();
 
+  const TimeoutManager* getTimeoutManager() {
+    return timeoutManager_;
+  }
+
   /**
    * Returns the internal handle to the event
    */
@@ -145,8 +149,74 @@ class AsyncTimeout : private boost::noncopyable {
     return &event_;
   }
 
+  /**
+   * Convenience function that wraps a function object as
+   * an AsyncTimeout instance and returns the wrapper.
+   *
+   * Specially useful when using lambdas as AsyncTimeout
+   * observers.
+   *
+   * Example:
+   *
+   *  void foo(TimeoutManager &manager) {
+   *    std::atomic_bool done = false;
+   *
+   *    auto observer = AsyncTimeout::make(manager, [&] {
+   *      std::cout << "hello, world!" << std::endl;
+   *      done = true;
+   *    });
+   *
+   *    observer->scheduleTimeout(std::chrono::seconds(5));
+   *
+   *    while (!done); // busy wait
+   *  }
+   *
+   * @author: Marcelo Juchem <marcelo@fb.com>
+   */
+  template <typename TCallback>
+  static std::unique_ptr<AsyncTimeout> make(
+    TimeoutManager &manager,
+    TCallback &&callback
+  );
+
+  /**
+   * Convenience function that wraps a function object as
+   * an AsyncTimeout instance and returns the wrapper
+   * after scheduling it using the given TimeoutManager.
+   *
+   * This is equivalent to calling `make_async_timeout`
+   * followed by a `scheduleTimeout` on the resulting
+   * wrapper.
+   *
+   * Specially useful when using lambdas as AsyncTimeout
+   * observers.
+   *
+   * Example:
+   *
+   *  void foo(TimeoutManager &manager) {
+   *    std::atomic_bool done = false;
+   *
+   *    auto observer = AsyncTimeout::schedule(
+   *      std::chrono::seconds(5), manager, [&] {
+   *        std::cout << "hello, world!" << std::endl;
+   *        done = true;
+   *      }
+   *    );
+   *
+   *    while (!done); // busy wait
+   *  }
+   *
+   * @author: Marcelo Juchem <marcelo@fb.com>
+   */
+  template <typename TCallback>
+  static std::unique_ptr<AsyncTimeout> schedule(
+    TimeoutManager::timeout_type timeout,
+    TimeoutManager &manager,
+    TCallback &&callback
+  );
+
  private:
-  static void libeventCallback(int fd, short events, void* arg);
+  static void libeventCallback(libevent_fd_t fd, short events, void* arg);
 
   struct event event_;
 
@@ -161,4 +231,59 @@ class AsyncTimeout : private boost::noncopyable {
   std::shared_ptr<RequestContext> context_;
 };
 
+namespace detail {
+
+/**
+ * Wraps a function object as an AsyncTimeout instance.
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+template <typename TCallback>
+struct async_timeout_wrapper:
+  public AsyncTimeout
+{
+  template <typename UCallback>
+  async_timeout_wrapper(TimeoutManager *manager, UCallback &&callback):
+    AsyncTimeout(manager),
+    callback_(std::forward<UCallback>(callback))
+  {}
+
+  void timeoutExpired() noexcept override {
+    static_assert(
+      noexcept(std::declval<TCallback>()()),
+      "callback must be declared noexcept, e.g.: `[]() noexcept {}`"
+    );
+    callback_();
+  }
+
+private:
+  TCallback callback_;
+};
+
+} // namespace detail {
+
+template <typename TCallback>
+std::unique_ptr<AsyncTimeout> AsyncTimeout::make(
+  TimeoutManager &manager,
+  TCallback &&callback
+) {
+  return std::unique_ptr<AsyncTimeout>(
+    new detail::async_timeout_wrapper<typename std::decay<TCallback>::type>(
+      std::addressof(manager),
+      std::forward<TCallback>(callback)
+    )
+  );
+}
+
+template <typename TCallback>
+std::unique_ptr<AsyncTimeout> AsyncTimeout::schedule(
+  TimeoutManager::timeout_type timeout,
+  TimeoutManager &manager,
+  TCallback &&callback
+) {
+  auto wrapper = AsyncTimeout::make(manager, std::forward<TCallback>(callback));
+  wrapper->scheduleTimeout(timeout);
+  return wrapper;
+}
+
 } // folly