lmkd: Implement watchdog thread am: af1b0e0627 am: ac20c53b63 am: c871dc1afc
Original change: https://android-review.googlesource.com/c/platform/system/memory/lmkd/+/1894200 Change-Id: I13e24f1b7dfe92359d56344e133843f0f64cec4e
This commit is contained in:
commit
bdb7ccf532
|
|
@ -15,6 +15,7 @@ cc_binary {
|
||||||
srcs: [
|
srcs: [
|
||||||
"lmkd.cpp",
|
"lmkd.cpp",
|
||||||
"reaper.cpp",
|
"reaper.cpp",
|
||||||
|
"watchdog.cpp",
|
||||||
],
|
],
|
||||||
shared_libs: [
|
shared_libs: [
|
||||||
"libcutils",
|
"libcutils",
|
||||||
|
|
|
||||||
121
lmkd.cpp
121
lmkd.cpp
|
|
@ -34,6 +34,8 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
#include <cutils/properties.h>
|
#include <cutils/properties.h>
|
||||||
#include <cutils/sockets.h>
|
#include <cutils/sockets.h>
|
||||||
#include <liblmkd_utils.h>
|
#include <liblmkd_utils.h>
|
||||||
|
|
@ -46,6 +48,7 @@
|
||||||
|
|
||||||
#include "reaper.h"
|
#include "reaper.h"
|
||||||
#include "statslog.h"
|
#include "statslog.h"
|
||||||
|
#include "watchdog.h"
|
||||||
|
|
||||||
#define BPF_FD_JUST_USE_INT
|
#define BPF_FD_JUST_USE_INT
|
||||||
#include "BpfSyscallWrappers.h"
|
#include "BpfSyscallWrappers.h"
|
||||||
|
|
@ -161,6 +164,8 @@ static inline void trace_kill_end() {}
|
||||||
|
|
||||||
#define LMKD_REINIT_PROP "lmkd.reinit"
|
#define LMKD_REINIT_PROP "lmkd.reinit"
|
||||||
|
|
||||||
|
#define WATCHDOG_TIMEOUT_SEC 2
|
||||||
|
|
||||||
/* default to old in-kernel interface if no memory pressure events */
|
/* default to old in-kernel interface if no memory pressure events */
|
||||||
static bool use_inkernel_interface = true;
|
static bool use_inkernel_interface = true;
|
||||||
static bool has_inkernel_module;
|
static bool has_inkernel_module;
|
||||||
|
|
@ -523,6 +528,11 @@ static struct proc *pidhash[PIDHASH_SZ];
|
||||||
|
|
||||||
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
|
#define ADJTOSLOT(adj) ((adj) + -OOM_SCORE_ADJ_MIN)
|
||||||
#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
|
#define ADJTOSLOT_COUNT (ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1)
|
||||||
|
|
||||||
|
// protects procadjslot_list from concurrent access
|
||||||
|
static std::shared_mutex adjslot_list_lock;
|
||||||
|
// procadjslot_list should be modified only from the main thread while exclusively holding
|
||||||
|
// adjslot_list_lock. Readers from non-main threads should hold adjslot_list_lock shared lock.
|
||||||
static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
|
static struct adjslot_list procadjslot_list[ADJTOSLOT_COUNT];
|
||||||
|
|
||||||
#define MAX_DISTINCT_OOM_ADJ 32
|
#define MAX_DISTINCT_OOM_ADJ 32
|
||||||
|
|
@ -912,13 +922,18 @@ static struct adjslot_list *adjslot_tail(struct adjslot_list *head) {
|
||||||
return asl == head ? NULL : asl;
|
return asl == head ? NULL : asl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be modified only from the main thread.
|
||||||
static void proc_slot(struct proc *procp) {
|
static void proc_slot(struct proc *procp) {
|
||||||
int adjslot = ADJTOSLOT(procp->oomadj);
|
int adjslot = ADJTOSLOT(procp->oomadj);
|
||||||
|
std::scoped_lock lock(adjslot_list_lock);
|
||||||
|
|
||||||
adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
|
adjslot_insert(&procadjslot_list[adjslot], &procp->asl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be modified only from the main thread.
|
||||||
static void proc_unslot(struct proc *procp) {
|
static void proc_unslot(struct proc *procp) {
|
||||||
|
std::scoped_lock lock(adjslot_list_lock);
|
||||||
|
|
||||||
adjslot_remove(&procp->asl);
|
adjslot_remove(&procp->asl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1955,16 +1970,24 @@ static void killinfo_log(struct proc* procp, int min_oom_score, int rss_kb,
|
||||||
|
|
||||||
/* log meminfo fields */
|
/* log meminfo fields */
|
||||||
for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
|
for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
|
||||||
android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
|
android_log_write_int32(ctx, mi ? (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX): 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* log lmkd wakeup information */
|
/* log lmkd wakeup information */
|
||||||
android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->last_event_tm, tm));
|
if (wi) {
|
||||||
android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->prev_wakeup_tm, tm));
|
android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->last_event_tm, tm));
|
||||||
android_log_write_int32(ctx, wi->wakeups_since_event);
|
android_log_write_int32(ctx, (int32_t)get_time_diff_ms(&wi->prev_wakeup_tm, tm));
|
||||||
android_log_write_int32(ctx, wi->skipped_wakeups);
|
android_log_write_int32(ctx, wi->wakeups_since_event);
|
||||||
|
android_log_write_int32(ctx, wi->skipped_wakeups);
|
||||||
|
} else {
|
||||||
|
android_log_write_int32(ctx, 0);
|
||||||
|
android_log_write_int32(ctx, 0);
|
||||||
|
android_log_write_int32(ctx, 0);
|
||||||
|
android_log_write_int32(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
android_log_write_int32(ctx, (int32_t)min(swap_kb, INT32_MAX));
|
android_log_write_int32(ctx, (int32_t)min(swap_kb, INT32_MAX));
|
||||||
android_log_write_int32(ctx, (int32_t)mi->field.total_gpu_kb);
|
android_log_write_int32(ctx, mi ? (int32_t)mi->field.total_gpu_kb : 0);
|
||||||
if (ki) {
|
if (ki) {
|
||||||
android_log_write_int32(ctx, ki->thrashing);
|
android_log_write_int32(ctx, ki->thrashing);
|
||||||
android_log_write_int32(ctx, ki->max_thrashing);
|
android_log_write_int32(ctx, ki->max_thrashing);
|
||||||
|
|
@ -1977,10 +2000,33 @@ static void killinfo_log(struct proc* procp, int min_oom_score, int rss_kb,
|
||||||
android_log_reset(ctx);
|
android_log_reset(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct proc *proc_adj_lru(int oomadj) {
|
// Note: returned entry is only an anchor and does not hold a valid process info.
|
||||||
|
// When called from a non-main thread, adjslot_list_lock read lock should be taken.
|
||||||
|
static struct proc *proc_adj_head(int oomadj) {
|
||||||
|
return (struct proc *)&procadjslot_list[ADJTOSLOT(oomadj)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// When called from a non-main thread, adjslot_list_lock read lock should be taken.
|
||||||
|
static struct proc *proc_adj_tail(int oomadj) {
|
||||||
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When called from a non-main thread, adjslot_list_lock read lock should be taken.
|
||||||
|
static struct proc *proc_adj_prev(int oomadj, int pid) {
|
||||||
|
struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
|
||||||
|
struct adjslot_list *curr = adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
|
||||||
|
|
||||||
|
while (curr != head) {
|
||||||
|
if (((struct proc *)curr)->pid == pid) {
|
||||||
|
return (struct proc *)curr->prev;
|
||||||
|
}
|
||||||
|
curr = curr->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When called from a non-main thread, adjslot_list_lock read lock should be taken.
|
||||||
static struct proc *proc_get_heaviest(int oomadj) {
|
static struct proc *proc_get_heaviest(int oomadj) {
|
||||||
struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
|
struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
|
||||||
struct adjslot_list *curr = head->next;
|
struct adjslot_list *curr = head->next;
|
||||||
|
|
@ -2004,6 +2050,55 @@ static struct proc *proc_get_heaviest(int oomadj) {
|
||||||
return maxprocp;
|
return maxprocp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool find_victim(int oom_score, int prev_pid, struct proc &target_proc) {
|
||||||
|
struct proc *procp;
|
||||||
|
std::shared_lock lock(adjslot_list_lock);
|
||||||
|
|
||||||
|
if (!prev_pid) {
|
||||||
|
procp = proc_adj_tail(oom_score);
|
||||||
|
} else {
|
||||||
|
procp = proc_adj_prev(oom_score, prev_pid);
|
||||||
|
if (!procp) {
|
||||||
|
// pid was removed, restart at the tail
|
||||||
|
procp = proc_adj_tail(oom_score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the list is empty at this oom_score or we looped through it
|
||||||
|
if (!procp || procp == proc_adj_head(oom_score)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a copy because original might be destroyed after adjslot_list_lock is released
|
||||||
|
target_proc = *procp;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void watchdog_callback() {
|
||||||
|
int prev_pid = 0;
|
||||||
|
|
||||||
|
ALOGW("lmkd watchdog timed out!");
|
||||||
|
for (int oom_score = OOM_SCORE_ADJ_MAX; oom_score >= 0;) {
|
||||||
|
struct proc target;
|
||||||
|
|
||||||
|
if (!find_victim(oom_score, prev_pid, target)) {
|
||||||
|
oom_score--;
|
||||||
|
prev_pid = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reaper.kill({ target.pidfd, target.pid }, true) == 0) {
|
||||||
|
ALOGW("lmkd watchdog killed process %d, oom_score_adj %d", target.pid, oom_score);
|
||||||
|
killinfo_log(&target, 0, 0, 0, NULL, NULL, NULL, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev_pid = target.pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Watchdog watchdog(WATCHDOG_TIMEOUT_SEC, watchdog_callback);
|
||||||
|
|
||||||
static bool is_kill_pending(void) {
|
static bool is_kill_pending(void) {
|
||||||
char buf[24];
|
char buf[24];
|
||||||
|
|
||||||
|
|
@ -2165,7 +2260,7 @@ static int kill_one_process(struct proc* procp, int min_oom_score, struct kill_i
|
||||||
trace_kill_start(pid, desc);
|
trace_kill_start(pid, desc);
|
||||||
|
|
||||||
start_wait_for_proc_kill(pidfd < 0 ? pid : pidfd);
|
start_wait_for_proc_kill(pidfd < 0 ? pid : pidfd);
|
||||||
kill_result = reaper.kill({ pidfd, pid });
|
kill_result = reaper.kill({ pidfd, pid }, false);
|
||||||
|
|
||||||
trace_kill_end();
|
trace_kill_end();
|
||||||
|
|
||||||
|
|
@ -2241,7 +2336,7 @@ static int find_and_kill_process(int min_score_adj, struct kill_info *ki, union
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
procp = choose_heaviest_task ?
|
procp = choose_heaviest_task ?
|
||||||
proc_get_heaviest(i) : proc_adj_lru(i);
|
proc_get_heaviest(i) : proc_adj_tail(i);
|
||||||
|
|
||||||
if (!procp)
|
if (!procp)
|
||||||
break;
|
break;
|
||||||
|
|
@ -3297,6 +3392,7 @@ static void call_handler(struct event_handler_info* handler_info,
|
||||||
struct polling_params *poll_params, uint32_t events) {
|
struct polling_params *poll_params, uint32_t events) {
|
||||||
struct timespec curr_tm;
|
struct timespec curr_tm;
|
||||||
|
|
||||||
|
watchdog.start();
|
||||||
poll_params->update = POLLING_DO_NOT_CHANGE;
|
poll_params->update = POLLING_DO_NOT_CHANGE;
|
||||||
handler_info->handler(handler_info->data, events, poll_params);
|
handler_info->handler(handler_info->data, events, poll_params);
|
||||||
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
|
||||||
|
|
@ -3328,6 +3424,7 @@ static void call_handler(struct event_handler_info* handler_info,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
watchdog.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mainloop(void) {
|
static void mainloop(void) {
|
||||||
|
|
@ -3409,7 +3506,9 @@ static void mainloop(void) {
|
||||||
if ((evt->events & EPOLLHUP) && evt->data.ptr) {
|
if ((evt->events & EPOLLHUP) && evt->data.ptr) {
|
||||||
ALOGI("lmkd data connection dropped");
|
ALOGI("lmkd data connection dropped");
|
||||||
handler_info = (struct event_handler_info*)evt->data.ptr;
|
handler_info = (struct event_handler_info*)evt->data.ptr;
|
||||||
|
watchdog.start();
|
||||||
ctrl_data_close(handler_info->data);
|
ctrl_data_close(handler_info->data);
|
||||||
|
watchdog.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3550,6 +3649,10 @@ int main(int argc, char **argv) {
|
||||||
reaper.thread_cnt());
|
reaper.thread_cnt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!watchdog.init()) {
|
||||||
|
ALOGE("Failed to initialize the watchdog");
|
||||||
|
}
|
||||||
|
|
||||||
mainloop();
|
mainloop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
11
reaper.cpp
11
reaper.cpp
|
|
@ -206,18 +206,23 @@ bool Reaper::async_kill(const struct target_proc& target) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Reaper::kill(const struct target_proc& target) {
|
int Reaper::kill(const struct target_proc& target, bool synchronous) {
|
||||||
/* CAP_KILL required */
|
/* CAP_KILL required */
|
||||||
if (target.pidfd < 0) {
|
if (target.pidfd < 0) {
|
||||||
return ::kill(target.pid, SIGKILL);
|
return ::kill(target.pid, SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (async_kill(target)) {
|
if (!synchronous && async_kill(target)) {
|
||||||
// we assume the kill will be successful and if it fails we will be notified
|
// we assume the kill will be successful and if it fails we will be notified
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pidfd_send_signal(target.pidfd, SIGKILL, NULL, 0);
|
int result = pidfd_send_signal(target.pidfd, SIGKILL, NULL, 0);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_reaping_supported() ? process_mrelease(target.pidfd, 0) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reaper::target_proc Reaper::dequeue_request() {
|
Reaper::target_proc Reaper::dequeue_request() {
|
||||||
|
|
|
||||||
2
reaper.h
2
reaper.h
|
|
@ -51,7 +51,7 @@ public:
|
||||||
bool debug_enabled() const { return debug_enabled_; }
|
bool debug_enabled() const { return debug_enabled_; }
|
||||||
|
|
||||||
// return 0 on success or error code returned by the syscall
|
// return 0 on success or error code returned by the syscall
|
||||||
int kill(const struct target_proc& target);
|
int kill(const struct target_proc& target, bool synchronous);
|
||||||
// below members are used only by reaper_main
|
// below members are used only by reaper_main
|
||||||
target_proc dequeue_request();
|
target_proc dequeue_request();
|
||||||
void request_complete();
|
void request_complete();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Google, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "lowmemorykiller"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <log/log.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <processgroup/processgroup.h>
|
||||||
|
|
||||||
|
#include "watchdog.h"
|
||||||
|
|
||||||
|
static void* watchdog_main(void* param) {
|
||||||
|
Watchdog *watchdog = static_cast<Watchdog*>(param);
|
||||||
|
sigset_t sigset;
|
||||||
|
int signum;
|
||||||
|
|
||||||
|
// Ensure the thread does not use little cores
|
||||||
|
if (!SetTaskProfiles(gettid(), {"CPUSET_SP_FOREGROUND"}, true)) {
|
||||||
|
ALOGE("Failed to assign cpuset to the watchdog thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!watchdog->create_timer(sigset)) {
|
||||||
|
ALOGE("Watchdog timer creation failed!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (sigwait(&sigset, &signum) == -1) {
|
||||||
|
ALOGE("sigwait failed: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
watchdog->bite();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watchdog::init() {
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
if (pthread_create(&thread, NULL, watchdog_main, this)) {
|
||||||
|
ALOGE("pthread_create failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pthread_setname_np(thread, "lmkd_watchdog")) {
|
||||||
|
ALOGW("pthread_setname_np failed: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watchdog::start() {
|
||||||
|
// Start the timer and keep it active until it's disarmed
|
||||||
|
struct itimerspec new_timer;
|
||||||
|
|
||||||
|
if (!timer_created_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_timer.it_value.tv_sec = timeout_;
|
||||||
|
new_timer.it_value.tv_nsec = 0;
|
||||||
|
new_timer.it_interval.tv_sec = timeout_;
|
||||||
|
new_timer.it_interval.tv_nsec = 0;
|
||||||
|
|
||||||
|
if (timer_settime(timer_, 0, &new_timer, NULL)) {
|
||||||
|
ALOGE("timer_settime failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watchdog::stop() {
|
||||||
|
struct itimerspec new_timer = {};
|
||||||
|
|
||||||
|
if (!timer_created_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer_settime(timer_, 0, &new_timer, NULL)) {
|
||||||
|
ALOGE("timer_settime failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Watchdog::create_timer(sigset_t &sigset) {
|
||||||
|
struct sigevent sevent;
|
||||||
|
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGALRM);
|
||||||
|
if (sigprocmask(SIG_BLOCK, &sigset, NULL)) {
|
||||||
|
ALOGE("sigprocmask failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sevent.sigev_notify = SIGEV_THREAD_ID;
|
||||||
|
sevent.sigev_notify_thread_id = gettid();
|
||||||
|
sevent.sigev_signo = SIGALRM;
|
||||||
|
if (timer_create(CLOCK_MONOTONIC, &sevent, &timer_)) {
|
||||||
|
ALOGE("timer_create failed: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer_created_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Google, 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 <atomic>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
class Watchdog {
|
||||||
|
private:
|
||||||
|
int timeout_;
|
||||||
|
timer_t timer_;
|
||||||
|
std::atomic<bool> timer_created_;
|
||||||
|
void (*callback_)();
|
||||||
|
public:
|
||||||
|
Watchdog(int timeout, void (*callback)()) :
|
||||||
|
timeout_(timeout), timer_created_(false), callback_(callback) {}
|
||||||
|
|
||||||
|
bool init();
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
// used by the watchdog_main
|
||||||
|
bool create_timer(sigset_t &sigset);
|
||||||
|
void bite() const { if (callback_) callback_(); }
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue