Coverage Report

Created: 2026-06-30 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/third_party/jemalloc/src/extent_dss.c
Line
Count
Source
1
#include "jemalloc/internal/jemalloc_preamble.h"
2
#include "jemalloc/internal/jemalloc_internal_includes.h"
3
4
#include "jemalloc/internal/assert.h"
5
#include "jemalloc/internal/extent_dss.h"
6
#include "jemalloc/internal/spin.h"
7
8
/******************************************************************************/
9
/* Data. */
10
11
/* NOLINTNEXTLINE(performance-no-int-to-ptr) */
12
6
#define SBRK_INVALID ((void *)-1)
13
14
const char  *opt_dss = DSS_DEFAULT;
15
16
const char  *const dss_prec_names[] = {
17
  "disabled",
18
  "primary",
19
  "secondary",
20
  "N/A"
21
};
22
23
/*
24
 * Current dss precedence default, used when creating new arenas.  NB: This is
25
 * stored as unsigned rather than dss_prec_t because in principle there's no
26
 * guarantee that sizeof(dss_prec_t) is the same as sizeof(unsigned), and we use
27
 * atomic operations to synchronize the setting.
28
 */
29
static atomic_u_t dss_prec_default = ATOMIC_INIT(
30
    (unsigned)DSS_PREC_DEFAULT);
