gnustep-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: NSAutoreleasePool issue related to Clang and libobjc2


From: Christopher Armstrong
Subject: Re: NSAutoreleasePool issue related to Clang and libobjc2
Date: Sat, 10 Nov 2012 13:48:22 +1100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121028 Thunderbird/16.0.2

Hi Quentin

I did some experiments by modifying the -[NSAutoreleasePool emptyPool] code for the ARC runtime to stuff a poison value in any _child instances it deallocates. I also added a check at the beginning of the method to print out when this occurs:

#define POISON (NSAutoreleasePool*)0xdeadbead
- (void) emptyPool
{
  struct autorelease_thread_vars *tv = ARP_THREAD_VARS;
  if (_child == POISON)
    {
       NSLog(@"This autorelease pool %p has already been emptied!", self);
    }

  while (_child)
    {
      NSAutoreleasePool *pool = _child;
      if (pool == POISON)
        {
          NSLog(@"Autorelease pool %p has already been emptied!", pool);
        }
      _child = pool->_child;
      pool->_child = POISON;
      push_pool_to_cache(tv, pool);
    }
  tv->current_pool = self;
  objc_autoreleasePoolPop(_released);
}

Sure enough, this code tripped up in the CoreObject TestCommon unit test - in this case, it was caused by TestCommon maintaining a reference to a NSAutoreleasePool as an instance variable. This violates the assumptions of the NSAutoreleasePool code above which believes that all NSAutoreleasePool variables are created on the stack. From what I understand, the code above is designed to clean up orphaned "child" NSAutoreleasePools that are leftover from a exception-caused stack unwind.

I'm not sure what the correct solution is - it seems that we should detect bad code, but leaving the POISON value in place I think is only going to catch the instance where object is returned to the autorelease cache, and the NSAutoreleasePool is sent -release again, but before it is re-used i.e.

Pool A allocated
Start exception handler
> Pool B allocated
> Pool B assigned to an instance variable of long-lived object N
>> Pool C allocated
>> Throw exception
Exception caught and handled by stack frame of Pool A
Pool A -release called
> Pool A -emptyPool
> Pool B emptied and deallocated by Pool A and returned to cache
> Pool C emptied and deallocated by Pool A and returned to cache
N deallocated
> -release sent to Pool B (Pool B not re-used yet)

I believe if Pool B had already been re-used *before* there is an attempt by other code to release it, the POISON value would not allow this to be detected. IMHO, the only safe thing we could do in this -emptyPool method is to release the NSAutoreleasePool instances and not return them to the cache i.e. we should only use the cache for objects that return themselves to the cache in -dealloc.

Cheers
Chris
On 03/11/12 08:11, Quentin Mathé wrote:
Le 2 nov. 2012 à 12:11, Tom Davie a écrit :

Heya,

We unfortunately are running into this same issue now.  Did you manage to find 
a solution to the crash?
Not yet. I was hoping that Richard or David had some ideas about this issue.

Did you try to disable the pool caching code entirely? I should probably try 
that next week.

Cheers,
Quentin.

On 31 Oct 2012, at 18:37, Quentin Mathé <address@hidden> wrote:

Hi,

The NSAutoreleasePool implementation bound to the ARC_RUNTIME has some issues I 
think.

For the same current pool instance, it calls push_pool_to_cache() twice, once 
in -emptyPool and another -dealloc. Which means a pool still in use can be 
reused, then it's possible to get stuck in an infinite loop. For example, in

- (void) emptyPool
{
struct autorelease_thread_vars *tv = ARP_THREAD_VARS;

// Infinite loop when pool == pool->_child
while (_child)
  {
    NSAutoreleasePool *pool = _child;
    _child = pool->_child;
    push_pool_to_cache(tv, pool);
  }

Here is a partial backtrace for this problem :

#0  0xb7a29933 in -[NSAutoreleasePool emptyPool] (self=0x8cf7f34, 
_cmd=0xb7f3d000)
  at NSAutoreleasePool.m:413
#1  0xb7a29d07 in -[NSAutoreleasePool dealloc] (self=0x8cf7f34, _cmd=0xb7f3cfe0)
  at NSAutoreleasePool.m:739
#2  0xb7a29c6e in -[NSAutoreleasePool release] (self=0x8cf7f34, _cmd=0xb7f6dc28)
  at NSAutoreleasePool.m:732
#3  0xb7c29493 in -[NSRunLoop limitDateForMode:] (self=0x85a1b9c, 
_cmd=0xb7f6db10,
  mode=0x80f02e4) at NSRunLoop.m:1080
#4  0xb7c2bac7 in -[NSRunLoop runMode:beforeDate:] (self=0x85a1b9c, 
_cmd=0xb7f8ab78,
  mode=0x80f02e4, date=0x8ea2dbc) at NSRunLoop.m:1255
#5  0xb7d4b3db in -[GSMessageHandle sendMessage:beforeDate:] (self=0x8cf7dcc,
  _cmd=0xb7f8ab10, components=0x86dd12c, when=0x8ea2dbc) at NSMessagePort.m:1028
#6  0xb7d5292c in -[NSMessagePort 
sendBeforeDate:msgid:components:from:reserved:] (
  self=0x8cf3f34, _cmd=0xb7f46428, when=0x8ea2dbc, msgId=0, 
components=0x86dd12c,
  receivingPort=0x8cf5174, length=16) at NSMessagePort.m:1960
