* 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 PLATFORMS_UNIX_LIBPANDABASE_SIGNAL_H
#define PLATFORMS_UNIX_LIBPANDABASE_SIGNAL_H
#include <csignal>
#include <functional>
#include <thread>
#include <condition_variable>
#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<int> signal_list = {})
{
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)
{
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<int> signals_list = {SIGUSR1}) : signal_ctl_(signals_list)
{
ASSERT(signals_list.size() > 0);
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<void()> after_thread_start_callback,
std::function<void()> 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 <typename SigAction, typename... Args>
void StartThread(SigAction *sig_action, Args... args)
{
ASSERT(catcher_thread_ == 0);
ASSERT(!is_running_);
if (!catch_only_catcher_thread_) {
signal_ctl_.Block();
}
catcher_thread_ = thread::ThreadStart(&SignalCatcherThread::Run<SigAction, Args...>, this, sig_action, args...);
std::unique_lock<std::mutex> cv_unique_lock(cv_lock_);
cv_.wait(cv_unique_lock, [this]() -> bool { return is_running_; });
}
void StopThread()
{
ASSERT(catcher_thread_ != 0);
ASSERT(is_running_);
is_running_ = false;
SendSignal(stop_chatcher_thread_signal_);
void **ret_val = nullptr;
thread::ThreadJoin(catcher_thread_, ret_val);
catcher_thread_ = 0;
if (!catch_only_catcher_thread_) {
signal_ctl_.Unblock();
}
}
private:
template <typename SigAction, typename... Args>
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<std::mutex> 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<void()> after_thread_start_callback_ {nullptr};
std::function<void()> before_thread_stop_callback_ {nullptr};
};
}
#endif