12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091 |
- /****************************************************************************
- Copyright (c) 2008-2010 Ricardo Quesada
- Copyright (c) 2010-2012 cocos2d-x.org
- Copyright (c) 2011 Zynga Inc.
- Copyright (c) 2013-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
- http://www.cocos2d-x.org
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- ****************************************************************************/
- #include "base/CCScheduler.h"
- #include "base/ccMacros.h"
- #include "base/CCDirector.h"
- #include "base/utlist.h"
- #include "base/ccCArray.h"
- #include "base/CCScriptSupport.h"
- NS_CC_BEGIN
- // data structures
- // A list double-linked list used for "updates with priority"
- typedef struct _listEntry
- {
- struct _listEntry *prev, *next;
- ccSchedulerFunc callback;
- void *target;
- int priority;
- bool paused;
- bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
- } tListEntry;
- typedef struct _hashUpdateEntry
- {
- tListEntry **list; // Which list does it belong to ?
- tListEntry *entry; // entry in the list
- void *target;
- ccSchedulerFunc callback;
- UT_hash_handle hh;
- } tHashUpdateEntry;
- // Hash Element used for "selectors with interval"
- typedef struct _hashSelectorEntry
- {
- ccArray *timers;
- void *target;
- int timerIndex;
- Timer *currentTimer;
- bool paused;
- UT_hash_handle hh;
- } tHashTimerEntry;
- // implementation Timer
- Timer::Timer()
- : _scheduler(nullptr)
- , _elapsed(-1)
- , _runForever(false)
- , _useDelay(false)
- , _timesExecuted(0)
- , _repeat(0)
- , _delay(0.0f)
- , _interval(0.0f)
- , _aborted(false)
- {
- }
- void Timer::setupTimerWithInterval(float seconds, unsigned int repeat, float delay)
- {
- _elapsed = -1;
- _interval = seconds;
- _delay = delay;
- _useDelay = (_delay > 0.0f) ? true : false;
- _repeat = repeat;
- _runForever = (_repeat == CC_REPEAT_FOREVER) ? true : false;
- _timesExecuted = 0;
- }
- void Timer::update(float dt)
- {
- if (_elapsed == -1)
- {
- _elapsed = 0;
- _timesExecuted = 0;
- return;
- }
- // accumulate elapsed time
- _elapsed += dt;
-
- // deal with delay
- if (_useDelay)
- {
- if (_elapsed < _delay)
- {
- return;
- }
- _timesExecuted += 1; // important to increment before call trigger
- trigger(_delay);
- _elapsed = _elapsed - _delay;
- _useDelay = false;
- // after delay, the rest time should compare with interval
- if (isExhausted())
- { //unschedule timer
- cancel();
- return;
- }
- }
-
- // if _interval == 0, should trigger once every frame
- float interval = (_interval > 0) ? _interval : _elapsed;
- while ((_elapsed >= interval) && !_aborted)
- {
- _timesExecuted += 1; // important to increment before call trigger
- trigger(interval);
- _elapsed -= interval;
- if (isExhausted())
- {
- cancel();
- break;
- }
- if (_elapsed <= 0.f)
- {
- break;
- }
- }
- }
- bool Timer::isExhausted() const
- {
- return !_runForever && _timesExecuted > _repeat;
- }
- // TimerTargetSelector
- TimerTargetSelector::TimerTargetSelector()
- : _target(nullptr)
- , _selector(nullptr)
- {
- }
- bool TimerTargetSelector::initWithSelector(Scheduler* scheduler, SEL_SCHEDULE selector, Ref* target, float seconds, unsigned int repeat, float delay)
- {
- _scheduler = scheduler;
- _target = target;
- _selector = selector;
- setupTimerWithInterval(seconds, repeat, delay);
- return true;
- }
- void TimerTargetSelector::trigger(float dt)
- {
- if (_target && _selector)
- {
- (_target->*_selector)(dt);
- }
- }
- void TimerTargetSelector::cancel()
- {
- _scheduler->unschedule(_selector, _target);
- }
- // TimerTargetCallback
- TimerTargetCallback::TimerTargetCallback()
- : _target(nullptr)
- , _callback(nullptr)
- {
- }
- bool TimerTargetCallback::initWithCallback(Scheduler* scheduler, const ccSchedulerFunc& callback, void *target, const std::string& key, float seconds, unsigned int repeat, float delay)
- {
- _scheduler = scheduler;
- _target = target;
- _callback = callback;
- _key = key;
- setupTimerWithInterval(seconds, repeat, delay);
- return true;
- }
- void TimerTargetCallback::trigger(float dt)
- {
- if (_callback)
- {
- _callback(dt);
- }
- }
- void TimerTargetCallback::cancel()
- {
- _scheduler->unschedule(_key, _target);
- }
- #if CC_ENABLE_SCRIPT_BINDING
- // TimerScriptHandler
- bool TimerScriptHandler::initWithScriptHandler(int handler, float seconds)
- {
- _scriptHandler = handler;
- _elapsed = -1;
- _interval = seconds;
-
- return true;
- }
- void TimerScriptHandler::trigger(float dt)
- {
- if (0 != _scriptHandler)
- {
- SchedulerScriptData data(_scriptHandler,dt);
- ScriptEvent event(kScheduleEvent,&data);
- ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&event);
- }
- }
- void TimerScriptHandler::cancel()
- {
- }
- #endif
- // implementation of Scheduler
- // Priority level reserved for system services.
- const int Scheduler::PRIORITY_SYSTEM = INT_MIN;
- // Minimum priority level for user scheduling.
- const int Scheduler::PRIORITY_NON_SYSTEM_MIN = PRIORITY_SYSTEM + 1;
- Scheduler::Scheduler(void)
- : _timeScale(1.0f)
- , _updatesNegList(nullptr)
- , _updates0List(nullptr)
- , _updatesPosList(nullptr)
- , _hashForUpdates(nullptr)
- , _hashForTimers(nullptr)
- , _currentTarget(nullptr)
- , _currentTargetSalvaged(false)
- , _updateHashLocked(false)
- #if CC_ENABLE_SCRIPT_BINDING
- , _scriptHandlerEntries(20)
- #endif
- {
- // I don't expect to have more than 30 functions to all per frame
- _functionsToPerform.reserve(30);
- }
- Scheduler::~Scheduler(void)
- {
- unscheduleAll();
- }
- void Scheduler::removeHashElement(_hashSelectorEntry *element)
- {
- ccArrayFree(element->timers);
- HASH_DEL(_hashForTimers, element);
- free(element);
- }
- void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, bool paused, const std::string& key)
- {
- this->schedule(callback, target, interval, CC_REPEAT_FOREVER, 0.0f, paused, key);
- }
- void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float interval, unsigned int repeat, float delay, bool paused, const std::string& key)
- {
- CCASSERT(target, "Argument target must be non-nullptr");
- CCASSERT(!key.empty(), "key should not be empty!");
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if (! element)
- {
- element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
- element->target = target;
- HASH_ADD_PTR(_hashForTimers, target, element);
- // Is this the 1st element ? Then set the pause level to all the selectors of this target
- element->paused = paused;
- }
- else
- {
- CCASSERT(element->paused == paused, "element's paused should be paused!");
- }
- if (element->timers == nullptr)
- {
- element->timers = ccArrayNew(10);
- }
- else
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
- if (timer && !timer->isExhausted() && key == timer->getKey())
- {
- CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
- timer->setupTimerWithInterval(interval, repeat, delay);
- return;
- }
- }
- ccArrayEnsureExtraCapacity(element->timers, 1);
- }
- TimerTargetCallback *timer = new (std::nothrow) TimerTargetCallback();
- timer->initWithCallback(this, callback, target, key, interval, repeat, delay);
- ccArrayAppendObject(element->timers, timer);
- timer->release();
- }
- void Scheduler::unschedule(const std::string &key, void *target)
- {
- // explicit handle nil arguments when removing an object
- if (target == nullptr || key.empty())
- {
- return;
- }
- //CCASSERT(target);
- //CCASSERT(selector);
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if (element)
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
- if (timer && key == timer->getKey())
- {
- if (timer == element->currentTimer && (! timer->isAborted()))
- {
- timer->retain();
- timer->setAborted();
- }
- ccArrayRemoveObjectAtIndex(element->timers, i, true);
- // update timerIndex in case we are in tick:, looping over the actions
- if (element->timerIndex >= i)
- {
- element->timerIndex--;
- }
- if (element->timers->num == 0)
- {
- if (_currentTarget == element)
- {
- _currentTargetSalvaged = true;
- }
- else
- {
- removeHashElement(element);
- }
- }
- return;
- }
- }
- }
- }
- void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused)
- {
- tListEntry *listElement = new (std::nothrow) tListEntry();
- listElement->callback = callback;
- listElement->target = target;
- listElement->priority = priority;
- listElement->paused = paused;
- listElement->next = listElement->prev = nullptr;
- listElement->markedForDeletion = false;
- // empty list ?
- if (! *list)
- {
- DL_APPEND(*list, listElement);
- }
- else
- {
- bool added = false;
- for (tListEntry *element = *list; element; element = element->next)
- {
- if (priority < element->priority)
- {
- if (element == *list)
- {
- DL_PREPEND(*list, listElement);
- }
- else
- {
- listElement->next = element;
- listElement->prev = element->prev;
- element->prev->next = listElement;
- element->prev = listElement;
- }
- added = true;
- break;
- }
- }
- // Not added? priority has the higher value. Append it.
- if (! added)
- {
- DL_APPEND(*list, listElement);
- }
- }
- // update hash entry for quick access
- tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
- hashElement->target = target;
- hashElement->list = list;
- hashElement->entry = listElement;
- memset(&hashElement->hh, 0, sizeof(hashElement->hh));
- HASH_ADD_PTR(_hashForUpdates, target, hashElement);
- }
- void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused)
- {
- tListEntry *listElement = new (std::nothrow) tListEntry();
- listElement->callback = callback;
- listElement->target = target;
- listElement->paused = paused;
- listElement->priority = 0;
- listElement->markedForDeletion = false;
- DL_APPEND(*list, listElement);
- // update hash entry for quicker access
- tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
- hashElement->target = target;
- hashElement->list = list;
- hashElement->entry = listElement;
- memset(&hashElement->hh, 0, sizeof(hashElement->hh));
- HASH_ADD_PTR(_hashForUpdates, target, hashElement);
- }
- void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused)
- {
- tHashUpdateEntry *hashElement = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &target, hashElement);
- if (hashElement)
- {
- // change priority: should unschedule it first
- if (hashElement->entry->priority != priority)
- {
- unscheduleUpdate(target);
- }
- else
- {
- // don't add it again
- //CCLOG("warning: don't update it again");
- return;
- }
- }
- // most of the updates are going to be 0, that's way there
- // is an special list for updates with priority 0
- if (priority == 0)
- {
- appendIn(&_updates0List, callback, target, paused);
- }
- else if (priority < 0)
- {
- priorityIn(&_updatesNegList, callback, target, priority, paused);
- }
- else
- {
- // priority > 0
- priorityIn(&_updatesPosList, callback, target, priority, paused);
- }
- }
- bool Scheduler::isScheduled(const std::string& key, const void *target) const
- {
- CCASSERT(!key.empty(), "Argument key must not be empty");
- CCASSERT(target, "Argument target must be non-nullptr");
-
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
-
- if (!element)
- {
- return false;
- }
-
- if (element->timers == nullptr)
- {
- return false;
- }
-
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
-
- if (timer && !timer->isExhausted() && key == timer->getKey())
- {
- return true;
- }
- }
-
- return false;
- }
- void Scheduler::removeUpdateFromHash(struct _listEntry *entry)
- {
- tHashUpdateEntry *element = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &entry->target, element);
- if (element)
- {
- // list entry
- DL_DELETE(*element->list, element->entry);
- if (!_updateHashLocked)
- CC_SAFE_DELETE(element->entry);
- else
- {
- element->entry->markedForDeletion = true;
- _updateDeleteVector.push_back(element->entry);
- }
- // hash entry
- HASH_DEL(_hashForUpdates, element);
- free(element);
- }
- }
- void Scheduler::unscheduleUpdate(void *target)
- {
- if (target == nullptr)
- {
- return;
- }
- tHashUpdateEntry *element = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &target, element);
- if (element)
- this->removeUpdateFromHash(element->entry);
- }
- void Scheduler::unscheduleAll(void)
- {
- unscheduleAllWithMinPriority(PRIORITY_SYSTEM);
- }
- void Scheduler::unscheduleAllWithMinPriority(int minPriority)
- {
- // Custom Selectors
- tHashTimerEntry *element = nullptr;
- tHashTimerEntry *nextElement = nullptr;
- for (element = _hashForTimers; element != nullptr;)
- {
- // element may be removed in unscheduleAllSelectorsForTarget
- nextElement = (tHashTimerEntry *)element->hh.next;
- unscheduleAllForTarget(element->target);
- element = nextElement;
- }
- // Updates selectors
- tListEntry *entry, *tmp;
- if(minPriority < 0)
- {
- DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
- {
- if(entry->priority >= minPriority)
- {
- unscheduleUpdate(entry->target);
- }
- }
- }
- if(minPriority <= 0)
- {
- DL_FOREACH_SAFE(_updates0List, entry, tmp)
- {
- unscheduleUpdate(entry->target);
- }
- }
- DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
- {
- if(entry->priority >= minPriority)
- {
- unscheduleUpdate(entry->target);
- }
- }
- #if CC_ENABLE_SCRIPT_BINDING
- _scriptHandlerEntries.clear();
- #endif
- }
- void Scheduler::unscheduleAllForTarget(void *target)
- {
- // explicit nullptr handling
- if (target == nullptr)
- {
- return;
- }
- // Custom Selectors
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if (element)
- {
- if (ccArrayContainsObject(element->timers, element->currentTimer)
- && (! element->currentTimer->isAborted()))
- {
- element->currentTimer->retain();
- element->currentTimer->setAborted();
- }
- ccArrayRemoveAllObjects(element->timers);
- if (_currentTarget == element)
- {
- _currentTargetSalvaged = true;
- }
- else
- {
- removeHashElement(element);
- }
- }
- // update selector
- unscheduleUpdate(target);
- }
- #if CC_ENABLE_SCRIPT_BINDING
- unsigned int Scheduler::scheduleScriptFunc(unsigned int handler, float interval, bool paused)
- {
- SchedulerScriptHandlerEntry* entry = SchedulerScriptHandlerEntry::create(handler, interval, paused);
- _scriptHandlerEntries.pushBack(entry);
- return entry->getEntryId();
- }
- void Scheduler::unscheduleScriptEntry(unsigned int scheduleScriptEntryID)
- {
- for (ssize_t i = _scriptHandlerEntries.size() - 1; i >= 0; i--)
- {
- SchedulerScriptHandlerEntry* entry = _scriptHandlerEntries.at(i);
- if (entry->getEntryId() == (int)scheduleScriptEntryID)
- {
- entry->markedForDeletion();
- break;
- }
- }
- }
- #endif
- void Scheduler::resumeTarget(void *target)
- {
- CCASSERT(target != nullptr, "target can't be nullptr!");
- // custom selectors
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if (element)
- {
- element->paused = false;
- }
- // update selector
- tHashUpdateEntry *elementUpdate = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &target, elementUpdate);
- if (elementUpdate)
- {
- CCASSERT(elementUpdate->entry != nullptr, "elementUpdate's entry can't be nullptr!");
- elementUpdate->entry->paused = false;
- }
- }
- void Scheduler::pauseTarget(void *target)
- {
- CCASSERT(target != nullptr, "target can't be nullptr!");
- // custom selectors
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if (element)
- {
- element->paused = true;
- }
- // update selector
- tHashUpdateEntry *elementUpdate = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &target, elementUpdate);
- if (elementUpdate)
- {
- CCASSERT(elementUpdate->entry != nullptr, "elementUpdate's entry can't be nullptr!");
- elementUpdate->entry->paused = true;
- }
- }
- bool Scheduler::isTargetPaused(void *target)
- {
- CCASSERT( target != nullptr, "target must be non nil" );
- // Custom selectors
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
- if( element )
- {
- return element->paused;
- }
-
- // We should check update selectors if target does not have custom selectors
- tHashUpdateEntry *elementUpdate = nullptr;
- HASH_FIND_PTR(_hashForUpdates, &target, elementUpdate);
- if ( elementUpdate )
- {
- return elementUpdate->entry->paused;
- }
-
- return false; // should never get here
- }
- std::set<void*> Scheduler::pauseAllTargets()
- {
- return pauseAllTargetsWithMinPriority(PRIORITY_SYSTEM);
- }
- std::set<void*> Scheduler::pauseAllTargetsWithMinPriority(int minPriority)
- {
- std::set<void*> idsWithSelectors;
- // Custom Selectors
- for(tHashTimerEntry *element = _hashForTimers; element != nullptr;
- element = (tHashTimerEntry*)element->hh.next)
- {
- element->paused = true;
- idsWithSelectors.insert(element->target);
- }
- // Updates selectors
- tListEntry *entry, *tmp;
- if(minPriority < 0)
- {
- DL_FOREACH_SAFE( _updatesNegList, entry, tmp )
- {
- if(entry->priority >= minPriority)
- {
- entry->paused = true;
- idsWithSelectors.insert(entry->target);
- }
- }
- }
- if(minPriority <= 0)
- {
- DL_FOREACH_SAFE( _updates0List, entry, tmp )
- {
- entry->paused = true;
- idsWithSelectors.insert(entry->target);
- }
- }
- DL_FOREACH_SAFE( _updatesPosList, entry, tmp )
- {
- if(entry->priority >= minPriority)
- {
- entry->paused = true;
- idsWithSelectors.insert(entry->target);
- }
- }
- return idsWithSelectors;
- }
- void Scheduler::resumeTargets(const std::set<void*>& targetsToResume)
- {
- for(const auto &obj : targetsToResume) {
- this->resumeTarget(obj);
- }
- }
- void Scheduler::performFunctionInCocosThread(std::function<void ()> function)
- {
- std::lock_guard<std::mutex> lock(_performMutex);
- _functionsToPerform.push_back(std::move(function));
- }
- void Scheduler::removeAllFunctionsToBePerformedInCocosThread()
- {
- std::unique_lock<std::mutex> lock(_performMutex);
- _functionsToPerform.clear();
- }
- // main loop
- void Scheduler::update(float dt)
- {
- _updateHashLocked = true;
- if (_timeScale != 1.0f)
- {
- dt *= _timeScale;
- }
- //
- // Selector callbacks
- //
- // Iterate over all the Updates' selectors
- tListEntry *entry, *tmp;
- // updates with priority < 0
- DL_FOREACH_SAFE(_updatesNegList, entry, tmp)
- {
- if ((! entry->paused) && (! entry->markedForDeletion))
- {
- entry->callback(dt);
- }
- }
- // updates with priority == 0
- DL_FOREACH_SAFE(_updates0List, entry, tmp)
- {
- if ((! entry->paused) && (! entry->markedForDeletion))
- {
- entry->callback(dt);
- }
- }
- // updates with priority > 0
- DL_FOREACH_SAFE(_updatesPosList, entry, tmp)
- {
- if ((! entry->paused) && (! entry->markedForDeletion))
- {
- entry->callback(dt);
- }
- }
- // Iterate over all the custom selectors
- for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )
- {
- _currentTarget = elt;
- _currentTargetSalvaged = false;
- if (! _currentTarget->paused)
- {
- // The 'timers' array may change while inside this loop
- for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
- {
- elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);
- CCASSERT
- ( !elt->currentTimer->isAborted(),
- "An aborted timer should not be updated" );
- elt->currentTimer->update(dt);
- if (elt->currentTimer->isAborted())
- {
- // The currentTimer told the remove itself. To prevent the timer from
- // accidentally deallocating itself before finishing its step, we retained
- // it. Now that step is done, it's safe to release it.
- elt->currentTimer->release();
- }
- elt->currentTimer = nullptr;
- }
- }
- // elt, at this moment, is still valid
- // so it is safe to ask this here (issue #490)
- elt = (tHashTimerEntry *)elt->hh.next;
- // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
- if (_currentTargetSalvaged && _currentTarget->timers->num == 0)
- {
- removeHashElement(_currentTarget);
- }
- }
-
- // delete all updates that are removed in update
- for (auto &e : _updateDeleteVector)
- delete e;
- _updateDeleteVector.clear();
- _updateHashLocked = false;
- _currentTarget = nullptr;
- #if CC_ENABLE_SCRIPT_BINDING
- //
- // Script callbacks
- //
- // Iterate over all the script callbacks
- if (!_scriptHandlerEntries.empty())
- {
- for (auto i = _scriptHandlerEntries.size() - 1; i >= 0; i--)
- {
- SchedulerScriptHandlerEntry* eachEntry = _scriptHandlerEntries.at(i);
- if (eachEntry->isMarkedForDeletion())
- {
- _scriptHandlerEntries.erase(i);
- }
- else if (!eachEntry->isPaused())
- {
- eachEntry->getTimer()->update(dt);
- }
- }
- }
- #endif
- //
- // Functions allocated from another thread
- //
- // Testing size is faster than locking / unlocking.
- // And almost never there will be functions scheduled to be called.
- if( !_functionsToPerform.empty() ) {
- _performMutex.lock();
- // fixed #4123: Save the callback functions, they must be invoked after '_performMutex.unlock()', otherwise if new functions are added in callback, it will cause thread deadlock.
- auto temp = std::move(_functionsToPerform);
- _performMutex.unlock();
-
- for (const auto &function : temp) {
- function();
- }
- }
- }
- void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, unsigned int repeat, float delay, bool paused)
- {
- CCASSERT(target, "Argument target must be non-nullptr");
-
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
-
- if (! element)
- {
- element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
- element->target = target;
-
- HASH_ADD_PTR(_hashForTimers, target, element);
-
- // Is this the 1st element ? Then set the pause level to all the selectors of this target
- element->paused = paused;
- }
- else
- {
- CCASSERT(element->paused == paused, "element's paused should be paused.");
- }
-
- if (element->timers == nullptr)
- {
- element->timers = ccArrayNew(10);
- }
- else
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
-
- if (timer && !timer->isExhausted() && selector == timer->getSelector())
- {
- CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
- timer->setupTimerWithInterval(interval, repeat, delay);
- return;
- }
- }
- ccArrayEnsureExtraCapacity(element->timers, 1);
- }
-
- TimerTargetSelector *timer = new (std::nothrow) TimerTargetSelector();
- timer->initWithSelector(this, selector, target, interval, repeat, delay);
- ccArrayAppendObject(element->timers, timer);
- timer->release();
- }
- void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, bool paused)
- {
- this->schedule(selector, target, interval, CC_REPEAT_FOREVER, 0.0f, paused);
- }
- bool Scheduler::isScheduled(SEL_SCHEDULE selector, const Ref *target) const
- {
- CCASSERT(selector, "Argument selector must be non-nullptr");
- CCASSERT(target, "Argument target must be non-nullptr");
-
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
-
- if (!element)
- {
- return false;
- }
-
- if (element->timers == nullptr)
- {
- return false;
- }
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
-
- if (timer && !timer->isExhausted() && selector == timer->getSelector())
- {
- return true;
- }
- }
-
- return false;
- }
- void Scheduler::unschedule(SEL_SCHEDULE selector, Ref *target)
- {
- // explicit handle nil arguments when removing an object
- if (target == nullptr || selector == nullptr)
- {
- return;
- }
-
- tHashTimerEntry *element = nullptr;
- HASH_FIND_PTR(_hashForTimers, &target, element);
-
- if (element)
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
-
- if (timer && selector == timer->getSelector())
- {
- if (timer == element->currentTimer && !timer->isAborted())
- {
- timer->retain();
- timer->setAborted();
- }
-
- ccArrayRemoveObjectAtIndex(element->timers, i, true);
-
- // update timerIndex in case we are in tick:, looping over the actions
- if (element->timerIndex >= i)
- {
- element->timerIndex--;
- }
-
- if (element->timers->num == 0)
- {
- if (_currentTarget == element)
- {
- _currentTargetSalvaged = true;
- }
- else
- {
- removeHashElement(element);
- }
- }
-
- return;
- }
- }
- }
- }
- NS_CC_END
|