
cocos2d-iphone - issue #1144
Bad access when unscheduling selector in a function which is called by an CCCallFunc action
What steps will reproduce the problem? 1. add object A to main layer 2. scheduleUpdate for this object 3. start delay + callfunc action on main layer 4. unscheduleUpdate for A in the method called by the callfunc action 5. This will result in bad access in the tick of CCScheduler
What is the expected output? What do you see instead? expected: no crash, I get: crash :)
What cocos2d version / SVN revision are you using ? 1.0.0b
What iPhoneSDK are you using ? 4.2/4.3
Debug or Release ? Both
Does this happens on device ? or on the simulator ? or on both ? both
Please provide any additional information below. A sample derived from CCActionTest is attached
- ActionsTest.m 4.27KB
- ActionsTest.h 606
Comment #1
Posted on Mar 24, 2011 by Quick Panda(No comment was entered for this change.)
Comment #2
Posted on Mar 26, 2011 by Helpful PandaProblem also exists in the 0.99.5 in a different setup see: http://www.cocos2d-iphone.org/forum/topic/14842
Comment #3
Posted on Mar 31, 2011 by Swift OxJust did some analysis on this in the 0.99.5 checkout.
What's happening is that entry->impMethod is a faulty value. What's happening is that during one tick of CCScheduler, unscheduleUpdateForTarget is being called for a target that's going to be used next in the DL_FOREACH_SAFE loop.
DL_FOREACH_SAFE actually isn't totally safe. Because it pre-fetches "next" into its third argument, if that "next" is deleted by DL_DELETE (in unscheduleUpdateForTarget) (which maintains the correct "next" pointer) then DL_FOREACH_SAFE still tries to use the old "next", which has been freed.
So DL_FOREACH_SAFE is safe for deleting the current schedule, but not the next schedule.
The workaround is not to call unscheduleUpdate: with a CCCallFuncN but call something which sets a flag, and have [self unscheduleUpdate] called in update().
The fix is... tricky. Since anything could be deleted during the call-out, you can't copy the current node, preload the next pointer, keep a pointer to prev's next pointer, or any other combination I can think of.
Your choices are to either iterate a copy of the linked list, or (like CCTouchDispatcher) delay removes until after the list iteration is complete. (Adds are safe, they will either be skipped this tick, or performed) However, delaying removes might produce the unwanted side-effect of ticking update: one last time after unscheduleUpdate is called.
Comment #4
Posted on Mar 31, 2011 by Quick PandaI would suggest to handle a list of timer that should be removed after the current tick. Calling unschedule will set a "remove" flag for this timer. In the FOR_EACH_SAFE loop only timers that are not marked as "remove" will be called.
Comment #5
Posted on Apr 1, 2011 by Quick PandaI send a pull request to Riq which fixes this bug.
Comment #6
Posted on May 18, 2011 by Massive Lionfixed https://github.com/cocos2d/cocos2d-iphone/commit/068de0d6961ca9da17b0f2b20d52c8339f935877
Status: Fixed
Labels:
Type-Defect
Priority-Medium
Milestone-Release1.0