31
32
/* Base address of the DSS. */
33
static void   *dss_base;
34
/* Atomic boolean indicating whether a thread is currently extending DSS. */
35
static atomic_b_t dss_extending;
36
/* Atomic boolean indicating whether the DSS is exhausted. */
37
static atomic_b_t dss_exhausted;
38
/* Atomic current upper limit on DSS addresses. */
39
static atomic_p_t dss_max;
40
41
/******************************************************************************/
42
43
static void *
44
6
extent_dss_sbrk(intptr_t increment) {
45
6
#ifdef JEMALLOC_DSS
46
6
  return sbrk(increment);
47
#else
48
  not_implemented();
49
  return NULL;
50
#endif
51
6
}
52
53
dss_prec_t
54
100
extent_dss_prec_get(void) {
55
100
  dss_prec_t ret;
56
57
100
  if (!have_dss) {
58
0
    return dss_prec_disabled;
59
0
  }
60
100
  ret = (dss_prec_t)atomic_load_u(&dss_prec_default, ATOMIC_ACQUIRE);
61
100
  return ret;
62
100
}
63
64
bool
65
0
extent_dss_prec_set(dss_prec_t dss_prec) {
66
0
  if (!have_dss) {
67
0
    return (dss_prec != dss_prec_disabled);
68
0
  }
69
0
  atomic_store_u(&dss_prec_default, (unsigned)dss_prec, ATOMIC_RELEASE);
70
0
  return false;
71
0
}
72
73
static void
74
0
extent_dss_extending_start(void) {
75
0
  spin_t spinner = SPIN_INITIALIZER;
76
0
  while (true) {
77
0
    bool expected = false;
78
0
    if (atomic_compare_exchange_weak_b(&dss_extending, &expected,
79
0
        true, ATOMIC_ACQ_REL, ATOMIC_RELAXED)) {
80
0
      break;
81
0
    }
82
0
    spin_adaptive(&spinner);
83
0
  }
84
0
}
85
86
static void
87
0
extent_dss_extending_finish(void) {
88
0
  assert(atomic_load_b(&dss_extending, ATOMIC_RELAXED));
89
90
0
  atomic_store_b(&dss_extending, false, ATOMIC_RELEASE);
91
0
}
92
93
static void *
94
0
extent_dss_max_update(void *new_addr) {
95
  /*
96
   * Get the current end of the DSS as max_cur and assure that dss_max is
97
   * up to date.
98
   */
99
0
  void *max_cur = extent_dss_sbrk(0);
100
0
  if (max_cur == SBRK_INVALID) {
101
0
    return NULL;
102
0
  }
103
0
  atomic_store_p(&dss_max, max_cur, ATOMIC_RELEASE);
104
  /* Fixed new_addr can only be supported if it is at the edge of DSS. */
105
0
  if (new_addr != NULL && max_cur != new_addr) {
106
0
    return NULL;
107
0
  }
108
0
  return max_cur;
109
0
}
110
111
void *
112
extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
113
0
    size_t alignment, bool *zero, bool *commit) {
114
0
  edata_t *gap;
115
116
0
  cassert(have_dss);
117
0
  assert(size > 0);
118
0
  assert(alignment == ALIGNMENT_CEILING(alignment, PAGE));
119
120
  /*
121
   * sbrk() uses a signed increment argument, so take care not to
122
   * interpret a large allocation request as a negative increment.
123
   */
124
0
  if ((intptr_t)size < 0) {
125
0
    return NULL;
126
0
  }
127
128
0
  gap = edata_cache_get(tsdn, &arena->pa_shard.edata_cache);
129
0
  if (gap == NULL) {
130
0
    return NULL;
131
0
  }
132
133
0
  extent_dss_extending_start();
134
0
  if (!atomic_load_b(&dss_exhausted, ATOMIC_ACQUIRE)) {
135
    /*
136
     * The loop is necessary to recover from races with other
137
     * threads that are using the DSS for something other than
138
     * malloc.
139
     */
140
0
    while (true) {
141
0
      void *max_cur = extent_dss_max_update(new_addr);
142
0
      if (max_cur == NULL) {
143
0
        goto label_oom;
144
0
      }
145
146
0
      bool head_state = opt_retain ? EXTENT_IS_HEAD :
147
0
          EXTENT_NOT_HEAD;
148
      /*
149
       * Compute how much page-aligned gap space (if any) is
150
       * necessary to satisfy alignment.  This space can be
151
       * recycled for later use.
152
       */
153
0
      void *gap_addr_page = ALIGNMENT_ADDR2CEILING(max_cur,
154
0
          PAGE);
155
0
      void *ret = ALIGNMENT_ADDR2CEILING(
156
0
          gap_addr_page, alignment);
157
0
      size_t gap_size_page = (uintptr_t)ret -
158
0
          (uintptr_t)gap_addr_page;
159
0
      if (gap_size_page != 0) {
160
0
        edata_init(gap, arena_ind_get(arena),
161
0
            gap_addr_page, gap_size_page, false,
162
0
            SC_NSIZES, extent_sn_next(
163
0
          &arena->pa_shard.pac),
164
0
            extent_state_active, false, true,
165
0
            EXTENT_PAI_PAC, head_state);
166
0
      }
167
      /*
168
       * Compute the address just past the end of the desired
169
       * allocation space.
170
       */
171
0
      void *dss_next = (void *)((byte_t *)ret + size);
172
0
      if ((uintptr_t)ret < (uintptr_t)max_cur ||
173
0
          (uintptr_t)dss_next < (uintptr_t)max_cur) {
174
0
        goto label_oom; /* Wrap-around. */
175
0
      }
176
      /* Compute the increment, including subpage bytes. */
177
0
      void *gap_addr_subpage = max_cur;
178
0
      size_t gap_size_subpage = (uintptr_t)ret -
179
0
          (uintptr_t)gap_addr_subpage;
180
0
      intptr_t incr = gap_size_subpage + size;
181
182
0
      assert((uintptr_t)max_cur + incr == (uintptr_t)ret +
183
0
          size);
184
185
      /* Try to allocate. */
186
0
      void *dss_prev = extent_dss_sbrk(incr);
187
0
      if (dss_prev == max_cur) {
188
        /* Success. */
189
0
        atomic_store_p(&dss_max, dss_next,
190
0
            ATOMIC_RELEASE);
191
0
        extent_dss_extending_finish();
192
193
0
        if (gap_size_page != 0) {
194
0
          ehooks_t *ehooks = arena_get_ehooks(
195
0
              arena);
196
0
          extent_dalloc_gap(tsdn,
197
0
              &arena->pa_shard.pac, ehooks, gap);
198
0
        } else {
199
0
          edata_cache_put(tsdn,
200
0
              &arena->pa_shard.edata_cache, gap);
201
0
        }
202
0
        if (!*commit) {
203
0
          *commit = pages_decommit(ret, size);
204
0
        }
205
0
        if (*zero && *commit) {
206
0
          edata_t edata = {0};
207
0
          ehooks_t *ehooks = arena_get_ehooks(
208
0
              arena);
209
210
0
          edata_init(&edata,
211
0
              arena_ind_get(arena), ret, size,
212
0
              size, false, SC_NSIZES,
213
0
              extent_state_active, false, true,
214
0
              EXTENT_PAI_PAC, head_state);
215
0
          if (extent_purge_forced_wrapper(tsdn,
216
0
              ehooks, &edata, 0, size)) {
217
0
            memset(ret, 0, size);
218
0
          }
219
0
        }
220
0
        return ret;
221
0
      }
222
      /*
223
       * Failure, whether due to OOM or a race with a raw
224
       * sbrk() call from outside the allocator.
225
       */
226
0
      if (dss_prev == SBRK_INVALID) {
227
        /* OOM. */
228
0
        atomic_store_b(&dss_exhausted, true,
229
0
            ATOMIC_RELEASE);
230
0
        goto label_oom;
231
0
      }
232
0
    }
233
0
  }
234
0
label_oom:
235
0
  extent_dss_extending_finish();
236
0
  edata_cache_put(tsdn, &arena->pa_shard.edata_cache, gap);
237
0
  return NULL;
238
0
}
239
240
static bool
241
8.55M
extent_in_dss_helper(void *addr, void *max) {
242
8.55M
  return ((uintptr_t)addr >= (uintptr_t)dss_base && (uintptr_t)addr <
243
8.55M
      (uintptr_t)max);
244
8.55M
}
245
246
bool
247
0
extent_in_dss(void *addr) {
248
0
  cassert(have_dss);
249
250
0
  return extent_in_dss_helper(addr, atomic_load_p(&dss_max,
251
0
      ATOMIC_ACQUIRE));
252
0
}
253
254
bool
255
4.27M
extent_dss_mergeable(void *addr_a, void *addr_b) {
256
4.27M
  void *max;
257
258
4.27M
  cassert(have_dss);
259
260
4.27M
  if ((uintptr_t)addr_a < (uintptr_t)dss_base && (uintptr_t)addr_b <
261
0
      (uintptr_t)dss_base) {
262
0
    return true;
263
0
  }
264
265
4.27M
  max = atomic_load_p(&dss_max, ATOMIC_ACQUIRE);
266
4.27M
  return (extent_in_dss_helper(addr_a, max) ==
267
4.27M
      extent_in_dss_helper(addr_b, max));
268
4.27M
}
269
270
void
271
6
extent_dss_boot(void) {
272
6
  cassert(have_dss);
273
274
6
  dss_base = extent_dss_sbrk(0);
275
6
  atomic_store_b(&dss_extending, false, ATOMIC_RELAXED);
276
6
  atomic_store_b(&dss_exhausted, dss_base == SBRK_INVALID, ATOMIC_RELAXED);
277
6
  atomic_store_p(&dss_max, dss_base, ATOMIC_RELAXED);
278
6
}
279
280
/******************************************************************************/