Skip to content

Commit a3cb7de

Browse files
Dimon4egminggo
authored andcommitted
Fix reschedule issue with same key (#17706)
* fix reschedule issue * correct code formatting * Fix isScheduled * improve a bit performance of executing functions in cocos thread * added test to verify reschedule issue * re-init timer in schedule if timer exists and alive
1 parent 55b14cb commit a3cb7de

File tree

4 files changed

+116
-55
lines changed

4 files changed

+116
-55
lines changed

cocos/base/CCScheduler.cpp

Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,17 @@ Timer::Timer()
8080
, _interval(0.0f)
8181
, _aborted(false)
8282
{
83-
8483
}
8584

8685
void Timer::setupTimerWithInterval(float seconds, unsigned int repeat, float delay)
8786
{
88-
_elapsed = -1;
89-
_interval = seconds;
90-
_delay = delay;
91-
_useDelay = (_delay > 0.0f) ? true : false;
92-
_repeat = repeat;
93-
_runForever = (_repeat == CC_REPEAT_FOREVER) ? true : false;
87+
_elapsed = -1;
88+
_interval = seconds;
89+
_delay = delay;
90+
_useDelay = (_delay > 0.0f) ? true : false;
91+
_repeat = repeat;
92+
_runForever = (_repeat == CC_REPEAT_FOREVER) ? true : false;
93+
_timesExecuted = 0;
9494
}
9595

9696
void Timer::update(float dt)
@@ -112,12 +112,12 @@ void Timer::update(float dt)
112112
{
113113
return;
114114
}
115+
_timesExecuted += 1; // important to increment before call trigger
115116
trigger(_delay);
116117
_elapsed = _elapsed - _delay;
117-
_timesExecuted += 1;
118118
_useDelay = false;
119119
// after delay, the rest time should compare with interval
120-
if (!_runForever && _timesExecuted > _repeat)
120+
if (isExhausted())
121121
{ //unschedule timer
122122
cancel();
123123
return;
@@ -128,11 +128,11 @@ void Timer::update(float dt)
128128
float interval = (_interval > 0) ? _interval : _elapsed;
129129
while ((_elapsed >= interval) && !_aborted)
130130
{
131+
_timesExecuted += 1; // important to increment before call trigger
131132
trigger(interval);
132133
_elapsed -= interval;
133-
_timesExecuted += 1;
134134

135-
if (!_runForever && _timesExecuted > _repeat)
135+
if (isExhausted())
136136
{
137137
cancel();
138138
break;
@@ -145,6 +145,11 @@ void Timer::update(float dt)
145145
}
146146
}
147147

148+
bool Timer::isExhausted() const
149+
{
150+
return !_runForever && _timesExecuted > _repeat;
151+
}
152+
148153
// TimerTargetSelector
149154

150155
TimerTargetSelector::TimerTargetSelector()
@@ -312,12 +317,12 @@ void Scheduler::schedule(const ccSchedulerFunc& callback, void *target, float in
312317
{
313318
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
314319

315-
if (timer && key == timer->getKey())
320+
if (timer && !timer->isExhausted() && key == timer->getKey())
316321
{
317-
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
318-
timer->setInterval(interval);
322+
CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
323+
timer->setupTimerWithInterval(interval, repeat, delay);
319324
return;
320-
}
325+
}
321326
}
322327
ccArrayEnsureExtraCapacity(element->timers, 1);
323328
}
@@ -512,22 +517,18 @@ bool Scheduler::isScheduled(const std::string& key, void *target)
512517
{
513518
return false;
514519
}
515-
else
520+
521+
for (int i = 0; i < element->timers->num; ++i)
516522
{
517-
for (int i = 0; i < element->timers->num; ++i)
523+
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
524+
525+
if (timer && !timer->isExhausted() && key == timer->getKey())
518526
{
519-
TimerTargetCallback *timer = dynamic_cast<TimerTargetCallback*>(element->timers->arr[i]);
520-
521-
if (timer && key == timer->getKey())
522-
{
523-
return true;
524-
}
527+
return true;
525528
}
526-
527-
return false;
528529
}
529530

530-
return false; // should never get here
531+
return false;
531532
}
532533

