Re: Accessing buffers in NSData/NSMutableData under garbage collection
Re: Accessing buffers in NSData/NSMutableData under garbage collection
- Subject: Re: Accessing buffers in NSData/NSMutableData under garbage collection
- From: Alastair Houghton <email@hidden>
- Date: Tue, 19 Feb 2008 11:48:22 +0000
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 ("x '%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
_______________________________________________
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