//////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include "scheduler.h" #if defined __EXCEPTION_TRACER__ #include "exception.h" #endif Scheduler::SchedulerState Scheduler::m_threadState = Scheduler::STATE_TERMINATED; Scheduler::Scheduler() { m_lastEvent = 0; Scheduler::m_threadState = STATE_RUNNING; boost::thread(boost::bind(&Scheduler::schedulerThread, (void*)this)); } void Scheduler::schedulerThread(void* p) { Scheduler* scheduler = (Scheduler*)p; #if defined __EXCEPTION_TRACER__ ExceptionHandler schedulerExceptionHandler; schedulerExceptionHandler.InstallHandler(); #endif srand((uint32_t)OTSYS_TIME()); boost::unique_lock eventLockUnique(scheduler->m_eventLock, boost::defer_lock); while(Scheduler::m_threadState != Scheduler::STATE_TERMINATED) { SchedulerTask* task = NULL; bool run = false, ret = false; // check if there are events waiting... eventLockUnique.lock(); if(scheduler->m_eventList.empty()) // unlock mutex and wait for signal scheduler->m_eventSignal.wait(eventLockUnique); else // unlock mutex and wait for signal or timeout ret = scheduler->m_eventSignal.timed_wait(eventLockUnique, scheduler->m_eventList.top()->getCycle()); // the mutex is locked again now... if(!ret && Scheduler::m_threadState != Scheduler::STATE_TERMINATED) { // ok we had a timeout, so there has to be an event we have to execute... task = scheduler->m_eventList.top(); scheduler->m_eventList.pop(); // check if the event was stopped EventIds::iterator it = scheduler->m_eventIds.find(task->getEventId()); if(it != scheduler->m_eventIds.end()) { // was not stopped so we should run it run = true; scheduler->m_eventIds.erase(it); } } eventLockUnique.unlock(); // add task to dispatcher if(task) { // if it was not stopped if(run) { task->unsetExpiration(); Dispatcher::getInstance().addTask(task); } else delete task; // was stopped, have to be deleted here } } #if defined __EXCEPTION_TRACER__ schedulerExceptionHandler.RemoveHandler(); #endif } uint32_t Scheduler::addEvent(SchedulerTask* task) { bool signal = false; m_eventLock.lock(); if(Scheduler::m_threadState == Scheduler::STATE_RUNNING) { // check if the event has a valid id if(!task->getEventId()) { // if not generate one if(m_lastEvent >= 0xFFFFFFFF) m_lastEvent = 0; ++m_lastEvent; task->setEventId(m_lastEvent); } // insert the eventid in the list of active events m_eventIds.insert(task->getEventId()); // add the event to the queue m_eventList.push(task); // if the list was empty or this event is the top in the list // we have to signal it signal = (task == m_eventList.top()); } #ifdef __DEBUG_SCHEDULER__ else std::cout << "[Error - Scheduler::addTask] Scheduler thread is terminated." << std::endl; #endif m_eventLock.unlock(); if(signal) m_eventSignal.notify_one(); return task->getEventId(); } bool Scheduler::stopEvent(uint32_t eventId) { if(!eventId) return false; m_eventLock.lock(); // search the event id... EventIds::iterator it = m_eventIds.find(eventId); if(it != m_eventIds.end()) { // if it is found erase from the list m_eventIds.erase(it); m_eventLock.unlock(); return true; } // this eventid is not valid m_eventLock.unlock(); return false; } void Scheduler::stop() { m_eventLock.lock(); m_threadState = Scheduler::STATE_CLOSING; m_eventLock.unlock(); } void Scheduler::shutdown() { m_eventLock.lock(); m_threadState = Scheduler::STATE_TERMINATED; //this list should already be empty while(!m_eventList.empty()) m_eventList.pop(); m_eventIds.clear(); m_eventLock.unlock(); }