• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful


  • Subject: Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
  • From: John Engelhart <email@hidden>
  • Date: Wed, 6 Feb 2008 20:06:24 -0500


On Feb 6, 2008, at 2:12 PM, Alastair Houghton wrote:

On 6 Feb 2008, at 17:52, Hamish Allan wrote:

On Feb 6, 2008 2:59 PM, John Engelhart <email@hidden> wrote:

"I'll take 'Not relevant' for $200 and 'Misunderstands the
fundamentals' for the win, Alex."

Speaking of "not relevant" and "misunderstands the fundamentals":

1) UTF8String returns a non-__strong pointer.

__strong isn't a type qualifier, it's an attribute (in the sense of the __attribute__ keyword). The distinction is perhaps a bit subtle, especially as attributes can be attached to a typedef'd type, but it's the reason that you can put __strong anywhere in a variable declaration and it still has the same effect. It *isn't* like const or volatile, and the ANSI C rules regarding type qualifiers absolutely *do not* apply.

As I mentioned previously, having no formal grammar of ObjC 2.0 makes this point debatable. Your interpretation that __strong is an __attribute__ extension could certainly be true, and is a valid way of looking at things. Without a grammar to guide us, I think both interpretations are equally valid.


However, consider for a moment if it was a type attribute, and followed type attribute rules, and how this would effect the examples cited. Off the top of my head, I think treating it as a type attribute would have prevent every single error I've pointed out. Hypothetically, consider if UTF8String propagated the __strong type, and the assignment of its pointer to the ivar 'const char *'.

The compiler would fail to compile the code, and generate an error.

Again, I'm pretty sure that every example posted would be caught by if __strong was treated as a type attribute.

Since we have two valid ways to interpret the meaning of __strong, I believe that the usefulness of catching these errors at compile time argues strongly in favor of considering it a type attribute.


Furthermore I *think* (and this is from memory, based on some work I did on GCC several years ago, so I might be wrong) that if you write something like


 void * __strong MyFunction(void);

you'll find that the __strong attribute is attached to the *function* rather than to the type. In any case it's going to be ignored because __strong only really affects variables, not types or functions.

I hate this part of the language. Each qualifier has it's quirks, and their application is non obvious, including exactly what they apply to. The distinction between


const char * ptr; and
char * const ptr;

isn't terribly clear by just looking at it.  Add to this

const char * const ptr;

Three, totally different things, and using const twice in this way just seems like it would be a bug at first glance.

2) You fail to copy the data at that pointer.
3) You cry foul when that data disappears.

Well the bug is *either* ignoring the bit in the -UTF8String docs where it says you should copy the string (though that does read like it was only intended to talk about the non-GC case---I just filed <rdar://5727581 > asking for a clarification), or not using __strong on the variable you're storing the result in.

Actually, I've thought of another example which addresses the use of (or lack of) __strong unambiguously and still demonstrates the problem:


#import <Foundation/Foundation.h>

@interface GCTest : NSObject {
  const char *title;
};

- (void)setTitle:(const char *)newTitle;
- (const char *)title;

@end

@implementation GCTest

- (void)setTitle:(const char *)newTitle
{
printf("Setting title. Old title: %p, new title %p = '%s'\n", title, newTitle, newTitle);
title = newTitle;
}


- (const char *)title
{
  return title;
}

@end

int main(int argc, char *argv[]) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  GCTest *gcConstTitle = NULL, *gcUTF8Title = NULL;
  void *ptr;

  gcConstTitle = [[GCTest alloc] init];
  gcUTF8Title = [[GCTest alloc] init];

[gcConstTitle setTitle:"Hello, world!"];
[gcUTF8Title setTitle:[[NSString stringWithUTF8String:"Hello, world \xC2\xA1"] UTF8String]];


  NSLog(@"Test: %@", @"hello");
  [[NSGarbageCollector defaultCollector] collectExhaustively];
  NSLog(@"GC test");

printf("gcConstTitle title: %p = '%s'\n", [gcConstTitle title], [gcConstTitle title]);
printf("gcUTF8Title title: %p = '%s'\n", [gcUTF8Title title], [gcUTF8Title title]);


[gcConstTitle setTitle:NULL]; // Must clear the pointer before popping pool.
[gcUTF8Title setTitle:NULL];


[pool release];
return(0);
}
[johne@LAPTOP_10_5] /tmp% gcc -framework Foundation -fobjc-gc-only -o gc -g gc.m
[johne@LAPTOP_10_5] /tmp% ./gc
Setting title. Old title: 0x0, new title 0x1ea4 = 'Hello, world!'
Setting title. Old title: 0x0, new title 0x1011860 = 'Hello, world¡'
2008-02-06 18:32:35.712 gc[18108:807] Test: hello
2008-02-06 18:32:35.798 gc[18108:807] GC test
gcConstTitle title: 0x1ea4 = 'Hello, world!'
gcUTF8Title title: 0x1011860 = 'Hello, world'
Setting title. Old title: 0x1ea4, new title 0x0 = '(null)'
Setting title. Old title: 0x1011860, new title 0x0 = '(null)'


Oddly, I had to add a second NSLog() in order to get some kind of lossage, but I think it's fair to chalk this up to the semi-random nature of allocations.

The above example is now perfectly legal by everyones definition of how things were under retain/release, and I correctly clear the pointer before it goes out of scope, and demonstrates that the GC system can, and does, reclaim live data out from under you.



A garbage collection systems sine qua non is to free the programmer
from having to deal with the issues memory allocation.

Exactly. So stop getting your knickers in a twist about whether or not
UTF8String actually returns memory from NSAllocateCollectable(), and
simply copy the result as required by the documentation for
UTF8String.

Indeed, I rather wish I hadn't mentioned NSAllocateCollectable(), since I think it's only muddied the waters further.

I don't. Again, consider the hypothetical case where __strong is type qualifier. During development of UTF8String, this would cause a warning or error (implementation dependent, but I'd argue for error). This would highlight the fact that the prototype is discarding a critical qualifier. Assuming that the definition of UTF8String was altered to include __strong, my attempt at assigning a __strong qualified pointer to a non-strong pointer would instantly be flagged and reported by the compiler.


Consider the case of handing a __strong pointer off to a function, such as CFStringCreateWithCStringNoCopy(). If the prototype does not have __strong for the buffer argument, my example of handing it an NSAllocateCollectable pointer would again, instantly trigger a compiler warning or error (I vote error considering the consequences).

It's hard to argue that this is not "The Right Thing" to be doing as it would have mooted every single point I have raised, and caught all of these errors at compile time before they could have become problems. This would have also caught the problem in the example I pasted above, at compile time, and alerted me that I just created a problem.

Finally, consider the effects of the current behavior of silently discarding __strong. As the example in this message shows, it's surprisingly easy to create conditions which violate the conditions required for proper GC behavior.

After four months of practical, hands on usage of Leopards GC system, my experience is that this is happening far, far more frequently than you might think. I have had to deal with endless problems under GC which have all the tell tale signs of race conditions. Because what I'm developing is dual mode, flipping over to retain/release works flawlessly, even after intensive multithreaded concurrent extreme stress testing. The retain/release side of thing never crashes, but the GC side of things mostly doesn't crash.

I think the evidence I've put forth is pretty strong. I think the example I give in this message addresses everyones concerns regarding the use of __strong. It's pretty clear that I don't need to use __strong for the pointer, and that under retain/release methodology, it is incapable of freeing the UTF8String buffer while its still in use. It's certainly open to debate how frequently this happens in practice. I have shown that it is possible. In my opinion, after four months of use, this is happening much more frequently than you might think at first approximation. And just like race conditions, it gets worse the harder you push, which is to be expected. It's pretty much a ticking time bomb, and everything seems to work fine when you're doing development, but then starts failing mysteriously out in the field._______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


  • Follow-Ups:
    • Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
      • From: Timothy Reaves <email@hidden>
    • Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
      • From: "Clark Cox" <email@hidden>
    • Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
      • From: Chris Suter <email@hidden>
    • Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
      • From: "Hamish Allan" <email@hidden>
References: 
 >Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: John Engelhart <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: Alastair Houghton <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: John Engelhart <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: Chris Hanson <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: John Engelhart <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: "Hamish Allan" <email@hidden>)
 >Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful (From: Alastair Houghton <email@hidden>)

  • Prev by Date: Searching for "whole word" in NSString
  • Next by Date: re: Understanding objc_assign_strongCast
  • Previous by thread: Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
  • Next by thread: Re: Use of Mac OS X 10.5 / Leopards Garbage Collection Considered Harmful
  • Index(es):
    • Date
    • Thread