123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095 |
- /****************************************************************************
- Copyright (c) 2008-2010 Ricardo Quesada
- Copyright (c) 2010-2012 cocos2d-x.org
- Copyright (c) 2011 Zynga Inc.
- Copyright (c) 2013-2017 Chukong Technologies Inc.
- 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;
- }
- 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;
- }
- trigger(_delay);
- _elapsed = _elapsed - _delay;
- _timesExecuted += 1;
- _useDelay = false;
- // after delay, the rest time should compare with interval
- if (!_runForever && _timesExecuted > _repeat)
- { //unschedule timer
- cancel();
- return;
- }
- }
-
- // if _interval == 0, should trigger once every frame
- float interval = (_interval > 0) ? _interval : _elapsed;
- while ((_elapsed >= interval) && !_aborted)
- {
- trigger(interval);
- _elapsed -= interval;
- _timesExecuted += 1;
- if (!_runForever && _timesExecuted > _repeat)
- {
- cancel();
- break;
- }
- if (_elapsed <= 0.f)
- {
- break;
- }
- }
- }
- // 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 && key == timer->getKey())
- {
- CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
- timer->setInterval(interval);
- 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;
- 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;
- 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, void *target)
- {
- 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;
- }
- else
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
-
- if (timer && key == timer->getKey())
- {
- return true;
- }
- }
-
- return false;
- }
-
- return false; // should never get here
- }
- 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(const std::function<void ()> &function)
- {
- _performMutex.lock();
- _functionsToPerform.push_back(function);
- _performMutex.unlock();
- }
- 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 = _functionsToPerform;
- _functionsToPerform.clear();
- _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 && selector == timer->getSelector())
- {
- CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
- timer->setInterval(interval);
- 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, Ref *target)
- {
- 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;
- }
- else
- {
- for (int i = 0; i < element->timers->num; ++i)
- {
- TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
-
- if (timer && selector == timer->getSelector())
- {
- return true;
- }
- }
-
- return false;
- }
-
- return false; // should never get here
- }
- 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
|