src/core/ngx_slab.c - nginx source code

Global variables defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */

  5. #include <ngx_config.h>
  6. #include <ngx_core.h>


  7. #define NGX_SLAB_PAGE_MASK   3
  8. #define NGX_SLAB_PAGE        0
  9. #define NGX_SLAB_BIG         1
  10. #define NGX_SLAB_EXACT       2
  11. #define NGX_SLAB_SMALL       3

  12. #if (NGX_PTR_SIZE == 4)

  13. #define NGX_SLAB_PAGE_FREE   0
  14. #define NGX_SLAB_PAGE_BUSY   0xffffffff
  15. #define NGX_SLAB_PAGE_START  0x80000000

  16. #define NGX_SLAB_SHIFT_MASK  0x0000000f
  17. #define NGX_SLAB_MAP_MASK    0xffff0000
  18. #define NGX_SLAB_MAP_SHIFT   16

  19. #define NGX_SLAB_BUSY        0xffffffff

  20. #else /* (NGX_PTR_SIZE == 8) */

  21. #define NGX_SLAB_PAGE_FREE   0
  22. #define NGX_SLAB_PAGE_BUSY   0xffffffffffffffff
  23. #define NGX_SLAB_PAGE_START  0x8000000000000000

  24. #define NGX_SLAB_SHIFT_MASK  0x000000000000000f
  25. #define NGX_SLAB_MAP_MASK    0xffffffff00000000
  26. #define NGX_SLAB_MAP_SHIFT   32

  27. #define NGX_SLAB_BUSY        0xffffffffffffffff

  28. #endif


  29. #define ngx_slab_slots(pool)                                                  \
  30.     (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t))

  31. #define ngx_slab_page_type(page)   ((page)->prev & NGX_SLAB_PAGE_MASK)

  32. #define ngx_slab_page_prev(page)                                              \
  33.     (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK)

  34. #define ngx_slab_page_addr(pool, page)                                        \
  35.     ((((page) - (pool)->pages) << ngx_pagesize_shift)                         \
  36.      + (uintptr_t) (pool)->start)


  37. #if (NGX_DEBUG_MALLOC)

  38. #define ngx_slab_junk(p, size)     ngx_memset(p, 0xA5, size)

  39. #elif (NGX_HAVE_DEBUG_MALLOC)

  40. #define ngx_slab_junk(p, size)                                                \
  41.     if (ngx_debug_malloc)          ngx_memset(p, 0xA5, size)

  42. #else

  43. #define ngx_slab_junk(p, size)

  44. #endif

  45. static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool,
  46.     ngx_uint_t pages);
  47. static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
  48.     ngx_uint_t pages);
  49. static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,
  50.     char *text);


  51. static ngx_uint_t  ngx_slab_max_size;
  52. static ngx_uint_t  ngx_slab_exact_size;
  53. static ngx_uint_t  ngx_slab_exact_shift;


  54. void
  55. ngx_slab_sizes_init(void)
  56. {
  57.     ngx_uint_t  n;

  58.     ngx_slab_max_size = ngx_pagesize / 2;
  59.     ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
  60.     for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
  61.         /* void */
  62.     }
  63. }


  64. void
  65. ngx_slab_init(ngx_slab_pool_t *pool)
  66. {
  67.     u_char           *p;
  68.     size_t            size;
  69.     ngx_int_t         m;
  70.     ngx_uint_t        i, n, pages;
  71.     ngx_slab_page_t  *slots, *page;

  72.     pool->min_size = (size_t) 1 << pool->min_shift;

  73.     slots = ngx_slab_slots(pool);

  74.     p = (u_char *) slots;
  75.     size = pool->end - p;

  76.     ngx_slab_junk(p, size);

  77.     n = ngx_pagesize_shift - pool->min_shift;

  78.     for (i = 0; i < n; i++) {
  79.         /* only "next" is used in list head */
  80.         slots[i].slab = 0;
  81.         slots[i].next = &slots[i];
  82.         slots[i].prev = 0;
  83.     }

  84.     p += n * sizeof(ngx_slab_page_t);

  85.     pool->stats = (ngx_slab_stat_t *) p;
  86.     ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t));

  87.     p += n * sizeof(ngx_slab_stat_t);

  88.     size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t));

  89.     pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));

  90.     pool->pages = (ngx_slab_page_t *) p;
  91.     ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t));

  92.     page = pool->pages;

  93.     /* only "next" is used in list head */
  94.     pool->free.slab = 0;
  95.     pool->free.next = page;
  96.     pool->free.prev = 0;

  97.     page->slab = pages;
  98.     page->next = &pool->free;
  99.     page->prev = (uintptr_t) &pool->free;

  100.     pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t),
  101.                                 ngx_pagesize);

  102.     m = pages - (pool->end - pool->start) / ngx_pagesize;
  103.     if (m > 0) {
  104.         pages -= m;
  105.         page->slab = pages;
  106.     }

  107.     pool->last = pool->pages + pages;
  108.     pool->pfree = pages;

  109.     pool->log_nomem = 1;
  110.     pool->log_ctx = &pool->zero;
  111.     pool->zero = '\0';
  112. }


  113. void *
  114. ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)
  115. {
  116.     void  *p;

  117.     ngx_shmtx_lock(&pool->mutex);

  118.     p = ngx_slab_alloc_locked(pool, size);

  119.     ngx_shmtx_unlock(&pool->mutex);

  120.     return p;
  121. }


  122. void *
  123. ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)
  124. {
  125.     size_t            s;
  126.     uintptr_t         p, m, mask, *bitmap;
  127.     ngx_uint_t        i, n, slot, shift, map;
  128.     ngx_slab_page_t  *page, *prev, *slots;

  129.     if (size > ngx_slab_max_size) {

  130.         ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
  131.                        "slab alloc: %uz", size);

  132.         page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)
  133.                                           + ((size % ngx_pagesize) ? 1 : 0));
  134.         if (page) {
  135.             p = ngx_slab_page_addr(pool, page);

  136.         } else {
  137.             p = 0;
  138.         }

  139.         goto done;
  140.     }

  141.     if (size > pool->min_size) {
  142.         shift = 1;
  143.         for (s = size - 1; s >>= 1; shift++) { /* void */ }
  144.         slot = shift - pool->min_shift;

  145.     } else {
  146.         shift = pool->min_shift;
  147.         slot = 0;
  148.     }

  149.     pool->stats[slot].reqs++;

  150.     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
  151.                    "slab alloc: %uz slot: %ui", size, slot);

  152.     slots = ngx_slab_slots(pool);
  153.     page = slots[slot].next;

  154.     if (page->next != page) {

  155.         if (shift < ngx_slab_exact_shift) {

  156.             bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);

  157.             map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));

  158.             for (n = 0; n < map; n++) {

  159.                 if (bitmap[n] != NGX_SLAB_BUSY) {

  160.                     for (m = 1, i = 0; m; m <<= 1, i++) {
  161.                         if (bitmap[n] & m) {
  162.                             continue;
  163.                         }

  164.                         bitmap[n] |= m;

  165.                         i = (n * 8 * sizeof(uintptr_t) + i) << shift;

  166.                         p = (uintptr_t) bitmap + i;

  167.                         pool->stats[slot].used++;

  168.                         if (bitmap[n] == NGX_SLAB_BUSY) {
  169.                             for (n = n + 1; n < map; n++) {
  170.                                 if (bitmap[n] != NGX_SLAB_BUSY) {
  171.                                     goto done;
  172.                                 }
  173.                             }

  174.                             prev = ngx_slab_page_prev(page);
  175.                             prev->next = page->next;
  176.                             page->next->prev = page->prev;

  177.                             page->next = NULL;
  178.                             page->prev = NGX_SLAB_SMALL;
  179.                         }

  180.                         goto done;
  181.                     }
  182.                 }
  183.             }

  184.         } else if (shift == ngx_slab_exact_shift) {

  185.             for (m = 1, i = 0; m; m <<= 1, i++) {
  186.                 if (page->slab & m) {
  187.                     continue;
  188.                 }

  189.                 page->slab |= m;

  190.                 if (page->slab == NGX_SLAB_BUSY) {
  191.                     prev = ngx_slab_page_prev(page);
  192.                     prev->next = page->next;
  193.                     page->next->prev = page->prev;

  194.                     page->next = NULL;
  195.                     page->prev = NGX_SLAB_EXACT;
  196.                 }

  197.                 p = ngx_slab_page_addr(pool, page) + (i << shift);

  198.                 pool->stats[slot].used++;

  199.                 goto done;
  200.             }

  201.         } else { /* shift > ngx_slab_exact_shift */

  202.             mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1;
  203.             mask <<= NGX_SLAB_MAP_SHIFT;

  204.             for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
  205.                  m & mask;
  206.                  m <<= 1, i++)
  207.             {
  208.                 if (page->slab & m) {
  209.                     continue;
  210.                 }

  211.                 page->slab |= m;

  212.                 if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {
  213.                     prev = ngx_slab_page_prev(page);
  214.                     prev->next = page->next;
  215.                     page->next->prev = page->prev;

  216.                     page->next = NULL;
  217.                     page->prev = NGX_SLAB_BIG;
  218.                 }

  219.                 p = ngx_slab_page_addr(pool, page) + (i << shift);

  220.                 pool->stats[slot].used++;

  221.                 goto done;
  222.             }
  223.         }

  224.         ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_alloc(): page is busy");
  225.         ngx_debug_point();
  226.     }

  227.     page = ngx_slab_alloc_pages(pool, 1);

  228.     if (page) {
  229.         if (shift < ngx_slab_exact_shift) {
  230.             bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);

  231.             n = (ngx_pagesize >> shift) / ((1 << shift) * 8);

  232.             if (n == 0) {
  233.                 n = 1;
  234.             }

  235.             /* "n" elements for bitmap, plus one requested */

  236.             for (i = 0; i < (n + 1) / (8 * sizeof(uintptr_t)); i++) {
  237.                 bitmap[i] = NGX_SLAB_BUSY;
  238.             }

  239.             m = ((uintptr_t) 1 << ((n + 1) % (8 * sizeof(uintptr_t)))) - 1;
  240.             bitmap[i] = m;

  241.             map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));

  242.             for (i = i + 1; i < map; i++) {
  243.                 bitmap[i] = 0;
  244.             }

  245.             page->slab = shift;
  246.             page->next = &slots[slot];
  247.             page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;

  248.             slots[slot].next = page;

  249.             pool->stats[slot].total += (ngx_pagesize >> shift) - n;

  250.             p = ngx_slab_page_addr(pool, page) + (n << shift);

  251.             pool->stats[slot].used++;

  252.             goto done;

  253.         } else if (shift == ngx_slab_exact_shift) {

  254.             page->slab = 1;
  255.             page->next = &slots[slot];
  256.             page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;

  257.             slots[slot].next = page;

  258.             pool->stats[slot].total += 8 * sizeof(uintptr_t);

  259.             p = ngx_slab_page_addr(pool, page);

  260.             pool->stats[slot].used++;

  261.             goto done;

  262.         } else { /* shift > ngx_slab_exact_shift */

  263.             page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;
  264.             page->next = &slots[slot];
  265.             page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;

  266.             slots[slot].next = page;

  267.             pool->stats[slot].total += ngx_pagesize >> shift;

  268.             p = ngx_slab_page_addr(pool, page);

  269.             pool->stats[slot].used++;

  270.             goto done;
  271.         }
  272.     }

  273.     p = 0;

  274.     pool->stats[slot].fails++;

  275. done:

  276.     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
  277.                    "slab alloc: %p", (void *) p);

  278.     return (void *) p;
  279. }


  280. void *
  281. ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size)
  282. {
  283.     void  *p;

  284.     ngx_shmtx_lock(&pool->mutex);

  285.     p = ngx_slab_calloc_locked(pool, size);

  286.     ngx_shmtx_unlock(&pool->mutex);

  287.     return p;
  288. }


  289. void *
  290. ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size)
  291. {
  292.     void  *p;

  293.     p = ngx_slab_alloc_locked(pool, size);
  294.     if (p) {
  295.         ngx_memzero(p, size);
  296.     }

  297.     return p;
  298. }


  299. void
  300. ngx_slab_free(ngx_slab_pool_t *pool, void *p)
  301. {
  302.     ngx_shmtx_lock(&pool->mutex);

  303.     ngx_slab_free_locked(pool, p);

  304.     ngx_shmtx_unlock(&pool->mutex);
  305. }


  306. void
  307. ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)
  308. {
  309.     size_t            size;
  310.     uintptr_t         slab, m, *bitmap;
  311.     ngx_uint_t        i, n, type, slot, shift, map;
  312.     ngx_slab_page_t  *slots, *page;

  313.     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);

  314.     if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
  315.         ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
  316.         goto fail;
  317.     }

  318.     n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
  319.     page = &pool->pages[n];
  320.     slab = page->slab;
  321.     type = ngx_slab_page_type(page);

  322.     switch (type) {

  323.     case NGX_SLAB_SMALL:

  324.         shift = slab & NGX_SLAB_SHIFT_MASK;
  325.         size = (size_t) 1 << shift;

  326.         if ((uintptr_t) p & (size - 1)) {
  327.             goto wrong_chunk;
  328.         }

  329.         n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;
  330.         m = (uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)));
  331.         n /= 8 * sizeof(uintptr_t);
  332.         bitmap = (uintptr_t *)
  333.                              ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));

  334.         if (bitmap[n] & m) {
  335.             slot = shift - pool->min_shift;

  336.             if (page->next == NULL) {
  337.                 slots = ngx_slab_slots(pool);

  338.                 page->next = slots[slot].next;
  339.                 slots[slot].next = page;

  340.                 page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
  341.                 page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;
  342.             }

  343.             bitmap[n] &= ~m;

  344.             n = (ngx_pagesize >> shift) / ((1 << shift) * 8);

  345.             if (n == 0) {
  346.                 n = 1;
  347.             }

  348.             i = n / (8 * sizeof(uintptr_t));
  349.             m = ((uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)))) - 1;

  350.             if (bitmap[i] & ~m) {
  351.                 goto done;
  352.             }

  353.             map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));

  354.             for (i = i + 1; i < map; i++) {
  355.                 if (bitmap[i]) {
  356.                     goto done;
  357.                 }
  358.             }

  359.             ngx_slab_free_pages(pool, page, 1);

  360.             pool->stats[slot].total -= (ngx_pagesize >> shift) - n;

  361.             goto done;
  362.         }

  363.         goto chunk_already_free;

  364.     case NGX_SLAB_EXACT:

  365.         m = (uintptr_t) 1 <<
  366.                 (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift);
  367.         size = ngx_slab_exact_size;

  368.         if ((uintptr_t) p & (size - 1)) {
  369.             goto wrong_chunk;
  370.         }

  371.         if (slab & m) {
  372.             slot = ngx_slab_exact_shift - pool->min_shift;

  373.             if (slab == NGX_SLAB_BUSY) {
  374.                 slots = ngx_slab_slots(pool);

  375.                 page->next = slots[slot].next;
  376.                 slots[slot].next = page;

  377.                 page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
  378.                 page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;
  379.             }

  380.             page->slab &= ~m;

  381.             if (page->slab) {
  382.                 goto done;
  383.             }

  384.             ngx_slab_free_pages(pool, page, 1);

  385.             pool->stats[slot].total -= 8 * sizeof(uintptr_t);

  386.             goto done;
  387.         }

  388.         goto chunk_already_free;

  389.     case NGX_SLAB_BIG:

  390.         shift = slab & NGX_SLAB_SHIFT_MASK;
  391.         size = (size_t) 1 << shift;

  392.         if ((uintptr_t) p & (size - 1)) {
  393.             goto wrong_chunk;
  394.         }

  395.         m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift)
  396.                               + NGX_SLAB_MAP_SHIFT);

  397.         if (slab & m) {
  398.             slot = shift - pool->min_shift;

  399.             if (page->next == NULL) {
  400.                 slots = ngx_slab_slots(pool);

  401.                 page->next = slots[slot].next;
  402.                 slots[slot].next = page;

  403.                 page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
  404.                 page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;
  405.             }

  406.             page->slab &= ~m;

  407.             if (page->slab & NGX_SLAB_MAP_MASK) {
  408.                 goto done;
  409.             }

  410.             ngx_slab_free_pages(pool, page, 1);

  411.             pool->stats[slot].total -= ngx_pagesize >> shift;

  412.             goto done;
  413.         }

  414.         goto chunk_already_free;

  415.     case NGX_SLAB_PAGE:

  416.         if ((uintptr_t) p & (ngx_pagesize - 1)) {
  417.             goto wrong_chunk;
  418.         }

  419.         if (!(slab & NGX_SLAB_PAGE_START)) {
  420.             ngx_slab_error(pool, NGX_LOG_ALERT,
  421.                            "ngx_slab_free(): page is already free");
  422.             goto fail;
  423.         }

  424.         if (slab == NGX_SLAB_PAGE_BUSY) {
  425.             ngx_slab_error(pool, NGX_LOG_ALERT,
  426.                            "ngx_slab_free(): pointer to wrong page");
  427.             goto fail;
  428.         }

  429.         size = slab & ~NGX_SLAB_PAGE_START;

  430.         ngx_slab_free_pages(pool, page, size);

  431.         ngx_slab_junk(p, size << ngx_pagesize_shift);

  432.         return;
  433.     }

  434.     /* not reached */

  435.     return;

  436. done:

  437.     pool->stats[slot].used--;

  438.     ngx_slab_junk(p, size);

  439.     return;

  440. wrong_chunk:

  441.     ngx_slab_error(pool, NGX_LOG_ALERT,
  442.                    "ngx_slab_free(): pointer to wrong chunk");

  443.     goto fail;

  444. chunk_already_free:

  445.     ngx_slab_error(pool, NGX_LOG_ALERT,
  446.                    "ngx_slab_free(): chunk is already free");

  447. fail:

  448.     return;
  449. }


  450. static ngx_slab_page_t *
  451. ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)
  452. {
  453.     ngx_slab_page_t  *page, *p;

  454.     for (page = pool->free.next; page != &pool->free; page = page->next) {

  455.         if (page->slab >= pages) {

  456.             if (page->slab > pages) {
  457.                 page[page->slab - 1].prev = (uintptr_t) &page[pages];

  458.                 page[pages].slab = page->slab - pages;
  459.                 page[pages].next = page->next;
  460.                 page[pages].prev = page->prev;

  461.                 p = (ngx_slab_page_t *) page->prev;
  462.                 p->next = &page[pages];
  463.                 page->next->prev = (uintptr_t) &page[pages];

  464.             } else {
  465.                 p = (ngx_slab_page_t *) page->prev;
  466.                 p->next = page->next;
  467.                 page->next->prev = page->prev;
  468.             }

  469.             page->slab = pages | NGX_SLAB_PAGE_START;
  470.             page->next = NULL;
  471.             page->prev = NGX_SLAB_PAGE;

  472.             pool->pfree -= pages;

  473.             if (--pages == 0) {
  474.                 return page;
  475.             }

  476.             for (p = page + 1; pages; pages--) {
  477.                 p->slab = NGX_SLAB_PAGE_BUSY;
  478.                 p->next = NULL;
  479.                 p->prev = NGX_SLAB_PAGE;
  480.                 p++;
  481.             }

  482.             return page;
  483.         }
  484.     }

  485.     if (pool->log_nomem) {
  486.         ngx_slab_error(pool, NGX_LOG_CRIT,
  487.                        "ngx_slab_alloc() failed: no memory");
  488.     }

  489.     return NULL;
  490. }


  491. static void
  492. ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
  493.     ngx_uint_t pages)
  494. {
  495.     ngx_slab_page_t  *prev, *join;

  496.     pool->pfree += pages;

  497.     page->slab = pages--;

  498.     if (pages) {
  499.         ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));
  500.     }

  501.     if (page->next) {
  502.         prev = ngx_slab_page_prev(page);
  503.         prev->next = page->next;
  504.         page->next->prev = page->prev;
  505.     }

  506.     join = page + page->slab;

  507.     if (join < pool->last) {

  508.         if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {

  509.             if (join->next != NULL) {
  510.                 pages += join->slab;
  511.                 page->slab += join->slab;

  512.                 prev = ngx_slab_page_prev(join);
  513.                 prev->next = join->next;
  514.                 join->next->prev = join->prev;

  515.                 join->slab = NGX_SLAB_PAGE_FREE;
  516.                 join->next = NULL;
  517.                 join->prev = NGX_SLAB_PAGE;
  518.             }
  519.         }
  520.     }

  521.     if (page > pool->pages) {
  522.         join = page - 1;

  523.         if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {

  524.             if (join->slab == NGX_SLAB_PAGE_FREE) {
  525.                 join = ngx_slab_page_prev(join);
  526.             }

  527.             if (join->next != NULL) {
  528.                 pages += join->slab;
  529.                 join->slab += page->slab;

  530.                 prev = ngx_slab_page_prev(join);
  531.                 prev->next = join->next;
  532.                 join->next->prev = join->prev;

  533.                 page->slab = NGX_SLAB_PAGE_FREE;
  534.                 page->next = NULL;
  535.                 page->prev = NGX_SLAB_PAGE;

  536.                 page = join;
  537.             }
  538.         }
  539.     }

  540.     if (pages) {
  541.         page[pages].prev = (uintptr_t) page;
  542.     }

  543.     page->prev = (uintptr_t) &pool->free;
  544.     page->next = pool->free.next;

  545.     page->next->prev = (uintptr_t) page;

  546.     pool->free.next = page;
  547. }


  548. static void
  549. ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)
  550. {
  551.     ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx);
  552. }