#ifdef _WIN32
	#include <windows.h>
#else
	#include <sys/time.h>
#endif

#include "boost/bind/bind.hpp"
#include "boost/chrono.hpp"
#include "boost/chrono/ceil.hpp"
#include "boost/date_time.hpp"
#include "boost/thread/concurrent_queues/sync_priority_queue.hpp"
#include "boost/thread/concurrent_queues/sync_timed_queue.hpp"
#include "boost/thread/future.hpp"
#include "boost/thread/mutex.hpp"
#include "boost/thread/recursive_mutex.hpp"
#include "boost/thread/shared_lock_guard.hpp"
#include "boost/thread/shared_mutex.hpp"
#include "boost/thread/thread.hpp"

#include <iomanip>
#ifdef TEST_CPP14_FEATURES
#include <future>
#include <mutex>
#include <shared_mutex>
#include <thread>
#endif

/******************************************************************************/

/*
 * Summary:
 *
 * This code tests the behavior of time-related functions in the presence of
 * system clock changes (jumps). It requires root/Administrator privileges in
 * order to run because it changes the system clock. NTP should also be disabled
 * while running this code so that NTP can't change the system clock.
 *
 * Each function to be tested is executed five times. The amount of time the
 * function waits before returning is measured against the amount of time the
 * function was expected to wait. If the difference exceeds a threshold value
 * (defined below) then the test fails.
 *
 * The following values are intentially:
 * - more than 200 milliseconds
 * - more than 200 milliseconds apart
 * - not a multiple of 100 milliseconds
 * - not a multiple of each other
 * - don't sum or diff to a multiple of 100 milliseconds
 */
const long long s_waitMs            = 580;
const long long s_shortJumpMs       = 230;
const long long s_longJumpMs        = 870; // Causes additional, unavoidable failures when BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC is disabled
const long long s_sleepBeforeJumpMs = 110;

#ifdef _WIN32
const long long s_maxEarlyErrorMs =  10
                                  + 100; // Windows is unpredictable, especially in a VM, so allow extra time if the function returns early
const long long s_maxLateErrorMs  = 110  // due to polling, functions may not return for up to 100 milliseconds after they are supposed to
                                  + 100; // Windows is slow, especially in a VM, so allow extra time for the functions to return
#else
const long long s_maxEarlyErrorMs =  10;
const long long s_maxLateErrorMs  = 110; // Due to polling, functions may not return for up to 100 milliseconds after they are supposed to
#endif

int g_numTestsRun    = 0;
int g_numTestsPassed = 0;
int g_numTestsFailed = 0;

/******************************************************************************/

// A custom clock based off the system clock but with a different epoch.

namespace custom
{
    class custom_boost_clock
    {
    public:
        typedef boost::chrono::microseconds                   duration; // intentionally not nanoseconds
        typedef duration::rep                                 rep;
        typedef duration::period                              period;
        typedef boost::chrono::time_point<custom_boost_clock> time_point;
        static bool is_steady;

        static time_point now();
    };

    bool custom_boost_clock::is_steady = false;

    custom_boost_clock::time_point custom_boost_clock::now()
    {
        return time_point(boost::chrono::ceil<duration>(boost::chrono::system_clock::now().time_since_epoch()) - boost::chrono::hours(10 * 365 * 24));
    }

#ifdef TEST_CPP14_FEATURES
    class custom_std_clock
    {
    public:
        typedef std::chrono::microseconds                 duration; // intentionally not nanoseconds
        typedef duration::rep                             rep;
        typedef duration::period                          period;
        typedef std::chrono::time_point<custom_std_clock> time_point;
        static bool is_steady;

        static time_point now();
    };

    bool custom_std_clock::is_steady = false;

    custom_std_clock::time_point custom_std_clock::now()
    {
        return time_point(std::chrono::duration_cast<duration>(std::chrono::system_clock::now().time_since_epoch()) - std::chrono::hours(10 * 365 * 24));
    }
#endif
}

/******************************************************************************/

template <typename MutexType = boost::mutex, typename CondType = boost::condition_variable>
struct BoostHelper
{
    typedef MutexType mutex;
    typedef CondType cond;

    typedef boost::lock_guard<MutexType> lock_guard;
    typedef boost::unique_lock<MutexType> unique_lock;

    typedef boost::chrono::milliseconds milliseconds;
    typedef boost::chrono::nanoseconds nanoseconds;

    typedef boost::chrono::system_clock system_clock;
    typedef boost::chrono::steady_clock steady_clock;
    typedef custom::custom_boost_clock custom_clock;

    typedef system_clock::time_point system_time_point;
    typedef steady_clock::time_point steady_time_point;
    typedef custom_clock::time_point custom_time_point;

    typedef boost::cv_status cv_status;
    typedef boost::future_status future_status;

    typedef boost::packaged_task<bool> packaged_task;
    typedef boost::future<bool> future;
    typedef boost::shared_future<bool> shared_future;

    typedef boost::thread thread;

    static const milliseconds waitDur;

    template <typename T>
    static void sleep_for(T d)
    {
        boost::this_thread::sleep_for(d);
    }

    template <typename T>
    static void sleep_for_no_int(T d)
    {
        boost::this_thread::no_interruption_point::sleep_for(d);
    }

    template <typename T>
    static void sleep_until(T t)
    {
        boost::this_thread::sleep_until(t);
    }

    template <typename T>
    static void sleep_until_no_int(T t)
    {
        boost::this_thread::no_interruption_point::sleep_until(t);
    }

    static system_time_point systemNow()
    {
        return system_clock::now();
    }

    static steady_time_point steadyNow()
    {
        return steady_clock::now();
    }

    static custom_time_point customNow()
    {
        return custom_clock::now();
    }

    template <class ToDuration, class Rep, class Period>
    static ToDuration duration_cast(const boost::chrono::duration<Rep, Period>& d)
    {
        return boost::chrono::duration_cast<ToDuration>(d);
    }

    static milliseconds zero()
    {
        return milliseconds(0);
    }
};

template <typename MutexType, typename CondType>
const typename BoostHelper<MutexType, CondType>::milliseconds
BoostHelper<MutexType, CondType>::waitDur = typename BoostHelper<MutexType, CondType>::milliseconds(s_waitMs);

#ifdef TEST_CPP14_FEATURES
template <typename MutexType = std::mutex, typename CondType = std::condition_variable>
struct StdHelper
{
    typedef MutexType mutex;
    typedef CondType cond;

    typedef std::lock_guard<MutexType> lock_guard;
    typedef std::unique_lock<MutexType> unique_lock;

    typedef std::chrono::milliseconds milliseconds;
    typedef std::chrono::nanoseconds nanoseconds;

    typedef std::chrono::system_clock system_clock;
    typedef std::chrono::steady_clock steady_clock;
    typedef custom::custom_std_clock custom_clock;

    typedef system_clock::time_point system_time_point;
    typedef steady_clock::time_point steady_time_point;
    typedef custom_clock::time_point custom_time_point;

    typedef std::cv_status cv_status;
    typedef std::future_status future_status;

    typedef std::packaged_task<bool()> packaged_task;
    typedef std::future<bool> future;
    typedef std::shared_future<bool> shared_future;

    typedef std::thread thread;

    static const milliseconds waitDur;

    template <typename T>
    static void sleep_for(T d)
    {
        std::this_thread::sleep_for(d);
    }

    template <typename T>
    static void sleep_until(T t)
    {
        std::this_thread::sleep_until(t);
    }

    static system_time_point systemNow()
    {
        return system_clock::now();
    }

    static steady_time_point steadyNow()
    {
        return steady_clock::now();
    }

    static custom_time_point customNow()
    {
        return custom_clock::now();
    }

    template <class ToDuration, class Rep, class Period>
    static ToDuration duration_cast(const std::chrono::duration<Rep, Period>& d)
    {
        return std::chrono::duration_cast<ToDuration>(d);
    }

    static milliseconds zero()
    {
        return milliseconds(0);
    }
};

template <typename MutexType, typename CondType>
const typename StdHelper<MutexType, CondType>::milliseconds
StdHelper<MutexType, CondType>::waitDur = typename StdHelper<MutexType, CondType>::milliseconds(s_waitMs);
#endif

/******************************************************************************/

#ifdef _WIN32

void changeSystemTime(long long changeMs)
{
	Sleep(s_sleepBeforeJumpMs);

	SYSTEMTIME systemTime;
	GetSystemTime(&systemTime);

    FILETIME fileTime;
    if (!SystemTimeToFileTime(&systemTime, &fileTime))
    {
        std::cout << "ERROR: Couldn't convert system time to file time" << std::endl;
    }

    ULARGE_INTEGER largeInt;
    largeInt.LowPart  = fileTime.dwLowDateTime;
    largeInt.HighPart = fileTime.dwHighDateTime;
    largeInt.QuadPart += changeMs * 10000;
    fileTime.dwLowDateTime  = largeInt.LowPart;
    fileTime.dwHighDateTime = largeInt.HighPart;

    if (!FileTimeToSystemTime(&fileTime, &systemTime))
    {
        std::cout << "ERROR: Couldn't convert file time to system time" << std::endl;
    }

    if (!SetSystemTime(&systemTime))
    {
        std::cout << "ERROR: Couldn't set system time" << std::endl;
    }
}

#else

void changeSystemTime(long long changeMs)
{
    struct timespec sleepTs;
    sleepTs.tv_sec  = (s_sleepBeforeJumpMs / 1000);
    sleepTs.tv_nsec = (s_sleepBeforeJumpMs % 1000) * 1000000;
    nanosleep(&sleepTs, NULL);

    struct timeval tv;
    if (gettimeofday(&tv, NULL) != 0)
    {
        std::cout << "ERROR: Couldn't get system time" << std::endl;
    }

    changeMs += tv.tv_sec  * 1000;
    changeMs += tv.tv_usec / 1000;
    tv.tv_sec  = (changeMs / 1000);
    tv.tv_usec = (changeMs % 1000) * 1000;

    if (settimeofday(&tv, NULL) != 0)
    {
        std::cout << "ERROR: Couldn't set system time" << std::endl;
    }
}

#endif

enum RcEnum
{
    e_no_timeout,
    e_timeout,
    e_failed_bad,
    e_failed_good,
    e_succeeded_bad,
    e_succeeded_good,
    e_ready_bad,
    e_not_ready_good,
    e_na
};

template <typename Helper>
void checkWaitTime(typename Helper::nanoseconds expected, typename Helper::nanoseconds actual, RcEnum rc)
{
    if (expected != Helper::zero() && expected < typename Helper::milliseconds(s_sleepBeforeJumpMs))
    {
        expected = typename Helper::milliseconds(s_sleepBeforeJumpMs);
    }

    typename Helper::milliseconds expectedMs = Helper::template duration_cast<typename Helper::milliseconds>(expected);
    typename Helper::milliseconds actualMs   = Helper::template duration_cast<typename Helper::milliseconds>(actual);

    std::cout << "Expected: " << std::setw(4) << expectedMs.count() << " ms"
              << ", Actual: " << std::setw(4) << actualMs.count() << " ms"
              << ", Returned: ";
    switch (rc)
    {
        case e_no_timeout     : std::cout << "no_timeout, "; break;
        case e_timeout        : std::cout << "timeout,    "; break;
        case e_failed_bad     : std::cout << "failed,     "; break;
        case e_failed_good    : std::cout << "failed,     "; break;
        case e_succeeded_bad  : std::cout << "succeeded,  "; break;
        case e_succeeded_good : std::cout << "succeeded,  "; break;
        case e_ready_bad      : std::cout << "ready,      "; break;
        case e_not_ready_good : std::cout << "not_ready,  "; break;
        default               : std::cout << "N/A,        "; break;
    }

    if (expectedMs == Helper::zero())
    {
        std::cout << "FAILED: SKIPPED (test would lock up if run)";
        g_numTestsFailed++;
    }
    else if (actual < expected - typename Helper::milliseconds(s_maxEarlyErrorMs))
    {
        std::cout << "FAILED: TOO SHORT";
        if (rc == e_timeout) // bad
        {
            std::cout << ", RETURNED TIMEOUT";
        }
        else if (rc == e_failed_bad)
        {
            std::cout << ", RETURNED FAILED";
        }
        else if (rc == e_succeeded_bad)
        {
            std::cout << ", RETURNED SUCCEEDED";
        }
        else if (rc == e_ready_bad)
        {
            std::cout << ", RETURNED READY";
        }
        g_numTestsFailed++;
    }
    else if (actual > expected + typename Helper::milliseconds(s_maxLateErrorMs))
    {
        std::cout << "FAILED: TOO LONG";
        if (rc == e_no_timeout) // bad
        {
            std::cout << ", RETURNED NO_TIMEOUT";
        }
        else if (rc == e_failed_bad)
        {
            std::cout << ", RETURNED FAILED";
        }
        else if (rc == e_succeeded_bad)
        {
            std::cout << ", RETURNED SUCCEEDED";
        }
        else if (rc == e_ready_bad)
        {
            std::cout << ", RETURNED READY";
        }
        g_numTestsFailed++;
    }
    else if (rc == e_no_timeout) // bad
    {
        std::cout << "FAILED: RETURNED NO_TIMEOUT";
        g_numTestsFailed++;
    }
    else if (rc == e_failed_bad)
    {
        std::cout << "FAILED: RETURNED FAILED";
        g_numTestsFailed++;
    }
    else if (rc == e_succeeded_bad)
    {
        std::cout << "FAILED: RETURNED SUCCEEDED";
        g_numTestsFailed++;
    }
    else if (rc == e_ready_bad)
    {
        std::cout << "FAILED: RETURNED READY";
        g_numTestsFailed++;
    }
    else
    {
        std::cout << "Passed";
        g_numTestsPassed++;
    }
    std::cout << std::endl;

    g_numTestsRun++;
}

void sleepForLongTime()
{
#ifdef _WIN32
	Sleep(10000);
#else
    struct timespec ts = {5, 0};
    nanosleep(&ts, NULL);
#endif
}

bool returnFalse()
{
    return false;
}

/******************************************************************************/

// Run the test in the context provided, which may be the current thread or a separate thread.
template <typename Helper, typename Context, typename Function>
void runTestInContext(Context context, Function func, const std::string name)
{
    std::cout << name << ":" << std::endl;

    {
        std::cout << "    While system clock remains stable:        ";
        context(func, 0);
    }

    {
        std::cout << "    While system clock jumps back (short):    ";
        typename Helper::thread t(boost::bind(changeSystemTime, -s_shortJumpMs));
        context(func, -s_shortJumpMs);
        t.join();
    }

    {
        std::cout << "    While system clock jumps back (long):     ";
        typename Helper::thread t(boost::bind(changeSystemTime, -s_longJumpMs));
        context(func, -s_longJumpMs);
        t.join();
    }

    {
        std::cout << "    While system clock jumps forward (short): ";
        typename Helper::thread t(boost::bind(changeSystemTime, s_shortJumpMs));
        context(func, s_shortJumpMs);
        t.join();
    }

    {
        std::cout << "    While system clock jumps forward (long):  ";
        typename Helper::thread t(boost::bind(changeSystemTime, s_longJumpMs));
        context(func, s_longJumpMs);
        t.join();
    }
}

//--------------------------------------

template <typename Helper, typename Function>
void noThreadContext(Function func, const long long jumpMs)
{
    func(jumpMs);
}

template <typename Helper, typename Function>
void threadContextWithNone(Function func, const long long jumpMs)
{
    typename Helper::thread t(boost::bind(func, jumpMs));
    t.join();
}

template <typename Helper, typename Function>
void threadContextWithUnique(Function func, const long long jumpMs)
{
    typename Helper::mutex m;
    typename Helper::lock_guard g(m);
    typename Helper::thread t(boost::bind(func, boost::ref(m), jumpMs));
    t.join();
}

template <typename Helper, typename Function>
void threadContextWithShared(Function func, const long long jumpMs)
{
    typename Helper::mutex m;
    boost::shared_lock_guard<typename Helper::mutex> g(m);
    typename Helper::thread t(boost::bind(func, boost::ref(m), jumpMs));
    t.join();
}

template <typename Helper, typename Function>
void threadContextWithUpgrade(Function func, const long long jumpMs)
{
    typename Helper::mutex m;
    boost::upgrade_lock<typename Helper::mutex> g(m);
    typename Helper::thread t(boost::bind(func, boost::ref(m), jumpMs));
    t.join();
}

//--------------------------------------

// Run the test in the current thread.
template <typename Helper, typename Function>
void runTest(Function func, const std::string name)
{
    runTestInContext<Helper>(noThreadContext<Helper, Function>, func, name);
}

// Run the test in a separate thread.
template <typename Helper, typename Function>
void runTestWithNone(Function func, const std::string name)
{
    runTestInContext<Helper>(threadContextWithNone<Helper, Function>, func, name);
}

// Run the test in a separate thread. Pass a locked mutex to the function under test.
template <typename Helper, typename Function>
void runTestWithUnique(Function func, const std::string name)
{
    runTestInContext<Helper>(threadContextWithUnique<Helper, Function>, func, name);
}

// Run the test in a separate thread. Pass a shared-locked mutex to the function under test.
template <typename Helper, typename Function>
void runTestWithShared(Function func, const std::string name)
{
    runTestInContext<Helper>(threadContextWithShared<Helper, Function>, func, name);
}

// Run the test in a separate thread. Pass an upgrade-locked mutex to the function under test.
template <typename Helper, typename Function>
void runTestWithUpgrade(Function func, const std::string name)
{
    runTestInContext<Helper>(threadContextWithUpgrade<Helper, Function>, func, name);
}

/******************************************************************************/

// Test Sleep

template <typename Helper>
void testSleepFor(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
}

template <typename Helper>
void testSleepUntilSteady(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
}

template <typename Helper>
void testSleepUntilSystem(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
}

template <typename Helper>
void testSleepUntilCustom(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
}

//--------------------------------------

template <typename Helper>
void testSleepRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    boost::this_thread::sleep(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testSleepAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    boost::this_thread::sleep(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testSleepStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testSleepFor        <Helper>, name + "::this_thread::sleep_for()");
    runTestWithNone<Helper>(testSleepUntilSteady<Helper>, name + "::this_thread::sleep_until(), steady time");
    runTestWithNone<Helper>(testSleepUntilSystem<Helper>, name + "::this_thread::sleep_until(), system time");
    runTestWithNone<Helper>(testSleepUntilCustom<Helper>, name + "::this_thread::sleep_until(), custom time");
}

template <typename Helper>
void testSleepBoost(const std::string& name)
{
    testSleepStd<Helper>(name);

    // Boost-only functions
    runTestWithNone<Helper>(testSleepRelative<Helper>, name + "::this_thread::sleep(), relative time");
    runTestWithNone<Helper>(testSleepAbsolute<Helper>, name + "::this_thread::sleep(), absolute time");
}

