/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * 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. */ #ifndef PANDA_LIBPANDABASE_OS_UNIX_SIGNAL_H_ #define PANDA_LIBPANDABASE_OS_UNIX_SIGNAL_H_ #include #include #include #include #include "libpandabase/macros.h" #include "libpandabase/utils/logger.h" #include "libpandabase/os/thread.h" #include "libpandabase/os/failure_retry.h" namespace panda::os::unix { class SignalCtl { public: SignalCtl(std::initializer_list signal_list = {}) // NOLINT(cppcoreguidelines-pro-type-member-init) { LOG_IF(::sigemptyset(&sigset_) == -1, FATAL, COMMON) << "sigemptyset failed"; for (int sig : signal_list) { Add(sig); } } ~SignalCtl() = default; NO_MOVE_SEMANTIC(SignalCtl); NO_COPY_SEMANTIC(SignalCtl); void Add(int sig) { LOG_IF(::sigaddset(&sigset_, sig) == -1, FATAL, COMMON) << "sigaddset failed"; } void Delete(int sig) { LOG_IF(::sigdelset(&sigset_, sig) == -1, FATAL, COMMON) << "sigaddset failed"; } bool IsExist(int sig) const { int ret = ::sigismember(&sigset_, sig); LOG_IF(ret == -1, FATAL, COMMON) << "sigismember failed"; return ret == 1; } void Block() { LOG_IF(::pthread_sigmask(SIG_BLOCK, &sigset_, nullptr) == -1, FATAL, COMMON) << "pthread_sigmask failed"; } void Unblock() { LOG_IF(::pthread_sigmask(SIG_UNBLOCK, &sigset_, nullptr) == -1, FATAL, COMMON) << "pthread_sigmask failed"; } int Wait() const { int sig = 0; LOG_IF(PANDA_FAILURE_RETRY(sigwait(&sigset_, &sig)) == -1, FATAL, COMMON) << "sigwait failed"; return sig; } static void GetCurrent(SignalCtl &out) // NOLINT(google-runtime-references) { LOG_IF(::pthread_sigmask(SIG_SETMASK, nullptr, &out.sigset_) == -1, FATAL, COMMON) << "pthread_sigmask failed"; } private: sigset_t sigset_; }; class SignalCatcherThread { public: SignalCatcherThread(std::initializer_list signals_list = {SIGUSR1}) : signal_ctl_(signals_list) { ASSERT(signals_list.size() > 0); // Use the first signal as the stop catcher thread signal stop_chatcher_thread_signal_ = *signals_list.begin(); } ~SignalCatcherThread() = default; NO_MOVE_SEMANTIC(SignalCatcherThread); NO_COPY_SEMANTIC(SignalCatcherThread); void CatchOnlyCatcherThread() { ASSERT(catcher_thread_ == 0 && "Use CatchOnlyCatcherThread() before StartThread()"); catch_only_catcher_thread_ = true; } void SetupCallbacks(std::function after_thread_start_callback, std::function before_thread_stop_callback) { after_thread_start_callback_ = std::move(after_thread_start_callback); before_thread_stop_callback_ = std::move(before_thread_stop_callback); } void SendSignal(int sig) { ASSERT(catcher_thread_ != 0); thread::ThreadSendSignal(catcher_thread_, sig); } template void StartThread(SigAction *sig_action, Args... args) { ASSERT(catcher_thread_ == 0); ASSERT(!is_running_); if (!catch_only_catcher_thread_) { signal_ctl_.Block(); } // Start catcher_thread_ catcher_thread_ = thread::ThreadStart(&SignalCatcherThread::Run, this, sig_action, args...); // Wait until the catcher_thread_ is started std::unique_lock cv_unique_lock(cv_lock_); cv_.wait(cv_unique_lock, [this]() -> bool { return is_running_; }); } void StopThread() { ASSERT(catcher_thread_ != 0); ASSERT(is_running_); // Stop catcher_thread_ is_running_ = false; SendSignal(stop_chatcher_thread_signal_); // Wait for catcher_thread_ to finish void **ret_val = nullptr; thread::ThreadJoin(catcher_thread_, ret_val); catcher_thread_ = 0; if (!catch_only_catcher_thread_) { signal_ctl_.Unblock(); } } private: template static void Run(SignalCatcherThread *self, SigAction *sig_action, Args... args) { LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: Starting the signal catcher thread"; if (self->after_thread_start_callback_ != nullptr) { self->after_thread_start_callback_(); } if (self->catch_only_catcher_thread_) { self->signal_ctl_.Block(); } { std::lock_guard lock_guard(self->cv_lock_); self->is_running_ = true; } self->cv_.notify_one(); while (true) { LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: waiting"; int sig = self->signal_ctl_.Wait(); if (!self->is_running_) { LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: exit loop, cause signal catcher thread was stopped"; break; } LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: signal[" << sig << "] handling begins"; sig_action(sig, args...); LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: signal[" << sig << "] handling ends"; } if (self->catch_only_catcher_thread_) { self->signal_ctl_.Unblock(); } if (self->before_thread_stop_callback_ != nullptr) { self->before_thread_stop_callback_(); } LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: Finishing the signal catcher thread"; } std::mutex cv_lock_; std::condition_variable cv_; SignalCtl signal_ctl_; thread::native_handle_type catcher_thread_ {0}; int stop_chatcher_thread_signal_ {SIGUSR1}; bool catch_only_catcher_thread_ {false}; std::atomic_bool is_running_ {false}; std::function after_thread_start_callback_ {nullptr}; std::function before_thread_stop_callback_ {nullptr}; }; } // namespace panda::os::unix #endif // PANDA_LIBPANDABASE_OS_UNIX_SIGNAL_H_