天天看點

iOS自動釋放池AutoreleasePool

介紹iOS的自動釋放池原理,把底層的方法分析一遍,并給每個方法都添加了注釋

我們一般使用自動釋放池是直接使用@autoreleasePool方法,如下

底層會幫我們再次翻譯成另一種形式

int main(int argc, const char * argv[]) {
          @autoreleasePool{

          }
         return 0;
}
           

底層會翻譯成這樣

建立一個局部變量接收push函數的傳回值,檢視objc_autoreleasePoolPush和objc_autoreleasePoolPop方法,會發現内部都調用了AutoreleasePoolPage,

//這個是翻譯後的自動釋放池結構體
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
     __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
     ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }
     void * atautoreleasepoolobj;
};
{ __AtAutoreleasePool __autoreleasepool;}
//建立釋放池
void * objc_autoreleasePoolPush(void) {
               return AutoreleasePoolPage::push();
     }
//銷毀釋放池
void objc_autoreleasePoolPop(void *ctxt) {
            AutoreleasePoolPage::pop(ctxt);
     }
//main方法,翻譯後的是這樣
int main(int argc, const char * argv[]) {
          { 
          //atautoreleasepoolobj是哨兵對象
          void * atautoreleasepoolobj = objc_autoreleasePoolPush();  
                     
                objc_autoreleasePoolPop(atautoreleasepoolobj);
         }
         return 0;
 }
           

AutoreleasePoolPage的具體實作是這樣的

struct magic_t {
    static const uint32_t M0 = 0xA1A1A1A1;
#   define M1 "AUTORELEASE!"
    static const size_t M1_len = 12;
    uint32_t m[4];
    
    magic_t() {
        assert(M1_len == strlen(M1));
        assert(M1_len == 3 * sizeof(m[1]));

        m[0] = M0;
        strncpy((char *)&m[1], M1, M1_len);
    }

    ~magic_t() {
        m[0] = m[1] = m[2] = m[3] = 0;
    }

    bool check() const {
        return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
    }

    bool fastcheck() const {
#if CHECK_AUTORELEASEPOOL
        return check();
#else
        return (m[0] == M0);
#endif
    }

#   undef M1
};
    /*
     @autoreleasepool{}
     extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
     extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
     struct __AtAutoreleasePool {
     __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
     ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj); }
     void * atautoreleasepoolobj;
     };
     { __AtAutoreleasePool __autoreleasepool;}
     
     
     void * objc_autoreleasePoolPush(void) {

               return AutoreleasePoolPage::push();

     }

     void objc_autoreleasePoolPop(void *ctxt) {

            AutoreleasePoolPage::pop(ctxt);

     }
     
     int main(int argc, const char * argv[]) {

          { void * atautoreleasepoolobj = objc_autoreleasePoolPush(); // do whatever you want             objc_autoreleasePoolPop(atautoreleasepoolobj);

         }

         return 0;

     }
     */
//自動釋放池,每頁4096個位元組,雙向連結清單結構
//自動釋放池的對象成員占用56個位元組,是以添加的對象是從第56個位元組開始存儲到釋放池中
class AutoreleasePoolPage 
{
    // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is 
    // pushed and it has never contained any objects. This saves memory 
    // when the top level (i.e. libdispatch) pushes and pops pools but 
    // never uses them.
#   define EMPTY_POOL_PLACEHOLDER ((id*)1)
//分界對象 (以前叫POOL_SENTINEL(哨兵對象)總之是一樣的作用)
//在每個自動釋放池初始化調用objc_autoreleasePoolPush的時候,都會把一個POOL_BOUNDARY push 到自動釋放池的棧頂,并且傳回這個POOL_BOUNDARY邊界對象。
#   define POOL_BOUNDARY nil
    static pthread_key_t const key = AUTORELEASE_POOL_KEY;
    static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
    static size_t const SIZE = 
#if PROTECT_AUTORELEASEPOOL
        PAGE_MAX_SIZE;  // must be multiple of vm page size
#else
        PAGE_MAX_SIZE;  // size and alignment, power of 2
#endif
    static size_t const COUNT = SIZE / sizeof(id);

    magic_t const magic;
    id *next;//指向下一個要添加的自動釋放對象的位置
    pthread_t const thread;
    AutoreleasePoolPage * const parent;//指向上一頁釋放池
    AutoreleasePoolPage *child;//指向下一頁釋放池
    uint32_t const depth;
    uint32_t hiwat;

    // SIZE-sizeof(*this) bytes of contents follow

    //建立一塊記憶體,大小4096個位元組
    static void * operator new(size_t size) {
        return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
    }
    //釋放記憶體
    static void operator delete(void * p) {
        return free(p);
    }

    //加鎖
    inline void protect() {
#if PROTECT_AUTORELEASEPOOL
        mprotect(this, SIZE, PROT_READ);
        check();
#endif
    }
//解鎖
    inline void unprotect() {
#if PROTECT_AUTORELEASEPOOL
        check();
        mprotect(this, SIZE, PROT_READ | PROT_WRITE);
#endif
    }

    //建立一個釋放池,參數是上一個已經full滿的釋放池
    AutoreleasePoolPage(AutoreleasePoolPage *newParent) 
        : magic(), next(begin()), thread(pthread_self()),
          parent(newParent), child(nil), 
          depth(parent ? 1+parent->depth : 0), 
          hiwat(parent ? parent->hiwat : 0)
    {
        //如果parent有值,說明上一個釋放池已經滿了
        if (parent) {
            parent->check();
            assert(!parent->child);
            parent->unprotect();
            parent->child = this;
            parent->protect();
        }
        protect();
    }

    ~AutoreleasePoolPage() 
    {
        check();
        unprotect();
        assert(empty());

        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        assert(!child);
    }

//顯示異常資訊
    void busted(bool die = true) 
    {
        magic_t right;
        (die ? _objc_fatal : _objc_inform)
            ("autorelease pool page %p corrupted\n"
             "  magic     0x%08x 0x%08x 0x%08x 0x%08x\n"
             "  should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
             "  pthread   %p\n"
             "  should be %p\n", 
             this, 
             magic.m[0], magic.m[1], magic.m[2], magic.m[3], 
             right.m[0], right.m[1], right.m[2], right.m[3], 
             this->thread, pthread_self());
    }
    //調用的方法check()用來檢查目前的result是不是一個AutoreleasePoolPage,通過檢查magic_t結構體中的某個成員是否為0xA1A1A1A1
    void check(bool die = true) 
    {
        if (!magic.check() || !pthread_equal(thread, pthread_self())) {
            busted(die);
        }
    }
//調用的方法fastCheck()用來檢查目前的result是不是一個AutoreleasePoolPage,通過檢查magic_t結構體中的某個成員是否為0xA1A1A1A1
    void fastcheck(bool die = true) 
    {
#if CHECK_AUTORELEASEPOOL
        check(die);
#else
        if (! magic.fastcheck()) {
            busted(die);
        }
#endif
    }

//起始位置
    id * begin() {
        return (id *) ((uint8_t *)this+sizeof(*this));//sizeof(*this) = 56個位元組
    }
//棧頂位置
    id * end() {
        return (id *) ((uint8_t *)this+SIZE);
    }

    //如果next指針等于begin,說明沒有要釋放的對象
    bool empty() {
        return next == begin();
    }

    //如果next指針等于end,說明滿了
    bool full() { 
        return next == end();
    }

    //釋放池中的數量是不是小于釋放池容量的一半
    bool lessThanHalfFull() {
        return (next - begin() < (end() - begin()) / 2);
    }

    //把對象添加到自動釋放池中,并傳回對象所在的自動釋放池中的位置的指針位址
    id *add(id obj)
    {
        assert(!full());
        unprotect();
        id *ret = next;  // faster than `return next-1` because of aliasing
        *next++ = obj;
        protect();
        return ret;
    }

    //釋放所有對象
    void releaseAll() 
    {
        releaseUntil(begin());
    }

    //釋放到stop所指定的指針位址這個位置的所有對象
    void releaseUntil(id *stop) 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        
        while (this->next != stop) {
            // Restart from hotPage() every time, in case -release
            // autoreleased more objects
            //每次都從hotpage中擷取對象去釋放,防止釋放過多
            AutoreleasePoolPage *page = hotPage();

            // fixme I think this `while` can be `if`, but I can't prove it
            //如果這個釋放池沒有一個對象,就去釋放他的上一個釋放池
            while (page->empty()) {
                page = page->parent;
                //更換最新釋放頁,把目前頁面設定為最新釋放頁
                setHotPage(page);
            }

            page->unprotect();
            //這句話可以這樣看*(--(page->next)),就是先擷取遊标的位址,減減(--)是擷取最後一個添加的對象所在的位址,因為next總是指向下一個空位址,
            //*是取位址所在的值
            id obj = *--page->next;
            //把next的位置設定為0xA3,标記為,以下都是要釋放的對象
            memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
            page->protect();

            //如果對象不為nil,就釋放對象
            if (obj != POOL_BOUNDARY) {
                //調釋放函數
                objc_release(obj);
            }
        }

        setHotPage(this);

#if DEBUG
        // we expect any children to be completely empty
        for (AutoreleasePoolPage *page = child; page; page = page->child) {
            assert(page->empty());
        }
#endif
    }

    //銷毀所有的釋放池
    void kill() 
    {
        // Not recursive: we don't want to blow out the stack 
        // if a thread accumulates a stupendous amount of garbage
        //通過循環,找到最新的一個釋放池
        AutoreleasePoolPage *page = this;
        while (page->child) page = page->child;

        AutoreleasePoolPage *deathptr;
        do {
            deathptr = page;
            page = page->parent;
            if (page) {
                page->unprotect();
                page->child = nil;
                page->protect();
            }
            delete deathptr;//釋放記憶體
        } while (deathptr != this);
    }
//清空釋放池
    static void tls_dealloc(void *p) 
    {
        if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
            // No objects or pool pages to clean up here.
            return;
        }

        // reinstate TLS value while we work
        setHotPage((AutoreleasePoolPage *)p);

        if (AutoreleasePoolPage *page = coldPage()) {
            if (!page->empty()) pop(page->begin());  // pop all of the pools
            if (DebugMissingPools || DebugPoolAllocation) {
                // pop() killed the pages already
            } else {
                page->kill();  // free all of the pages
            }
        }
        
        // clear TLS value so TLS destruction doesn't loop
        setHotPage(nil);
    }

    //根據哨兵對象的指針位址擷取目前頁的釋放池的首位址,一般傳入的參數是哨兵對象
    static AutoreleasePoolPage *pageForPointer(const void *p) 
    {
        return pageForPointer((uintptr_t)p);
    }
    //根據哨兵對象的指針位址擷取目前頁的釋放池的首位址,一般傳入的參數是哨兵對象
    static AutoreleasePoolPage *pageForPointer(uintptr_t p) 
    {
        AutoreleasePoolPage *result;
        uintptr_t offset = p % SIZE;

        assert(offset >= sizeof(AutoreleasePoolPage));

        result = (AutoreleasePoolPage *)(p - offset);
        result->fastcheck();

        return result;
    }


    static inline bool haveEmptyPoolPlaceholder()
    {
        id *tls = (id *)tls_get_direct(key);
        return (tls == EMPTY_POOL_PLACEHOLDER);
    }

    static inline id* setEmptyPoolPlaceholder()
    {
        assert(tls_get_direct(key) == nil);
        tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
        return EMPTY_POOL_PLACEHOLDER;
    }
//hotPage可以了解為目前正在使用的AutoreleasePoolPage
    static inline AutoreleasePoolPage *hotPage() 
    {
        AutoreleasePoolPage *result = (AutoreleasePoolPage *)
            tls_get_direct(key);
        if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
        if (result) result->fastcheck();
        return result;
    }
//hotPage可以了解為目前正在使用的AutoreleasePoolPage
    static inline void setHotPage(AutoreleasePoolPage *page) 
    {
        if (page) page->fastcheck();
        tls_set_direct(key, (void *)page);
    }

    //擷取第一個釋放池
    static inline AutoreleasePoolPage *coldPage() 
    {
        AutoreleasePoolPage *result = hotPage();
        if (result) {
            while (result->parent) {
                result = result->parent;
                result->fastcheck();
            }
        }
        return result;
    }

//快速添加對象到釋放池方法
    static inline id *autoreleaseFast(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page && !page->full()) {
            //有hotPage并且目前page不滿,調用page->add(obj)方法将對象添加至AutoreleasePoolPage的棧中
            return page->add(obj);
        } else if (page) {
            //有hotPage并且目前page已滿,調用autoreleaseFullPage初始化一個新的頁,調用page->add(obj)方法将對象添加至AutoreleasePoolPage的棧中
            return autoreleaseFullPage(obj, page);
        } else {
            //無hotPage,調用autoreleaseNoPage建立一個hotPage,調用page->add(obj)方法将對象添加至AutoreleasePoolPage的棧中,最後的都會調用page->add(obj)将對象添加到自動釋放池中。
            return autoreleaseNoPage(obj);
        }
    }

    //目前的釋放池滿的時候,會調用這個方法,建立新的釋放池,并添加對象
    static __attribute__((noinline))
    id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
    {
        // The hot page is full. 
        // Step to the next non-full page, adding a new page if necessary.
        // Then add the object to that page.
        assert(page == hotPage());
        assert(page->full()  ||  DebugPoolAllocation);

        do {
            if (page->child) page = page->child;
            else page = new AutoreleasePoolPage(page);
        } while (page->full());

        setHotPage(page);
        return page->add(obj);
    }
無hotPage,調用autoreleaseNoPage建立一個hotPage,調用page->add(obj)方法将對象添加至AutoreleasePoolPage的棧中,最後的都會調用page->add(obj)将對象添加到自動釋放池中。
    static __attribute__((noinline))
    id *autoreleaseNoPage(id obj)
    {
        // "No page" could mean no pool has been pushed
        // or an empty placeholder pool has been pushed and has no contents yet
        assert(!hotPage());

        bool pushExtraBoundary = false;
        //判斷是否需要插入哨兵
        if (haveEmptyPoolPlaceholder()) {
            // We are pushing a second pool over the empty placeholder pool
            // or pushing the first object into the empty placeholder pool.
            // Before doing that, push a pool boundary on behalf of the pool 
            // that is currently represented by the empty placeholder.
            pushExtraBoundary = true;
        }
        else if (obj != POOL_BOUNDARY  &&  DebugMissingPools) {
            //報出錯誤
            // We are pushing an object with no pool in place, 
            // and no-pool debugging was requested by environment.
            // 我們正在推送一個沒有池的對象,
            // 并且環境請求了無池調試。
            _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
                         "autoreleased with no pool in place - "
                         "just leaking - break on "
                         "objc_autoreleaseNoPool() to debug", 
                         pthread_self(), (void*)obj, object_getClassName(obj));
            objc_autoreleaseNoPool(obj);
            return nil;
        }
        else if (obj == POOL_BOUNDARY  &&  !DebugPoolAllocation) {
            //報出錯誤
            // 我們正在推送一個沒有池子的池子,
                         // 并且沒有請求 alloc-per-pool 調試。
                         // 安裝并傳回空池占位符。
            // We are pushing a pool with no pool in place,
            // and alloc-per-pool debugging was not requested.
            // Install and return the empty pool placeholder.
            //如果obj是個nil對象,就建立一個空釋放池
            return setEmptyPoolPlaceholder();
        }

        // We are pushing an object or a non-placeholder'd pool.

        // Install the first page.
        //建立一個空釋放池
        AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
        //設定為目前釋放池
        setHotPage(page);
        
        // Push a boundary on behalf of the previously-placeholder'd pool.
        if (pushExtraBoundary) {
//            是否需要添加哨兵
            page->add(POOL_BOUNDARY);
        }
        
        // Push the requested object or pool.
        //添加釋放對象
        return page->add(obj);
    }


    static __attribute__((noinline))
    id *autoreleaseNewPage(id obj)
    {
        AutoreleasePoolPage *page = hotPage();
        if (page) return autoreleaseFullPage(obj, page);
        else return autoreleaseNoPage(obj);
    }

public:
    //把對象添加到自動釋放池中
    static inline id autorelease(id obj)
    {
        assert(obj);
        assert(!obj->isTaggedPointer());
        id *dest __unused = autoreleaseFast(obj);//調用快速添加到自動釋放池方法,把對象加到釋放池中
        assert(!dest  ||  dest == EMPTY_POOL_PLACEHOLDER  ||  *dest == obj);
        return obj;
    }

//當我是用@autoreleasepool{},内部會調用這個靜态方法去實作一個釋放池對象,
    static inline void *push() 
    {
        id *dest;
        if (DebugPoolAllocation) {
            // Each autorelease pool starts on a new pool page.
            dest = autoreleaseNewPage(POOL_BOUNDARY);
        } else {
            //dest這個就建立釋放池成功後傳回的哨兵對象,
            //後面調用pop方法釋放的時候,會傳入這個dest對象
            //這裡是快速建立一個釋放池,并添加一個哨兵對象
            dest = autoreleaseFast(POOL_BOUNDARY);
        }
        assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
        return dest;//把哨兵對象傳回
    }
//釋放出現異常,調用這個
    static void badPop(void *token)
    {
        // Error. For bincompat purposes this is not 
        // fatal in executables built with old SDKs.

        if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
            // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
            _objc_fatal
                ("Invalid or prematurely-freed autorelease pool %p.", token);
        }

        // Old SDK. Bad pop is warned once.
        static bool complained = false;
        if (!complained) {
            complained = true;
            _objc_inform_now_and_on_crash
                ("Invalid or prematurely-freed autorelease pool %p. "
                 "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
                 "Proceeding anyway because the app is old "
                 "(SDK version " SDK_FORMAT "). Memory errors are likely.",
                     token, FORMAT_SDK(sdkVersion()));
        }
        objc_autoreleasePoolInvalid(token);
    }
    //釋放對象方法,參數是push方法傳回的哨兵對象
    static inline void pop(void *token) 
    {
        AutoreleasePoolPage *page;
        id *stop;
//這裡是如果傳入的token不是哨兵
        if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
            // Popping the top-level placeholder pool.
            if (hotPage()) {
                // Pool was used. Pop its contents normally.
                // Pool pages remain allocated for re-use as usual.
                //這裡是擷取第一個釋放池的哨兵對象,開始釋放
                pop(coldPage()->begin());
            } else {
                // Pool was never used. Clear the placeholder.
                setHotPage(nil);//如果沒用過釋放池,直接清空
            }
            return;
        }

        page = pageForPointer(token);//擷取哨兵對象所在的釋放池頁面
        stop = (id *)token;
        //如果token不是哨兵對象
        if (*stop != POOL_BOUNDARY) {
            //如果stop等于這個釋放頁開始位置,并且釋放頁沒有上一頁,也就是這個釋放頁是第一頁,不做處理
            if (stop == page->begin()  &&  !page->parent) {
                // Start of coldest page may correctly not be POOL_BOUNDARY:
                // 1. top-level pool is popped, leaving the cold page in place
                // 2. an object is autoreleased with no pool
            } else {
                // Error. For bincompat purposes this is not 
                // fatal in executables built with old SDKs.
                //否則報出錯誤
                return badPop(token);
            }
        }

        if (PrintPoolHiwat) printHiwat();
