001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
/*
* 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.
*/

#if !defined(OMNI_TIMER_HPP)
#define OMNI_TIMER_HPP 1
#include <omni/defs/class_macros.hpp>
#include <omni/delegate/1.hpp>
#include <omni/sync/basic_thread.hpp>
#include <omni/chrono/stopwatch.hpp>
#include <omni/generic_ptr.hpp>
#if defined(OMNI_SAFE_TIMER)
    #include <omni/sync/basic_lock.hpp>
#endif
#include <deque>

namespace omni {
    class timer
    {
        public:
            typedef struct tick_event_type {
                typedef enum enum_t {
                    /**
                     * The tick event will fire in a separate detached thread and continue; the 'tick' delegate
                     * used to subscribe to the `timer::tick` event must be re-entrant.
                     */

                    SYNC_NOBLOCK = 0, // default
                    /**
                     * The tick event will not return (i.e. block the _run thread) until the delegate returns;
                     * the next event won't be fired until 'interval' has passed.
                     */

                    SYNC_BLOCK = 1,
                    /**
                     * The tick event will fire, but all subsequent 'ticks' will be queued if delegate blocks;
                     * each queued call will be called in succession without regards to the tick event.
                     */

                    SYNC_QUEUE = 2,
                    /**
                     * The tick event will file, but all subsequent 'ticks' will be ignored until the delegate
                     * has finished. It is possible to have an events fire immediately after each other if the
                     * timing works out such that the delegate call finishes as the tick event is about to occur.
                     */

                    SYNC_SKIP = 4
                } enum_t;
            } tick_event_type;
            
            typedef struct tick_event_args {
                    omni::chrono::tick_t signal_time;
                    omni::generic_ptr state_object;
                
                    tick_event_args() : signal_time(), state_object() {}
                    tick_event_args(omni::chrono::tick_t st, const omni::generic_ptr& sobj) : signal_time(st), state_object(sobj) {}
                    omni::timer::tick_event_args& operator=(const omni::timer::tick_event_args& o) {
                        this->signal_time = o.signal_time;
                        this->state_object = o.state_object;
                        return *this;
                    }
                    bool operator==(const omni::timer::tick_event_args& o) const {
                        if (this == &o) { return true; }
                        return (omni::chrono::equal(this->signal_time, o.signal_time) &&
                                this->state_object == o.state_object);
                    }
                    bool operator!=(const omni::timer::tick_event_args& o) const {
                        return !(*this == o);
                    }
            } tick_event_args;
            
            typedef omni::event1< void, const omni::timer::tick_event_args& > tick_event;
            typedef omni::delegate1< void, const omni::timer::tick_event_args& > tick_delegate;
            typedef omni::timer::tick_event_type::enum_t tick_event_type_t;
            
            timer();
            explicit timer(omni::timer::tick_event_type_t tetype);
            timer(omni::timer::tick_event_type_t tetype, std::size_t ival);
            timer(omni::timer::tick_event_type_t tetype, std::size_t ival, omni::generic_ptr so);
            timer(omni::timer::tick_event_type_t tetype, const omni::timer::tick_delegate& fn, std::size_t ival);
            timer(omni::timer::tick_event_type_t tetype, const omni::timer::tick_delegate& fn, std::size_t ival, omni::generic_ptr so);
            timer(omni::timer::tick_event_type_t tetype, const omni::timer::tick_delegate& fn, std::size_t ival, std::size_t delay);
            timer(omni::timer::tick_event_type_t tetype, const omni::timer::tick_delegate& fn, std::size_t ival, std::size_t delay, omni::generic_ptr so);
            explicit timer(std::size_t ival);
            timer(std::size_t ival, omni::generic_ptr so);
            timer(const omni::timer::tick_delegate& fn, std::size_t ival);
            timer(const omni::timer::tick_delegate& fn, std::size_t ival, omni::generic_ptr so);
            timer(const omni::timer::tick_delegate& fn, std::size_t ival, std::size_t delay);
            timer(const omni::timer::tick_delegate& fn, std::size_t ival, std::size_t delay, omni::generic_ptr so);
            timer(const omni::timer& cp);
            ~timer();
            bool auto_reset() const;
            std::size_t interval() const;
            bool is_running() const;
            void reset();
            void set_auto_reset(bool auto_reset);
            void set_interval(std::size_t ival);
            void start();
            void start(std::size_t delay);
            void stop();
            const omni::timer::tick_event_type_t tick_type() const { return this->m_tktype; }
            omni::timer& operator=(const omni::timer& other);
            bool operator==(const omni::timer& o) const;
            bool operator!=(const omni::timer& o) const;
            
            omni::generic_ptr state_object; // state object used when tick event passes
            omni::timer::tick_event tick; // Occurs when the interval has passed
            OMNI_MEMBERS_FW(omni::timer) // disposing,name,type(),hash()
            
        private:
            void _add_queue(const omni::timer::tick_event_args& tea);
            void _run(void* param);
            void _run_queued();
            void _set_running(bool ir);
            bool _stopreq() const;
            bool _queue_empty() const;
            
            #if defined(OMNI_TYPE_INFO)
                omni::type<omni::timer> m_type;
            #endif
            #if defined(OMNI_SAFE_TIMER)
                mutable omni::sync::basic_lock m_mtx;
                mutable omni::sync::basic_lock m_qmtx;
            #endif
            std::deque<omni::timer::tick_event_args> m_que; // the queue
            omni::sync::basic_thread *m_thread; // the main timer thread to _run on
            omni::sync::basic_thread *m_qthread; // the thread to run the queue on
            std::size_t m_int; // "elapsed" interval in ms between ticks
            omni::timer::tick_event_type_t m_tktype;
            volatile bool m_auto; // true by default, false for tick once then stop
            volatile bool m_isrun; // is running
            volatile bool m_stopreq; // stop requested
    };
} // namespace omni

#endif // OMNI_TIMER_HPP