From d2711b8a7416ea6d4fb17ce47e5a74f6ab78e0df Mon Sep 17 00:00:00 2001 From: Alan Frindell Date: Thu, 2 Apr 2015 10:43:38 -0700 Subject: [PATCH] Move TAsyncSignalHandler into folly 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 | 89 ++++++++++++++++++++++++++ folly/io/async/AsyncSignalHandler.h | 92 +++++++++++++++++++++++++++ folly/io/async/README.md | 6 +- 3 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 folly/io/async/AsyncSignalHandler.cpp create mode 100644 folly/io/async/AsyncSignalHandler.h diff --git a/folly/io/async/AsyncSignalHandler.cpp b/folly/io/async/AsyncSignalHandler.cpp new file mode 100644 index 00000000..b2c91848 --- /dev/null +++ b/folly/io/async/AsyncSignalHandler.cpp @@ -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 + +#include + +#include + +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 ret = + signalEvents_.insert(make_pair(signum, event())); + if (!ret.second) { + // This signal has already been registered + throw std::runtime_error(folly::to( + "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( + "error initializing event handler for signal ", + signum)); + } + + if (event_add(ev, nullptr) != 0) { + throw std::runtime_error(folly::to( + "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( + "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(arg); + handler->signalReceived(signum); +} + +} // folly diff --git a/folly/io/async/AsyncSignalHandler.h b/folly/io/async/AsyncSignalHandler.h new file mode 100644 index 00000000..ca35bc17 --- /dev/null +++ b/folly/io/async/AsyncSignalHandler.h @@ -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 +#include +#include + +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 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 diff --git a/folly/io/async/README.md b/folly/io/async/README.md index e613bfc9..ce61f64e 100644 --- a/folly/io/async/README.md +++ b/folly/io/async/README.md @@ -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. -- 2.34.1