533534
void Scheduler::removeUpdateFromHash(struct _listEntry *entry)
@@ -734,11 +735,11 @@ bool Scheduler::isTargetPaused(void *target)
734735
}
735736

736737
// We should check update selectors if target does not have custom selectors
737-
tHashUpdateEntry *elementUpdate = nullptr;
738-
HASH_FIND_PTR(_hashForUpdates, &target, elementUpdate);
739-
if ( elementUpdate )
738+
tHashUpdateEntry *elementUpdate = nullptr;
739+
HASH_FIND_PTR(_hashForUpdates, &target, elementUpdate);
740+
if ( elementUpdate )
740741
{
741-
return elementUpdate->entry->paused;
742+
return elementUpdate->entry->paused;
742743
}
743744

744745
return false; // should never get here
@@ -943,13 +944,12 @@ void Scheduler::update(float dt)
943944
if( !_functionsToPerform.empty() ) {
944945
_performMutex.lock();
945946
// 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.
946-
auto temp = _functionsToPerform;
947-
_functionsToPerform.clear();
947+
auto temp = std::move(_functionsToPerform);
948948
_performMutex.unlock();
949-
for( const auto &function : temp ) {
949+
950+
for (const auto &function : temp) {
950951
function();
951952
}
952-
953953
}
954954
}
955955

@@ -985,10 +985,10 @@ void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, uns
985985
{
986986
TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
987987

988-
if (timer && selector == timer->getSelector())
988+
if (timer && !timer->isExhausted() && selector == timer->getSelector())
989989
{
990-
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
991-
timer->setInterval(interval);
990+
CCLOG("CCScheduler#schedule. Reiniting timer with interval %.4f, repeat %u, delay %.4f", interval, repeat, delay);
991+
timer->setupTimerWithInterval(interval, repeat, delay);
992992
return;
993993
}
994994
}
@@ -1023,22 +1023,18 @@ bool Scheduler::isScheduled(SEL_SCHEDULE selector, Ref *target)
10231023
{
10241024
return false;
10251025
}
1026-
else
1026+
1027+
for (int i = 0; i < element->timers->num; ++i)
10271028
{
1028-
for (int i = 0; i < element->timers->num; ++i)
1029+
TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
1030+
1031+
if (timer && !timer->isExhausted() && selector == timer->getSelector())
10291032
{
1030-
TimerTargetSelector *timer = dynamic_cast<TimerTargetSelector*>(element->timers->arr[i]);
1031-
1032-
if (timer && selector == timer->getSelector())
1033-
{
1034-
return true;
1035-
}
1033+
return true;
10361034
}
1037-
1038-
return false;
10391035
}
10401036

1041-
return false; // should never get here
1037+
return false;
10421038
}
10431039

10441040
void Scheduler::unschedule(SEL_SCHEDULE selector, Ref *target)
@@ -1060,7 +1056,7 @@ void Scheduler::unschedule(SEL_SCHEDULE selector, Ref *target)
10601056