//開始釋放對象
        page->releaseUntil(stop);

        // memory: delete empty children
//        如果釋放池頁是空的直接清空,且上一個釋放池頁不是空,把上一個釋放池頁設定成hotpage目前頁面
        if (DebugPoolAllocation  &&  page->empty()) {
            // special case: delete everything during page-per-pool debugging
            AutoreleasePoolPage *parent = page->parent;
            page->kill();
            setHotPage(parent);
        } else if (DebugMissingPools  &&  page->empty()  &&  !page->parent) {
            // special case: delete everything for pop(top) 
            // when debugging missing autorelease pools
            //如果這個釋放池是空,上一個也是空,直接清除,設定目前釋放池為空
            page->kill();
            setHotPage(nil);
        } 
        else if (page->child) {
            
            // hysteresis: keep one empty child if page is more than half full
            //釋放下一頁的釋放池
            if (page->lessThanHalfFull()) {
                page->child->kill();
            }
            else if (page->child->child) {
                page->child->child->kill();
            }
        }
    }

    static void init()
    {
        int r __unused = pthread_key_init_np(AutoreleasePoolPage::key, 
                                             AutoreleasePoolPage::tls_dealloc);
        assert(r == 0);
    }

    void print() 
    {
        _objc_inform("[%p]  ................  PAGE %s %s %s", this, 
                     full() ? "(full)" : "", 
                     this == hotPage() ? "(hot)" : "", 
                     this == coldPage() ? "(cold)" : "");
        check(false);
        for (id *p = begin(); p < next; p++) {
            if (*p == POOL_BOUNDARY) {
                _objc_inform("[%p]  ################  POOL %p", p, p);
            } else {
                _objc_inform("[%p]  %#16lx  %s", 
                             p, (unsigned long)*p, object_getClassName(*p));
            }
        }
    }

    static void printAll()
    {        
        _objc_inform("##############");
        _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());

        AutoreleasePoolPage *page;
        ptrdiff_t objects = 0;
        for (page = coldPage(); page; page = page->child) {
            objects += page->next - page->begin();
        }
        _objc_inform("%llu releases pending.", (unsigned long long)objects);

        if (haveEmptyPoolPlaceholder()) {
            _objc_inform("[%p]  ................  PAGE (placeholder)", 
                         EMPTY_POOL_PLACEHOLDER);
            _objc_inform("[%p]  ################  POOL (placeholder)", 
                         EMPTY_POOL_PLACEHOLDER);
        }
        else {
            for (page = coldPage(); page; page = page->child) {
                page->print();
            }
        }

        _objc_inform("##############");
    }

    static void printHiwat()
    {
        // Check and propagate high water mark
        // Ignore high water marks under 256 to suppress noise.
        AutoreleasePoolPage *p = hotPage();
        uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
        if (mark > p->hiwat  &&  mark > 256) {
            for( ; p; p = p->parent) {
                p->unprotect();
                p->hiwat = mark;
                p->protect();
            }
            
            _objc_inform("POOL HIGHWATER: new high water mark of %u "
                         "pending releases for thread %p:", 
                         mark, pthread_self());
            
            void *stack[128];
            int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
            char **sym = backtrace_symbols(stack, count);
            for (int i = 0; i < count; i++) {
                _objc_inform("POOL HIGHWATER:     %s", sym[i]);
            }
            free(sym);
        }
    }

#undef POOL_BOUNDARY
};