On Thu, Sep 3, 2009 at 10:31 PM, David Leimbach <leimy2k@gmail.com> wrote:


On Thu, Sep 3, 2009 at 9:44 PM, erik quanstrom <quanstro@quanstro.net> wrote:
> > that sucker is on the stack.  by-by no-execute stack.
> > how does it get to the stack?  is it just copied from
> > the text segment or is it compiled at run time?
> >
>
> I don't think I posted the whole code, so that's my bad.  The X was on the
> stack to begin with as the first X was an automatic variable in a function.
>  I'd be a little surprised to find an automatic variable in the text
> segment, but perhaps that's just my not remembering things properly.
>  (didn't mean that tongue in cheek, I don't think about that stuff much
> these days, as I've spent the last year or so doing Erlang and Haskell.)

it is the block itself that apple claims is on the stacp
(your grand centeral reference, p. 38).  and i wonder
how it gets there.  is it just copied from the text segment?
that seems kind of pointless.  why not just execute it
from the text segment?  or is it modified (compiled?)
at run time?

Right, my understanding is that blocks live on the stack, however it appears that they get copied to a particular queue before being run, either explicitly or implicitly depending on how it gets submitted.  If you use dispatch_after, it gets copied and released automatically after a certain amount of time.

The whole thing is very "eventy".

Some of the documentation is not terribly explicit about how things get copied if they do at all.  Example is the dispatch_async call, which behaves based on the queue to which it is submitted, but whether or not a copy occurs is not mentioned.

This document goes into more detail:
"Block literals may occur within functions where the structure is created in stack local memory. They may also appear as initialization expressions for Block variables of global or static local variables.
When a Block literal expression is evaluated the stack based structure is initialized as follows:

1) static descriptor structure is declared and initialized as follows:
1a) the invoke function pointer is set to a function that takes the Block structure as its first argument and the rest of the arguments (if any) to the Block and executes the Block compound statement.
1b) the size field is set to the size of the following Block literal structure.
1c) the copy_helper and dispose_helper function pointers are set to respective helper functions if they are required by the Block literal
2) a stack (or global) Block literal data structure is created and initialized as follows:
2a) the isa field is set to the address of the external _NSConcreteStackBlock, which is a block of uninitialized memory supplied in libSystem, or _NSConcreteGlobalBlock if this is a static or file level block literal.
2) The flags field is set to zero unless there are variables imported into the block that need helper functions for program level Block_copy() and Block_release() operations, in which case the (1<<25) flags bit is set."
I'm still left feeling somewhat not understanding how it all works :-)
 

Actually, reading on a bit more they deal with the "variable capture" talking about const copies.

Automatic storage variables not marked with __block are imported as const copies.

The simplest example is that of importing a variable of type int.

   int x = 10;
   void (^vv)(void) = ^{ printf("x is %d\n", x); }
   x = 11;
   vv();

would be compiled

struct __block_literal_2 {
    void *isa;
    int flags;
    int reserved; 
    void (*invoke)(struct __block_literal_2 *);
    struct __block_descriptor_2 *descriptor;
    const int x;
};

void __block_invoke_2(struct __block_literal_2 *_block) {
    printf("x is %d\n", _block->x);
}

static struct __block_descriptor_2 {
    unsigned long int reserved;
    unsigned long int Block_size;
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };

and

  struct __block_literal_2 __block_literal_2 = {
	&_NSConcreteStackBlock,
	(1<<29), <uninitialized>,
	__block_invoke_2,
	&__block_descriptor_2,
        x
   };

In summary, scalars, structures, unions, and function pointers are generally imported as const copies with no need for helper functions.

 

- erik