#7  0xb7a9201d in -[NSConnection(Private) _sendOutRmc:type:] (self=0x8cf577c,
  _cmd=0xb7f46198, c=0x86dd0bc, msgid=0) at NSConnection.m:3426
#8  0xb7a8312b in -[NSConnection(GNUstepExtensions) 
forwardInvocation:forProxy:] (
  self=0x8cf577c, _cmd=0xb7f4c0f8, inv=0x86dd064, object=0x8cf920c)
  at NSConnection.m:2041
#9  0xb7ae4ed1 in -[NSDistantObject forwardInvocation:] (self=0x8cf920c,
  _cmd=0xb7f8bba0, anInvocation=0x86dd064) at NSDistantObject.m:606
#10 0xb7d5a030 in GSFFIInvocationCallback (cif=0x86dcf50, retp=0xbfffe380,
  args=0xbfffe300, user=0x8eb94b4) at GSFFIInvocation.m:623
#11 0xb72e3176 in ?? () from /usr/lib/i386-linux-gnu/libffi.so.6
#12 0xb72e3416 in ?? () from /usr/lib/i386-linux-gnu/libffi.so.6
#13 0xb7aea7ea in -[NSDistributedNotificationCenter 
addObserver:selector:name:object:suspensionBehavior:] (self=0x8cf11d4, 
_cmd=0xb7f4d1e8, anObserver=0x8f99244,
  aSelector=0xb5a108e8, notificationName=0xb5a107b8, anObject=0x0,
  suspensionBehavior=2) at NSDistributedNotificationCenter.m:344
#14 0xb7aea162 in -[NSDistributedNotificationCenter 
addObserver:selector:name:object:] (
  self=0x8cf11d4, _cmd=0xb5a10a68, anObserver=0x8f99244, aSelector=0xb5a108e8,
  notificationName=0xb5a107b8, anObject=0x0) at 
NSDistributedNotificationCenter.m:267

If I apply the patch in attachment, it isn't enough to solve the problem, 
because I now get a crash I cannot explain when running the NSRunLoop. Here is 
the backtrace :

Program received signal SIGSEGV, Segmentation fault.
0xb7852346 in objc_test_class_flag (aClass=0x74696c71, 
flag=objc_class_flag_hidden_class)
  at ./class.h:248
248             return (aClass->info & (unsigned long)flag) == (unsigned 
long)flag;
(gdb) bt
#0  0xb7852346 in objc_test_class_flag (aClass=0x74696c71,
  flag=objc_class_flag_hidden_class) at ./class.h:248
#1  0xb7852d4b in object_getClass (obj=0x8e9454c) at runtime.c:757
#2  0xb7d5a799 in GSObjCIsInstance (obj=0x8e9454c) at GSObjCRuntime.m:84
#3  0xb7ab3139 in otherTime (other=0x8e9454c) at NSDate.m:109
#4  0xb7ab65c4 in -[NSGDate earlierDate:] (self=0x8624f04, _cmd=0xb7f6da60,
  otherDate=0x8e9454c) at NSDate.m:1461
#5  0xb7c2bf6a in -[NSRunLoop runMode:beforeDate:] (self=0x85a1b9c, 
_cmd=0xb7f6da40,
  mode=0xb7f6d7f0, date=0x8e9454c) at NSRunLoop.m:1269
#6  0xb7c2c40b in -[NSRunLoop runUntilDate:] (self=0x85a1b9c, _cmd=0xb7fd7d58,
  date=0x8e9454c) at NSRunLoop.m:1305
#7  0xb7fc5189 in -[UKRunner runTest:onObject:class:] (self=0x8117c4c, 
_cmd=0xb7fd7d78,
  testSelector=0x8452e70, testObject=0x8e8177c, testClass=0xb5a38fe0) at 
UKRunner.m:344
#8  0xb7fc57fe in -[UKRunner runTests:onObject:] (self=0x8117c4c, 
_cmd=0xb7fd7e30,
  testMethods=0x8e81724, testObject=0x8e81744) at UKRunner.m:470
#9  0xb7fc60e3 in -[UKRunner runTestsInClass:] (self=0x8117c4c, _cmd=0xb7fd7eb0,
  testClass=0xb5a38fe0) at UKRunner.m:522
#10 0xb7fc689e in -[UKRunner runTests:inBundle:principalClass:] (self=0x8117c4c,
  _cmd=0xb7fd7cf0, testClasses=0x846e79c, bundle=0x8130984, principalClass=0x0)
  at UKRunner.m:560
#11 0xb7fc2ecb in -[UKRunner runTests:inBundleAtPath:currentDirectory:] 
(self=0x8117c4c,
  _cmd=0xb7fd7e38, testClasses=0x846e79c, bundlePath=0x812c22c, cwd=0x8117c0c)
  at UKRunner.m:145
#12 0xb7fc3d33 in +[UKRunner runTests] (self=0xb7fd7c40, _cmd=0x804b7c0)
  at UKRunner.m:190
#13 0x08048977 in main (argc=1, argv=0xbffff814) at main.m:33

Any suggestions would be welcome, I'm a bit stuck :)

I'm using both GNUstep Base and libobjc2 SVN trunk from today, Clang 3.1 (with 
the non-fragile ABI) and Ubuntu Linux x86-32.

Cheers,
Quentin.

<NSAutoreleasePool.m.patch>_______________________________________________
Gnustep-dev mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/gnustep-dev

_______________________________________________
Gnustep-dev mailing list
address@hidden
https://lists.gnu.org/mailman/listinfo/gnustep-dev




reply via email to

[Prev in Thread] Current Thread [Next in Thread]