Move TAsyncSignalHandler into folly
authorAlan Frindell <afrind@fb.com>
Thu, 2 Apr 2015 17:43:38 +0000 (10:43 -0700)
committerafrind <afrind@fb.com>
Thu, 2 Apr 2015 19:02:50 +0000 (12:02 -0700)
Summary:
TODO item, trying to remove unecessary dependencies on thrift

Test Plan: Unit tests

Reviewed By: davejwatson@fb.com

Subscribers: doug, fbcode-common-diffs@, davejwatson, andrewcox, alandau, bmatheny, anca, darshan, mshneer, folly-diffs@, bil, yfeldblum, haijunz, chalfant

FB internal diff: D1960215

Signature: t1:1960215:1427920934:8abd7e94c50676b05bf7ff79800df0db1bd04266

folly/io/async/AsyncSignalHandler.cpp [new file with mode: 0644]
folly/io/async/AsyncSignalHandler.h [new file with mode: 0644]
folly/io/async/README.md

diff --git a/folly/io/async/AsyncSignalHandler.cpp b/folly/io/async/AsyncSignalHandler.cpp
new file mode 100644 (file)
index 0000000..b2c9184
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 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.
+ */
+#include <folly/io/async/AsyncSignalHandler.h>
+
+#include <folly/io/async/EventBase.h>
+
+#include <folly/Conv.h>
+
+using std::make_pair;
+using std::pair;
+using std::string;
+
+namespace folly {
+
+AsyncSignalHandler::AsyncSignalHandler(EventBase* eventBase)
+  : eventBase_(eventBase) {
+}
+
+AsyncSignalHandler::~AsyncSignalHandler() {
+  // Unregister any outstanding events
+  for (SignalEventMap::iterator it = signalEvents_.begin();
+       it != signalEvents_.end();
+       ++it) {
+    event_del(&it->second);
+  }
+}
+
+void AsyncSignalHandler::registerSignalHandler(int signum) {
+  pair<SignalEventMap::iterator, bool> ret =
+    signalEvents_.insert(make_pair(signum, event()));
+  if (!ret.second) {
+    // This signal has already been registered
+    throw std::runtime_error(folly::to<string>(
+                               "handler already registered for signal ",
+                               signum));
+  }
+
+  struct event* ev = &(ret.first->second);
+  try {
+    signal_set(ev, signum, libeventCallback, this);
+    if (event_base_set(eventBase_->getLibeventBase(), ev) != 0 ) {
+      throw std::runtime_error(folly::to<string>(
+                                 "error initializing event handler for signal ",
+                                 signum));
+    }
+
+    if (event_add(ev, nullptr) != 0) {
+      throw std::runtime_error(folly::to<string>(
+                                 "error adding event handler for signal ",
+                                 signum));
+    }
+  } catch (...) {
+    signalEvents_.erase(ret.first);
+    throw;
+  }
+}
+
+void AsyncSignalHandler::unregisterSignalHandler(int signum) {
+  SignalEventMap::iterator it = signalEvents_.find(signum);
+  if (it == signalEvents_.end()) {
+    throw std::runtime_error(folly::to<string>(
+                               "unable to unregister handler for signal ",
+                               signum, ": signal not registered"));
+  }
+
+  event_del(&it->second);
+  signalEvents_.erase(it);
+}
+
+void AsyncSignalHandler::libeventCallback(int signum, short events,
+                                           void* arg) {
+  AsyncSignalHandler* handler = static_cast<AsyncSignalHandler*>(arg);
+  handler->signalReceived(signum);
+}
+
+} // folly
diff --git a/folly/io/async/AsyncSignalHandler.h b/folly/io/async/AsyncSignalHandler.h
new file mode 100644 (file)
index 0000000..ca35bc1
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 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.
+ */
+#pragma once
+
+#include <folly/io/async/EventBase.h>
+#include <event.h>
+#include <map>
+
+namespace folly {
+
+/**
+ * A handler to receive notification about POSIX signals.
+ *
+ * TAsyncSignalHandler allows code to process signals from within a EventBase
+ * loop.  Standard signal handlers interrupt execution of the main thread, and
+ * are run while the main thread is paused.  As a result, great care must be
+ * taken to avoid race conditions if the signal handler has to access or modify
+ * any data used by the main thread.
+ *
+ * TAsyncSignalHandler solves this problem by running the TAsyncSignalHandler
+ * callback in normal thread of execution, as a EventBase callback.
+ *
+ * TAsyncSignalHandler may only be used in a single thread.  It will only
+ * process signals received by the thread where the TAsyncSignalHandler is
+ * registered.  It is the user's responsibility to ensure that signals are
+ * delivered to the desired thread in multi-threaded programs.
+ */
+class AsyncSignalHandler {
+ public:
+  /**
+   * Create a new AsyncSignalHandler.
+   */
+  explicit AsyncSignalHandler(EventBase* eventBase);
+  virtual ~AsyncSignalHandler();
+
+  /**
+   * Register to receive callbacks about the specified signal.
+   *
+   * Once the handler has been registered for a particular signal,
+   * signalReceived() will be called each time this thread receives this
+   * signal.
+   *
+   * Throws a TException if an error occurs, or if this handler is already
+   * registered for this signal.
+   */
+  void registerSignalHandler(int signum);
+
+  /**
+   * Unregister for callbacks about the specified signal.
+   *
+   * Throws a TException if an error occurs, or if this signal was not
+   * registered.
+   */
+  void unregisterSignalHandler(int signum);
+
+  /**
+   * signalReceived() will called to indicate that the specified signal has
+   * been received.
+   *
+   * signalReceived() will always be invoked from the EventBase loop (i.e.,
+   * after the main POSIX signal handler has returned control to the EventBase
+   * thread).
+   */
+  virtual void signalReceived(int signum) noexcept = 0;
+
+ private:
+  typedef std::map<int, struct event> SignalEventMap;
+
+  // Forbidden copy constructor and assignment operator
+  AsyncSignalHandler(AsyncSignalHandler const &);
+  AsyncSignalHandler& operator=(AsyncSignalHandler const &);
+
+  static void libeventCallback(int signum, short events, void* arg);
+
+  EventBase* eventBase_;
+  SignalEventMap signalEvents_;
+};
+
+} // folly
index e613bfc90b1566357481b88637370c2eb363fed3..ce61f64effee3f658c59bd58ff698d240bb538ed 100644 (file)
@@ -77,7 +77,7 @@ Unsupported libevent event types, and why-
 * TIMEOUT - this library has specific timeout support, instead of
   being attached to read/write fds.
 * SIGNAL - similarly, signals are handled separately, see
-  AsyncSignalHandler (TODO:currently in fbthrift)
+  AsyncSignalHandler
 * EV_ET - Currently all the implementations of EventHandler are set up
   for level triggered.  Benchmarking hasn't shown that edge triggered
   provides much improvement.
@@ -249,9 +249,7 @@ per application.   Using HHWheelTimer instead can clean up the code quite
 a bit, because only a single HHWheelTimer is needed per thread, as
 opposed to one AsyncTimeoutSet per timeout time per thread.
 
-### TAsyncSignalHandler
-
-TODO: still in fbthrift
+### AsyncSignalHandler
 
 Used to handle AsyncSignals.  Similar to AsyncTimeout, for code
 clarity, we don't reuse the same fd as a socket to receive signals.