mirror of https://github.com/JAJames/jessilib.git
Jessica James
7 years ago
17 changed files with 874 additions and 302 deletions
@ -1,7 +1,9 @@ |
|||
cmake_minimum_required(VERSION 3.8) |
|||
project(jessilib) |
|||
|
|||
#set(CMAKE_CXX_STANDARD 14) |
|||
set(CMAKE_CXX_STANDARD 17) |
|||
|
|||
include(build/CMakeLists.txt) |
|||
|
|||
# Setup source files |
|||
add_subdirectory(src) |
|||
|
@ -1,171 +0,0 @@ |
|||
/**
|
|||
* Copyright (C) 2017 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include "timer.hpp" |
|||
#include <iostream> |
|||
#include <cassert> |
|||
#include "impl/timer_manager.hpp" |
|||
|
|||
namespace jessilib { |
|||
|
|||
/** helper */ |
|||
timer::function_t callback_with_iterations(timer::iterations_t iterations, timer::function_t callback) { |
|||
assert(iterations > 0); |
|||
|
|||
return [iterations, callback](timer& timer) mutable { |
|||
callback(timer); |
|||
|
|||
if (--iterations == 0) { |
|||
timer.cancel(); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** timer */ |
|||
|
|||
timer::timer() |
|||
: m_callback{ nullptr }, |
|||
m_self{ impl::timer_manager::instance().m_detached_timers.end() } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
timer::timer(timer&& in_timer) |
|||
: m_period{ in_timer.m_period }, |
|||
m_next{ in_timer.m_next }, |
|||
m_callback{ std::move(in_timer.m_callback) }, |
|||
m_self{ impl::timer_manager::instance().m_detached_timers.end() } { |
|||
|
|||
// Cancel in_timer
|
|||
in_timer.m_callback = nullptr; |
|||
|
|||
// Replace active timer
|
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
{ |
|||
std::lock_guard<std::mutex> lock(manager.m_mutex); |
|||
manager.m_active_timers.erase(&in_timer); // remove in_timer
|
|||
manager.m_active_timers.insert(this); // insert this
|
|||
} |
|||
|
|||
// Notify
|
|||
manager.m_cvar.notify_one(); |
|||
} |
|||
|
|||
timer::timer(duration_t in_period, function_t in_callback) |
|||
: m_period{ in_period }, |
|||
m_next{ calc_next() }, |
|||
m_callback{ in_callback }, |
|||
m_self{ impl::timer_manager::instance().m_detached_timers.end() } { |
|||
// PROBLEM: timer may be executing while moving data????
|
|||
|
|||
// Assertion checks
|
|||
assert(m_callback != nullptr); |
|||
assert(m_period.count() != 0); |
|||
|
|||
// Add timer
|
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
{ |
|||
std::lock_guard<std::mutex> lock(manager.m_mutex); |
|||
manager.m_active_timers.insert(this); |
|||
} |
|||
|
|||
// Notify
|
|||
manager.m_cvar.notify_one(); |
|||
} |
|||
|
|||
timer::timer(duration_t in_period, iterations_t in_iterations, function_t in_callback) |
|||
: timer{ in_period, callback_with_iterations(in_iterations, in_callback) } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
timer& timer::operator=(timer&& in_timer) { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
m_period = in_timer.m_period; |
|||
m_next = in_timer.m_next; |
|||
m_callback = std::move(in_timer.m_callback); |
|||
m_self = in_timer.m_self; |
|||
} |
|||
|
|||
timer::~timer() { |
|||
// If it's null, then it was either never added (default constructed) or already removed (as part of move)
|
|||
if (!null()) { |
|||
cancel(); |
|||
} |
|||
} |
|||
|
|||
timer::time_point_t timer::next() const { |
|||
return m_next; |
|||
} |
|||
|
|||
timer::duration_t timer::period() const { |
|||
return m_period; |
|||
} |
|||
|
|||
timer::function_t timer::function() const { |
|||
return m_callback; |
|||
} |
|||
|
|||
bool timer::null() const { |
|||
return m_callback == nullptr; |
|||
} |
|||
|
|||
bool timer::current() const { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
return manager.m_current_timer == this && manager.m_thread.get_id() == std::this_thread::get_id(); |
|||
} |
|||
|
|||
bool timer::detached() const { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
std::lock_guard<std::mutex> lock(manager.m_detached_timers_mutex); |
|||
return m_self != manager.m_detached_timers.end(); |
|||
} |
|||
|
|||
void timer::detach() { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
assert(!current()); // you cannot detach a timer from within itself, because that would destroy the callback you're currently executing
|
|||
assert(!detached()); // you cannot detach a timer that is already detached
|
|||
std::lock_guard<std::mutex> lock(manager.m_detached_timers_mutex); |
|||
manager.m_detached_timers.emplace_back(std::move(*this)); |
|||
--manager.m_detached_timers.back().m_self; // Is this a race condition?
|
|||
} |
|||
|
|||
void timer::cancel() { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
if (current()) { |
|||
manager.m_active_timers.erase(this); |
|||
return; |
|||
} |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> lock(manager.m_mutex); |
|||
manager.m_active_timers.erase(this); |
|||
} |
|||
manager.m_cvar.notify_one(); |
|||
} |
|||
|
|||
// Internals
|
|||
|
|||
timer::time_point_t timer::calc_next() { |
|||
return std::chrono::steady_clock::now() + m_period; |
|||
} |
|||
|
|||
} // namespace jessilib
|
@ -0,0 +1,75 @@ |
|||
/**
|
|||
* Copyright (C) 2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include <atomic> |
|||
#include "timer.hpp" |
|||
|
|||
namespace jessilib { |
|||
|
|||
/** cancel_token_context */ |
|||
|
|||
namespace impl { |
|||
struct cancel_token_context { |
|||
bool m_expired{ false }; |
|||
std::atomic<size_t> m_weak_reference_count{ 0 }; |
|||
}; |
|||
} // namespace impl
|
|||
|
|||
/** cancel_token */ |
|||
|
|||
cancel_token::cancel_token() |
|||
: m_context{ new impl::cancel_token_context } { |
|||
// Empty ctor body
|
|||
}; |
|||
|
|||
cancel_token::cancel_token(cancel_token&& in_token) |
|||
: m_context{ in_token.m_context } { |
|||
in_token.m_context = nullptr; |
|||
} |
|||
|
|||
cancel_token::~cancel_token() { |
|||
if (m_context != nullptr) { |
|||
m_context->m_expired = true; |
|||
|
|||
if (m_context->m_weak_reference_count == 0) { |
|||
delete m_context; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** cancel_detector */ |
|||
|
|||
cancel_detector::cancel_detector(const cancel_token& in_token) |
|||
: m_context{ in_token.m_context } { |
|||
// Increment reference count
|
|||
++m_context->m_weak_reference_count; |
|||
} |
|||
|
|||
cancel_detector::~cancel_detector() { |
|||
// Decrement reference count
|
|||
if (--m_context->m_weak_reference_count == 0 && m_context->m_expired) { |
|||
// No other references exist to the context block; delete it
|
|||
delete m_context; |
|||
} |
|||
} |
|||
|
|||
bool cancel_detector::expired() const { |
|||
return m_context->m_expired; |
|||
}; |
|||
|
|||
} // namespace jessilib
|
@ -0,0 +1,78 @@ |
|||
/**
|
|||
* Copyright (C) 2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include <atomic> |
|||
#include "timer.hpp" |
|||
|
|||
namespace jessilib { |
|||
|
|||
/** synchronized_timer */ |
|||
|
|||
class synchonrized_callback { |
|||
public: |
|||
synchonrized_callback(timer::function_t in_callback) |
|||
: m_callback{ std::move(in_callback) }, |
|||
m_calls{ 0 } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
synchonrized_callback(const synchonrized_callback& in_callback) |
|||
: m_callback{ in_callback.m_callback } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
synchonrized_callback(synchonrized_callback&& in_callback) |
|||
: m_callback{ std::move(in_callback.m_callback) } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
void operator()(timer& in_timer) { |
|||
cancel_detector detector = m_cancel_token; |
|||
// Iterate calls
|
|||
if (++m_calls == 1) { |
|||
// No other calls were queued; this should be safe.
|
|||
do { |
|||
m_callback(in_timer); |
|||
|
|||
// callback may have cancelled the timer (and thus destructed this callback); check
|
|||
if (detector.expired()) { |
|||
// We cannot access any members of this struct outside of the stack; return immediately
|
|||
return; |
|||
} |
|||
} |
|||
while (--m_calls != 0); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
timer::function_t m_callback; |
|||
std::atomic<unsigned int> m_calls{ 0 }; |
|||
cancel_token m_cancel_token; |
|||
}; |
|||
|
|||
syncrhonized_timer::syncrhonized_timer(duration_t in_period, function_t in_callback) |
|||
: timer{ in_period, synchonrized_callback{ std::move(in_callback) } } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
syncrhonized_timer::syncrhonized_timer(duration_t in_period, iterations_t in_iterations, function_t in_callback) |
|||
: timer{ in_period, in_iterations, synchonrized_callback{ std::move(in_callback) } } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
} // namespace jessilib
|
@ -0,0 +1,164 @@ |
|||
/**
|
|||
* Copyright (C) 2017-2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include "timer.hpp" |
|||
#include "assert.hpp" |
|||
#include "impl/timer_manager.hpp" |
|||
#include "impl/timer_context.hpp" |
|||
|
|||
namespace jessilib { |
|||
|
|||
/** callback_with_iterations */ |
|||
|
|||
class callback_with_iterations { |
|||
public: |
|||
callback_with_iterations(timer::iterations_t in_iterations, timer::function_t in_callback) |
|||
: m_callback{ std::move(in_callback) }, |
|||
m_iterations{ in_iterations } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
callback_with_iterations(const callback_with_iterations& in_callback) |
|||
: m_callback{ in_callback.m_callback }, |
|||
m_iterations{ in_callback.m_iterations } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
callback_with_iterations(callback_with_iterations&& in_callback) |
|||
: m_callback{ std::move(in_callback.m_callback) }, |
|||
m_iterations{ in_callback.m_iterations } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
void operator()(timer& in_timer) { |
|||
cancel_detector detector{ m_cancel_token }; |
|||
|
|||
{ |
|||
std::unique_lock<std::mutex> iterations_guard(m_iterations_mutex); |
|||
// Ensure timer is not already expired
|
|||
if (m_iterations == 0) { |
|||
return; |
|||
} |
|||
|
|||
// Decrement iterations and cancel if necessary
|
|||
--m_iterations; |
|||
} |
|||
|
|||
// Call callback
|
|||
m_callback(in_timer); |
|||
|
|||
if (!detector.expired()) { |
|||
std::unique_lock<std::mutex> iterations_guard(m_iterations_mutex); |
|||
if (m_iterations == 0) { |
|||
// Cancel the timer
|
|||
in_timer.cancel(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private: |
|||
timer::function_t m_callback; |
|||
timer::iterations_t m_iterations; |
|||
std::mutex m_iterations_mutex; |
|||
cancel_token m_cancel_token; |
|||
}; |
|||
|
|||
/** timer */ |
|||
|
|||
timer::timer(duration_t in_period, function_t in_callback) |
|||
: m_context{ new impl::timer_context{ in_period, std::move(in_callback) } } { |
|||
// Add timer_context to timer_manager
|
|||
// Note: this logic must be here (rather than timer_context) to ensure timer_context is wrapped in a shared_ptr
|
|||
if jessilib_assert(in_period.count() != 0) |
|||
if jessilib_assert(!m_context->null()) { |
|||
// Add timer
|
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
{ |
|||
std::lock_guard<std::mutex> lock(manager.m_mutex); |
|||
manager.m_active_timers.insert(m_context.get()); |
|||
} |
|||
|
|||
// Notify
|
|||
manager.m_cvar.notify_one(); |
|||
} |
|||
} |
|||
|
|||
timer::timer(duration_t in_period, iterations_t in_iterations, function_t in_callback) |
|||
: timer{ in_period, callback_with_iterations(in_iterations, std::move(in_callback)) } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
bool timer::operator==(const timer& rhs) const { |
|||
return m_context == rhs.m_context; |
|||
} |
|||
|
|||
bool timer::operator!=(const timer& rhs) const { |
|||
return !(rhs == *this); |
|||
} |
|||
|
|||
timer::time_point_t timer::next() const { |
|||
if (m_context != nullptr) { |
|||
return m_context->next(); |
|||
} |
|||
|
|||
return {}; |
|||
} |
|||
|
|||
timer::duration_t timer::period() const { |
|||
if (m_context != nullptr) { |
|||
return m_context->period(); |
|||
} |
|||
|
|||
return {}; |
|||
} |
|||
|
|||
const timer::function_t& timer::function() const { |
|||
if (m_context != nullptr) { |
|||
return m_context->function(); |
|||
} |
|||
|
|||
// No context; return nullptr
|
|||
static timer::function_t s_null_function_t { nullptr }; |
|||
return s_null_function_t; |
|||
} |
|||
|
|||
bool timer::null() const { |
|||
return m_context == nullptr || m_context->null(); |
|||
} |
|||
|
|||
bool timer::detached() const { |
|||
if (m_context != nullptr) { |
|||
return m_context->detached(); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
void timer::detach() { |
|||
if (m_context != nullptr) { |
|||
m_context->detach(); |
|||
} |
|||
} |
|||
|
|||
void timer::cancel() { |
|||
if (m_context != nullptr) { |
|||
m_context->cancel(); |
|||
} |
|||
} |
|||
|
|||
} // namespace jessilib
|
@ -0,0 +1,119 @@ |
|||
/**
|
|||
* Copyright (C) 2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include "impl/timer_context.hpp" |
|||
#include "impl/timer_manager.hpp" |
|||
#include "assert.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace impl { |
|||
|
|||
timer_context::timer_context(duration_t in_period, function_t in_callback) |
|||
: m_period{ in_period }, |
|||
// m_next{ calc_next() },
|
|||
m_callback{ std::move(in_callback) }, |
|||
m_self{ timer_manager::instance().m_detached_timers.end() }, |
|||
m_last_fire_finish{ std::chrono::steady_clock::now() } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
timer_context::~timer_context() { |
|||
// All references to the context have been destroyed
|
|||
if (!null()) { |
|||
cancel(); |
|||
} |
|||
} |
|||
|
|||
timer_context::time_point_t timer_context::next() const { |
|||
return m_next; |
|||
} |
|||
|
|||
timer_context::duration_t timer_context::period() const { |
|||
return m_period; |
|||
} |
|||
|
|||
const timer::function_t& timer_context::function() const { |
|||
return m_callback; |
|||
} |
|||
|
|||
bool timer_context::null() const { |
|||
return m_callback == nullptr; |
|||
} |
|||
|
|||
bool timer_context::detached() const { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
return m_self != manager.m_detached_timers.end(); |
|||
} |
|||
|
|||
void timer_context::detach() { |
|||
if (!null()) { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
std::list<std::shared_ptr<timer_context>>& detached_timers = manager.m_detached_timers; |
|||
|
|||
jessilib_assert(!detached()); // you cannot detach a timer that is already detached
|
|||
std::lock_guard<std::mutex> lock(manager.m_detached_timers_mutex); |
|||
// We need to attach this new shared_ptr to the callback
|
|||
m_self = detached_timers.emplace(detached_timers.end(), shared_from_this()); |
|||
} |
|||
} |
|||
|
|||
void timer_context::cancel() { |
|||
impl::timer_manager& manager = impl::timer_manager::instance(); |
|||
|
|||
// Unlock mutex if it's currently being held by this thread
|
|||
bool needs_shared_lock = false; |
|||
if (manager.thread_callback_timer().m_context.get() == this) { |
|||
needs_shared_lock = true; |
|||
m_mutex.unlock_shared(); |
|||
} |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> manager_guard(manager.m_mutex); |
|||
std::lock_guard<std::shared_mutex> context_guard(m_mutex); |
|||
|
|||
if (!null()) { |
|||
// Remove from active timers
|
|||
manager.m_active_timers.erase(this); |
|||
|
|||
// Nullify timer
|
|||
m_callback = nullptr; |
|||
|
|||
// Remove from detached timers (if it's detached)
|
|||
if (detached()) { |
|||
std::lock_guard<std::mutex> detached_timers_lock(manager.m_detached_timers_mutex); |
|||
manager.m_detached_timers.erase(m_self); |
|||
} |
|||
} |
|||
|
|||
manager.is_timeout = false; |
|||
} |
|||
|
|||
// Re-lock mutex
|
|||
if (needs_shared_lock) { |
|||
m_mutex.lock_shared(); |
|||
} |
|||
|
|||
manager.m_cvar.notify_one(); |
|||
} |
|||
|
|||
timer_context::time_point_t timer_context::calc_next() { |
|||
return std::chrono::steady_clock::now() + m_period; |
|||
} |
|||
|
|||
} // namespace impl
|
|||
} // namespace jessilib
|
@ -0,0 +1,68 @@ |
|||
/**
|
|||
* Copyright (C) 2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <stdexcept> |
|||
|
|||
/** Macros */ |
|||
|
|||
#define STRINGIFY(arg) \ |
|||
#arg |
|||
#define STRINGIFY_HELPER(line) \ |
|||
STRINGIFY(line) |
|||
|
|||
// Returns a boolean expression indicating assertion success/failure
|
|||
#define jessilib_assert(expression) \ |
|||
(::jessilib::impl::_assert_helper( expression, "Failed assertion: '" #expression "' at " __FILE__ ":" STRINGIFY_HELPER(__LINE__) )) |
|||
|
|||
namespace jessilib { |
|||
|
|||
/** Exception type */ |
|||
class assertion_failed : public std::logic_error { |
|||
public: |
|||
inline explicit assertion_failed(const char* expression) |
|||
: std::logic_error{ expression } { |
|||
} |
|||
}; |
|||
|
|||
namespace impl { |
|||
|
|||
/** Macro helpers */ |
|||
inline bool _assert_helper(bool value, [[maybe_unused]] const char* message) { |
|||
#ifndef NDEBUG |
|||
if (!value) { |
|||
throw assertion_failed{message}; |
|||
} |
|||
#endif // NDEBUG
|
|||
|
|||
return value; |
|||
} |
|||
|
|||
} // namespace impl
|
|||
} // namespace jessilib
|
|||
|
|||
// Provides for disabling of assertions; will likely produce warnings
|
|||
#ifdef DISABLE_ASSERTIONS |
|||
|
|||
// Disable jessilib_assert
|
|||
#undef jessilib_assert |
|||
#define jessilib_assert(expression) \ |
|||
(true) |
|||
|
|||
#endif // DISABLE_ASSERTIONS
|
@ -0,0 +1,75 @@ |
|||
/**
|
|||
* Copyright (C) 2018 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <atomic> |
|||
#include <shared_mutex> |
|||
#include "timer.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace impl { |
|||
|
|||
class timer_context : public std::enable_shared_from_this<timer_context> { |
|||
public: |
|||
// Destructor (needs to be public for shared_ptr)
|
|||
~timer_context(); |
|||
|
|||
private: |
|||
// Types
|
|||
using function_t = timer::function_t; /** Function type called by the timer */ |
|||
using time_point_t = timer::time_point_t; /** Type representing the point in time at which a timer will be called */ |
|||
using duration_t = timer::duration_t; /** Type representing the time between calls */ |
|||
|
|||
// Constructor
|
|||
timer_context(duration_t in_period, function_t in_callback); |
|||
|
|||
// Explicitly delete all implicit constructors/assignment operators
|
|||
timer_context() = delete; |
|||
timer_context(const timer_context&) = delete; |
|||
timer_context(timer_context&&) = delete; |
|||
timer_context& operator=(const timer_context&) = delete; |
|||
timer_context& operator=(timer_context&&) = delete; |
|||
|
|||
// Accessors
|
|||
time_point_t next() const; |
|||
duration_t period() const; |
|||
const function_t& function() const; |
|||
bool null() const; |
|||
bool detached() const; |
|||
time_point_t calc_next(); |
|||
|
|||
// Mutators
|
|||
void detach(); |
|||
void cancel(); |
|||
|
|||
// Members
|
|||
duration_t m_period; |
|||
function_t m_callback; |
|||
time_point_t m_next; |
|||
std::list<std::shared_ptr<timer_context>>::iterator m_self; // Necessary to cancel detached timers, and implement detached()
|
|||
std::shared_mutex m_mutex; |
|||
std::chrono::steady_clock::time_point m_last_fire_finish; // Set and accessed only from timer_manager
|
|||
|
|||
// Friends
|
|||
friend timer; |
|||
friend timer_manager; |
|||
}; |
|||
|
|||
} |
|||
} |
Loading…
Reference in new issue