From mboxrd@z Thu Jan 1 00:00:00 1970 To: Fans of the OS Plan 9 from Bell Labs <9fans@9fans.net> In-reply-to: Your message of "Fri, 04 Sep 2009 00:44:35 EDT." <0084d6ddb9d674fa38925596dabc8d78@quanstro.net> References: <0084d6ddb9d674fa38925596dabc8d78@quanstro.net> From: Bakul Shah Date: Thu, 3 Sep 2009 23:45:47 -0700 Message-Id: <20090904064547.A1ED95B3E@mail.bitblocks.com> Subject: Re: [9fans] "Blocks" in C Topicbox-Message-UUID: 63bd5d3a-ead5-11e9-9d60-3106f5b1d025 On Fri, 04 Sep 2009 00:44:35 EDT erik quanstrom wrote: > > > that sucker is on the stack. by-by no-execute stack. I don't think so. See below. > > > 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? [Note: I am simply guessing and have no idea how they actually do this but this model seems workable] Consider this example: int foo(int a) { __block int b = 0; int (^g()) = ^{ return a + ++b; } ... return g() + g(); } My guess is the above will be translated to something like this: struct anon1 { int (*f)(struct anon1*); const int a; int *const bp; }; int anon1_f(struct anon1* x) { return x->a + ++(*x->bp); } int foo(int a) { int *bp = malloc(sizeof *bp); // not quite. see the correction below *bp = 0; struct anon1 _anon = { &anon1_f, a, &b }; struct anon1* g = &_anon; ... return g->f(&_anon) + g->f(&_anon); } As you can see, _anon will disappear when foo() is exited. But if you were to Block_copy() _anon, it will be allocated on the heap and a ptr to it returned. Now you do have everything needed to call this anon function even after returning from foo(). &_anon can also be passed to another thread etc. with no problem. Most likely __block variables are allocated on the heap and ref counted. Ref count is decremented on exit from the lexical scope where a __block var is defined. Block_copy() increments refcount of every __block var referenced by the block, Block_release() decrements it. So this is basically a function closure. They seem to have very carefully navigated around C's semantic sand bars.