/*
* This file is part of the Omni C++ framework
*
* Copyright (c) 2016, Zeriph Enterprises, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of Zeriph, Zeriph Enterprises, LLC, nor the names
* of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ZERIPH AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ZERIPH AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* DEV_NOTE: this file is not intended to be used directly by any user code!
i.e. don't #include <omni/xxx_impl.hxx> and don't compile this source directly.
this file is #include'd directly in other source.
The logic is that
omni::cstring and
omni::wstring namespaces segregate the types
for explicit calling; i.e. you can call
omni::cstring::X to check on a std:string
and similarly can call
omni::wstring::X to check a std::wstring, while still having access
to the
omni::string_t and omn::string::X functions (which are aliases for the other namespaces).
Since
omni::wstring and
omni::cstring are merely wrappers for the
omni::string::util functions
(which are templated) that then pass in the appropriate types (std::string/wstring char/wchar_t)
putting the relevant code in a header with a few #defs for types makes keeping the files
in sync (for functions) less messy. It does introduce slight confusion to anyone who might
want to read this specific code or documentation though, hence this note.
*/
// so as not to accidentally build this file with the source
// these macros are defined in chrono
#if !defined(OMNI_TIMER_T_FW) || !defined(OMNI_TMR_ALOCK_FW) || !defined(OMNI_TMR_MLOCK_FW) || !defined(OMNI_TMR_ULOCK_FW)
#error invalid preprocessor directive detected
#endif
#if !defined(OMNI_TIMER_EX_STOP_FW)
#define OMNI_TIMER_EX_STOP_FW
#endif
#if !defined(OMNI_TIMER_EX_RUN_BEG_FW)
#define OMNI_TIMER_EX_RUN_BEG_FW
#endif
#if !defined(OMNI_TIMER_EX_RUN_END_FW)
#define OMNI_TIMER_EX_RUN_END_FW
#endif
omni::chrono::OMNI_TIMER_T_FW::~OMNI_TIMER_T_FW()
{
OMNI_TRY_FW
OMNI_DTOR_FW
this->stop();
OMNI_CATCH_FW
OMNI_D5_FW(
"destroyed");
}
bool omni::chrono::OMNI_TIMER_T_FW::auto_reset()
const
{
OMNI_TMR_ALOCK_FW
return this->m_auto;
}
std::size_t omni::chrono::OMNI_TIMER_T_FW::interval()
const
{
OMNI_TMR_ALOCK_FW
return this->m_int;
}
bool omni::chrono::OMNI_TIMER_T_FW::is_running()
const
{
OMNI_TMR_ALOCK_FW
return this->m_isrun;
}
void omni::chrono::OMNI_TIMER_T_FW::reset()
{
this->stop();
this->start();
}
void omni::chrono::OMNI_TIMER_T_FW::set_auto_reset(
bool autoreset)
{
OMNI_TMR_ALOCK_FW
this->m_auto = autoreset;
}
void omni::chrono::OMNI_TIMER_T_FW::set_interval(std::size_t ival)
{
OMNI_TMR_ALOCK_FW
this->m_int = ival;
}
void omni::chrono::OMNI_TIMER_T_FW::start()
{
this->start(
0);
}
void omni::chrono::OMNI_TIMER_T_FW::start(std::size_t delay)
{
if (!
this->is_running()) {
{
// error check scope (for auto mutex)
OMNI_TMR_ALOCK_FW
if (!
this->tick) {
OMNI_ERR_RET_FW(
OMNI_INVALID_DELEGATE_FUNC_STR,
omni::invalid_delegate())
}
if (
this->m_int ==
0) {
OMNI_ERRV_RET_FW(
"Start error: ",
OMNI_INDEX_OOR_STR,
omni::index_out_of_range())
}
this->m_stopreq =
false;
}
if (delay ==
0) {
this->m_thread =
omni::sync::allocate_basic_thread<omni::chrono::OMNI_TIMER_T_FW,
&omni::chrono::OMNI_TIMER_T_FW::_run>(
this);
}
else {
this->m_thread =
omni::sync::allocate_basic_thread_parameterized<
omni::chrono::OMNI_TIMER_T_FW,
&omni::chrono::OMNI_TIMER_T_FW::_run_delayed>
(
this,
omni::sync::thread_start_type::NOW, &delay);
}
while (!
this->is_running()) {
OMNI_THREAD_YIELD(); }
}
#if defined(OMNI_DBG_L2)
else { OMNI_D2_FW(
"The timer is already running"); }
#endif
}
void omni::chrono::OMNI_TIMER_T_FW::stop()
{
this->stop(
0,
true);
}
void omni::chrono::OMNI_TIMER_T_FW::stop(std::size_t join_timeout)
{
this->stop(join_timeout,
true);
}
void omni::chrono::OMNI_TIMER_T_FW::stop(std::size_t join_timeout,
bool kill_on_timeout)
{
if (
this->is_running()) {
OMNI_TMR_MLOCK_FW
this->m_stopreq =
true;
OMNI_TMR_ULOCK_FW
if (join_timeout ==
0) {
this->m_thread->join();
}
else {
this->m_thread->join(join_timeout);
if (kill_on_timeout &&
this->is_running()) {
// still running?
OMNI_D2_FW(
"timer still running after stop request, killing thread...");
this->m_thread->kill();
}
}
OMNI_TIMER_EX_STOP_FW
delete this->m_thread;
this->m_thread =
OMNI_NULL;
}
#if defined(OMNI_DBG_L2)
else { OMNI_D2_FW(
"the timer is not running"); }
#endif
}
void omni::chrono::OMNI_TIMER_T_FW::_run_delayed(
omni::generic_ptr dly)
// only called if delay > 0
{
std::size_t delay = *(
static_cast<std::size_t*>(dly));
OMNI_D2_FW(
"waiting to start, delay " << delay <<
"ms");
OMNI_TMR_MLOCK_FW
this->m_isrun =
true;
OMNI_TMR_ULOCK_FW
OMNI_SLEEP_INIT();
omni::chrono::tick_t st =
omni::chrono::monotonic_tick();
while (!
this->_stopreq() && (
omni::chrono::elapsed_ms(st) < delay)) {
OMNI_SLEEP1();
}
this->_do_run();
}
void omni::chrono::OMNI_TIMER_T_FW::_run()
{
OMNI_D2_FW(
"start timer");
OMNI_TMR_MLOCK_FW
this->m_isrun =
true;
OMNI_TMR_ULOCK_FW
this->_do_run();
}
void omni::chrono::OMNI_TIMER_T_FW::_do_run()
{
OMNI_TIMER_EX_RUN_BEG_FW
omni::chrono::tick_t tickt;
OMNI_SLEEP_INIT();
do {
if (
this->_stopreq()) {
break; }
tickt =
omni::chrono::monotonic_tick();
while (
omni::chrono::elapsed_ms(tickt) <
this->interval()) {
OMNI_SLEEP1();
if (
this->_stopreq()) {
break; }
}
if (
this->_stopreq()) {
break; }
this->_do_tick();
}
while (
this->auto_reset());
OMNI_TIMER_EX_RUN_END_FW
OMNI_TMR_MLOCK_FW
this->m_isrun =
false;
OMNI_TMR_ULOCK_FW
}
bool omni::chrono::OMNI_TIMER_T_FW::_stopreq()
const
{
OMNI_TMR_ALOCK_FW
return this->m_stopreq;
}
#undef OMNI_TIMER_EX_STOP_FW
#undef OMNI_TIMER_EX_RUN_BEG_FW
#undef OMNI_TIMER_EX_RUN_END_FW