10611057
if (timer && selector == timer->getSelector())
10621058
{
1063-
if (timer == element->currentTimer && (! timer->isAborted()))
1059+
if (timer == element->currentTimer && !timer->isAborted())
10641060
{
10651061
timer->retain();
10661062
timer->setAborted();

cocos/base/CCScheduler.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,10 @@ class CC_DLL Timer : public Ref
5050
protected:
5151
Timer();
5252
public:
53-
/** get interval in seconds */
54-
float getInterval() const { return _interval; }
55-
/** set interval in seconds */
56-
void setInterval(float interval) { _interval = interval; }
57-
5853
void setupTimerWithInterval(float seconds, unsigned int repeat, float delay);
5954
void setAborted() { _aborted = true; }
6055
bool isAborted() const { return _aborted; }
56+
bool isExhausted() const;
6157

6258
virtual void trigger(float dt) = 0;
6359
virtual void cancel() = 0;
@@ -66,7 +62,6 @@ class CC_DLL Timer : public Ref
6662
void update(float dt);
6763

6864
protected:
69-
7065
Scheduler* _scheduler; // weak ref
7166
float _elapsed;
7267
bool _runForever;

tests/cpp-tests/Classes/SchedulerTest/SchedulerTest.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#include "SchedulerTest.h"
22
#include "../testResource.h"
3+
#include "ui/UIText.h"
34

45
USING_NS_CC;
56
USING_NS_CC_EXT;
7+
using namespace cocos2d::ui;
68

79
enum {
810
kTagAnimationDance = 1,
@@ -26,6 +28,7 @@ SchedulerTests::SchedulerTests()
2628
ADD_TEST_CASE(RescheduleSelector);
2729
ADD_TEST_CASE(SchedulerDelayAndRepeat);
2830
ADD_TEST_CASE(SchedulerIssue2268);
31+
ADD_TEST_CASE(SchedulerIssueWithReschedule);
2932
ADD_TEST_CASE(ScheduleCallbackTest);
3033
ADD_TEST_CASE(ScheduleUpdatePriority);
3134
ADD_TEST_CASE(SchedulerIssue10232);
@@ -983,6 +986,8 @@ std::string TwoSchedulers::subtitle() const
983986
return "Three schedulers. 2 custom + 1 default. Two different time scales";
984987
}
985988

989+
// SchedulerIssue2268
990+
986991
class TestNode2 : public Node
987992
{
988993
public:
@@ -1040,6 +1045,61 @@ std::string SchedulerIssue2268::subtitle() const
10401045
return "Should not crash";
10411046
}
10421047

1048+
// SchedulerIssueWithReschedule
1049+
// https://github.com/cocos2d/cocos2d-x/pull/17706
1050+
1051+
void SchedulerIssueWithReschedule::onEnter()
1052+
{
1053+
SchedulerTestLayer::onEnter();
1054+
1055+
Size widgetSize = getContentSize();
1056+
1057+
auto status_text = Text::create("Checking..", "fonts/Marker Felt.ttf", 18);
1058+
status_text->setColor(Color3B(255, 255, 255));
1059+
status_text->setPosition(Vec2(widgetSize.width / 2.0f, widgetSize.height / 2.0f));
1060+
addChild(status_text);
1061+
1062+
// schedule(callback, target, interval, repeat, delay, paused, key);
1063+
auto verified = std::make_shared<bool>();
1064+
*verified = false;
1065+
1066+
_scheduler->schedule([this, verified](float dt){
1067+
log("SchedulerIssueWithReschedule - first timer");
1068+
1069+
_scheduler->schedule([this, verified](float dt){
1070+
log("SchedulerIssueWithReschedule - second timer. OK");
1071+
*verified = true;
1072+
}, this, 0.1f, 0, 0, false, "test_timer");
1073+
1074+
}, this, 0.1f, 0, 0, false, "test_timer");
1075+
1076+
_scheduler->schedule([verified, status_text](float dt){
1077+
if (*verified)
1078+
{
1079+
log("SchedulerIssueWithReschedule - test OK");
1080+
status_text->setString("OK");
1081+
status_text->setColor(Color3B(0, 255, 0));
1082+
}
1083+
else
1084+
{
1085+
log("SchedulerIssueWithReschedule - test failed!");
1086+
status_text->setString("Failed");
1087+
status_text->setColor(Color3B(255, 0, 0));
1088+
}
1089+
1090+
}, this, 0.5f, 0, 0, false, "test_verify_timer");
1091+
}
1092+
1093+
std::string SchedulerIssueWithReschedule::title() const
1094+
{
1095+
return "Issue with reschedule";
1096+
}
1097+
1098+
std::string SchedulerIssueWithReschedule::subtitle() const
1099+
{
1100+
return "reschedule issue with same key";
1101+
}
1102+
10431103
// ScheduleCallbackTest
10441104

10451105
ScheduleCallbackTest::~ScheduleCallbackTest()

tests/cpp-tests/Classes/SchedulerTest/SchedulerTest.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ class SchedulerIssue2268 : public SchedulerTestLayer
284284
cocos2d::Node* testNode;
285285
};
286286

287+
class SchedulerIssueWithReschedule : public SchedulerTestLayer
288+
{
289+
public:
290+
CREATE_FUNC(SchedulerIssueWithReschedule);
291+
292+
virtual std::string title() const override;
293+
virtual std::string subtitle() const override;
294+
void onEnter() override;
295+
};
296+
287297
class ScheduleCallbackTest : public SchedulerTestLayer
288298
{
289299
public:

0 commit comments

Comments
 (0)