template <typename Helper>
void testSleepNoThreadStd(const std::string& name)
{
    std::cout << std::endl;
    runTest<Helper>(testSleepFor        <Helper>, name + "::this_thread::sleep_for(), no thread");
    runTest<Helper>(testSleepUntilSteady<Helper>, name + "::this_thread::sleep_until(), no thread, steady time");
    runTest<Helper>(testSleepUntilSystem<Helper>, name + "::this_thread::sleep_until(), no thread, system time");
    runTest<Helper>(testSleepUntilCustom<Helper>, name + "::this_thread::sleep_until(), no thread, custom time");
}

template <typename Helper>
void testSleepNoThreadBoost(const std::string& name)
{
    testSleepNoThreadStd<Helper>(name);

    // Boost-only functions
    runTest<Helper>(testSleepRelative<Helper>, name + "::this_thread::sleep(), no thread, relative time");
    runTest<Helper>(testSleepAbsolute<Helper>, name + "::this_thread::sleep(), no thread, absolute time");
}

/******************************************************************************/

// Test Sleep, No Interruption Point

template <typename Helper>
void testSleepForNoInt(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_for_no_int(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
}

template <typename Helper>
void testSleepUntilNoIntSteady(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until_no_int(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
}

template <typename Helper>
void testSleepUntilNoIntSystem(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until_no_int(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
}

template <typename Helper>
void testSleepUntilNoIntCustom(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    Helper::sleep_until_no_int(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
}

//--------------------------------------

#ifndef SKIP_NO_INT_SLEEP

template <typename Helper>
void testSleepNoIntRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    boost::this_thread::no_interruption_point::sleep(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, e_na);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testSleepNoIntAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    boost::this_thread::no_interruption_point::sleep(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, e_na);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

#endif

//--------------------------------------

// Only Boost supports no_interruption_point

template <typename Helper>
void testSleepNoIntBoost(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testSleepForNoInt        <Helper>, name + "::this_thread::no_interruption_point::sleep_for()");
    runTestWithNone<Helper>(testSleepUntilNoIntSteady<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), steady time");
    runTestWithNone<Helper>(testSleepUntilNoIntSystem<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), system time");
    runTestWithNone<Helper>(testSleepUntilNoIntCustom<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), custom time");

#ifndef SKIP_NO_INT_SLEEP
    runTestWithNone<Helper>(testSleepNoIntRelative<Helper>, name + "::this_thread::no_interruption_point::sleep(), relative time");
    runTestWithNone<Helper>(testSleepNoIntAbsolute<Helper>, name + "::this_thread::no_interruption_point::sleep(), absolute time");
#endif
}

template <typename Helper>
void testSleepNoThreadNoIntBoost(const std::string& name)
{
    std::cout << std::endl;
    runTest<Helper>(testSleepForNoInt        <Helper>, name + "::this_thread::no_interruption_point::sleep_for(), no thread");
    runTest<Helper>(testSleepUntilNoIntSteady<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), no thread, steady time");
    runTest<Helper>(testSleepUntilNoIntSystem<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), no thread, system time");
    runTest<Helper>(testSleepUntilNoIntCustom<Helper>, name + "::this_thread::no_interruption_point::sleep_until(), no thread, custom time");

#ifndef SKIP_NO_INT_SLEEP
    runTest<Helper>(testSleepNoIntRelative<Helper>, name + "::this_thread::no_interruption_point::sleep(), no thread, relative time");
    runTest<Helper>(testSleepNoIntAbsolute<Helper>, name + "::this_thread::no_interruption_point::sleep(), no thread, absolute time");
#endif
}

/******************************************************************************/

// Test Try Join

template <typename Helper>
void testTryJoinFor(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.try_join_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryJoinUntilSteady(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.try_join_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryJoinUntilSystem(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.try_join_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryJoinUntilCustom(const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.try_join_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTimedJoinRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.timed_join(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testTimedJoinAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    typename Helper::thread t3(sleepForLongTime);
    bool succeeded = t3.timed_join(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

// Only Boost supports timed try_join functions

template <typename Helper>
void testJoinBoost(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testTryJoinFor        <Helper>, name + "::thread::try_join_for()");
    runTestWithNone<Helper>(testTryJoinUntilSteady<Helper>, name + "::thread::try_join_until(), steady time");
    runTestWithNone<Helper>(testTryJoinUntilSystem<Helper>, name + "::thread::try_join_until(), system time");
    runTestWithNone<Helper>(testTryJoinUntilCustom<Helper>, name + "::thread::try_join_until(), custom time");
    runTestWithNone<Helper>(testTimedJoinRelative <Helper>, name + "::thread::timed_join(), relative time");
    runTestWithNone<Helper>(testTimedJoinAbsolute <Helper>, name + "::thread::timed_join(), absolute time");
}

/******************************************************************************/

// Test Condition Variable Wait

template <typename Helper>
void testCondVarWaitFor(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = (cv.wait_for(g, Helper::waitDur) == Helper::cv_status::no_timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilSteady(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = (cv.wait_until(g, Helper::steadyNow() + Helper::waitDur) == Helper::cv_status::no_timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilSystem(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = (cv.wait_until(g, Helper::systemNow() + Helper::waitDur) == Helper::cv_status::no_timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilCustom(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = (cv.wait_until(g, Helper::customNow() + Helper::waitDur) == Helper::cv_status::no_timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
}

//--------------------------------------

template <typename Helper>
void testCondVarTimedWaitRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = cv.timed_wait(g, ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testCondVarTimedWaitAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = cv.timed_wait(g, ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testCondVarStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testCondVarWaitFor        <Helper>, name + "::wait_for()");
    runTestWithNone<Helper>(testCondVarWaitUntilSteady<Helper>, name + "::wait_until(), steady time");
    runTestWithNone<Helper>(testCondVarWaitUntilSystem<Helper>, name + "::wait_until(), system time");
    runTestWithNone<Helper>(testCondVarWaitUntilCustom<Helper>, name + "::wait_until(), custom time");
}

template <typename Helper>
void testCondVarBoost(const std::string& name)
{
    testCondVarStd<Helper>(name);

    // Boost-only functions
    runTestWithNone<Helper>(testCondVarTimedWaitRelative<Helper>, name + "::timed_wait(), relative time");
    runTestWithNone<Helper>(testCondVarTimedWaitAbsolute<Helper>, name + "::timed_wait(), absolute time");
}

/******************************************************************************/

// Test Condition Variable Wait with Predicate

template <typename Helper>
void testCondVarWaitForPred(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = cv.wait_for(g, Helper::waitDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilPredSteady(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = cv.wait_until(g, Helper::steadyNow() + Helper::waitDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilPredSystem(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = cv.wait_until(g, Helper::systemNow() + Helper::waitDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
}

template <typename Helper>
void testCondVarWaitUntilPredCustom(const long long jumpMs)
{
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool noTimeout = cv.wait_until(g, Helper::customNow() + Helper::waitDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
}

//--------------------------------------

template <typename Helper>
void testCondVarTimedWaitPredRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = cv.timed_wait(g, ptDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testCondVarTimedWaitPredAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::cond cv;
    typename Helper::mutex m;
    typename Helper::unique_lock g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = cv.timed_wait(g, ptNow + ptDur, returnFalse);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testCondVarPredStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testCondVarWaitForPred        <Helper>, name + "::wait_for(), with predicate");
    runTestWithNone<Helper>(testCondVarWaitUntilPredSteady<Helper>, name + "::wait_until(), with predicate, steady time");
    runTestWithNone<Helper>(testCondVarWaitUntilPredSystem<Helper>, name + "::wait_until(), with predicate, system time");
    runTestWithNone<Helper>(testCondVarWaitUntilPredCustom<Helper>, name + "::wait_until(), with predicate, custom time");
}

template <typename Helper>
void testCondVarPredBoost(const std::string& name)
{
    testCondVarPredStd<Helper>(name);

    // Boost-only functions
    runTestWithNone<Helper>(testCondVarTimedWaitPredRelative<Helper>, name + "::timed_wait(), with predicate, relative time");
    runTestWithNone<Helper>(testCondVarTimedWaitPredAbsolute<Helper>, name + "::timed_wait(), with predicate, absolute time");
}

/******************************************************************************/

// Test Try Lock

template <typename Helper>
void testTryLockFor(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTimedLockRelative(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testTimedLockAbsolute(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testMutexStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithUnique<Helper>(testTryLockFor        <Helper>, name + "::try_lock_for()");
    runTestWithUnique<Helper>(testTryLockUntilSteady<Helper>, name + "::try_lock_until(), steady time");
    runTestWithUnique<Helper>(testTryLockUntilSystem<Helper>, name + "::try_lock_until(), system time");
    runTestWithUnique<Helper>(testTryLockUntilCustom<Helper>, name + "::try_lock_until(), custom time");
}

template <typename Helper>
void testMutexBoost(const std::string& name)
{
    testMutexStd<Helper>(name);

    // Boost-only functions
    runTestWithUnique<Helper>(testTimedLockRelative<Helper>, name + "::timed_lock(), relative time");
    runTestWithUnique<Helper>(testTimedLockAbsolute<Helper>, name + "::timed_lock(), absolute time");
}

/******************************************************************************/

// Test Try Lock Shared

template <typename Helper>
void testTryLockSharedFor(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_shared_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockSharedUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_shared_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockSharedUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_shared_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockSharedUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_shared_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTimedLockSharedRelative(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock_shared(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testTimedLockSharedAbsolute(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock_shared(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testMutexSharedStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithUnique<Helper>(testTryLockSharedFor        <Helper>, name + "::try_lock_shared_for()");
    runTestWithUnique<Helper>(testTryLockSharedUntilSteady<Helper>, name + "::try_lock_shared_until(), steady time");
    runTestWithUnique<Helper>(testTryLockSharedUntilSystem<Helper>, name + "::try_lock_shared_until(), system time");
    runTestWithUnique<Helper>(testTryLockSharedUntilCustom<Helper>, name + "::try_lock_shared_until(), custom time");
}

template <typename Helper>
void testMutexSharedBoost(const std::string& name)
{
    testMutexSharedStd<Helper>(name);

    // Boost-only functions
    runTestWithUnique<Helper>(testTimedLockSharedRelative<Helper>, name + "::timed_lock_shared(), relative time");
    runTestWithUnique<Helper>(testTimedLockSharedAbsolute<Helper>, name + "::timed_lock_shared(), absolute time");
}

/******************************************************************************/

// Test Try Lock Upgrade

#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS

template <typename Helper>
void testTryLockUpgradeFor(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_upgrade_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUpgradeUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_upgrade_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUpgradeUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_upgrade_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryLockUpgradeUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_lock_upgrade_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTimedLockUpgradeRelative(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock_upgrade(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testTimedLockUpgradeAbsolute(typename Helper::mutex& m, const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool succeeded = m.timed_lock_upgrade(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testTryUnlockSharedAndLockFor(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTryUnlockUpgradeAndLockFor(typename Helper::mutex& m, const long long jumpMs)
{
    boost::upgrade_lock<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_upgrade_and_lock_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockUpgradeAndLockUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    boost::upgrade_lock<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_upgrade_and_lock_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockUpgradeAndLockUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    boost::upgrade_lock<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_upgrade_and_lock_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockUpgradeAndLockUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    boost::upgrade_lock<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_upgrade_and_lock_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

//--------------------------------------

template <typename Helper>
void testTryUnlockSharedAndLockUpgradeFor(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_upgrade_for(Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUpgradeUntilSteady(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_upgrade_until(Helper::steadyNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUpgradeUntilSystem(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_upgrade_until(Helper::systemNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

template <typename Helper>
void testTryUnlockSharedAndLockUpgradeUntilCustom(typename Helper::mutex& m, const long long jumpMs)
{
    boost::shared_lock_guard<typename Helper::mutex> g(m);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = m.try_unlock_shared_and_lock_upgrade_until(Helper::customNow() + Helper::waitDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_bad : e_failed_good);
}

#endif

//--------------------------------------

// Only Boost supports upgrade mutexes

template <typename Helper>
void testMutexUpgradeBoost(const std::string& name)
{
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
    std::cout << std::endl;
    runTestWithUnique<Helper>(testTryLockUpgradeFor        <Helper>, name + "::try_lock_upgrade_for()");
    runTestWithUnique<Helper>(testTryLockUpgradeUntilSteady<Helper>, name + "::try_lock_upgrade_until(), steady time");
    runTestWithUnique<Helper>(testTryLockUpgradeUntilSystem<Helper>, name + "::try_lock_upgrade_until(), system time");
    runTestWithUnique<Helper>(testTryLockUpgradeUntilCustom<Helper>, name + "::try_lock_upgrade_until(), custom time");
    runTestWithUnique<Helper>(testTimedLockUpgradeRelative <Helper>, name + "::timed_lock_upgrade(), relative time");
    runTestWithUnique<Helper>(testTimedLockUpgradeAbsolute <Helper>, name + "::timed_lock_upgrade(), absolute time");

    std::cout << std::endl;
    runTestWithShared<Helper>(testTryUnlockSharedAndLockFor        <Helper>, name + "::try_unlock_shared_and_lock_for()");
    runTestWithShared<Helper>(testTryUnlockSharedAndLockUntilSteady<Helper>, name + "::try_unlock_shared_and_lock_until(), steady time");
    runTestWithShared<Helper>(testTryUnlockSharedAndLockUntilSystem<Helper>, name + "::try_unlock_shared_and_lock_until(), system time");
    runTestWithShared<Helper>(testTryUnlockSharedAndLockUntilCustom<Helper>, name + "::try_unlock_shared_and_lock_until(), custom time");

    std::cout << std::endl;
    runTestWithShared<Helper>(testTryUnlockUpgradeAndLockFor        <Helper>, name + "::try_unlock_upgrade_and_lock_for()");
    runTestWithShared<Helper>(testTryUnlockUpgradeAndLockUntilSteady<Helper>, name + "::try_unlock_upgrade_and_lock_until(), steady time");
    runTestWithShared<Helper>(testTryUnlockUpgradeAndLockUntilSystem<Helper>, name + "::try_unlock_upgrade_and_lock_until(), system time");
    runTestWithShared<Helper>(testTryUnlockUpgradeAndLockUntilCustom<Helper>, name + "::try_unlock_upgrade_and_lock_until(), custom time");

    std::cout << std::endl;
    runTestWithUpgrade<Helper>(testTryUnlockSharedAndLockFor        <Helper>, name + "::try_unlock_shared_and_lock_upgrade_for()");
    runTestWithUpgrade<Helper>(testTryUnlockSharedAndLockUntilSteady<Helper>, name + "::try_unlock_shared_and_lock_upgrade_until(), steady time");
    runTestWithUpgrade<Helper>(testTryUnlockSharedAndLockUntilSystem<Helper>, name + "::try_unlock_shared_and_lock_upgrade_until(), system time");
    runTestWithUpgrade<Helper>(testTryUnlockSharedAndLockUntilCustom<Helper>, name + "::try_unlock_shared_and_lock_upgrade_until(), custom time");
#endif
}

/******************************************************************************/

// Test Future Wait

template <typename Helper>
void testFutureWaitFor(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (f.wait_for(Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testFutureWaitUntilSteady(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (f.wait_until(Helper::steadyNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testFutureWaitUntilSystem(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (f.wait_until(Helper::systemNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testFutureWaitUntilCustom(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (f.wait_until(Helper::customNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

//--------------------------------------

template <typename Helper>
void testFutureTimedWaitRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = f.timed_wait(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testFutureTimedWaitAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = f.timed_wait_until(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testFutureStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testFutureWaitFor        <Helper>, name + "::wait_for()");
    runTestWithNone<Helper>(testFutureWaitUntilSteady<Helper>, name + "::wait_until(), steady time");
    runTestWithNone<Helper>(testFutureWaitUntilSystem<Helper>, name + "::wait_until(), system time");
    runTestWithNone<Helper>(testFutureWaitUntilCustom<Helper>, name + "::wait_until(), custom time");
}

template <typename Helper>
void testFutureBoost(const std::string& name)
{
    testFutureStd<Helper>(name);

    // Boost-only functions
    runTestWithNone<Helper>(testFutureTimedWaitRelative<Helper>, name + "::timed_wait(), relative time");
    runTestWithNone<Helper>(testFutureTimedWaitAbsolute<Helper>, name + "::timed_wait_until(), absolute time");
}

/******************************************************************************/

// Test Shared Future Wait

template <typename Helper>
void testSharedFutureWaitFor(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (sf.wait_for(Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSharedFutureWaitUntilSteady(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (sf.wait_until(Helper::steadyNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSharedFutureWaitUntilSystem(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (sf.wait_until(Helper::systemNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSharedFutureWaitUntilCustom(const long long jumpMs)
{
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (sf.wait_until(Helper::customNow() + Helper::waitDur) == Helper::future_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

//--------------------------------------

template <typename Helper>
void testSharedFutureTimedWaitRelative(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = sf.timed_wait(ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

template <typename Helper>
void testSharedFutureTimedWaitAbsolute(const long long jumpMs)
{
#ifndef SKIP_DATETIME_FUNCTIONS
    typename Helper::packaged_task pt(returnFalse);
    typename Helper::future f = pt.get_future();
    typename Helper::shared_future sf = boost::move(f);

    typename Helper::steady_time_point before(Helper::steadyNow());
    boost::posix_time::ptime ptNow(boost::posix_time::microsec_clock::universal_time());
    boost::posix_time::milliseconds ptDur(boost::chrono::duration_cast<boost::chrono::milliseconds>(Helper::waitDur).count());
    bool noTimeout = sf.timed_wait_until(ptNow + ptDur);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, noTimeout ? e_no_timeout : e_timeout);
#else
    checkWaitTime<Helper>(Helper::zero(), Helper::zero(), e_na);
#endif
}

//--------------------------------------

template <typename Helper>
void testSharedFutureStd(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testSharedFutureWaitFor        <Helper>, name + "::wait_for()");
    runTestWithNone<Helper>(testSharedFutureWaitUntilSteady<Helper>, name + "::wait_until(), steady time");
    runTestWithNone<Helper>(testSharedFutureWaitUntilSystem<Helper>, name + "::wait_until(), system time");
    runTestWithNone<Helper>(testSharedFutureWaitUntilCustom<Helper>, name + "::wait_until(), custom time");
}

template <typename Helper>
void testSharedFutureBoost(const std::string& name)
{
    testSharedFutureStd<Helper>(name);

    // Boost-only functions
    runTestWithNone<Helper>(testSharedFutureTimedWaitRelative<Helper>, name + "::timed_wait(), relative time");
    runTestWithNone<Helper>(testSharedFutureTimedWaitAbsolute<Helper>, name + "::timed_wait_until(), absolute time");
}

/******************************************************************************/

// Test Sync Priority Queue

template <typename Helper>
void testSyncPriorityQueuePullFor(const long long jumpMs)
{
    boost::sync_priority_queue<int> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncPriorityQueuePullUntilSteady(const long long jumpMs)
{
    boost::sync_priority_queue<int> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::steadyNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncPriorityQueuePullUntilSystem(const long long jumpMs)
{
    boost::sync_priority_queue<int> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::systemNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncPriorityQueuePullUntilCustom(const long long jumpMs)
{
    boost::sync_priority_queue<int> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::customNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

//--------------------------------------

// Only Boost supports sync_priority_queue

template <typename Helper>
void testSyncPriorityQueueBoost(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testSyncPriorityQueuePullFor        <Helper>, name + "::pull_for()");
    runTestWithNone<Helper>(testSyncPriorityQueuePullUntilSteady<Helper>, name + "::pull_until(), steady time");
    runTestWithNone<Helper>(testSyncPriorityQueuePullUntilSystem<Helper>, name + "::pull_until(), system time");
    runTestWithNone<Helper>(testSyncPriorityQueuePullUntilCustom<Helper>, name + "::pull_until(), custom time");
}

/******************************************************************************/

// Test Sync Timed Queue

template <typename Helper>
void testSyncTimedQueuePullForEmptySteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncTimedQueuePullForEmptySystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncTimedQueuePullForEmptyCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncTimedQueuePullUntilEmptySteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::steadyNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncTimedQueuePullUntilEmptySystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::systemNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

template <typename Helper>
void testSyncTimedQueuePullUntilEmptyCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool timeout = (q.pull_until(Helper::customNow() + Helper::waitDur, i) == boost::queue_op_status::timeout);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, timeout ? e_timeout : e_no_timeout);
}

//--------------------------------------

template <typename Helper>
void testSyncTimedQueuePullForNotReadySteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, notReady ? e_not_ready_good : e_ready_bad);
}

template <typename Helper>
void testSyncTimedQueuePullForNotReadySystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, notReady ? e_not_ready_good : e_ready_bad);
}

template <typename Helper>
void testSyncTimedQueuePullForNotReadyCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_for(Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, notReady ? e_not_ready_good : e_ready_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilNotReadySteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_until(Helper::steadyNow() + Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, notReady ? e_not_ready_good : e_ready_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilNotReadySystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_until(Helper::systemNow() + Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, notReady ? e_not_ready_good : e_ready_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilNotReadyCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    q.push(0, typename Helper::milliseconds(10000)); // a long time
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool notReady = (q.pull_until(Helper::customNow() + Helper::waitDur, i) == boost::queue_op_status::not_ready);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, notReady ? e_not_ready_good : e_ready_bad);
}

//--------------------------------------

template <typename Helper>
void testSyncTimedQueuePullForSucceedsSteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    q.push(0, Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_for(typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueuePullForSucceedsSystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    q.push(0, Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_for(typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueuePullForSucceedsCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    q.push(0, Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_for(typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilSucceedsSteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    q.push(0, Helper::steadyNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_until(Helper::steadyNow() + typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilSucceedsSystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    q.push(0, Helper::systemNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_until(Helper::systemNow() + typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueuePullUntilSucceedsCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    q.push(0, Helper::customNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.pull_until(Helper::customNow() + typename Helper::milliseconds(10000), i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

//--------------------------------------

template <typename Helper>
void testSyncTimedQueueWaitPullSucceedsSteady(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::steady_clock> q;
    q.push(0, Helper::steadyNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.wait_pull(i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur, after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueueWaitPullSucceedsSystem(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::system_clock> q;
    q.push(0, Helper::systemNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.wait_pull(i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

template <typename Helper>
void testSyncTimedQueueWaitPullSucceedsCustom(const long long jumpMs)
{
    boost::sync_timed_queue<int, typename Helper::custom_clock> q;
    q.push(0, Helper::customNow() + Helper::waitDur);
    int i;

    typename Helper::steady_time_point before(Helper::steadyNow());
    bool succeeded = (q.wait_pull(i) == boost::queue_op_status::success);
    typename Helper::steady_time_point after(Helper::steadyNow());

    checkWaitTime<Helper>(Helper::waitDur - typename Helper::milliseconds(jumpMs), after - before, succeeded ? e_succeeded_good : e_failed_bad);
}

//--------------------------------------

// Only Boost supports sync_timed_queue

template <typename Helper>
void testSyncTimedQueueBoost(const std::string& name)
{
    std::cout << std::endl;
    runTestWithNone<Helper>(testSyncTimedQueuePullForEmptySteady  <Helper>, name + "::pull_for(), empty, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForEmptySystem  <Helper>, name + "::pull_for(), empty, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForEmptyCustom  <Helper>, name + "::pull_for(), empty, custom time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilEmptySteady<Helper>, name + "::pull_until(), empty, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilEmptySystem<Helper>, name + "::pull_until(), empty, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilEmptyCustom<Helper>, name + "::pull_until(), empty, custom time");

    std::cout << std::endl;
    runTestWithNone<Helper>(testSyncTimedQueuePullForNotReadySteady  <Helper>, name + "::pull_for(), not ready, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForNotReadySystem  <Helper>, name + "::pull_for(), not ready, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForNotReadyCustom  <Helper>, name + "::pull_for(), not ready, custom time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilNotReadySteady<Helper>, name + "::pull_until(), not ready, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilNotReadySystem<Helper>, name + "::pull_until(), not ready, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilNotReadyCustom<Helper>, name + "::pull_until(), not ready, custom time");

    std::cout << std::endl;
    runTestWithNone<Helper>(testSyncTimedQueuePullForSucceedsSteady  <Helper>, name + "::pull_for(), succeeds, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForSucceedsSystem  <Helper>, name + "::pull_for(), succeeds, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullForSucceedsCustom  <Helper>, name + "::pull_for(), succeeds, custom time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilSucceedsSteady<Helper>, name + "::pull_until(), succeeds, steady time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilSucceedsSystem<Helper>, name + "::pull_until(), succeeds, system time");
    runTestWithNone<Helper>(testSyncTimedQueuePullUntilSucceedsCustom<Helper>, name + "::pull_until(), succeeds, custom time");

    std::cout << std::endl;
    runTestWithNone<Helper>(testSyncTimedQueueWaitPullSucceedsSteady<Helper>, name + "::wait_pull(), succeeds, steady time");
    runTestWithNone<Helper>(testSyncTimedQueueWaitPullSucceedsSystem<Helper>, name + "::wait_pull(), succeeds, system time");
    runTestWithNone<Helper>(testSyncTimedQueueWaitPullSucceedsCustom<Helper>, name + "::wait_pull(), succeeds, custom time");
}

/******************************************************************************/

int main()
{
    std::cout << std::endl;
    std::cout << "INFO: This test requires root/Administrator privileges in order to change the system clock." << std::endl;
    std::cout << "INFO: Disable NTP while running this test to prevent NTP from changing the system clock." << std::endl;
    std::cout << std::endl;

    std::cout << "BOOST_HAS_PTHREAD_DELAY_NP:                             ";
#ifdef BOOST_HAS_PTHREAD_DELAY_NP
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_HAS_NANOSLEEP:                                    ";
#ifdef BOOST_HAS_NANOSLEEP
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_THREAD_SLEEP_FOR_IS_STEADY:                       ";
#ifdef BOOST_THREAD_SLEEP_FOR_IS_STEADY
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "CLOCK_MONOTONIC:                                        ";
#ifdef CLOCK_MONOTONIC
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN:      ";
#ifdef BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS: ";
#ifdef BOOST_THREAD_PROVIDES_SHARED_MUTEX_UPWARDS_CONVERSIONS
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_THREAD_V2_SHARED_MUTEX:                           ";
#ifdef BOOST_THREAD_V2_SHARED_MUTEX
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << "BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC:          ";
#ifdef BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC
    std::cout << "YES" << std::endl;
#else
    std::cout << "NO" << std::endl;
#endif

    std::cout << std::endl;
    std::cout << "Wait Time:              " << s_waitMs            << " ms" << std::endl;
    std::cout << "Short Jump Time:        " << s_shortJumpMs       << " ms" << std::endl;
    std::cout << "Long Jump Time:         " << s_longJumpMs        << " ms" << std::endl;
    std::cout << "Sleep Before Jump Time: " << s_sleepBeforeJumpMs << " ms" << std::endl;
    std::cout << "Max Early Error:        " << s_maxEarlyErrorMs   << " ms" << std::endl;
    std::cout << "Max Late Error:         " << s_maxLateErrorMs    << " ms" << std::endl;

    testSleepBoost             <BoostHelper<                                           > >("boost");
    testSleepNoIntBoost        <BoostHelper<                                           > >("boost");
    testSleepNoThreadBoost     <BoostHelper<                                           > >("boost");
    testSleepNoThreadNoIntBoost<BoostHelper<                                           > >("boost");
    testJoinBoost              <BoostHelper<                                           > >("boost");
    testCondVarBoost           <BoostHelper<boost::mutex, boost::condition_variable    > >("boost::condition_variable");
    testCondVarPredBoost       <BoostHelper<boost::mutex, boost::condition_variable    > >("boost::condition_variable");
    testCondVarBoost           <BoostHelper<boost::mutex, boost::condition_variable_any> >("boost::condition_variable_any");
    testCondVarPredBoost       <BoostHelper<boost::mutex, boost::condition_variable_any> >("boost::condition_variable_any");
    testMutexBoost             <BoostHelper<boost::timed_mutex                         > >("boost::timed_mutex");
    testMutexBoost             <BoostHelper<boost::recursive_timed_mutex               > >("boost::recursive_timed_mutex");
    testMutexBoost             <BoostHelper<boost::shared_mutex                        > >("boost::shared_mutex"); // upgrade_mutex is a typedef of shared_mutex, so no need to test upgrade_mutex
    testMutexSharedBoost       <BoostHelper<boost::shared_mutex                        > >("boost::shared_mutex"); // upgrade_mutex is a typedef of shared_mutex, so no need to test upgrade_mutex
    testMutexUpgradeBoost      <BoostHelper<boost::shared_mutex                        > >("boost::shared_mutex"); // upgrade_mutex is a typedef of shared_mutex, so no need to test upgrade_mutex
    testFutureBoost            <BoostHelper<                                           > >("boost::future");
    testSharedFutureBoost      <BoostHelper<                                           > >("boost::shared_future");
    testSyncPriorityQueueBoost <BoostHelper<                                           > >("boost::sync_priority_queue");
    testSyncTimedQueueBoost    <BoostHelper<                                           > >("boost::sync_timed_queue");

#ifdef TEST_CPP14_FEATURES
    testSleepStd        <StdHelper<                                       > >("std");
    testSleepNoThreadStd<StdHelper<                                       > >("std");
    testCondVarStd      <StdHelper<std::mutex, std::condition_variable    > >("std::condition_variable");
    testCondVarPredStd  <StdHelper<std::mutex, std::condition_variable    > >("std::condition_variable");
    testCondVarStd      <StdHelper<std::mutex, std::condition_variable_any> >("std::condition_variable_any");
    testCondVarPredStd  <StdHelper<std::mutex, std::condition_variable_any> >("std::condition_variable_any");
    testMutexStd        <StdHelper<std::timed_mutex                       > >("std::timed_mutex");
    testMutexStd        <StdHelper<std::recursive_timed_mutex             > >("std::recursive_timed_mutex");
    testMutexStd        <StdHelper<std::shared_timed_mutex                > >("std::shared_timed_mutex");
    testMutexSharedStd  <StdHelper<std::shared_timed_mutex                > >("std::shared_timed_mutex");
    testFutureStd       <StdHelper<                                       > >("std::future");
    testSharedFutureStd <StdHelper<                                       > >("std::shared_future");
#endif

    std::cout << std::endl;
    std::cout << "Number of Tests Run:    " << g_numTestsRun << std::endl;
    std::cout << "Number of Tests Passed: " << g_numTestsPassed << std::endl;
    std::cout << "Number of Tests Failed: " << g_numTestsFailed << std::endl;

    return 0;
}
