Add a macro to check if a task that shouldn't yield actually yields.
This commit is contained in:
parent
8fa21821ae
commit
978de7885a
6 changed files with 98 additions and 2 deletions
|
|
@ -101,6 +101,7 @@ set( fc_sources
|
||||||
src/thread/spin_lock.cpp
|
src/thread/spin_lock.cpp
|
||||||
src/thread/spin_yield_lock.cpp
|
src/thread/spin_yield_lock.cpp
|
||||||
src/thread/mutex.cpp
|
src/thread/mutex.cpp
|
||||||
|
src/thread/non_preemptable_scope_check.cpp
|
||||||
src/asio.cpp
|
src/asio.cpp
|
||||||
src/string.cpp
|
src/string.cpp
|
||||||
src/shared_ptr.cpp
|
src/shared_ptr.cpp
|
||||||
|
|
|
||||||
35
include/fc/thread/non_preemptable_scope_check.hpp
Normal file
35
include/fc/thread/non_preemptable_scope_check.hpp
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
#include <fc/time.hpp>
|
||||||
|
#include <fc/thread/spin_yield_lock.hpp>
|
||||||
|
|
||||||
|
// This file defines a macro:
|
||||||
|
// ASSERT_TASK_NOT_PREEMPTED()
|
||||||
|
// This macro is used to declare that the current scope is not expected to yield.
|
||||||
|
// If the task does yield, it will generate an assertion.
|
||||||
|
//
|
||||||
|
// Use this when you you're coding somethign that must not yield, and you think you
|
||||||
|
// have done it (just by virtue of the fact that you don't think you've called any
|
||||||
|
// functions that could yield). This will help detect when your assumptions are
|
||||||
|
// wrong and you accidentally call something that yields.
|
||||||
|
//
|
||||||
|
// This has no cost in release mode, and is extremely cheap in debug mode.
|
||||||
|
|
||||||
|
#define FC_NON_PREEMPTABLE_SCOPE_COMBINE_HELPER(x,y) x ## y
|
||||||
|
#define FC_NON_PREEMPTABLE_SCOPE_COMBINE(x,y) FC_NON_PREEMPTABLE_SCOPE_COMBINE_HELPER(x,y)
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
# define ASSERT_TASK_NOT_PREEMPTED() do {} while (0)
|
||||||
|
#else
|
||||||
|
# define ASSERT_TASK_NOT_PREEMPTED() fc::non_preemptable_scope_check FC_NON_PREEMPTABLE_SCOPE_COMBINE(scope_checker_, __LINE__)
|
||||||
|
|
||||||
|
namespace fc
|
||||||
|
{
|
||||||
|
class non_preemptable_scope_check
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
non_preemptable_scope_check();
|
||||||
|
~non_preemptable_scope_check();
|
||||||
|
};
|
||||||
|
} // namespace fc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -119,6 +119,9 @@ namespace fc {
|
||||||
friend class promise_base;
|
friend class promise_base;
|
||||||
friend class thread_d;
|
friend class thread_d;
|
||||||
friend class mutex;
|
friend class mutex;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
friend class non_preemptable_scope_check;
|
||||||
|
#endif
|
||||||
friend void yield();
|
friend void yield();
|
||||||
friend void usleep(const microseconds&);
|
friend void usleep(const microseconds&);
|
||||||
friend void sleep_until(const time_point&);
|
friend void sleep_until(const time_point&);
|
||||||
|
|
|
||||||
20
src/thread/non_preemptable_scope_check.cpp
Normal file
20
src/thread/non_preemptable_scope_check.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#include <fc/thread/non_preemptable_scope_check.hpp>
|
||||||
|
#include <fc/thread/thread.hpp>
|
||||||
|
#include "thread_d.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace fc
|
||||||
|
{
|
||||||
|
non_preemptable_scope_check::non_preemptable_scope_check()
|
||||||
|
{
|
||||||
|
++thread::current().my->non_preemptable_scope_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
non_preemptable_scope_check::~non_preemptable_scope_check()
|
||||||
|
{
|
||||||
|
assert(thread::current().my->non_preemptable_scope_count > 0);
|
||||||
|
--thread::current().my->non_preemptable_scope_count;
|
||||||
|
}
|
||||||
|
} // fc
|
||||||
|
#endif
|
||||||
|
|
@ -27,6 +27,9 @@ namespace fc {
|
||||||
ready_head(0),
|
ready_head(0),
|
||||||
ready_tail(0),
|
ready_tail(0),
|
||||||
blocked(0)
|
blocked(0)
|
||||||
|
#ifndef NDEBUG
|
||||||
|
,non_preemptable_scope_count(0)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
static boost::atomic<int> cnt(0);
|
static boost::atomic<int> cnt(0);
|
||||||
name = fc::string("th_") + char('a'+cnt++);
|
name = fc::string("th_") + char('a'+cnt++);
|
||||||
|
|
@ -84,8 +87,9 @@ namespace fc {
|
||||||
fc::context* ready_tail;
|
fc::context* ready_tail;
|
||||||
|
|
||||||
fc::context* blocked;
|
fc::context* blocked;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
unsigned non_preemptable_scope_count;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
void debug( const fc::string& s ) {
|
void debug( const fc::string& s ) {
|
||||||
|
|
@ -284,6 +288,7 @@ namespace fc {
|
||||||
* have it wait for something to do.
|
* have it wait for something to do.
|
||||||
*/
|
*/
|
||||||
bool start_next_fiber( bool reschedule = false ) {
|
bool start_next_fiber( bool reschedule = false ) {
|
||||||
|
assert(non_preemptable_scope_count == 0);
|
||||||
check_for_timeouts();
|
check_for_timeouts();
|
||||||
if( !current ) current = new fc::context( &fc::thread::current() );
|
if( !current ) current = new fc::context( &fc::thread::current() );
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,38 @@
|
||||||
#include <fc/thread/thread.hpp>
|
#include <fc/thread/thread.hpp>
|
||||||
#include <fc/log/logger.hpp>
|
#include <fc/log/logger.hpp>
|
||||||
#include <fc/exception/exception.hpp>
|
#include <fc/exception/exception.hpp>
|
||||||
|
#include <fc/thread/non_preemptable_scope_check.hpp>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE( test_non_preemptable_assertion )
|
||||||
|
{
|
||||||
|
return; // this isn't a real test, because the thing it tries to test works by asserting, not by throwing
|
||||||
|
fc::usleep(fc::milliseconds(10)); // this should not assert
|
||||||
|
{
|
||||||
|
ASSERT_TASK_NOT_PREEMPTED();
|
||||||
|
fc::usleep(fc::seconds(1)); // this should assert
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ASSERT_TASK_NOT_PREEMPTED();
|
||||||
|
{
|
||||||
|
ASSERT_TASK_NOT_PREEMPTED();
|
||||||
|
fc::usleep(fc::seconds(1)); // this should assert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ASSERT_TASK_NOT_PREEMPTED();
|
||||||
|
{
|
||||||
|
ASSERT_TASK_NOT_PREEMPTED();
|
||||||
|
int i = 4;
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
fc::usleep(fc::seconds(1)); // this should assert
|
||||||
|
}
|
||||||
|
|
||||||
|
fc::usleep(fc::seconds(1)); // this should not assert
|
||||||
|
BOOST_TEST_PASSPOINT();
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE( cancel_an_active_task )
|
BOOST_AUTO_TEST_CASE( cancel_an_active_task )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue