LWN.net Logo

Tornado and Grand Central Dispatch: a quick look

Tornado and Grand Central Dispatch: a quick look

Posted Sep 16, 2009 20:37 UTC (Wed) by wahern (subscriber, #37304)
In reply to: Tornado and Grand Central Dispatch: a quick look by mgedmin
Parent article: Tornado and Grand Central Dispatch: a quick look

Do you mean this?

int i;
int *p = &i;
(void)(^block)(void) = ^{ *p = 0; }

I'm fairly certain (but you can read the documentation yourself), that such code is fine. I think you might be confusing (or rather, not distinguishing):

int *const p;

with

const int *p;
and/or
const int *const p;

The first is effectively what you get from the perspective of the Block's scope. Note also that, by default, the semantics of the Block's "closure" are copy-by-value, not by-reference. This is the reason for the read-only restriction. So that:

int i = 42;
(void)(^block)(void) = ^{ printf("%d", i); }
i = 7
printf("%d", i); // yields 7
block(); // yields 42

You'd get the expected result--both printing 7--with:

__block int i = 42;

But this is all explained, more-or-less, in Apple's documentation. What I'm still unclear of is how the run-time handles the collection of __block-storage objects. It uses some sort of reference counting on the Block objects, and the __block-storage objects are referenced by the Block, presumably. But how it manages to decrement the reference count, I'm not sure of. It might not do it at all, so that in order for a Block to remain valid outside of the scope it was defined in you have to use the API, i.e. Block_copy, etc.


(Log in to post comments)

Tornado and Grand Central Dispatch: a quick look

Posted Sep 16, 2009 22:05 UTC (Wed) by wahern (subscriber, #37304) [Link]

I should clarify. The dereference of the const pointer, and assignment to the dereferenced non-const object is okay on its face, but like most everything else in C, it's your responsibility to make sure that the pointer points to a valid object. And in this case, it's critical to understand that the pointer value, not the pointer object, is what's being bound by the Block (unless, of course, you used the __block scope/storage qualifier). You can visualize the normal case by adding a pseudo-prologue:

int i;
int *p = &i;
(void)(^block)(void) = ^{ int *const local_p = p; *local_p = 0; };
// is the equivalent of ^{ *p = 0; }

Finally, I'm fairly certain now that in order to return a Block from a function you have to manually use Block_copy() and Block_release(). So, there's no magic garbage collection going on under the hood, though there is some magic in the optimization of moving a Block from auto storage to dynamic storage--thus the __block qualifier and rules.

Implication being, that if you are passed a Block object and aren't going to use it exclusively from the current scope (e.g. the code flow falls back into an event loop, storing the Block for later use), you need to manage it with Block_copy() and Block_release(), similar to malloc()/free(). And I'm sure libdispatch/GCD does exactly this when passed a Block.

Tornado and Grand Central Dispatch: a quick look

Posted Sep 17, 2009 8:54 UTC (Thu) by mgedmin (subscriber, #34497) [Link]

Thank you for the answer.

Tornado and Grand Central Dispatch: a quick look

Posted Sep 18, 2009 10:39 UTC (Fri) by madcoder (subscriber, #30027) [Link]

What I'm still unclear of is how the run-time handles the collection of __block-storage objects. It uses some sort of reference counting on the Block objects, and the __block-storage objects are referenced by the Block, presumably. But how it manages to decrement the reference count, I'm not sure of. It might not do it at all, so that in order for a Block to remain valid outside of the scope it was defined in you have to use the API, i.e. Block_copy, etc.
Well, there is a Block_release() function for that. And actually, wrt __block variables, it stores a reference (pointer) to the variable on stack until you do your first Block_copy, then (if that happens) it moves the __block variable to the heap, alongside the copied block. IOW the __block variable address may change between two runs of the same block, that's why __block is incompatible with static.

Copyright © 2012, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds