http://codereview.stackexchange.com/questions/84697/timeout-watchdog-using-a-standby-thread

he simple but generic timeout class to be used watching for network connections, user input, filesystem events, and is intended to have a very simple interface specific to only our use cases (i.e. no satisfy-all attitude).

Intended steps to use:

  1. Construct
  2. Activate
  3. Potentially react to a timeout, deactivates itself
  4. Re-activate
  5. Destruct cleanly

After triggering the alarm the guard is expected to be inactive until explicitly activated. The code expected to be reasonably tested (that is one of the issues).

This posted code as a testable Visual Studio 2013 project lives on the GitHub.

Besides a general feedback on the code quality - or if, please, please, please, you see a bug, I would love to hear about these areas:

  1. Destruction. Although I did my best to tell the guard thread to end, I am still concerned about having to join() in the destructor. Generally I love my destructors short and sweet for emergency landings - is it possible here? Is there an STL way to brutally kill that thread?

  2. Tests. Existing ones test for intended simple scenarios. I am not sure this is enough to claim that the code works as intended. Is it? I did not find a better way to test timing edge cases. Also, as tests are time-dependant, they occasionally spuriously fail when run on slow VMs. Or are they? Is it sufficient for code like this to know that if tests run somewhere consistently? If I increase timeouts the spurious fails go away, but that lengthens the overall project test run.

Below are the current header, implementation, and tests files to save the GitHub trip.

Header:

#pragma once

namespace utility
{
/**
The `clock` alias is for easy switching to `steady_clock` once Microsoft fixes it
*/
typedef std::chrono::system_clock clock; /**
The `TimeoutGuard` class triggers the `alarm` callback from the `guard_thread`
if `touch` was not called for at least the `timeout` duration. Because of the way the `guard_thread` sleeps, the actual detection may happen
as late as after `timeout` + `naptime` duration. Hence it is possible that the alarm
will not be called if the `TimeoutGuard` instance is touched within the
'timeout` and `timeout` + `naptime` timeframe. If not provided, by default the `naptime` is same as `timeout`. The `TimeoutGuard` is not active after construction, whicn means, that the
`guard_thread` will block until it is activated by calling the `watch` method. The `TimeoutGuard` class is not copyable and not moveable.
*/
class TimeoutGuard
{
public:
TimeoutGuard(
clock::duration timeout,
std::function<void( void )> alarm,
clock::duration naptime
); TimeoutGuard(
clock::duration timeout,
std::function<void( void )> alarm
); ~TimeoutGuard(); TimeoutGuard( const TimeoutGuard & ) = delete;
TimeoutGuard & operator=(const TimeoutGuard & ) = delete; TimeoutGuard( TimeoutGuard && ) = delete;
TimeoutGuard & operator=( TimeoutGuard && ) = delete; void watch();
void touch(); private: void guard(); clock::duration timeout;
clock::duration naptime;
std::function<void( void )> alarm; std::atomic_bool idle;
std::atomic_bool live; std::atomic<clock::time_point> touched; std::thread guard_thread;
std::mutex guard_mutex;
std::condition_variable wakeup;
};
}

Here is the implementation:

#include "stdafx.h"
#include "TimeoutGuard.h" namespace utility
{
TimeoutGuard::TimeoutGuard(
clock::duration timeout,
std::function<void( void )> alarm,
clock::duration naptime
)
: timeout( timeout )
, alarm( alarm )
, naptime( naptime )
{
idle.store( true );
live.store( true ); guard_thread = std::thread( std::bind( &TimeoutGuard::guard, this ) );
} TimeoutGuard::TimeoutGuard(
clock::duration timeout,
std::function<void( void )> alarm
)
: TimeoutGuard( timeout, alarm, timeout )
{}; TimeoutGuard::~TimeoutGuard()
{
live.store( false );
wakeup.notify_all();
guard_thread.join();
} void TimeoutGuard::guard()
{
while ( live.load() )
{
if ( idle.load() )
{
// Sleep indefinitely until either told to become active or destruct
std::unique_lock<std::mutex> live_lock( guard_mutex );
wakeup.wait( live_lock, [this]() { return ! this->idle.load() || ! this->live.load(); } );
}; // quit the loop if destructing
if ( ! live.load() ) break; // the actual timeout checking
auto now = clock::now(); if ( ( now - touched.load() ) > timeout )
{
idle.store( true );
alarm();
continue; // skip waiting for next timeout
} {
// sleep until next timeout check or destruction
std::unique_lock<std::mutex> live_lock( guard_mutex );
wakeup.wait_for( live_lock, naptime, [this](){ return ! this->live.load(); } );
}
};
} void TimeoutGuard::watch()
{
touch();
idle.store( false );
wakeup.notify_all();
} void TimeoutGuard::touch()
{
touched.store( clock::now() );
}
}

And, finally, existing tests:

#include "stdafx.h"
#include "CppUnitTest.h" #include "TimeoutGuard.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace utility
{
TEST_CLASS( TimeoutGuardTest )
{
public: bool triggered = false; void shoud_trigger()
{
triggered = true;
} TEST_METHOD( TimeoutGuardExpiration )
{
TimeoutGuard tg{
std::chrono::milliseconds{ 5 },
std::bind( &TimeoutGuardTest::shoud_trigger, this )
}; triggered = false;
tg.watch();
std::this_thread::sleep_for( std::chrono::milliseconds{ 10 } );
Assert::IsTrue( triggered, L"Failed to call the timeout alarm on the first run", LINE_INFO() ); triggered = false;
tg.watch();
std::this_thread::sleep_for( std::chrono::milliseconds{ 10 } );
Assert::IsTrue( triggered, L"Failed to call the timeout alarm on the second run", LINE_INFO() );
} TEST_METHOD( TimeoutGuardNoAlarm )
{
TimeoutGuard tg{
std::chrono::milliseconds{ 5 },
std::bind( &TimeoutGuardTest::shoud_trigger, this )
}; triggered = false;
tg.watch();
std::this_thread::sleep_for( std::chrono::milliseconds{ 1 } );
Assert::IsFalse( triggered, L"Wrongly called the timeout alarm on the first run", LINE_INFO() ); triggered = false;
tg.watch();
for (auto i = 0; i < 10; ++i)
{
std::this_thread::sleep_for( std::chrono::milliseconds{ 1 } );
tg.touch();
}
Assert::IsFalse( triggered, L"Wrongly called the timeout alarm on the second run", LINE_INFO() );
}
};
}

Timeout watchdog using a standby thread的更多相关文章

  1. Concurrent.Thread.js

    (function(){ if ( !this.Data || (typeof this.Data != 'object' && typeof this.Data != 'functi ...

  2. 多线程爬坑之路-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

  3. java中多线程中Runnable接口和Thread类介绍

    java中的线程时通过调用操作系统底层的线程来实现线程的功能的. 先看如下代码,并写出输出结果. // 请问输出结果是什么? public static void main(String[] args ...

  4. Thread的run()与start()的区别

    Java的线程是通过java.lang.Thread类来实现的.VM启动时会有一个由主方法所定义的线程.可以通过创建Thread的实例来创建新的线程.每个线程都是通过某个特定Thread对象所对应的方 ...

  5. Thread类源码剖析

    目录 1.引子 2.JVM线程状态 3.Thread常用方法 4.拓展点 一.引子 说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒. ...

  6. 从源码解读线程(Thread)和线程池(ThreadPoolExecutor)的状态

    线程是比进程更加轻量级的调度执行单位,理解线程是理解并发编程的不可或缺的一部分:而生产过程中不可能永远使用裸线程,需要线程池技术,线程池是管理和调度线程的资源池.因为前不久遇到了一个关于线程状态的问题 ...

  7. HttpClient throws TaskCanceledException on timeout

    error msg: HttpClient throws TaskCanceledException on timeout HttpClient is throwing a TaskCanceledE ...

  8. java Thread 类的源码阅读(oracle jdk1.8)

    java线程类的源码分析阅读技巧: 首先阅读thread类重点关注一下几个问题: 1.start() ,启动一个线程是如何实现的? 2.java线程状态机的变化过程以及如何实现的? 3. 1.star ...

  9. [Java多线程]-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

随机推荐

  1. 39、apk瘦身(转载)

    本文转自::Android开发中文站 » 关于APK瘦身值得分享的一些经验 从APK的文件结构说起 APK在安装和更新之前都需要经过网络将其下载到手机,如果APK越大消耗的流量就会越多,特别是对于使用 ...

  2. Robotium接入到Jenkins持续集成自动化测试

    6.3 将测试用例接入到Jenkins 由于我是自己学习的手机自动化测试,没有实际投入到工作中使用,jenkins的接入也没有具体操作,现摘抄一下网页:http://www.tuicool.com/a ...

  3. 初识面向对象-python

    Python 面向对象 一.概念的区分: 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发“更快更好 ...

  4. lsof命令查看端口关联的文件

    lsof命令查看端口关联的文件 lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网 ...

  5. [BZOJ2036]聪明的阿卑多

    [BZOJ2036]聪明的阿卑多 试题描述 也许你从没听说过阿卑多,但你一定知道他爷爷的爷爷的爷爷,那就是聪明绝顶的阿凡提先生.是的,阿卑多也是个聪明的小孩. 一天,阿卑多骑着他的小毛驴,在小镇上晃悠 ...

  6. bzoj 2387: [Ceoi2011]Traffic

    bzoj 2387: [Ceoi2011]Traffic 题目描述 The center of Gdynia is located on an island in the middle of the ...

  7. 旅行商(sale)

    旅行商(sale) 题目描述 camp国有n座城市,由1,2,-,n编号.城市由n–1条双向道路相连.任意两个城市之间存在唯一的道路连通.有m个旅行商,第i个旅行商会从城市ai旅行到城市bi,贩卖ci ...

  8. jQuery初级

    一.简介 定义 jQuery创始人是美国John Resig,是优秀的Javascript框架: jQuery是一个轻量级.快速简洁的javaScript库.源码戳这 jQuery对象 jQuery产 ...

  9. mrpt安装

    1.mrpt2.0参 See PPA for mrpt 2.0 branch (for mrpt 1.5.* read here). sudo add-apt-repository ppa:josel ...

  10. C语言中 单引号与双引号的区别

    在C语言中,字符用单引号,字符串用双引号.在c1='a';中,'a'是字符常量,必须用单引号."a"表示字符串,包含两个字符,一个是'a',一个是'\0'. 用数组来存储字符串. ...