Accessing buffers in NSData/NSMutableData under garbage collection
-
I have a question about recommended coding style under GC, which I'll
preface with some background.
I've been trying to learn about garbage collection under Leopard by
turning it on in some older apps and watching how they fail. As
stated in the GC docs, you probably don't want to switch to GC in a
mature application that was developed under the older memory
management scheme. However studying the effects of GC in old apps
was certainly helpful in planning better design patterns for future
projects.
One of the patterns that breaks badly is the following type of thing:
float *myPointer = [[NSMutableData dataWithLength:size*sizeof(float)]
mutableBytes];
This was convenient as a lazy (if perhaps misguided) way to replace
malloc with something that I knew I could use until the end of the
current event loop, and which would then be freed automatically. Note
that aside from the lazy, autoreleased malloc replacement, there are
many other cases where I use NSMutableDatas (e.g. where the data will
be used as an instance variable for one or more objects)
It's quite possible that this was a bad thing to do under the old
style of memory management, but it's fatal under GC. As far as the
collector is concerned, the inner NSMutableData object is no longer
reachable at later points during execution (whatever that means after
compiler optimization). If you use myPointer later, it's quite likely
that it will point to freed memory. Using
__strong float *myPointer = [[NSMutableData
dataWithLength:size*sizeof(float)] mutableBytes];
does not seem to be a solution, since (at least as I understand it)
the collector cares about references specifically to the NSMutableData
object and a strong pointer to an internal storage buffer (not an
object) won't prevent the object and its associated bytes from being
reclaimed (since the buffer can not "reach" the object).
Ok, fine. I can live with that. Now, however, I need to decide how
to replace the following kind of thing:
////////////////////////////////////////////////////////////////////// Start
code snippet //////////////////////////////////////////
NSMutableData *myData = [NSMutableData
dataWithLength:size*sizeof(float)];
float *myPointer = [myData mutableBytes]; // Don't think __strong
would make a difference here
int ix;
for (ix=0; ix<size; ix++) myPointer[ix] = mysillyfunction(ix); // This
causes an intermittent crash
// Insert Thousands of lines of code here
BOOL someFlag = YES;
if (someFlag) {
// Collector will probably have nuked myData a long time ago...
usually crashes by here
float answer = myPointer[3];
}
////////////////////////////////////////////////////////////////////// End
code snippet //////////////////////////////////////////
So this leads to the following questions:
- am I correct in understanding that it is a bad idea to *ever* copy
the pointer returned by -mutableBytes (or any other pointer-returning
method) to a variable for future use, since this assignment can't be
followed by the collector, which might at any time reclaim the parent
object? (use of __strong not withstanding?)
- so is the preferred if not mandatory alternative to *always* use the
-mutableBytes method in any line of code that needs to access the data
of an NSMutableData object? I can do this, but it seems to make the
code less readable (not the end of the world - clearly it's better
than crashing).
- should I expect a significant performance cost to the overhead of
the method call inside of a tight loop? e.g. for
(ix=0:ix<size;ix++) *((float*)[myData mutableBytes] + ix) =
somefunc(ix*3.0);
vs.
(ix=0:ix<size;ix++) myPointer[ix] = somefunc(ix*3.0);
- would I have been better to just use __strong *float myPointer =
NSAllocateCollectible(size*sizeof(float),0)? My instinct (under pre-
GC) has been to use NSData or NSMutableData for memory buffers that
might end up being assigned as instance variables to other objects.
Under the GC world, what are the advantages of NSData/NSMutableData
vs. a buffer returned by NSAllocateCollectible()? I have encountered
situations where NSMutableData is unable to allocate very large memory
buffers (e.g. 700MB) when GC is turned on (the same line of code is
works with GC off). NSAllocateCollectible() does not seem hindered by
these limitations for large blocks of memory under GC.
Thanks in advance for any suggestions,
Rick -
On Feb 18, 2008, at 5:21 PM, Rick Hoge wrote:> One of the patterns that breaks badly is the following type of thing:
>
> float *myPointer = [[NSMutableData
> dataWithLength:size*sizeof(float)] mutableBytes];
It makes sense that this wouldn't work, even if you declare myPointer
to be __strong. NSMutableData was written before GC was introduced,
so the only sensible way to implement it would have been for the
NSMutableData object to free the contained data.
However, it seems like there should be a way to have pointers to
primitive types that are managed by GC. The Garbage Collection
Programming Guide has this example of the use of __strong, in the
Garbage Collection API section:
@interface MyClass {
__strong int *ptr1;
int * __strong ptr2;
int __strong * ptr3;
}
The point of the example was just to show the various positions that
__strong can appear in a declaration, but the fact that the example
uses int* as the pointer type made me think there is a way to allocate
pointers to primitive types that get manage by the GC. However the GC
Programming Guide fails to mention how to do this. It would be nice
if I could do something like this:
__strong int* ptr = GCableMalloc(numBytes);
and ptr would be managed by the GC. Is there an actual function like
GCableMalloc? -
On Feb 18, 2008, at 9:00 PM, Adam P Jenkins wrote:> The point of the example was just to show the various positions that
> __strong can appear in a declaration, but the fact that the example
> uses int* as the pointer type made me think there is a way to
> allocate pointers to primitive types that get manage by the GC.
> However the GC Programming Guide fails to mention how to do this.
> It would be nice if I could do something like this:
>
> __strong int* ptr = GCableMalloc(numBytes);
>
> and ptr would be managed by the GC. Is there an actual function
> like GCableMalloc?
<http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection
/Articles/gcAPI.html>
mmalc -
On Feb 19, 2008, at 12:07 AM, mmalc crawford wrote:>> >
> On Feb 18, 2008, at 9:00 PM, Adam P Jenkins wrote:
>
>> The point of the example was just to show the various positions
>> that __strong can appear in a declaration, but the fact that the
>> example uses int* as the pointer type made me think there is a way
>> to allocate pointers to primitive types that get manage by the GC.
>> However the GC Programming Guide fails to mention how to do this.
>> It would be nice if I could do something like this:
>>
>> __strong int* ptr = GCableMalloc(numBytes);
>>
>> and ptr would be managed by the GC. Is there an actual function
>> like GCableMalloc?
>
> <http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection
/Articles/gcAPI.html
Cool! I just tried this:
__strong int* ptr = NSAllocateCollectable(1000, 0);
and verified that ptr does get GCed. I.e. I can write:
NSGarbageCollector *gc = [NSGarbageCollector defaultCollector];
while (1) {
__strong int* ptr = NSAllocateCollectable(1000, 0);
[gc collectIfNeeded];
}
and if I build the program that contains this with -fobj-gc-only and
run it, the memory usage stays constant. So if you don't need your
code to work in non-GC mode, then you can just replace
float *myPointer = [[NSMutableData dataWithLength:size*sizeof(float)]
mutableBytes];
with
float *myPointer = NSAllocateCollectable(size*sizeof(float), 0);
and get the same effect. -
On Feb 19, 2008, at 12:19 AM, Adam P Jenkins wrote:> So if you don't need your code to work in non-GC mode, then you can
> just replace
>
> float *myPointer = [[NSMutableData
> dataWithLength:size*sizeof(float)] mutableBytes];
>
> with
>
> float *myPointer = NSAllocateCollectable(size*sizeof(float), 0);
>
> and get the same effect.
>
Oops, I left out __strong, I meant the second statement to be:
__strong float *myPointer = NSAllocateCollectable(size*sizeof(float),
0); -
On 19/02/2008, at 4:23 PM, Adam P Jenkins wrote:> Oops, I left out __strong, I meant the second statement to be:
>
> __strong float *myPointer =
> NSAllocateCollectable(size*sizeof(float), 0);
It doesn't make any difference.
Everything on the stack or in a register is implicitly strong.
The other thing to be careful of when allocating arrays like this is
that pointers to anything other than the beginning of the array aren't
considered. So, for example, you'd be on unsafe ground with something
like the following:
float *myPointer = NSAllocateCollectable(size * sizeof(float), 0);
for (n = 0; n < size; ++n, ++myPointer) {
// Do something with myPointer
}
I don't use garbage collection myself so someone with more experience
might know a way of getting interior pointers to work (I believe
that's the name for them). Even if there was a way, I'm not sure it's
a good ideaâ¦
You could still be in trouble if you decided to do something like:
float *myArray = NSAllocateCollectable(size * sizeof(float), 0);
// myArray is not referenced after the following line
for (n = 0, myPointer = myArray; n < size; ++n, ++myPointer) {
// Do something with myPointer
}
In this case, there's nothing stopping the compiler turning it into
the same as my first example. For this reason, I don't think I'd be
allocating temporary garbage collected arrays like thisâyou're relying
on the compiler to leave the right pointer on the stack or in a
register but with optimisations that may not be the case. You'll run
into similar problems if you try and use NSMutableData.
What is needed, I believe, is a way of getting auto-released memory.
This would solve this problem and also things like -[NSString
UTF8String] which suffer from the same problem, and are worse because
you often want to pass the result of it to library routines (which you
have no control over).
- Chris
p.s. here's the test program I just used:
#include <setjmp.h>
#include <Foundation/Foundation.h>
static __strong float *p;
static __strong const char *x;
int main ()
{
jmp_buf env;
if (!setjmp (env)) {
p = (float *)NSAllocateCollectable (100, 0) + 1;
*p = 567.89f;
x = [@"1234" UTF8String] + 1;
printf ("%f %s\n", *p, x);
longjmp (env, 1);
}
[[NSGarbageCollector defaultCollector] collectExhaustively];
printf ("%f %s\n", *p, x);
return 0;
}
dog:tmp csuter$ MallocScribble=YES ./test
test(25679) malloc: enabling scribbling to detect mods to free blocks
567.890015 234
nan UUUUUUUUUUU? -
On 19 Feb 2008, at 06:24, Chris Suter wrote:> I don't use garbage collection myself so someone with more
> experience might know a way of getting interior pointers to work (I
> believe that's the name for them).
AFAIK the Cocoa GC doesn't support interior pointers; in order for a
garbage collector to do so, it has to be able to find the start
address of the block to which a given pointer belongs, which has
unpleasant performance implications.> You could still be in trouble if you decided to do something like:
>
> float *myArray = NSAllocateCollectable(size * sizeof(float), 0);
>
> // myArray is not referenced after the following line
> for (n = 0, myPointer = myArray; n < size; ++n, ++myPointer) {
> // Do something with myPointer
> }
>
> In this case, there's nothing stopping the compiler turning it into
> the same as my first example.
Indeed, the following code is broken, but only when compiled using
optimization:
/* ---- tst.m -----------------------------------------------------
*/
#import <Foundation/Foundation.h>
int
main (int argc, char **argv)
{
float *myArray = NSAllocateCollectable(1000 * sizeof(float), 0);
unsigned n;
float *ptr;
for (n = 0, ptr = myArray; n < 100; ++n, ++ptr) {
*ptr = 1.0;
}
[[NSGarbageCollector defaultCollector] collectExhaustively];
for (n = 0; n < 100; ++n, --ptr) {
printf ("%f\n", *ptr);
}
return 0;
}
/* ----------------------------------------------------------------
*/
To see it break,
gcc -O3 -fobjc-gc tst.m -framework Foundation -o tst
then
MallocScribble=YES ./tst
The fix is, of course, to use "volatile" to prevent the optimization:
float * volatile myArray = NSAllocateCollectable(1000 *
sizeof(float), 0);
(Perhaps ironically, __strong is *not* sufficient to prevent this from
happening...)
On 19 Feb 2008, at 06:24, Chris Suter wrote:> What is needed, I believe, is a way of getting auto-released memory.
> This would solve this problem and also things like -[NSString
> UTF8String] which suffer from the same problem, and are worse
> because you often want to pass the result of it to library routines
> (which you have no control over).
Yikes! That's unpleasant. The following, for example, will fail on
PowerPC at -O3:
/* ---- tst2.m ----------------------------------------------------
*/
#import <Foundation/Foundation.h>
extern void foobar (float *ptr);
int
main (int argc, char **argv)
{
float *myArray = NSAllocateCollectable(1000 * sizeof(float), 0);
foobar (myArray);
return 0;
}
/* ----------------------------------------------------------------
*/
/* ---- tst3.m ----------------------------------------------------
*/
#import <Foundation/Foundation.h>
void
foobar (float *ptr)
{
unsigned n;
for (n = 0; n < 100; ++n, ++ptr)
*ptr = 1.0;
[[NSGarbageCollector defaultCollector] collectExhaustively];
for (n = 0; n < 100; ++n, --ptr)
printf ("%f\n", *ptr);
}
/* ----------------------------------------------------------------
*/
Again, to see it break:
gcc -arch ppc -O3 -fobjc-gc tst2.m tst3.m -framework Foundation -o
tst2
then
MallocScribble=YES ./tst2
and again, you need a "volatile" to fix it.
IMHO, as a result, using NSAllocateCollectable() is risky. Worse,
however, so is -UTF8String (*):
/* ---- tst4.m ----------------------------------------------------
*/
#import <Foundation/Foundation.h>
extern void foobar (const char *ptr);
int
main (int argc, char **argv)
{
NSString *str = [NSString stringWithFormat:@"%d", 1000];
foobar ([str UTF8String]);
return 0;
}
/* ----------------------------------------------------------------
*/
/* ---- tst5.m ----------------------------------------------------
*/
#import <Foundation/Foundation.h>
void
foobar (const char *ptr)
{
unsigned n;
for (n = 0; n < 3; ++n, ++ptr);
[[NSGarbageCollector defaultCollector] collectExhaustively];
for (n = 0; n < 3; ++n, --ptr)
printf ("%02x '%c'\n", *ptr, *ptr);
}
/* ----------------------------------------------------------------
*/
which again breaks on PowerPC:
gcc -arch ppc -O3 -fobjc-gc tst4.m tst5.m -framework Foundation -o
tst3
MallocScribble=YES ./tst3
and the only way to fix it is to do:
const char * volatile utf8 = [str UTF8String];
foobar (utf8);
(or, I suppose, to store it into a __strong global or instance
variable first)
:-( :-(
I just filed <rdar://5751702> about this issue. I could cope with
having to worry about optimization when calling
NSAllocateCollectable(), but surely it's too much to expect us to do
so for -UTF8String et al?
I'm not sure what the best fix is for this. Like Chris said, one
option would be to allow us to allocate "autoreleased" memory (in
which case -UTF8String should probably be doing that too). Or maybe
the GC should support interior pointers to memory allocated with
NSAllocateCollectable()?
Kind regards,
Alastair.
(*) And before John Engelhart jumps on this statement and claims he
was "right all along", he was talking about an entirely separate
problem. This is specifically a problem caused by the lack of
interior pointer support, and it *only happens* if the library routine
being called modifies (the only copy of) the pointer it's passed
without storing it anywhere first. I doubt that's very common in
practice, but it is a risk as the (admittedly contrived) examples
above demonstrate.
--
http://alastairs-place.net -
On Feb 19, 2008, at 1:24 AM, Chris Suter wrote:>
> On 19/02/2008, at 4:23 PM, Adam P Jenkins wrote:
>
>> Oops, I left out __strong, I meant the second statement to be:
>>
>> __strong float *myPointer =
>> NSAllocateCollectable(size*sizeof(float), 0);
>
> It doesn't make any difference.
>
> Everything on the stack or in a register is implicitly strong.
Where did you find that information? I'm not saying it isn't true,
but I'd just like to know what else I should be reading. In the
definition of __strong in the GC Programming Guide, it has this to say:
<quote>
__strong is implicitly part of any declaration of an Objective-C
object reference type. You must use it explicitly if you need to use
Core Foundation types, void *, or other non-object references
</quote>
which led me to believe I needed to use __strong any time I want a non-
object pointer to be noticed by the GC.
Also, regarding the NSString UTF8String problem you mentioned: It
seems to me that the problem would be just as possible if you used an
NSMutableData object to allocate the memory, even if you made sure to
store a reference to the object in a variable. That is, doing
NSMutableData *data = [NSMutableData
dataWithLength:count*sizeof(float)];
float *ptr = [data mutableBytes];
// do stuff with ptr, never refer to data variable
could also land you in trouble. It seems to me that the compiler
might optimize away the data variable in the above code if it was
never accessed again after the line which calls mutableBytes, so you
could run into the same problem, where the NSMutableData object gets
GCed before you're done using ptr, unless you declared data to be
volatile. So the problem isn't with NSAllocateCollectable only. -
On 19 Feb 2008, at 14:26, Adam P Jenkins wrote:> On Feb 19, 2008, at 1:24 AM, Chris Suter wrote:
>
>>
>> On 19/02/2008, at 4:23 PM, Adam P Jenkins wrote:
>>
>>> Oops, I left out __strong, I meant the second statement to be:
>>>
>>> __strong float *myPointer =
>>> NSAllocateCollectable(size*sizeof(float), 0);
>>
>> It doesn't make any difference.
>>
>> Everything on the stack or in a register is implicitly strong.
>
> Where did you find that information? I'm not saying it isn't true,
> but I'd just like to know what else I should be reading. In the
> definition of __strong in the GC Programming Guide, it has this to
> say:
Well it isn't *quite* true. The point is that a thread's stack and
registers are scanned by the collector, and anything that looks like a
pointer to an object will be traced.
"__strong" has two purposes; it ensures that a write barrier is used
when accessing the variable, which is important for detection of
intergenerational pointers and also (I think) for synchronising access
between the mutator (your code) and the collector, plus it causes the
compiler to add information about the variable to the Objective-C
section of the final Mach-O binary. There is no place in the binary
for information about stack variables and registers, and since the
stack and registers aren't part of a particular object's storage, they
can't be intergenerational pointers either; they're also thread-local,
so the collector doesn't need to synchronise access because it stops
the thread in order to scan the stack.
Anyway, the long and short of it is that __strong doesn't currently do
anything for local variables.> <quote>
> __strong is implicitly part of any declaration of an Objective-C
> object reference type. You must use it explicitly if you need to use
> Core Foundation types, void *, or other non-object references
> </quote>
>
> which led me to believe I needed to use __strong any time I want a
> non-object pointer to be noticed by the GC.
It's safe to use __strong everywhere, but it won't actually do
anything (and as a result isn't really necessary) for variables held
on the stack (i.e. local variables).> Also, regarding the NSString UTF8String problem you mentioned: It
> seems to me that the problem would be just as possible if you used
> an NSMutableData object to allocate the memory, even if you made
> sure to store a reference to the object in a variable. That is, doing
>
> NSMutableData *data = [NSMutableData
> dataWithLength:count*sizeof(float)];
> float *ptr = [data mutableBytes];
>
> // do stuff with ptr, never refer to data variable
>
> could also land you in trouble. It seems to me that the compiler
> might optimize away the data variable in the above code
Assuming you never refer to the data variable again, yes, it could. A
solution would be to stick "volatile" on the "data" variable, e.g.
NSMutableData * volatile data = [NSMutableData dataWithLength:count
* sizeof(float)];
to prevent the compiler from optimizing it away.> if it was never accessed again after the line which calls
> mutableBytes, so you could run into the same problem, where the
> NSMutableData object gets GCed before you're done using ptr, unless
> you declared data to be volatile. So the problem isn't with
> NSAllocateCollectable only.
Indeed.
Kind regards,
Alastair.
--
http://alastairs-place.net -
On Feb 19, 2008, at 03:48, Alastair Houghton wrote:> Indeed, the following code is broken, but only when compiled using
> optimization:
>
> /* ---- tst.m -----------------------------------------------------
> */
>
> #import <Foundation/Foundation.h>
>
> int
> main (int argc, char **argv)
> {
> float *myArray = NSAllocateCollectable(1000 * sizeof(float), 0);
> unsigned n;
> float *ptr;
>
> for (n = 0, ptr = myArray; n < 100; ++n, ++ptr) {
> *ptr = 1.0;
> }
>
> [[NSGarbageCollector defaultCollector] collectExhaustively];
>
> for (n = 0; n < 100; ++n, --ptr) {
> printf ("%f\n", *ptr);
> }
>
> return 0;
> }
Another way of looking at this is that the following is implicit in
the above:
__strong float *myArray = NSAllocateCollectable(1000 *
sizeof(float), 0);
unsigned n;
__strong float *ptr;
for (n = 0, ptr = myArray; n < 100; ++n) {
*ptr = 1.0;
ptr = ptr + 1;
}
and that 'ptr + 1' isn't really compatible with the __strong variable
it's being assigned to. (If the runtime supported interior pointers,
it would be compatible.) That incompatibility is a can of worms, if
you imagine the effects of having the compiler flag it as an error,
but at least it's a purely syntactic can of worms.
Furthermore, your example made me wonder if there's another
optimization hole, that has nothing to do with interior pointers, but
which also reflects a variable lifetime indeterminacy:
__weak NSString* string1 = [@"string1" copy];
NSString* string2 = @"string2";
[[NSGarbageCollector defaultCollector] collectExhaustively];
Couldn't this result in the garbage collector zeroing 'string2' under
unlucky (but common) optimization conditions? -
On 19/02/2008, at 10:48 PM, Alastair Houghton wrote:> I'm not sure what the best fix is for this. Like Chris said, one
> option would be to allow us to allocate "autoreleased" memory (in
> which case -UTF8String should probably be doing that too). Or maybe
> the GC should support interior pointers to memory allocated with
> NSAllocateCollectable()?
I'm not convinced that supporting interior pointers is the right
solution. I can imagine a situation where the pointer that's lying
around is pointing beyond the end of the array. For example, with
optimisations this might break:
for (n = 0, myPointer = myArray; n < size; ++n, ++myPointer) {
// Do something
}
// In the following the compiler could optimise this to be myPointer[-1]
doSomethingWith (myArray[size - 1]);
// No further references to myArray -
On Feb 19, 2008 1:36 PM, Quincey Morris <quinceymorris...> wrote:> Furthermore, your example made me wonder if there's another
> optimization hole, that has nothing to do with interior pointers, but
> which also reflects a variable lifetime indeterminacy:
>
> __weak NSString* string1 = [@"string1" copy];
> NSString* string2 = @"string2";
> [[NSGarbageCollector defaultCollector] collectExhaustively];
>
> Couldn't this result in the garbage collector zeroing 'string2' under
> unlucky (but common) optimization conditions?
All local variables (variables stored on the stack and in registers)
are strong. Even the ones marked __weak. __weak (and __strong) only
apply for variables stored on the heap.
Mike -
On 19 Feb 2008, at 19:03, Michael Ash wrote:> On Feb 19, 2008 1:36 PM, Quincey Morris
> <quinceymorris...> wrote:
>> Furthermore, your example made me wonder if there's another
>> optimization hole, that has nothing to do with interior pointers, but
>> which also reflects a variable lifetime indeterminacy:
>>
>> __weak NSString* string1 = [@"string1" copy];
>> NSString* string2 = @"string2";
>> [[NSGarbageCollector defaultCollector] collectExhaustively];
>>
>> Couldn't this result in the garbage collector zeroing 'string2' under
>> unlucky (but common) optimization conditions?
>
> All local variables (variables stored on the stack and in registers)
> are strong. Even the ones marked __weak. __weak (and __strong) only
> apply for variables stored on the heap.
...and globals...
Anyway, the point is not to read too much into __strong or __weak.
They do what the documentation says, no more, no less. It isn't a
good idea to invent all sorts of theories on the basis of "__strong
types" etcetera, because that isn't (presently) how things work.
There's nothing wrong with pointer arithmetic on pointers returned by
NSAllocateCollectable(), it's just that you have to be careful not to
dispose of the pointer to the start of the block before you're done
with it. Sadly, right now, that requires an awareness of what the
optimiser might do to your code.
Kind regards,
Alastair.
--
http://alastairs-place.net -
On Feb 19, 2008, at 11:03, Michael Ash wrote:> All local variables (variables stored on the stack and in registers)
> are strong. Even the ones marked __weak. __weak (and __strong) only
> apply for variables stored on the heap.
I went back and read the documentation again. It's actually silent
(unless I missed something somewhere) on the issue of whether the
following local variables are strong or weak references:
__weak id var1;
__weak void* var2;
Certainly, 'id var1' is implicitly strong and 'void* var2' is not, but
the document doesn't say whether "__weak" is either legal or dominant
over the implicit attribute for a local variable.
However, the point (that I originally missed) is that the answer
doesn't matter. Local variables don't inhibit collection because
they're strong, they inhibit collection of their referenced object/
memory because it is in the garbage collector's root set.
On Feb 19, 2008, at 11:28, Alastair Houghton wrote:> ...and globals...
>
> Anyway, the point is not to read too much into __strong or __weak.
> They do what the documentation says, no more, no less. It isn't a
> good idea to invent all sorts of theories on the basis of "__strong
> types" etcetera, because that isn't (presently) how things work.
>
> There's nothing wrong with pointer arithmetic on pointers returned
> by NSAllocateCollectable(), it's just that you have to be careful
> not to dispose of the pointer to the start of the block before
> you're done with it. Sadly, right now, that requires an awareness
> of what the optimiser might do to your code.
I wasn't actually making the mistake of thinking that "__strong" is a
type attribute. I was just trying to make the perhaps unimportant
point that assigning an interior pointer to a non-interior pointer can
be detected syntactically, which means there are 3 possible avenues
for dealing with this case of optimization fragility, not just the 2
you mentioned:
1. Enhance GC (code generation and runtime) to work with interior
pointers.
2. Be careful not to let the non-interior pointer variable's lifetime
expire before you're done with the interior pointer.
and
3. Enhance Objective-C -- to warn about or disallow certain patterns
of interior pointer usage, say, or to generate code that preserves the
base pointer reference when pointer arithmetic is used. -
On 20/02/2008, at 8:12 AM, Quincey Morris wrote:> which means there are 3 possible avenues for dealing with this case
> of optimization fragility, not just the 2 you mentioned:
>
> 1. Enhance GC (code generation and runtime) to work with interior
> pointers.
I'm not convinced this would work. See earlier e-mail.> 2. Be careful not to let the non-interior pointer variable's
> lifetime expire before you're done with the interior pointer.
This is little better than using malloc and free.> and
>
> 3. Enhance Objective-C -- to warn about or disallow certain patterns
> of interior pointer usage, say, or to generate code that preserves
> the base pointer reference when pointer arithmetic is used.
This won't work for libraries that you have no control over. For
example, you might pass -[NSString UTF8String] to other library
routines that aren't Objective C and have no knowledge of garbage
collection (which is a fairly common usage pattern).
The only solution that I can see is viable is the one I proposedâuse
autoreleased memory for these kinds of temporary objects.
- Chris -
On Feb 19, 2008, at 14:42, Chris Suter wrote:> The only solution that I can see is viable is the one I proposedâuse
> autoreleased memory for these kinds of temporary objects.
OK, I'll fess up and admit I don't know what this means. At what point
does the lifetime of such an object end? -
On Feb 19, 2008 4:12 PM, Quincey Morris <quinceymorris...> wrote:>
> On Feb 19, 2008, at 11:03, Michael Ash wrote:
>
>> All local variables (variables stored on the stack and in registers)
>> are strong. Even the ones marked __weak. __weak (and __strong) only
>> apply for variables stored on the heap.
>
> I went back and read the documentation again. It's actually silent
> (unless I missed something somewhere) on the issue of whether the
> following local variables are strong or weak references:
>
> __weak id var1;
> __weak void* var2;
It is quite clear:
"The initial root set of objects is comprised of global variables,
stack variables, and objects with external references."
In other words, anything on the stack is strong.> Certainly, 'id var1' is implicitly strong and 'void* var2' is not, but
> the document doesn't say whether "__weak" is either legal or dominant
> over the implicit attribute for a local variable.
This is not the case. When used to declare local variables, both "id
var1" and "void *var2" are strong.
What's more, even something like "int var3" is implicitly strong when
a local variable, although this one is an implementation detail you
shouldn't rely on.> However, the point (that I originally missed) is that the answer
> doesn't matter. Local variables don't inhibit collection because
> they're strong, they inhibit collection of their referenced object/
> memory because it is in the garbage collector's root set.
That's really just another way of saying the same thing. You can think
of all local variables as being part of the root set (as the docs
describe it), or you can think of the stack itself as being part of
the root set, with all of its contents being strong references. They
are equivalent ways to describe the situation.
All of this should become clear when you think about how the collector
actually works inside. It needs to be able to track what references
what, and it needs to be able to zero out weak references when objects
they point to disappear. This is done by using read and write barriers
for anything on the heap. But for the stack, it doesn't use any
barriers. Instead it occasionally pauses threads to scan their stack
"manually" in an exhaustive fashion.
Since stack variables don't generate read/write barriers, they can't
be weak. Since no annotations are generated to describe the data types
of what's on the stack, everything is treated as strong, even integers
(and floats, and char arrays, and dead spaces not being used for
anything at the moment) containing the bit-patterns of pointers.
The stack and heap are two different universes with this GC system.
The heap relies on the compiler to understand types and to generate
read and write barriers as needed. The stack is a big formless blob
which is scanned exhaustively, and held in place during this process
by temporarily pausing the only code which is allowed to touch it. All
else flows from this.
(And don't forget that there's a third universe, the non-scanned,
non-collected heap, which you can get with malloc/free. The GC doesn't
touch it, but that very fact makes it interesting.)
Mike -
On 20/02/2008, at 10:35 AM, Quincey Morris wrote:>
> On Feb 19, 2008, at 14:42, Chris Suter wrote:
>
>> The only solution that I can see is viable is the one I proposedâ
>> use autoreleased memory for these kinds of temporary objects.
>
> OK, I'll fess up and admit I don't know what this means. At what
> point does the lifetime of such an object end?
It's exactly the same as how it used to work in the non GC world. In
the old world -[NSString UTF8String] would have created an auto-
released object (that you don't get to access) and when it gets
released (i.e. when the associated auto-release pool gets released)
the memory pointed to by --[NSString UTF8String] gets released. The
problem with it is that it relies on you releasing an auto-release
pool periodically (or equivalent) which is fine for the main thread
but it's a pain to ask the user to do that for secondary threadsâso
maybe it's not the best solution after all. :-)
Thinking about it now, maybe a better way is if you can flag return
values to the compiler so that the compiler ensures that the return
value is left on the stack or in a register for the lifetime of the
callers stack frame. But then that has its own issuesâyou can't
reclaim the memory until the function returns.
Hmm, it's a tricky one to solve. Thankfully I don't have to solve it
and I'm not using GC.
- Chris


