Coverage Report

Created: 2026-03-31 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/duckdb/third_party/concurrentqueue/concurrentqueue.h
Line
Count
Source
1
// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue.
2
// An overview, including benchmark results, is provided here:
3
//     http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++
4
// The full design is also described in excruciating detail at:
5
//    http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue
6
7
// Simplified BSD license:
8
// Copyright (c) 2013-2016, Cameron Desrochers.
9
// All rights reserved.
10
//
11
// Redistribution and use in source and binary forms, with or without modification,
12
// are permitted provided that the following conditions are met:
13
//
14
// - Redistributions of source code must retain the above copyright notice, this list of
15
// conditions and the following disclaimer.
16
// - Redistributions in binary form must reproduce the above copyright notice, this list of
17
// conditions and the following disclaimer in the documentation and/or other materials
18
// provided with the distribution.
19
//
20
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
25
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31
#pragma once
32
33
#if defined(__GNUC__)
34
// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and
35
// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings
36
// upon assigning any computed values)
37
38
#endif
39
40
#if defined(__APPLE__)
41
#include <TargetConditionals.h>
42
#endif
43
44
#include <atomic>   // Requires C++11. Sorry VS2010.
45
#include <cassert>
46
#include <cstddef>              // for max_align_t
47
#include <cstdint>
48
#include <cstdlib>
49
#include <type_traits>
50
#include <algorithm>
51
#include <utility>
52
#include <limits>
53
#include <climits>    // for CHAR_BIT
54
#include <array>
55
#include <thread>   // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading
56
57
// Platform-specific definitions of a numeric thread ID type and an invalid value
58
namespace duckdb_moodycamel { namespace details {
59
  template<typename thread_id_t> struct thread_id_converter {
60
    typedef thread_id_t thread_id_numeric_size_t;
61
    typedef thread_id_t thread_id_hash_t;
62
218k
    static thread_id_hash_t prehash(thread_id_t const& x) { return x; }
63
  };
64
} }
65
#if defined(MCDBGQ_USE_RELACY)
66
namespace duckdb_moodycamel { namespace details {
67
  typedef std::uint32_t thread_id_t;
68
  static const thread_id_t invalid_thread_id  = 0xFFFFFFFFU;
69
  static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU;
70
  static inline thread_id_t thread_id() { return rl::thread_index(); }
71
} }
72
#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__)
73
// No sense pulling in windows.h in a header, we'll manually declare the function
74
// we use and rely on backwards-compatibility for this not to break
75
extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void);
76
namespace duckdb_moodycamel { namespace details {
77
  static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows");
78
  typedef std::uint32_t thread_id_t;
79
  static const thread_id_t invalid_thread_id  = 0;      // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx
80
  static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU;  // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4.
81
  static inline thread_id_t thread_id() { return static_cast<thread_id_t>(::GetCurrentThreadId()); }
82
} }
83
#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) || defined(__MVS__)
84
namespace duckdb_moodycamel { namespace details {
85
  static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes");
86
  
87
  typedef std::thread::id thread_id_t;
88
  static const thread_id_t invalid_thread_id;         // Default ctor creates invalid ID
89
90
  // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's
91
  // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't
92
  // be.
93
  static inline thread_id_t thread_id() { return std::this_thread::get_id(); }
94
95
  template<std::size_t> struct thread_id_size { };
96
  template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; };
97
  template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; };
98
99
  template<> struct thread_id_converter<thread_id_t> {
100
    typedef thread_id_size<sizeof(thread_id_t)>::numeric_t thread_id_numeric_size_t;
101
#ifndef __APPLE__
102
    typedef std::size_t thread_id_hash_t;
103
#else
104
    typedef thread_id_numeric_size_t thread_id_hash_t;
105
#endif
106
107
    static thread_id_hash_t prehash(thread_id_t const& x)
108
    {
109
#ifndef __APPLE__
110
      return std::hash<std::thread::id>()(x);
111
#else
112
      return *reinterpret_cast<thread_id_hash_t const*>(&x);
113
#endif
114
    }
115
  };
116
} }
117
#else
118
// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475
119
// In order to get a numeric thread ID in a platform-independent way, we use a thread-local
120
// static variable's address as a thread identifier :-)
121
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
122
#define MOODYCAMEL_THREADLOCAL __thread
123
#elif defined(_MSC_VER)
124
#define MOODYCAMEL_THREADLOCAL __declspec(thread)
125
#else
126
// Assume C++11 compliant compiler
127
#define MOODYCAMEL_THREADLOCAL thread_local
128
#endif
129
namespace duckdb_moodycamel { namespace details {
130
  typedef std::uintptr_t thread_id_t;
131
  static const thread_id_t invalid_thread_id  = 0;    // Address can't be nullptr
132
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
133
  static const thread_id_t invalid_thread_id2 = 1;    // Member accesses off a null pointer are also generally invalid. Plus it's not aligned.
134
#endif
135
218k
  inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast<thread_id_t>(&x); }
136
} }
137
#endif
138
139
// Constexpr if
140
#ifndef MOODYCAMEL_CONSTEXPR_IF
141
#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || __cplusplus > 201402L
142
1.55M
#define MOODYCAMEL_CONSTEXPR_IF if constexpr
143
#define MOODYCAMEL_MAYBE_UNUSED [[maybe_unused]]
144
#else
145
#define MOODYCAMEL_CONSTEXPR_IF if
146
#define MOODYCAMEL_MAYBE_UNUSED
147
#endif
148
#endif
149
150
// Exceptions
151
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
152
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
153
#define MOODYCAMEL_EXCEPTIONS_ENABLED
154
#endif
155
#endif
156
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
157
0
#define MOODYCAMEL_TRY try
158
#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__)
159
0
#define MOODYCAMEL_RETHROW throw
160
#define MOODYCAMEL_THROW(expr) throw (expr)
161
#else
162
#define MOODYCAMEL_TRY MOODYCAMEL_CONSTEXPR_IF (true)
163
#define MOODYCAMEL_CATCH(...) else MOODYCAMEL_CONSTEXPR_IF (false)
164
#define MOODYCAMEL_RETHROW
165
#define MOODYCAMEL_THROW(expr)
166
#endif
167
168
#ifndef MOODYCAMEL_NOEXCEPT
169
#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED)
170
#define MOODYCAMEL_NOEXCEPT
171
#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true
172
#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true
173
#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800
174
// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-(
175
// We have to assume *all* non-trivial constructors may throw on VS2012!
176
#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
177
#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference<valueType>::value && std::is_move_constructible<type>::value ? std::is_trivially_move_constructible<type>::value : std::is_trivially_copy_constructible<type>::value)
178
#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference<valueType>::value && std::is_move_assignable<type>::value ? std::is_trivially_move_assignable<type>::value || std::is_nothrow_move_assignable<type>::value : std::is_trivially_copy_assignable<type>::value || std::is_nothrow_copy_assignable<type>::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
179
#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900
180
#define MOODYCAMEL_NOEXCEPT _NOEXCEPT
181
#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference<valueType>::value && std::is_move_constructible<type>::value ? std::is_trivially_move_constructible<type>::value || std::is_nothrow_move_constructible<type>::value : std::is_trivially_copy_constructible<type>::value || std::is_nothrow_copy_constructible<type>::value)
182
#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference<valueType>::value && std::is_move_assignable<type>::value ? std::is_trivially_move_assignable<type>::value || std::is_nothrow_move_assignable<type>::value : std::is_trivially_copy_assignable<type>::value || std::is_nothrow_copy_assignable<type>::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr))
183
#else
184
#define MOODYCAMEL_NOEXCEPT noexcept
185
2.67M
#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr)
186
1.00M
#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr)
187
#endif
188
#endif
189
190
#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
191
#ifdef MCDBGQ_USE_RELACY
192
#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
193
#else
194
// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445
195
// g++ <=4.7 doesn't support thread_local either.
196
// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work
197
#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) && !defined(__MVS__)
198
// Assume `thread_local` is fully supported in all other C++11 compilers/platforms
199
//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED    // always disabled for now since several users report having problems with it on
200
#endif
201
#endif
202
#endif
203
204
// VS2012 doesn't support deleted functions. 
205
// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called.
206
#ifndef MOODYCAMEL_DELETE_FUNCTION
207
#if defined(_MSC_VER) && _MSC_VER < 1800
208
#define MOODYCAMEL_DELETE_FUNCTION
209
#else
210
#define MOODYCAMEL_DELETE_FUNCTION = delete
211
#endif
212
#endif
213
214
#ifndef MOODYCAMEL_ALIGNAS
215
// VS2013 doesn't support alignas or alignof
216
#if defined(_MSC_VER) && _MSC_VER <= 1800
217
#define MOODYCAMEL_ALIGNAS(alignment) __declspec(align(alignment))
218
#define MOODYCAMEL_ALIGNOF(obj) __alignof(obj)
219
#else
220
#define MOODYCAMEL_ALIGNAS(alignment) alignas(alignment)
221
#define MOODYCAMEL_ALIGNOF(obj) alignof(obj)
222
#endif
223
#endif
224
225
226
227
// Compiler-specific likely/unlikely hints
228
namespace duckdb_moodycamel { namespace details {
229
230
#if defined(__GNUC__)
231
1.59M
  static inline bool (likely)(bool x) { return __builtin_expect((x), true); }
ub_duckdb_parallel.cpp:duckdb_moodycamel::details::likely(bool)
Line
Count
Source
231
1.59M
  static inline bool (likely)(bool x) { return __builtin_expect((x), true); }
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:duckdb_moodycamel::details::likely(bool)
Unexecuted instantiation: ub_duckdb_storage.cpp:duckdb_moodycamel::details::likely(bool)
232
//  static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); }
233
#else
234
  static inline bool (likely)(bool x) { return x; }
235
//  static inline bool (unlikely)(bool x) { return x; }
236
#endif
237
} }
238
239
namespace duckdb_moodycamel {
240
namespace details {
241
  template<typename T>
242
  struct const_numeric_max {
243
    static_assert(std::is_integral<T>::value, "const_numeric_max can only be used with integers");
244
    static const T value = std::numeric_limits<T>::is_signed
245
      ? (static_cast<T>(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast<T>(1)
246
      : static_cast<T>(-1);
247
  };
248
249
#if defined(__GLIBCXX__)
250
  typedef ::max_align_t std_max_align_t;      // libstdc++ forgot to add it to std:: for a while
251
#else
252
  typedef std::max_align_t std_max_align_t;   // Others (e.g. MSVC) insist it can *only* be accessed via std::
253
#endif
254
255
  // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting
256
  // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64.
257
  typedef union {
258
    std_max_align_t x;
259
    long long y;
260
    void* z;
261
  } max_align_t;
262
}
263
264
// Default traits for the ConcurrentQueue. To change some of the
265
// traits without re-implementing all of them, inherit from this
266
// struct and shadow the declarations you wish to be different;
267
// since the traits are used as a template type parameter, the
268
// shadowed declarations will be used where defined, and the defaults
269
// otherwise.
270
struct ConcurrentQueueDefaultTraits
271
{
272
  // General-purpose size type. std::size_t is strongly recommended.
273
  typedef std::size_t size_t;
274
  
275
  // The type used for the enqueue and dequeue indices. Must be at least as
276
  // large as size_t. Should be significantly larger than the number of elements
277
  // you expect to hold at once, especially if you have a high turnover rate;
278
  // for example, on 32-bit x86, if you expect to have over a hundred million
279
  // elements or pump several million elements through your queue in a very
280
  // short space of time, using a 32-bit type *may* trigger a race condition.
281
  // A 64-bit int type is recommended in that case, and in practice will
282
  // prevent a race condition no matter the usage of the queue. Note that
283
  // whether the queue is lock-free with a 64-int type depends on the whether
284
  // std::atomic<std::uint64_t> is lock-free, which is platform-specific.
285
  typedef std::size_t index_t;
286
  
287
  // Internally, all elements are enqueued and dequeued from multi-element
288
  // blocks; this is the smallest controllable unit. If you expect few elements
289
  // but many producers, a smaller block size should be favoured. For few producers
290
  // and/or many elements, a larger block size is preferred. A sane default
291
  // is provided. Must be a power of 2.
292
  static const size_t BLOCK_SIZE = 32;
293
  
294
  // For explicit producers (i.e. when using a producer token), the block is
295
  // checked for being empty by iterating through a list of flags, one per element.
296
  // For large block sizes, this is too inefficient, and switching to an atomic
297
  // counter-based approach is faster. The switch is made for block sizes strictly
298
  // larger than this threshold.
299
  static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32;
300
  
301
  // How many full blocks can be expected for a single explicit producer? This should
302
  // reflect that number's maximum for optimal performance. Must be a power of 2.
303
  static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32;
304
  
305
  // How many full blocks can be expected for a single implicit producer? This should
306
  // reflect that number's maximum for optimal performance. Must be a power of 2.
307
  static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32;
308
  
309
  // The initial size of the hash table mapping thread IDs to implicit producers.
310
  // Note that the hash is resized every time it becomes half full.
311
  // Must be a power of two, and either 0 or at least 1. If 0, implicit production
312
  // (using the enqueue methods without an explicit producer token) is disabled.
313
  static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32;
314
  
315
  // Controls the number of items that an explicit consumer (i.e. one with a token)
316
  // must consume before it causes all consumers to rotate and move on to the next
317
  // internal queue.
318
  static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256;
319
  
320
  // The maximum number of elements (inclusive) that can be enqueued to a sub-queue.
321
  // Enqueue operations that would cause this limit to be surpassed will fail. Note
322
  // that this limit is enforced at the block level (for performance reasons), i.e.
323
  // it's rounded up to the nearest block size.
324
  static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max<size_t>::value;
325
  
326
  
327
#ifndef MCDBGQ_USE_RELACY
328
  // Memory allocation can be customized if needed.
329
  // malloc should return nullptr on failure, and handle alignment like std::malloc.
330
#if defined(malloc) || defined(free)
331
  // Gah, this is 2015, stop defining macros that break standard code already!
332
  // Work around malloc/free being special macros:
333
  static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); }
334
  static inline void WORKAROUND_free(void* ptr) { return free(ptr); }
335
  static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); }
336
  static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); }
337
#else
338
206k
  static inline void* malloc(size_t size) { return std::malloc(size); }
339
206k
  static inline void free(void* ptr) { return std::free(ptr); }
340
#endif
341
#else
342
  // Debug versions when running under the Relacy race detector (ignore
343
  // these in user code)
344
  static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); }
345
  static inline void free(void* ptr) { return rl::rl_free(ptr, $); }
346
#endif
347
};
348
349
350
// When producing or consuming many elements, the most efficient way is to:
351
//    1) Use one of the bulk-operation methods of the queue with a token
352
//    2) Failing that, use the bulk-operation methods without a token
353
//    3) Failing that, create a token and use that with the single-item methods
354
//    4) Failing that, use the single-parameter methods of the queue
355
// Having said that, don't create tokens willy-nilly -- ideally there should be
356
// a maximum of one token per thread (of each kind).
357
struct ProducerToken;
358
struct ConsumerToken;
359
360
template<typename T, typename Traits> class ConcurrentQueue;
361
template<typename T, typename Traits> class BlockingConcurrentQueue;
362
class ConcurrentQueueTests;
363
364
365
namespace details
366
{
367
  struct ConcurrentQueueProducerTypelessBase
368
  {
369
    ConcurrentQueueProducerTypelessBase* next;
370
    std::atomic<bool> inactive;
371
    ProducerToken* token;
372
    
373
    ConcurrentQueueProducerTypelessBase()
374
41.1k
      : next(nullptr), inactive(false), token(nullptr)
375
41.1k
    {
376
41.1k
    }
377
  };
378
  
379
  template<bool use32> struct _hash_32_or_64 {
380
    static inline std::uint32_t hash(std::uint32_t h)
381
    {
382
      // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
383
      // Since the thread ID is already unique, all we really want to do is propagate that
384
      // uniqueness evenly across all the bits, so that we can use a subset of the bits while
385
      // reducing collisions significantly
386
      h ^= h >> 16;
387
      h *= 0x85ebca6b;
388
      h ^= h >> 13;
389
      h *= 0xc2b2ae35;
390
      return h ^ (h >> 16);
391
    }
392
  };
393
  template<> struct _hash_32_or_64<1> {
394
    static inline std::uint64_t hash(std::uint64_t h)
395
218k
    {
396
218k
      h ^= h >> 33;
397
218k
      h *= 0xff51afd7ed558ccd;
398
218k
      h ^= h >> 33;
399
218k
      h *= 0xc4ceb9fe1a85ec53;
400
218k
      return h ^ (h >> 33);
401
218k
    }
402
  };
403
  template<std::size_t size> struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> {  };
404
  
405
  static inline size_t hash_thread_id(thread_id_t id)
406
218k
  {
407
218k
    static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values");
408
218k
    return static_cast<size_t>(hash_32_or_64<sizeof(thread_id_converter<thread_id_t>::thread_id_hash_t)>::hash(
409
218k
      thread_id_converter<thread_id_t>::prehash(id)));
410
218k
  }
Unexecuted instantiation: ub_duckdb_parallel.cpp:duckdb_moodycamel::details::hash_thread_id(unsigned long)
ub_duckdb_storage_buffer.cpp:duckdb_moodycamel::details::hash_thread_id(unsigned long)
Line
Count
Source
406
218k
  {
407
218k
    static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values");
408
218k
    return static_cast<size_t>(hash_32_or_64<sizeof(thread_id_converter<thread_id_t>::thread_id_hash_t)>::hash(
409
218k
      thread_id_converter<thread_id_t>::prehash(id)));
410
218k
  }
Unexecuted instantiation: ub_duckdb_storage.cpp:duckdb_moodycamel::details::hash_thread_id(unsigned long)
411
  
412
  template<typename T>
413
  static inline bool circular_less_than(T a, T b)
414
167M
  {
415
#ifdef _MSC_VER
416
#pragma warning(push)
417
#pragma warning(disable: 4554)
418
#endif
419
167M
    static_assert(std::is_integral<T>::value && !std::numeric_limits<T>::is_signed, "circular_less_than is intended to be used only with unsigned integer types");
420
167M
    return static_cast<T>(a - b) > static_cast<T>(static_cast<T>(1) << static_cast<T>(sizeof(T) * CHAR_BIT - 1));
421
#ifdef _MSC_VER
422
#pragma warning(pop)
423
#endif
424
167M
  }
ub_duckdb_parallel.cpp:bool duckdb_moodycamel::details::circular_less_than<unsigned long>(unsigned long, unsigned long)
Line
Count
Source
414
167M
  {
415
#ifdef _MSC_VER
416
#pragma warning(push)
417
#pragma warning(disable: 4554)
418
#endif
419
167M
    static_assert(std::is_integral<T>::value && !std::numeric_limits<T>::is_signed, "circular_less_than is intended to be used only with unsigned integer types");
420
167M
    return static_cast<T>(a - b) > static_cast<T>(static_cast<T>(1) << static_cast<T>(sizeof(T) * CHAR_BIT - 1));
421
#ifdef _MSC_VER
422
#pragma warning(pop)
423
#endif
424
167M
  }
ub_duckdb_storage_buffer.cpp:bool duckdb_moodycamel::details::circular_less_than<unsigned long>(unsigned long, unsigned long)
Line
Count
Source
414
33.5k
  {
415
#ifdef _MSC_VER
416
#pragma warning(push)
417
#pragma warning(disable: 4554)
418
#endif
419
33.5k
    static_assert(std::is_integral<T>::value && !std::numeric_limits<T>::is_signed, "circular_less_than is intended to be used only with unsigned integer types");
420
33.5k
    return static_cast<T>(a - b) > static_cast<T>(static_cast<T>(1) << static_cast<T>(sizeof(T) * CHAR_BIT - 1));
421
#ifdef _MSC_VER
422
#pragma warning(pop)
423
#endif
424
33.5k
  }
Unexecuted instantiation: ub_duckdb_storage.cpp:bool duckdb_moodycamel::details::circular_less_than<unsigned long>(unsigned long, unsigned long)
425
  
426
  template<typename U>
427
  static inline char* align_for(char* ptr)
428
74.1k
  {
429
74.1k
    const std::size_t alignment = std::alignment_of<U>::value;
430
74.1k
    return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
431
74.1k
  }
ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::BlockIndexEntry>(char*)
Line
Count
Source
428
9.65k
  {
429
9.65k
    const std::size_t alignment = std::alignment_of<U>::value;
430
9.65k
    return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
431
9.65k
  }
Unexecuted instantiation: ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry>(char*)
Unexecuted instantiation: ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry*>(char*)
Unexecuted instantiation: ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(char*)
Unexecuted instantiation: ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(char*)
Unexecuted instantiation: ub_duckdb_parallel.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(char*)
ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducerKVP>(char*)
Line
Count
Source
428
1.36k
  {
429
1.36k
    const std::size_t alignment = std::alignment_of<U>::value;
430
1.36k
    return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
431
1.36k
  }
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::BlockIndexEntry>(char*)
ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry>(char*)
Line
Count
Source
428
31.5k
  {
429
31.5k
    const std::size_t alignment = std::alignment_of<U>::value;
430
31.5k
    return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
431
31.5k
  }
ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry*>(char*)
Line
Count
Source
428
31.5k
  {
429
31.5k
    const std::size_t alignment = std::alignment_of<U>::value;
430
31.5k
    return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
431
31.5k
  }
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(char*)
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(char*)
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducerKVP>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::BlockIndexEntry>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry*>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(char*)
Unexecuted instantiation: ub_duckdb_storage.cpp:char* duckdb_moodycamel::details::align_for<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(char*)
432
433
  template<typename T>
434
  static inline T ceil_to_pow_2(T x)
435
9.65k
  {
436
9.65k
    static_assert(std::is_integral<T>::value && !std::numeric_limits<T>::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types");
437
438
    // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
439
9.65k
    --x;
440
9.65k
    x |= x >> 1;
441
9.65k
    x |= x >> 2;
442
9.65k
    x |= x >> 4;
443
38.6k
    for (std::size_t i = 1; i < sizeof(T); i <<= 1) {
444
28.9k
      x |= x >> (i << 3);
445
28.9k
    }
446
9.65k
    ++x;
447
9.65k
    return x;
448
9.65k
  }
ub_duckdb_parallel.cpp:unsigned long duckdb_moodycamel::details::ceil_to_pow_2<unsigned long>(unsigned long)
Line
Count
Source
435
9.65k
  {
436
9.65k
    static_assert(std::is_integral<T>::value && !std::numeric_limits<T>::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types");
437
438
    // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
439
9.65k
    --x;
440
9.65k
    x |= x >> 1;
441
9.65k
    x |= x >> 2;
442
9.65k
    x |= x >> 4;
443
38.6k
    for (std::size_t i = 1; i < sizeof(T); i <<= 1) {
444
28.9k
      x |= x >> (i << 3);
445
28.9k
    }
446
9.65k
    ++x;
447
9.65k
    return x;
448
9.65k
  }
Unexecuted instantiation: ub_duckdb_storage_buffer.cpp:unsigned long duckdb_moodycamel::details::ceil_to_pow_2<unsigned long>(unsigned long)
Unexecuted instantiation: ub_duckdb_storage.cpp:unsigned long duckdb_moodycamel::details::ceil_to_pow_2<unsigned long>(unsigned long)
449
  
450
  template<typename T>
451
  static inline void swap_relaxed(std::atomic<T>& left, std::atomic<T>& right)
452
  {
453
    T temp = std::move(left.load(std::memory_order_relaxed));
454
    left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed);
455
    right.store(std::move(temp), std::memory_order_relaxed);
456
  }
457
  
458
  template<typename T>
459
  static inline T const& nomove(T const& x)
460
  {
461
    return x;
462
  }
463
  
464
  template<bool Enable>
465
  struct nomove_if
466
  {
467
    template<typename T>
468
    static inline T const& eval(T const& x)
469
    {
470
      return x;
471
    }
472
  };
473
  
474
  template<>
475
  struct nomove_if<false>
476
  {
477
    template<typename U>
478
    static inline auto eval(U&& x)
479
      -> decltype(std::forward<U>(x))
480
0
    {
481
0
      return std::forward<U>(x);
482
0
    }
Unexecuted instantiation: _ZN17duckdb_moodycamel7details9nomove_ifILb0EE4evalIN6duckdb10shared_ptrINS4_4TaskELb1EEEEEDTclsr3stdE7forwardIT_Efp_EEOS8_
Unexecuted instantiation: _ZN17duckdb_moodycamel7details9nomove_ifILb0EE4evalIRN6duckdb18BufferEvictionNodeEEEDTclsr3stdE7forwardIT_Efp_EEOS7_
Unexecuted instantiation: _ZN17duckdb_moodycamel7details9nomove_ifILb0EE4evalIRjEEDTclsr3stdE7forwardIT_Efp_EEOS5_
483
  };
484
  
485
  template<typename It>
486
  static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it)
487
  {
488
    return *it;
489
  }
490
  
491
#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
492
  template<typename T> struct is_trivially_destructible : std::is_trivially_destructible<T> { };
493
#else
494
  template<typename T> struct is_trivially_destructible : std::has_trivial_destructor<T> { };
495
#endif
496
  
497
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
498
#ifdef MCDBGQ_USE_RELACY
499
  typedef RelacyThreadExitListener ThreadExitListener;
500
  typedef RelacyThreadExitNotifier ThreadExitNotifier;
501
#else
502
  struct ThreadExitListener
503
  {
504
    typedef void (*callback_t)(void*);
505
    callback_t callback;
506
    void* userData;
507
    
508
    ThreadExitListener* next;   // reserved for use by the ThreadExitNotifier
509
  };
510
  
511
  
512
  class ThreadExitNotifier
513
  {
514
  public:
515
    static void subscribe(ThreadExitListener* listener)
516
    {
517
      auto& tlsInst = instance();
518
      listener->next = tlsInst.tail;
519
      tlsInst.tail = listener;
520
    }
521
    
522
    static void unsubscribe(ThreadExitListener* listener)
523
    {
524
      auto& tlsInst = instance();
525
      ThreadExitListener** prev = &tlsInst.tail;
526
      for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) {
527
        if (ptr == listener) {
528
          *prev = ptr->next;
529
          break;
530
        }
531
        prev = &ptr->next;
532
      }
533
    }
534
    
535
  private:
536
    ThreadExitNotifier() : tail(nullptr) { }
537
    ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
538
    ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION;
539
    
540
    ~ThreadExitNotifier()
541
    {
542
      // This thread is about to exit, let everyone know!
543
      assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined.");
544
      for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) {
545
        ptr->callback(ptr->userData);
546
      }
547
    }
548
    
549
    // Thread-local
550
    static inline ThreadExitNotifier& instance()
551
    {
552
      static thread_local ThreadExitNotifier notifier;
553
      return notifier;
554
    }
555
    
556
  private:
557
    ThreadExitListener* tail;
558
  };
559
#endif
560
#endif
561
  
562
  template<typename T> struct static_is_lock_free_num { enum { value = 0 }; };
563
  template<> struct static_is_lock_free_num<signed char> { enum { value = ATOMIC_CHAR_LOCK_FREE }; };
564
  template<> struct static_is_lock_free_num<short> { enum { value = ATOMIC_SHORT_LOCK_FREE }; };
565
  template<> struct static_is_lock_free_num<int> { enum { value = ATOMIC_INT_LOCK_FREE }; };
566
  template<> struct static_is_lock_free_num<long> { enum { value = ATOMIC_LONG_LOCK_FREE }; };
567
  template<> struct static_is_lock_free_num<long long> { enum { value = ATOMIC_LLONG_LOCK_FREE }; };
568
  template<typename T> struct static_is_lock_free : static_is_lock_free_num<typename std::make_signed<T>::type> {  };
569
  template<> struct static_is_lock_free<bool> { enum { value = ATOMIC_BOOL_LOCK_FREE }; };
570
  template<typename U> struct static_is_lock_free<U*> { enum { value = ATOMIC_POINTER_LOCK_FREE }; };
571
}
572
573
574
struct ProducerToken
575
{
576
  template<typename T, typename Traits>
577
  explicit ProducerToken(ConcurrentQueue<T, Traits>& queue);
578
  
579
  template<typename T, typename Traits>
580
  explicit ProducerToken(BlockingConcurrentQueue<T, Traits>& queue);
581
  
582
  ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
583
    : producer(other.producer)
584
0
  {
585
0
    other.producer = nullptr;
586
0
    if (producer != nullptr) {
587
0
      producer->token = this;
588
0
    }
589
0
  }
590
  
591
  inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT
592
0
  {
593
0
    swap(other);
594
0
    return *this;
595
0
  }
596
  
597
  void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT
598
0
  {
599
0
    std::swap(producer, other.producer);
600
0
    if (producer != nullptr) {
601
0
      producer->token = this;
602
0
    }
603
0
    if (other.producer != nullptr) {
604
0
      other.producer->token = &other;
605
0
    }
606
0
  }
607
  
608
  // A token is always valid unless:
609
  //     1) Memory allocation failed during construction
610
  //     2) It was moved via the move constructor
611
  //        (Note: assignment does a swap, leaving both potentially valid)
612
  //     3) The associated queue was destroyed
613
  // Note that if valid() returns true, that only indicates
614
  // that the token is valid for use with a specific queue,
615
  // but not which one; that's up to the user to track.
616
0
  inline bool valid() const { return producer != nullptr; }
617
  
618
  ~ProducerToken()
619
86.4k
  {
620
86.4k
    if (producer != nullptr) {
621
86.4k
      producer->token = nullptr;
622
86.4k
      producer->inactive.store(true, std::memory_order_release);
623
86.4k
    }
624
86.4k
  }
625
  
626
  // Disable copying and assignment
627
  ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
628
  ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION;
629
  
630
private:
631
  template<typename T, typename Traits> friend class ConcurrentQueue;
632
  friend class ConcurrentQueueTests;
633
  
634
protected:
635
  details::ConcurrentQueueProducerTypelessBase* producer;
636
};
637
638
639
struct ConsumerToken
640
{
641
  template<typename T, typename Traits>
642
  explicit ConsumerToken(ConcurrentQueue<T, Traits>& q);
643
  
644
  template<typename T, typename Traits>
645
  explicit ConsumerToken(BlockingConcurrentQueue<T, Traits>& q);
646
  
647
  ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
648
    : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer)
649
0
  {
650
0
  }
651
  
652
  inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT
653
0
  {
654
0
    swap(other);
655
0
    return *this;
656
0
  }
657
  
658
  void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT
659
0
  {
660
0
    std::swap(initialOffset, other.initialOffset);
661
0
    std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset);
662
0
    std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent);
663
0
    std::swap(currentProducer, other.currentProducer);
664
0
    std::swap(desiredProducer, other.desiredProducer);
665
0
  }
666
  
667
  // Disable copying and assignment
668
  ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
669
  ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION;
670
671
private:
672
  template<typename T, typename Traits> friend class ConcurrentQueue;
673
  friend class ConcurrentQueueTests;
674
  
675
private: // but shared with ConcurrentQueue
676
  std::uint32_t initialOffset;
677
  std::uint32_t lastKnownGlobalOffset;
678
  std::uint32_t itemsConsumedFromCurrent;
679
  details::ConcurrentQueueProducerTypelessBase* currentProducer;
680
  details::ConcurrentQueueProducerTypelessBase* desiredProducer;
681
};
682
683
// Need to forward-declare this swap because it's in a namespace.
684
// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces
685
template<typename T, typename Traits>
686
inline void swap(typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& a, typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT;
687
688
689
template<typename T, typename Traits = ConcurrentQueueDefaultTraits>
690
class ConcurrentQueue
691
{
692
public:
693
  typedef ::duckdb_moodycamel::ProducerToken producer_token_t;
694
  typedef ::duckdb_moodycamel::ConsumerToken consumer_token_t;
695
  
696
  typedef typename Traits::index_t index_t;
697
  typedef typename Traits::size_t size_t;
698
  
699
  static const size_t BLOCK_SIZE = static_cast<size_t>(Traits::BLOCK_SIZE);
700
  static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast<size_t>(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD);
701
  static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast<size_t>(Traits::EXPLICIT_INITIAL_INDEX_SIZE);
702
  static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast<size_t>(Traits::IMPLICIT_INITIAL_INDEX_SIZE);
703
  static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast<size_t>(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE);
704
  static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast<std::uint32_t>(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE);
705
#ifdef _MSC_VER
706
#pragma warning(push)
707
#pragma warning(disable: 4307)    // + integral constant overflow (that's what the ternary expression is for!)
708
#pragma warning(disable: 4309)    // static_cast: Truncation of constant value
709
#endif
710
  static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max<size_t>::value - static_cast<size_t>(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max<size_t>::value : ((static_cast<size_t>(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE);
711
#ifdef _MSC_VER
712
#pragma warning(pop)
713
#endif
714
715
  static_assert(!std::numeric_limits<size_t>::is_signed && std::is_integral<size_t>::value, "Traits::size_t must be an unsigned integral type");
716
  static_assert(!std::numeric_limits<index_t>::is_signed && std::is_integral<index_t>::value, "Traits::index_t must be an unsigned integral type");
717
  static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t");
718
  static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)");
719
  static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)");
720
  static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)");
721
  static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)");
722
  static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2");
723
  static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)");
724
725
public:
726
  // Creates a queue with at least `capacity` element slots; note that the
727
  // actual number of elements that can be inserted without additional memory
728
  // allocation depends on the number of producers and the block size (e.g. if
729
  // the block size is equal to `capacity`, only a single block will be allocated
730
  // up-front, which means only a single producer will be able to enqueue elements
731
  // without an extra allocation -- blocks aren't shared between producers).
732
  // This method is not thread safe -- it is up to the user to ensure that the
733
  // queue is fully constructed before it starts being used by other threads (this
734
  // includes making the memory effects of construction visible, possibly with a
735
  // memory barrier).
736
  explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
737
98.0k
    : producerListTail(nullptr),
738
98.0k
    producerCount(0),
739
98.0k
    initialBlockPoolIndex(0),
740
98.0k
    nextExplicitConsumerId(0),
741
98.0k
    globalExplicitConsumerOffset(0)
742
98.0k
  {
743
98.0k
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
744
98.0k
    populate_initial_implicit_producer_hash();
745
98.0k
    populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
746
    
747
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
748
    // Track all the producers using a fully-resolved typed list for
749
    // each kind; this makes it possible to debug them starting from
750
    // the root queue object (otherwise wacky casts are needed that
751
    // don't compile in the debugger's expression evaluator).
752
    explicitProducers.store(nullptr, std::memory_order_relaxed);
753
    implicitProducers.store(nullptr, std::memory_order_relaxed);
754
#endif
755
98.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ConcurrentQueue(unsigned long)
Line
Count
Source
737
8.91k
    : producerListTail(nullptr),
738
8.91k
    producerCount(0),
739
8.91k
    initialBlockPoolIndex(0),
740
8.91k
    nextExplicitConsumerId(0),
741
8.91k
    globalExplicitConsumerOffset(0)
742
8.91k
  {
743
8.91k
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
744
8.91k
    populate_initial_implicit_producer_hash();
745
8.91k
    populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
746
    
747
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
748
    // Track all the producers using a fully-resolved typed list for
749
    // each kind; this makes it possible to debug them starting from
750
    // the root queue object (otherwise wacky casts are needed that
751
    // don't compile in the debugger's expression evaluator).
752
    explicitProducers.store(nullptr, std::memory_order_relaxed);
753
    implicitProducers.store(nullptr, std::memory_order_relaxed);
754
#endif
755
8.91k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ConcurrentQueue(unsigned long)
Line
Count
Source
737
71.2k
    : producerListTail(nullptr),
738
71.2k
    producerCount(0),
739
71.2k
    initialBlockPoolIndex(0),
740
71.2k
    nextExplicitConsumerId(0),
741
71.2k
    globalExplicitConsumerOffset(0)
742
71.2k
  {
743
71.2k
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
744
71.2k
    populate_initial_implicit_producer_hash();
745
71.2k
    populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
746
    
747
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
748
    // Track all the producers using a fully-resolved typed list for
749
    // each kind; this makes it possible to debug them starting from
750
    // the root queue object (otherwise wacky casts are needed that
751
    // don't compile in the debugger's expression evaluator).
752
    explicitProducers.store(nullptr, std::memory_order_relaxed);
753
    implicitProducers.store(nullptr, std::memory_order_relaxed);
754
#endif
755
71.2k
  }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ConcurrentQueue(unsigned long)
Line
Count
Source
737
17.8k
    : producerListTail(nullptr),
738
17.8k
    producerCount(0),
739
17.8k
    initialBlockPoolIndex(0),
740
17.8k
    nextExplicitConsumerId(0),
741
17.8k
    globalExplicitConsumerOffset(0)
742
17.8k
  {
743
17.8k
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
744
17.8k
    populate_initial_implicit_producer_hash();
745
17.8k
    populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1));
746
    
747
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
748
    // Track all the producers using a fully-resolved typed list for
749
    // each kind; this makes it possible to debug them starting from
750
    // the root queue object (otherwise wacky casts are needed that
751
    // don't compile in the debugger's expression evaluator).
752
    explicitProducers.store(nullptr, std::memory_order_relaxed);
753
    implicitProducers.store(nullptr, std::memory_order_relaxed);
754
#endif
755
17.8k
  }
756
  
757
  // Computes the correct amount of pre-allocated blocks for you based
758
  // on the minimum number of elements you want available at any given
759
  // time, and the maximum concurrent number of each type of producer.
760
  ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
761
    : producerListTail(nullptr),
762
    producerCount(0),
763
    initialBlockPoolIndex(0),
764
    nextExplicitConsumerId(0),
765
    globalExplicitConsumerOffset(0)
766
  {
767
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
768
    populate_initial_implicit_producer_hash();
769
    size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers);
770
    populate_initial_block_list(blocks);
771
    
772
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
773
    explicitProducers.store(nullptr, std::memory_order_relaxed);
774
    implicitProducers.store(nullptr, std::memory_order_relaxed);
775
#endif
776
  }
777
  
778
  // Note: The queue should not be accessed concurrently while it's
779
  // being deleted. It's up to the user to synchronize this.
780
  // This method is not thread safe.
781
  ~ConcurrentQueue()
782
98.0k
  {
783
    // Destroy producers
784
98.0k
    auto ptr = producerListTail.load(std::memory_order_relaxed);
785
139k
    while (ptr != nullptr) {
786
41.2k
      auto next = ptr->next_prod();
787
41.2k
      if (ptr->token != nullptr) {
788
0
        ptr->token->producer = nullptr;
789
0
      }
790
41.2k
      destroy(ptr);
791
41.2k
      ptr = next;
792
41.2k
    }
793
    
794
    // Destroy implicit producer hash tables
795
98.0k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
796
98.0k
      auto hash = implicitProducerHash.load(std::memory_order_relaxed);
797
197k
      while (hash != nullptr) {
798
99.3k
        auto prev = hash->prev;
799
99.3k
        if (prev != nullptr) {   // The last hash is part of this object and was not allocated dynamically
800
111k
          for (size_t i = 0; i != hash->capacity; ++i) {
801
110k
            hash->entries[i].~ImplicitProducerKVP();
802
110k
          }
803
1.36k
          hash->~ImplicitProducerHash();
804
1.36k
          (Traits::free)(hash);
805
1.36k
        }
806
99.3k
        hash = prev;
807
99.3k
      }
808
98.0k
    }
809
    
810
    // Destroy global free list
811
98.0k
    auto block = freeList.head_unsafe();
812
142k
    while (block != nullptr) {
813
44.4k
      auto next = block->freeListNext.load(std::memory_order_relaxed);
814
44.4k
      if (block->dynamicallyAllocated) {
815
25.0k
        destroy(block);
816
25.0k
      }
817
44.4k
      block = next;
818
44.4k
    }
819
    
820
    // Destroy initial free list
821
98.0k
    destroy_array(initialBlockPool, initialBlockPoolSize);
822
98.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::~ConcurrentQueue()
Line
Count
Source
782
8.91k
  {
783
    // Destroy producers
784
8.91k
    auto ptr = producerListTail.load(std::memory_order_relaxed);
785
18.5k
    while (ptr != nullptr) {
786
9.65k
      auto next = ptr->next_prod();
787
9.65k
      if (ptr->token != nullptr) {
788
0
        ptr->token->producer = nullptr;
789
0
      }
790
9.65k
      destroy(ptr);
791
9.65k
      ptr = next;
792
9.65k
    }
793
    
794
    // Destroy implicit producer hash tables
795
8.91k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
796
8.91k
      auto hash = implicitProducerHash.load(std::memory_order_relaxed);
797
17.8k
      while (hash != nullptr) {
798
8.91k
        auto prev = hash->prev;
799
8.91k
        if (prev != nullptr) {   // The last hash is part of this object and was not allocated dynamically
800
0
          for (size_t i = 0; i != hash->capacity; ++i) {
801
0
            hash->entries[i].~ImplicitProducerKVP();
802
0
          }
803
0
          hash->~ImplicitProducerHash();
804
0
          (Traits::free)(hash);
805
0
        }
806
8.91k
        hash = prev;
807
8.91k
      }
808
8.91k
    }
809
    
810
    // Destroy global free list
811
8.91k
    auto block = freeList.head_unsafe();
812
19.7k
    while (block != nullptr) {
813
10.8k
      auto next = block->freeListNext.load(std::memory_order_relaxed);
814
10.8k
      if (block->dynamicallyAllocated) {
815
0
        destroy(block);
816
0
      }
817
10.8k
      block = next;
818
10.8k
    }
819
    
820
    // Destroy initial free list
821
8.91k
    destroy_array(initialBlockPool, initialBlockPoolSize);
822
8.91k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::~ConcurrentQueue()
Line
Count
Source
782
71.2k
  {
783
    // Destroy producers
784
71.2k
    auto ptr = producerListTail.load(std::memory_order_relaxed);
785
102k
    while (ptr != nullptr) {
786
31.5k
      auto next = ptr->next_prod();
787
31.5k
      if (ptr->token != nullptr) {
788
0
        ptr->token->producer = nullptr;
789
0
      }
790
31.5k
      destroy(ptr);
791
31.5k
      ptr = next;
792
31.5k
    }
793
    
794
    // Destroy implicit producer hash tables
795
71.2k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
796
71.2k
      auto hash = implicitProducerHash.load(std::memory_order_relaxed);
797
143k
      while (hash != nullptr) {
798
72.6k
        auto prev = hash->prev;
799
72.6k
        if (prev != nullptr) {   // The last hash is part of this object and was not allocated dynamically
800
111k
          for (size_t i = 0; i != hash->capacity; ++i) {
801
110k
            hash->entries[i].~ImplicitProducerKVP();
802
110k
          }
803
1.36k
          hash->~ImplicitProducerHash();
804
1.36k
          (Traits::free)(hash);
805
1.36k
        }
806
72.6k
        hash = prev;
807
72.6k
      }
808
71.2k
    }
809
    
810
    // Destroy global free list
811
71.2k
    auto block = freeList.head_unsafe();
812
104k
    while (block != nullptr) {
813
33.5k
      auto next = block->freeListNext.load(std::memory_order_relaxed);
814
33.5k
      if (block->dynamicallyAllocated) {
815
25.0k
        destroy(block);
816
25.0k
      }
817
33.5k
      block = next;
818
33.5k
    }
819
    
820
    // Destroy initial free list
821
71.2k
    destroy_array(initialBlockPool, initialBlockPoolSize);
822
71.2k
  }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::~ConcurrentQueue()
Line
Count
Source
782
17.8k
  {
783
    // Destroy producers
784
17.8k
    auto ptr = producerListTail.load(std::memory_order_relaxed);
785
17.8k
    while (ptr != nullptr) {
786
0
      auto next = ptr->next_prod();
787
0
      if (ptr->token != nullptr) {
788
0
        ptr->token->producer = nullptr;
789
0
      }
790
0
      destroy(ptr);
791
0
      ptr = next;
792
0
    }
793
    
794
    // Destroy implicit producer hash tables
795
17.8k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) {
796
17.8k
      auto hash = implicitProducerHash.load(std::memory_order_relaxed);
797
35.6k
      while (hash != nullptr) {
798
17.8k
        auto prev = hash->prev;
799
17.8k
        if (prev != nullptr) {   // The last hash is part of this object and was not allocated dynamically
800
0
          for (size_t i = 0; i != hash->capacity; ++i) {
801
0
            hash->entries[i].~ImplicitProducerKVP();
802
0
          }
803
0
          hash->~ImplicitProducerHash();
804
0
          (Traits::free)(hash);
805
0
        }
806
17.8k
        hash = prev;
807
17.8k
      }
808
17.8k
    }
809
    
810
    // Destroy global free list
811
17.8k
    auto block = freeList.head_unsafe();
812
17.8k
    while (block != nullptr) {
813
0
      auto next = block->freeListNext.load(std::memory_order_relaxed);
814
0
      if (block->dynamicallyAllocated) {
815
0
        destroy(block);
816
0
      }
817
0
      block = next;
818
0
    }
819
    
820
    // Destroy initial free list
821
17.8k
    destroy_array(initialBlockPool, initialBlockPoolSize);
822
17.8k
  }
823
824
  // Disable copying and copy assignment
825
  ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
826
  ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION;
827
  
828
  // Moving is supported, but note that it is *not* a thread-safe operation.
829
  // Nobody can use the queue while it's being moved, and the memory effects
830
  // of that move must be propagated to other threads before they can use it.
831
  // Note: When a queue is moved, its tokens are still valid but can only be
832
  // used with the destination queue (i.e. semantically they are moved along
833
  // with the queue itself).
834
  ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
835
    : producerListTail(other.producerListTail.load(std::memory_order_relaxed)),
836
    producerCount(other.producerCount.load(std::memory_order_relaxed)),
837
    initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)),
838
    initialBlockPool(other.initialBlockPool),
839
    initialBlockPoolSize(other.initialBlockPoolSize),
840
    freeList(std::move(other.freeList)),
841
    nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)),
842
    globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed))
843
  {
844
    // Move the other one into this, and leave the other one as an empty queue
845
    implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
846
    populate_initial_implicit_producer_hash();
847
    swap_implicit_producer_hashes(other);
848
    
849
    other.producerListTail.store(nullptr, std::memory_order_relaxed);
850
    other.producerCount.store(0, std::memory_order_relaxed);
851
    other.nextExplicitConsumerId.store(0, std::memory_order_relaxed);
852
    other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed);
853
    
854
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
855
    explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed);
856
    other.explicitProducers.store(nullptr, std::memory_order_relaxed);
857
    implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed);
858
    other.implicitProducers.store(nullptr, std::memory_order_relaxed);
859
#endif
860
    
861
    other.initialBlockPoolIndex.store(0, std::memory_order_relaxed);
862
    other.initialBlockPoolSize = 0;
863
    other.initialBlockPool = nullptr;
864
    
865
    reown_producers();
866
  }
867
  
868
  inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT
869
  {
870
    return swap_internal(other);
871
  }
872
  
873
  // Swaps this queue's state with the other's. Not thread-safe.
874
  // Swapping two queues does not invalidate their tokens, however
875
  // the tokens that were created for one queue must be used with
876
  // only the swapped queue (i.e. the tokens are tied to the
877
  // queue's movable state, not the object itself).
878
  inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT
879
  {
880
    swap_internal(other);
881
  }
882
  
883
private:
884
  ConcurrentQueue& swap_internal(ConcurrentQueue& other)
885
  {
886
    if (this == &other) {
887
      return *this;
888
    }
889
    
890
    details::swap_relaxed(producerListTail, other.producerListTail);
891
    details::swap_relaxed(producerCount, other.producerCount);
892
    details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex);
893
    std::swap(initialBlockPool, other.initialBlockPool);
894
    std::swap(initialBlockPoolSize, other.initialBlockPoolSize);
895
    freeList.swap(other.freeList);
896
    details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId);
897
    details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset);
898
    
899
    swap_implicit_producer_hashes(other);
900
    
901
    reown_producers();
902
    other.reown_producers();
903
    
904
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
905
    details::swap_relaxed(explicitProducers, other.explicitProducers);
906
    details::swap_relaxed(implicitProducers, other.implicitProducers);
907
#endif
908
    
909
    return *this;
910
  }
911
  
912
public:
913
  // Enqueues a single item (by copying it).
914
  // Allocates memory if required. Only fails if memory allocation fails (or implicit
915
  // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
916
  // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
917
  // Thread-safe.
918
  inline bool enqueue(T const& item)
919
  {
920
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
921
    else return inner_enqueue<CanAlloc>(item);
922
  }
923
  
924
  // Enqueues a single item (by moving it, if possible).
925
  // Allocates memory if required. Only fails if memory allocation fails (or implicit
926
  // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0,
927
  // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
928
  // Thread-safe.
929
  inline bool enqueue(T&& item)
930
218k
  {
931
218k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
932
218k
    else return inner_enqueue<CanAlloc>(std::move(item));
933
218k
  }
934
  
935
  // Enqueues a single item (by copying it) using an explicit producer token.
936
  // Allocates memory if required. Only fails if memory allocation fails (or
937
  // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
938
  // Thread-safe.
939
  inline bool enqueue(producer_token_t const& token, T const& item)
940
  {
941
    return inner_enqueue<CanAlloc>(token, item);
942
  }
943
  
944
  // Enqueues a single item (by moving it, if possible) using an explicit producer token.
945
  // Allocates memory if required. Only fails if memory allocation fails (or
946
  // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
947
  // Thread-safe.
948
  inline bool enqueue(producer_token_t const& token, T&& item)
949
3.60k
  {
950
3.60k
    return inner_enqueue<CanAlloc>(token, std::move(item));
951
3.60k
  }
952
  
953
  // Enqueues several items.
954
  // Allocates memory if required. Only fails if memory allocation fails (or
955
  // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
956
  // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
957
  // Note: Use std::make_move_iterator if the elements should be moved instead of copied.
958
  // Thread-safe.
959
  template<typename It>
960
  bool enqueue_bulk(It itemFirst, size_t count)
961
0
  {
962
0
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
963
0
    else return inner_enqueue_bulk<CanAlloc>(itemFirst, count);
964
0
  }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::enqueue_bulk<std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::enqueue_bulk<std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::enqueue_bulk<unsigned int*>(unsigned int*, unsigned long)
965
  
966
  // Enqueues several items using an explicit producer token.
967
  // Allocates memory if required. Only fails if memory allocation fails
968
  // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed).
969
  // Note: Use std::make_move_iterator if the elements should be moved
970
  // instead of copied.
971
  // Thread-safe.
972
  template<typename It>
973
  bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
974
855k
  {
975
855k
    return inner_enqueue_bulk<CanAlloc>(token, itemFirst, count);
976
855k
  }
977
  
978
  // Enqueues a single item (by copying it).
979
  // Does not allocate memory. Fails if not enough room to enqueue (or implicit
980
  // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE
981
  // is 0).
982
  // Thread-safe.
983
  inline bool try_enqueue(T const& item)
984
  {
985
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
986
    else return inner_enqueue<CannotAlloc>(item);
987
  }
988
  
989
  // Enqueues a single item (by moving it, if possible).
990
  // Does not allocate memory (except for one-time implicit producer).
991
  // Fails if not enough room to enqueue (or implicit production is
992
  // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
993
  // Thread-safe.
994
  inline bool try_enqueue(T&& item)
995
  {
996
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
997
    else return inner_enqueue<CannotAlloc>(std::move(item));
998
  }
999
  
1000
  // Enqueues a single item (by copying it) using an explicit producer token.
1001
  // Does not allocate memory. Fails if not enough room to enqueue.
1002
  // Thread-safe.
1003
  inline bool try_enqueue(producer_token_t const& token, T const& item)
1004
  {
1005
    return inner_enqueue<CannotAlloc>(token, item);
1006
  }
1007
  
1008
  // Enqueues a single item (by moving it, if possible) using an explicit producer token.
1009
  // Does not allocate memory. Fails if not enough room to enqueue.
1010
  // Thread-safe.
1011
  inline bool try_enqueue(producer_token_t const& token, T&& item)
1012
  {
1013
    return inner_enqueue<CannotAlloc>(token, std::move(item));
1014
  }
1015
  
1016
  // Enqueues several items.
1017
  // Does not allocate memory (except for one-time implicit producer).
1018
  // Fails if not enough room to enqueue (or implicit production is
1019
  // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0).
1020
  // Note: Use std::make_move_iterator if the elements should be moved
1021
  // instead of copied.
1022
  // Thread-safe.
1023
  template<typename It>
1024
  bool try_enqueue_bulk(It itemFirst, size_t count)
1025
  {
1026
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false;
1027
    else return inner_enqueue_bulk<CannotAlloc>(itemFirst, count);
1028
  }
1029
  
1030
  // Enqueues several items using an explicit producer token.
1031
  // Does not allocate memory. Fails if not enough room to enqueue.
1032
  // Note: Use std::make_move_iterator if the elements should be moved
1033
  // instead of copied.
1034
  // Thread-safe.
1035
  template<typename It>
1036
  bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
1037
  {
1038
    return inner_enqueue_bulk<CannotAlloc>(token, itemFirst, count);
1039
  }
1040
  
1041
  
1042
  
1043
  // Attempts to dequeue from the queue.
1044
  // Returns false if all producer streams appeared empty at the time they
1045
  // were checked (so, the queue is likely but not guaranteed to be empty).
1046
  // Never allocates. Thread-safe.
1047
  template<typename U>
1048
  bool try_dequeue(U& item)
1049
1.27M
  {
1050
    // Instead of simply trying each producer in turn (which could cause needless contention on the first
1051
    // producer), we score them heuristically.
1052
1.27M
    size_t nonEmptyCount = 0;
1053
1.27M
    ProducerBase* best = nullptr;
1054
1.27M
    size_t bestSize = 0;
1055
2.83M
    for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) {
1056
1.55M
      auto size = ptr->size_approx();
1057
1.55M
      if (size > 0) {
1058
596k
        if (size > bestSize) {
1059
596k
          bestSize = size;
1060
596k
          best = ptr;
1061
596k
        }
1062
596k
        ++nonEmptyCount;
1063
596k
      }
1064
1.55M
    }
1065
    
1066
    // If there was at least one non-empty queue but it appears empty at the time
1067
    // we try to dequeue from it, we need to make sure every queue's been tried
1068
1.27M
    if (nonEmptyCount > 0) {
1069
596k
      if ((details::likely)(best->dequeue(item))) {
1070
593k
        return true;
1071
593k
      }
1072
9.55k
      for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1073
6.26k
        if (ptr != best && ptr->dequeue(item)) {
1074
0
          return true;
1075
0
        }
1076
6.26k
      }
1077
3.29k
    }
1078
683k
    return false;
1079
1.27M
  }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_dequeue<duckdb::shared_ptr<duckdb::Task, true> >(duckdb::shared_ptr<duckdb::Task, true>&)
Line
Count
Source
1049
1.27M
  {
1050
    // Instead of simply trying each producer in turn (which could cause needless contention on the first
1051
    // producer), we score them heuristically.
1052
1.27M
    size_t nonEmptyCount = 0;
1053
1.27M
    ProducerBase* best = nullptr;
1054
1.27M
    size_t bestSize = 0;
1055
2.83M
    for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) {
1056
1.55M
      auto size = ptr->size_approx();
1057
1.55M
      if (size > 0) {
1058
596k
        if (size > bestSize) {
1059
596k
          bestSize = size;
1060
596k
          best = ptr;
1061
596k
        }
1062
596k
        ++nonEmptyCount;
1063
596k
      }
1064
1.55M
    }
1065
    
1066
    // If there was at least one non-empty queue but it appears empty at the time
1067
    // we try to dequeue from it, we need to make sure every queue's been tried
1068
1.27M
    if (nonEmptyCount > 0) {
1069
596k
      if ((details::likely)(best->dequeue(item))) {
1070
593k
        return true;
1071
593k
      }
1072
9.55k
      for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1073
6.26k
        if (ptr != best && ptr->dequeue(item)) {
1074
0
          return true;
1075
0
        }
1076
6.26k
      }
1077
3.29k
    }
1078
683k
    return false;
1079
1.27M
  }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_dequeue<duckdb::BufferEvictionNode>(duckdb::BufferEvictionNode&)
1080
  
1081
  // Attempts to dequeue from the queue.
1082
  // Returns false if all producer streams appeared empty at the time they
1083
  // were checked (so, the queue is likely but not guaranteed to be empty).
1084
  // This differs from the try_dequeue(item) method in that this one does
1085
  // not attempt to reduce contention by interleaving the order that producer
1086
  // streams are dequeued from. So, using this method can reduce overall throughput
1087
  // under contention, but will give more predictable results in single-threaded
1088
  // consumer scenarios. This is mostly only useful for internal unit tests.
1089
  // Never allocates. Thread-safe.
1090
  template<typename U>
1091
  bool try_dequeue_non_interleaved(U& item)
1092
  {
1093
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1094
      if (ptr->dequeue(item)) {
1095
        return true;
1096
      }
1097
    }
1098
    return false;
1099
  }
1100
  
1101
  // Attempts to dequeue from the queue using an explicit consumer token.
1102
  // Returns false if all producer streams appeared empty at the time they
1103
  // were checked (so, the queue is likely but not guaranteed to be empty).
1104
  // Never allocates. Thread-safe.
1105
  template<typename U>
1106
  bool try_dequeue(consumer_token_t& token, U& item)
1107
  {
1108
    // The idea is roughly as follows:
1109
    // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less
1110
    // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place
1111
    // If there's no items where you're supposed to be, keep moving until you find a producer with some items
1112
    // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it
1113
    
1114
    if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) {
1115
      if (!update_current_producer_after_rotation(token)) {
1116
        return false;
1117
      }
1118
    }
1119
    
1120
    // If there was at least one non-empty queue but it appears empty at the time
1121
    // we try to dequeue from it, we need to make sure every queue's been tried
1122
    if (static_cast<ProducerBase*>(token.currentProducer)->dequeue(item)) {
1123
      if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) {
1124
        globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed);
1125
      }
1126
      return true;
1127
    }
1128
    
1129
    auto tail = producerListTail.load(std::memory_order_acquire);
1130
    auto ptr = static_cast<ProducerBase*>(token.currentProducer)->next_prod();
1131
    if (ptr == nullptr) {
1132
      ptr = tail;
1133
    }
1134
    while (ptr != static_cast<ProducerBase*>(token.currentProducer)) {
1135
      if (ptr->dequeue(item)) {
1136
        token.currentProducer = ptr;
1137
        token.itemsConsumedFromCurrent = 1;
1138
        return true;
1139
      }
1140
      ptr = ptr->next_prod();
1141
      if (ptr == nullptr) {
1142
        ptr = tail;
1143
      }
1144
    }
1145
    return false;
1146
  }
1147
  
1148
  // Attempts to dequeue several elements from the queue.
1149
  // Returns the number of items actually dequeued.
1150
  // Returns 0 if all producer streams appeared empty at the time they
1151
  // were checked (so, the queue is likely but not guaranteed to be empty).
1152
  // Never allocates. Thread-safe.
1153
  template<typename It>
1154
  size_t try_dequeue_bulk(It itemFirst, size_t max)
1155
0
  {
1156
0
    size_t count = 0;
1157
0
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1158
0
      count += ptr->dequeue_bulk(itemFirst, max - count);
1159
0
      if (count == max) {
1160
0
        break;
1161
0
      }
1162
0
    }
1163
0
    return count;
1164
0
  }
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_dequeue_bulk<std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>, unsigned long)
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_dequeue_bulk<std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>, unsigned long)
1165
  
1166
  // Attempts to dequeue several elements from the queue using an explicit consumer token.
1167
  // Returns the number of items actually dequeued.
1168
  // Returns 0 if all producer streams appeared empty at the time they
1169
  // were checked (so, the queue is likely but not guaranteed to be empty).
1170
  // Never allocates. Thread-safe.
1171
  template<typename It>
1172
  size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max)
1173
  {
1174
    if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) {
1175
      if (!update_current_producer_after_rotation(token)) {
1176
        return 0;
1177
      }
1178
    }
1179
    
1180
    size_t count = static_cast<ProducerBase*>(token.currentProducer)->dequeue_bulk(itemFirst, max);
1181
    if (count == max) {
1182
      if ((token.itemsConsumedFromCurrent += static_cast<std::uint32_t>(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) {
1183
        globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed);
1184
      }
1185
      return max;
1186
    }
1187
    token.itemsConsumedFromCurrent += static_cast<std::uint32_t>(count);
1188
    max -= count;
1189
    
1190
    auto tail = producerListTail.load(std::memory_order_acquire);
1191
    auto ptr = static_cast<ProducerBase*>(token.currentProducer)->next_prod();
1192
    if (ptr == nullptr) {
1193
      ptr = tail;
1194
    }
1195
    while (ptr != static_cast<ProducerBase*>(token.currentProducer)) {
1196
      auto dequeued = ptr->dequeue_bulk(itemFirst, max);
1197
      count += dequeued;
1198
      if (dequeued != 0) {
1199
        token.currentProducer = ptr;
1200
        token.itemsConsumedFromCurrent = static_cast<std::uint32_t>(dequeued);
1201
      }
1202
      if (dequeued == max) {
1203
        break;
1204
      }
1205
      max -= dequeued;
1206
      ptr = ptr->next_prod();
1207
      if (ptr == nullptr) {
1208
        ptr = tail;
1209
      }
1210
    }
1211
    return count;
1212
  }
1213
  
1214
  
1215
  
1216
  // Attempts to dequeue from a specific producer's inner queue.
1217
  // If you happen to know which producer you want to dequeue from, this
1218
  // is significantly faster than using the general-case try_dequeue methods.
1219
  // Returns false if the producer's queue appeared empty at the time it
1220
  // was checked (so, the queue is likely but not guaranteed to be empty).
1221
  // Never allocates. Thread-safe.
1222
  template<typename U>
1223
  inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item)
1224
163M
  {
1225
163M
    return static_cast<ExplicitProducer*>(producer.producer)->dequeue(item);
1226
163M
  }
1227
  
1228
  // Attempts to dequeue several elements from a specific producer's inner queue.
1229
  // Returns the number of items actually dequeued.
1230
  // If you happen to know which producer you want to dequeue from, this
1231
  // is significantly faster than using the general-case try_dequeue methods.
1232
  // Returns 0 if the producer's queue appeared empty at the time it
1233
  // was checked (so, the queue is likely but not guaranteed to be empty).
1234
  // Never allocates. Thread-safe.
1235
  template<typename It>
1236
  inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max)
1237
  {
1238
    return static_cast<ExplicitProducer*>(producer.producer)->dequeue_bulk(itemFirst, max);
1239
  }
1240
  
1241
  
1242
  // Returns an estimate of the total number of elements currently in the queue. This
1243
  // estimate is only accurate if the queue has completely stabilized before it is called
1244
  // (i.e. all enqueue and dequeue operations have completed and their memory effects are
1245
  // visible on the calling thread, and no further operations start while this method is
1246
  // being called).
1247
  // Thread-safe.
1248
  size_t size_approx() const
1249
0
  {
1250
0
    size_t size = 0;
1251
0
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1252
0
      size += ptr->size_approx();
1253
0
    }
1254
0
    return size;
1255
0
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::size_approx() const
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::size_approx() const
1256
  
1257
1258
  // Returns the number of producers currently associated with the queue.
1259
  size_t size_producers_approx() const
1260
0
  {
1261
0
    size_t size = 0;
1262
0
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
1263
0
      size += 1;
1264
0
    }
1265
0
    return size;
1266
0
  }
1267
1268
  // Returns the number of elements currently in the queue for a specific producer.
1269
  size_t size_producer_approx(producer_token_t const& producer) const
1270
0
  {
1271
0
    return static_cast<ExplicitProducer*>(producer.producer)->size_approx();
1272
0
  }
1273
1274
  
1275
  // Returns true if the underlying atomic variables used by
1276
  // the queue are lock-free (they should be on most platforms).
1277
  // Thread-safe.
1278
  static bool is_lock_free()
1279
  {
1280
    return
1281
      details::static_is_lock_free<bool>::value == 2 &&
1282
      details::static_is_lock_free<size_t>::value == 2 &&
1283
      details::static_is_lock_free<std::uint32_t>::value == 2 &&
1284
      details::static_is_lock_free<index_t>::value == 2 &&
1285
      details::static_is_lock_free<void*>::value == 2 &&
1286
      details::static_is_lock_free<typename details::thread_id_converter<details::thread_id_t>::thread_id_numeric_size_t>::value == 2;
1287
  }
1288
1289
1290
private:
1291
  friend struct ProducerToken;
1292
  friend struct ConsumerToken;
1293
  struct ExplicitProducer;
1294
  friend struct ExplicitProducer;
1295
  struct ImplicitProducer;
1296
  friend struct ImplicitProducer;
1297
  friend class ConcurrentQueueTests;
1298
    
1299
  enum AllocationMode { CanAlloc, CannotAlloc };
1300
  
1301
  
1302
  ///////////////////////////////
1303
  // Queue methods
1304
  ///////////////////////////////
1305
  
1306
  template<AllocationMode canAlloc, typename U>
1307
  inline bool inner_enqueue(producer_token_t const& token, U&& element)
1308
3.60k
  {
1309
3.60k
    return static_cast<ExplicitProducer*>(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue<canAlloc>(std::forward<U>(element));
1310
3.60k
  }
1311
  
1312
  template<AllocationMode canAlloc, typename U>
1313
  inline bool inner_enqueue(U&& element)
1314
218k
  {
1315
218k
    auto producer = get_or_add_implicit_producer();
1316
218k
    return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue<canAlloc>(std::forward<U>(element));
1317
218k
  }
1318
  
1319
  template<AllocationMode canAlloc, typename It>
1320
  inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count)
1321
855k
  {
1322
855k
    return static_cast<ExplicitProducer*>(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk<canAlloc>(itemFirst, count);
1323
855k
  }
1324
  
1325
  template<AllocationMode canAlloc, typename It>
1326
  inline bool inner_enqueue_bulk(It itemFirst, size_t count)
1327
0
  {
1328
0
    auto producer = get_or_add_implicit_producer();
1329
0
    return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk<canAlloc>(itemFirst, count);
1330
0
  }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::inner_enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::inner_enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::inner_enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, unsigned int*>(unsigned int*, unsigned long)
1331
  
1332
  inline bool update_current_producer_after_rotation(consumer_token_t& token)
1333
  {
1334
    // Ah, there's been a rotation, figure out where we should be!
1335
    auto tail = producerListTail.load(std::memory_order_acquire);
1336
    if (token.desiredProducer == nullptr && tail == nullptr) {
1337
      return false;
1338
    }
1339
    auto prodCount = producerCount.load(std::memory_order_relaxed);
1340
    auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed);
1341
    if (token.desiredProducer == nullptr) {
1342
      // Aha, first time we're dequeueing anything.
1343
      // Figure out our local position
1344
      // Note: offset is from start, not end, but we're traversing from end -- subtract from count first
1345
      std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount);
1346
      token.desiredProducer = tail;
1347
      for (std::uint32_t i = 0; i != offset; ++i) {
1348
        token.desiredProducer = static_cast<ProducerBase*>(token.desiredProducer)->next_prod();
1349
        if (token.desiredProducer == nullptr) {
1350
          token.desiredProducer = tail;
1351
        }
1352
      }
1353
    }
1354
    
1355
    std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset;
1356
    if (delta >= prodCount) {
1357
      delta = delta % prodCount;
1358
    }
1359
    for (std::uint32_t i = 0; i != delta; ++i) {
1360
      token.desiredProducer = static_cast<ProducerBase*>(token.desiredProducer)->next_prod();
1361
      if (token.desiredProducer == nullptr) {
1362
        token.desiredProducer = tail;
1363
      }
1364
    }
1365
    
1366
    token.lastKnownGlobalOffset = globalOffset;
1367
    token.currentProducer = token.desiredProducer;
1368
    token.itemsConsumedFromCurrent = 0;
1369
    return true;
1370
  }
1371
  
1372
  
1373
  ///////////////////////////
1374
  // Free list
1375
  ///////////////////////////
1376
  
1377
  template <typename N>
1378
  struct FreeListNode
1379
  {
1380
    FreeListNode() : freeListRefs(0), freeListNext(nullptr) { }
1381
    
1382
    std::atomic<std::uint32_t> freeListRefs;
1383
    std::atomic<N*> freeListNext;
1384
  };
1385
  
1386
  // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but
1387
  // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly
1388
  // speedy under low contention.
1389
  template<typename N>    // N must inherit FreeListNode or have the same fields (and initialization of them)
1390
  struct FreeList
1391
  {
1392
98.0k
    FreeList() : freeListHead(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::FreeList()
Line
Count
Source
1392
8.91k
    FreeList() : freeListHead(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::FreeList()
Line
Count
Source
1392
71.2k
    FreeList() : freeListHead(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::FreeList()
Line
Count
Source
1392
17.8k
    FreeList() : freeListHead(nullptr) { }
1393
    FreeList(FreeList&& other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { other.freeListHead.store(nullptr, std::memory_order_relaxed); }
1394
    void swap(FreeList& other) { details::swap_relaxed(freeListHead, other.freeListHead); }
1395
    
1396
    FreeList(FreeList const&) MOODYCAMEL_DELETE_FUNCTION;
1397
    FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION;
1398
    
1399
    inline void add(N* node)
1400
44.4k
    {
1401
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1402
      debug::DebugLock lock(mutex);
1403
#endif    
1404
      // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
1405
      // set it using a fetch_add
1406
44.4k
      if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) {
1407
        // Oh look! We were the last ones referencing this node, and we know
1408
        // we want to add it to the free list, so let's do it!
1409
44.4k
        add_knowing_refcount_is_zero(node);
1410
44.4k
      }
1411
44.4k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
1400
10.8k
    {
1401
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1402
      debug::DebugLock lock(mutex);
1403
#endif    
1404
      // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
1405
      // set it using a fetch_add
1406
10.8k
      if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) {
1407
        // Oh look! We were the last ones referencing this node, and we know
1408
        // we want to add it to the free list, so let's do it!
1409
10.8k
        add_knowing_refcount_is_zero(node);
1410
10.8k
      }
1411
10.8k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
1400
33.5k
    {
1401
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1402
      debug::DebugLock lock(mutex);
1403
#endif    
1404
      // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
1405
      // set it using a fetch_add
1406
33.5k
      if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) {
1407
        // Oh look! We were the last ones referencing this node, and we know
1408
        // we want to add it to the free list, so let's do it!
1409
33.5k
        add_knowing_refcount_is_zero(node);
1410
33.5k
      }
1411
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
1412
    
1413
    inline N* try_get()
1414
25.0k
    {
1415
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1416
      debug::DebugLock lock(mutex);
1417
#endif    
1418
25.0k
      auto head = freeListHead.load(std::memory_order_acquire);
1419
25.0k
      while (head != nullptr) {
1420
0
        auto prevHead = head;
1421
0
        auto refs = head->freeListRefs.load(std::memory_order_relaxed);
1422
0
        if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) {
1423
0
          head = freeListHead.load(std::memory_order_acquire);
1424
0
          continue;
1425
0
        }
1426
        
1427
        // Good, reference count has been incremented (it wasn't at zero), which means we can read the
1428
        // next and not worry about it changing between now and the time we do the CAS
1429
0
        auto next = head->freeListNext.load(std::memory_order_relaxed);
1430
0
        if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) {
1431
          // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no
1432
          // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on).
1433
0
          assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0);
1434
          
1435
          // Decrease refcount twice, once for our ref, and once for the list's ref
1436
0
          head->freeListRefs.fetch_sub(2, std::memory_order_release);
1437
0
          return head;
1438
0
        }
1439
        
1440
        // OK, the head must have changed on us, but we still need to decrease the refcount we increased.
1441
        // Note that we don't need to release any memory effects, but we do need to ensure that the reference
1442
        // count decrement happens-after the CAS on the head.
1443
0
        refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel);
1444
0
        if (refs == SHOULD_BE_ON_FREELIST + 1) {
1445
0
          add_knowing_refcount_is_zero(prevHead);
1446
0
        }
1447
0
      }
1448
      
1449
25.0k
      return nullptr;
1450
25.0k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::try_get()
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::try_get()
Line
Count
Source
1414
25.0k
    {
1415
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1416
      debug::DebugLock lock(mutex);
1417
#endif    
1418
25.0k
      auto head = freeListHead.load(std::memory_order_acquire);
1419
25.0k
      while (head != nullptr) {
1420
0
        auto prevHead = head;
1421
0
        auto refs = head->freeListRefs.load(std::memory_order_relaxed);
1422
0
        if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) {
1423
0
          head = freeListHead.load(std::memory_order_acquire);
1424
0
          continue;
1425
0
        }
1426
        
1427
        // Good, reference count has been incremented (it wasn't at zero), which means we can read the
1428
        // next and not worry about it changing between now and the time we do the CAS
1429
0
        auto next = head->freeListNext.load(std::memory_order_relaxed);
1430
0
        if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) {
1431
          // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no
1432
          // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on).
1433
0
          assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0);
1434
          
1435
          // Decrease refcount twice, once for our ref, and once for the list's ref
1436
0
          head->freeListRefs.fetch_sub(2, std::memory_order_release);
1437
0
          return head;
1438
0
        }
1439
        
1440
        // OK, the head must have changed on us, but we still need to decrease the refcount we increased.
1441
        // Note that we don't need to release any memory effects, but we do need to ensure that the reference
1442
        // count decrement happens-after the CAS on the head.
1443
0
        refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel);
1444
0
        if (refs == SHOULD_BE_ON_FREELIST + 1) {
1445
0
          add_knowing_refcount_is_zero(prevHead);
1446
0
        }
1447
0
      }
1448
      
1449
25.0k
      return nullptr;
1450
25.0k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::try_get()
1451
    
1452
    // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes)
1453
98.0k
    N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::head_unsafe() const
Line
Count
Source
1453
8.91k
    N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::head_unsafe() const
Line
Count
Source
1453
71.2k
    N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::head_unsafe() const
Line
Count
Source
1453
17.8k
    N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); }
1454
    
1455
  private:
1456
    inline void add_knowing_refcount_is_zero(N* node)
1457
44.4k
    {
1458
      // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run
1459
      // only one copy of this method per node at a time, i.e. the single thread case), then we know
1460
      // we can safely change the next pointer of the node; however, once the refcount is back above
1461
      // zero, then other threads could increase it (happens under heavy contention, when the refcount
1462
      // goes to zero in between a load and a refcount increment of a node in try_get, then back up to
1463
      // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS
1464
      // to add the node to the actual list fails, decrease the refcount and leave the add operation to
1465
      // the next thread who puts the refcount back at zero (which could be us, hence the loop).
1466
44.4k
      auto head = freeListHead.load(std::memory_order_relaxed);
1467
44.4k
      while (true) {
1468
44.4k
        node->freeListNext.store(head, std::memory_order_relaxed);
1469
44.4k
        node->freeListRefs.store(1, std::memory_order_release);
1470
44.4k
        if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) {
1471
          // Hmm, the add failed, but we can only try again when the refcount goes back to zero
1472
0
          if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) {
1473
0
            continue;
1474
0
          }
1475
0
        }
1476
44.4k
        return;
1477
44.4k
      }
1478
44.4k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add_knowing_refcount_is_zero(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
1457
10.8k
    {
1458
      // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run
1459
      // only one copy of this method per node at a time, i.e. the single thread case), then we know
1460
      // we can safely change the next pointer of the node; however, once the refcount is back above
1461
      // zero, then other threads could increase it (happens under heavy contention, when the refcount
1462
      // goes to zero in between a load and a refcount increment of a node in try_get, then back up to
1463
      // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS
1464
      // to add the node to the actual list fails, decrease the refcount and leave the add operation to
1465
      // the next thread who puts the refcount back at zero (which could be us, hence the loop).
1466
10.8k
      auto head = freeListHead.load(std::memory_order_relaxed);
1467
10.8k
      while (true) {
1468
10.8k
        node->freeListNext.store(head, std::memory_order_relaxed);
1469
10.8k
        node->freeListRefs.store(1, std::memory_order_release);
1470
10.8k
        if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) {
1471
          // Hmm, the add failed, but we can only try again when the refcount goes back to zero
1472
0
          if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) {
1473
0
            continue;
1474
0
          }
1475
0
        }
1476
10.8k
        return;
1477
10.8k
      }
1478
10.8k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add_knowing_refcount_is_zero(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
1457
33.5k
    {
1458
      // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run
1459
      // only one copy of this method per node at a time, i.e. the single thread case), then we know
1460
      // we can safely change the next pointer of the node; however, once the refcount is back above
1461
      // zero, then other threads could increase it (happens under heavy contention, when the refcount
1462
      // goes to zero in between a load and a refcount increment of a node in try_get, then back up to
1463
      // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS
1464
      // to add the node to the actual list fails, decrease the refcount and leave the add operation to
1465
      // the next thread who puts the refcount back at zero (which could be us, hence the loop).
1466
33.5k
      auto head = freeListHead.load(std::memory_order_relaxed);
1467
33.5k
      while (true) {
1468
33.5k
        node->freeListNext.store(head, std::memory_order_relaxed);
1469
33.5k
        node->freeListRefs.store(1, std::memory_order_release);
1470
33.5k
        if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) {
1471
          // Hmm, the add failed, but we can only try again when the refcount goes back to zero
1472
0
          if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) {
1473
0
            continue;
1474
0
          }
1475
0
        }
1476
33.5k
        return;
1477
33.5k
      }
1478
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::FreeList<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>::add_knowing_refcount_is_zero(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
1479
    
1480
  private:
1481
    // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention)
1482
    std::atomic<N*> freeListHead;
1483
  
1484
  static const std::uint32_t REFS_MASK = 0x7FFFFFFF;
1485
  static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
1486
    
1487
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
1488
    debug::DebugMutex mutex;
1489
#endif
1490
  };
1491
  
1492
  
1493
  ///////////////////////////
1494
  // Block
1495
  ///////////////////////////
1496
  
1497
  enum InnerQueueContext { implicit_context = 0, explicit_context = 1 };
1498
  
1499
  struct Block
1500
  {
1501
    Block()
1502
613k
      : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true)
1503
613k
    {
1504
#ifdef MCDBGQ_TRACKMEM
1505
      owner = nullptr;
1506
#endif
1507
613k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::Block()
Line
Count
Source
1502
53.4k
      : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true)
1503
53.4k
    {
1504
#ifdef MCDBGQ_TRACKMEM
1505
      owner = nullptr;
1506
#endif
1507
53.4k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::Block()
Line
Count
Source
1502
452k
      : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true)
1503
452k
    {
1504
#ifdef MCDBGQ_TRACKMEM
1505
      owner = nullptr;
1506
#endif
1507
452k
    }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::Block()
Line
Count
Source
1502
106k
      : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true)
1503
106k
    {
1504
#ifdef MCDBGQ_TRACKMEM
1505
      owner = nullptr;
1506
#endif
1507
106k
    }
1508
    
1509
    template<InnerQueueContext context>
1510
    inline bool is_empty() const
1511
37.6k
    {
1512
37.6k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1513
        // Check flags
1514
895k
        for (size_t i = 0; i < BLOCK_SIZE; ++i) {
1515
868k
          if (!emptyFlags[i].load(std::memory_order_relaxed)) {
1516
10.8k
            return false;
1517
10.8k
          }
1518
868k
        }
1519
        
1520
        // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set
1521
26.7k
        std::atomic_thread_fence(std::memory_order_acquire);
1522
26.7k
        return true;
1523
      }
1524
      else {
1525
        // Check counter
1526
        if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) {
1527
          std::atomic_thread_fence(std::memory_order_acquire);
1528
          return true;
1529
        }
1530
        assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE);
1531
        return false;
1532
      }
1533
37.6k
    }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::is_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>() const
Line
Count
Source
1511
37.6k
    {
1512
37.6k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1513
        // Check flags
1514
895k
        for (size_t i = 0; i < BLOCK_SIZE; ++i) {
1515
868k
          if (!emptyFlags[i].load(std::memory_order_relaxed)) {
1516
10.8k
            return false;
1517
10.8k
          }
1518
868k
        }
1519
        
1520
        // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set
1521
26.7k
        std::atomic_thread_fence(std::memory_order_acquire);
1522
26.7k
        return true;
1523
      }
1524
      else {
1525
        // Check counter
1526
        if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) {
1527
          std::atomic_thread_fence(std::memory_order_acquire);
1528
          return true;
1529
        }
1530
        assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE);
1531
        return false;
1532
      }
1533
37.6k
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::is_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>() const
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::is_empty<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>() const
1534
    
1535
    // Returns true if the block is now empty (does not apply in explicit context)
1536
    template<InnerQueueContext context>
1537
    inline bool set_empty(MOODYCAMEL_MAYBE_UNUSED index_t i)
1538
998k
    {
1539
998k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1540
        // Set flag
1541
998k
        assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].load(std::memory_order_relaxed));
1542
998k
        emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].store(true, std::memory_order_release);
1543
998k
        return false;
1544
      }
1545
0
      else {
1546
        // Increment counter
1547
0
        auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release);
1548
0
        assert(prevVal < BLOCK_SIZE);
1549
0
        return prevVal == BLOCK_SIZE - 1;
1550
0
      }
1551
998k
    }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>(unsigned long)
Line
Count
Source
1538
998k
    {
1539
998k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1540
        // Set flag
1541
998k
        assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].load(std::memory_order_relaxed));
1542
998k
        emptyFlags[BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1))].store(true, std::memory_order_release);
1543
998k
        return false;
1544
      }
1545
      else {
1546
        // Increment counter
1547
        auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release);
1548
        assert(prevVal < BLOCK_SIZE);
1549
        return prevVal == BLOCK_SIZE - 1;
1550
      }
1551
998k
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>(unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>(unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>(unsigned long)
1552
    
1553
    // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0).
1554
    // Returns true if the block is now empty (does not apply in explicit context).
1555
    template<InnerQueueContext context>
1556
    inline bool set_many_empty(MOODYCAMEL_MAYBE_UNUSED index_t i, size_t count)
1557
0
    {
1558
0
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1559
        // Set flags
1560
0
        std::atomic_thread_fence(std::memory_order_release);
1561
0
        i = BLOCK_SIZE - 1 - static_cast<size_t>(i & static_cast<index_t>(BLOCK_SIZE - 1)) - count + 1;
1562
0
        for (size_t j = 0; j != count; ++j) {
1563
0
          assert(!emptyFlags[i + j].load(std::memory_order_relaxed));
1564
0
          emptyFlags[i + j].store(true, std::memory_order_relaxed);
1565
0
        }
1566
0
        return false;
1567
      }
1568
0
      else {
1569
        // Increment counter
1570
0
        auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release);
1571
0
        assert(prevVal + count <= BLOCK_SIZE);
1572
0
        return prevVal + count == BLOCK_SIZE;
1573
0
      }
1574
0
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_many_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>(unsigned long, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_many_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>(unsigned long, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_many_empty<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>(unsigned long, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::set_many_empty<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>(unsigned long, unsigned long)
1575
    
1576
    template<InnerQueueContext context>
1577
    inline void set_all_empty()
1578
10.6k
    {
1579
10.6k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1580
        // Set all flags
1581
352k
        for (size_t i = 0; i != BLOCK_SIZE; ++i) {
1582
342k
          emptyFlags[i].store(true, std::memory_order_relaxed);
1583
342k
        }
1584
      }
1585
      else {
1586
        // Reset counter
1587
        elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed);
1588
      }
1589
10.6k
    }
1590
    
1591
    template<InnerQueueContext context>
1592
    inline void reset_empty()
1593
69.9k
    {
1594
69.9k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1595
        // Reset flags
1596
1.20M
        for (size_t i = 0; i != BLOCK_SIZE; ++i) {
1597
1.16M
          emptyFlags[i].store(false, std::memory_order_relaxed);
1598
1.16M
        }
1599
      }
1600
33.5k
      else {
1601
        // Reset counter
1602
33.5k
        elementsCompletelyDequeued.store(0, std::memory_order_relaxed);
1603
33.5k
      }
1604
69.9k
    }
void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::reset_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)1>()
Line
Count
Source
1593
36.3k
    {
1594
36.3k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1595
        // Reset flags
1596
1.20M
        for (size_t i = 0; i != BLOCK_SIZE; ++i) {
1597
1.16M
          emptyFlags[i].store(false, std::memory_order_relaxed);
1598
1.16M
        }
1599
      }
1600
      else {
1601
        // Reset counter
1602
        elementsCompletelyDequeued.store(0, std::memory_order_relaxed);
1603
      }
1604
36.3k
    }
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::reset_empty<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>()
Line
Count
Source
1593
33.5k
    {
1594
33.5k
      MOODYCAMEL_CONSTEXPR_IF (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) {
1595
        // Reset flags
1596
        for (size_t i = 0; i != BLOCK_SIZE; ++i) {
1597
          emptyFlags[i].store(false, std::memory_order_relaxed);
1598
        }
1599
      }
1600
33.5k
      else {
1601
        // Reset counter
1602
33.5k
        elementsCompletelyDequeued.store(0, std::memory_order_relaxed);
1603
33.5k
      }
1604
33.5k
    }
Unexecuted instantiation: void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::reset_empty<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::InnerQueueContext)0>()
1605
    
1606
2.43M
    inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast<T*>(static_cast<void*>(elements)) + static_cast<size_t>(idx & static_cast<index_t>(BLOCK_SIZE - 1)); }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::operator[](unsigned long)
Line
Count
Source
1606
2.00M
    inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast<T*>(static_cast<void*>(elements)) + static_cast<size_t>(idx & static_cast<index_t>(BLOCK_SIZE - 1)); }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::operator[](unsigned long)
Line
Count
Source
1606
437k
    inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast<T*>(static_cast<void*>(elements)) + static_cast<size_t>(idx & static_cast<index_t>(BLOCK_SIZE - 1)); }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block::operator[](unsigned long)
1607
    inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return static_cast<T const*>(static_cast<void const*>(elements)) + static_cast<size_t>(idx & static_cast<index_t>(BLOCK_SIZE - 1)); }
1608
    
1609
  private:
1610
    static_assert(std::alignment_of<T>::value <= sizeof(T), "The queue does not support types with an alignment greater than their size at this time");
1611
    MOODYCAMEL_ALIGNAS(MOODYCAMEL_ALIGNOF(T)) char elements[sizeof(T) * BLOCK_SIZE];
1612
  public:
1613
    Block* next;
1614
    std::atomic<size_t> elementsCompletelyDequeued;
1615
    std::atomic<bool> emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1];
1616
  public:
1617
    std::atomic<std::uint32_t> freeListRefs;
1618
    std::atomic<Block*> freeListNext;
1619
    std::atomic<bool> shouldBeOnFreeList;
1620
    bool dynamicallyAllocated;    // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool'
1621
    
1622
#ifdef MCDBGQ_TRACKMEM
1623
    void* owner;
1624
#endif
1625
  };
1626
  static_assert(std::alignment_of<Block>::value >= std::alignment_of<T>::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping");
1627
1628
1629
#ifdef MCDBGQ_TRACKMEM
1630
public:
1631
  struct MemStats;
1632
private:
1633
#endif
1634
  
1635
  ///////////////////////////
1636
  // Producer base
1637
  ///////////////////////////
1638
  
1639
  struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase
1640
  {
1641
    ProducerBase(ConcurrentQueue* parent_, bool isExplicit_) :
1642
41.1k
      tailIndex(0),
1643
41.1k
      headIndex(0),
1644
41.1k
      dequeueOptimisticCount(0),
1645
41.1k
      dequeueOvercommit(0),
1646
41.1k
      tailBlock(nullptr),
1647
41.1k
      isExplicit(isExplicit_),
1648
41.1k
      parent(parent_)
1649
41.1k
    {
1650
41.1k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::ProducerBase(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*, bool)
Line
Count
Source
1642
9.65k
      tailIndex(0),
1643
9.65k
      headIndex(0),
1644
9.65k
      dequeueOptimisticCount(0),
1645
9.65k
      dequeueOvercommit(0),
1646
9.65k
      tailBlock(nullptr),
1647
9.65k
      isExplicit(isExplicit_),
1648
9.65k
      parent(parent_)
1649
9.65k
    {
1650
9.65k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::ProducerBase(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*, bool)
Line
Count
Source
1642
31.5k
      tailIndex(0),
1643
31.5k
      headIndex(0),
1644
31.5k
      dequeueOptimisticCount(0),
1645
31.5k
      dequeueOvercommit(0),
1646
31.5k
      tailBlock(nullptr),
1647
31.5k
      isExplicit(isExplicit_),
1648
31.5k
      parent(parent_)
1649
31.5k
    {
1650
31.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::ProducerBase(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*, bool)
1651
    
1652
41.2k
    virtual ~ProducerBase() { };
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::~ProducerBase()
Line
Count
Source
1652
9.65k
    virtual ~ProducerBase() { };
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::~ProducerBase()
Line
Count
Source
1652
31.5k
    virtual ~ProducerBase() { };
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::~ProducerBase()
1653
    
1654
    template<typename U>
1655
    inline bool dequeue(U& element)
1656
598k
    {
1657
598k
      if (isExplicit) {
1658
598k
        return static_cast<ExplicitProducer*>(this)->dequeue(element);
1659
598k
      }
1660
18.4E
      else {
1661
18.4E
        return static_cast<ImplicitProducer*>(this)->dequeue(element);
1662
18.4E
      }
1663
598k
    }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::dequeue<duckdb::shared_ptr<duckdb::Task, true> >(duckdb::shared_ptr<duckdb::Task, true>&)
Line
Count
Source
1656
598k
    {
1657
598k
      if (isExplicit) {
1658
598k
        return static_cast<ExplicitProducer*>(this)->dequeue(element);
1659
598k
      }
1660
18.4E
      else {
1661
18.4E
        return static_cast<ImplicitProducer*>(this)->dequeue(element);
1662
18.4E
      }
1663
598k
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::dequeue<duckdb::BufferEvictionNode>(duckdb::BufferEvictionNode&)
1664
    
1665
    template<typename It>
1666
    inline size_t dequeue_bulk(It& itemFirst, size_t max)
1667
0
    {
1668
0
      if (isExplicit) {
1669
0
        return static_cast<ExplicitProducer*>(this)->dequeue_bulk(itemFirst, max);
1670
0
      }
1671
0
      else {
1672
0
        return static_cast<ImplicitProducer*>(this)->dequeue_bulk(itemFirst, max);
1673
0
      }
1674
0
    }
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::dequeue_bulk<std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>&, unsigned long)
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::dequeue_bulk<std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>&, unsigned long)
1675
    
1676
2.00M
    inline ProducerBase* next_prod() const { return static_cast<ProducerBase*>(next); }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::next_prod() const
Line
Count
Source
1676
1.57M
    inline ProducerBase* next_prod() const { return static_cast<ProducerBase*>(next); }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::next_prod() const
Line
Count
Source
1676
423k
    inline ProducerBase* next_prod() const { return static_cast<ProducerBase*>(next); }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::next_prod() const
1677
    
1678
    inline size_t size_approx() const
1679
1.55M
    {
1680
1.55M
      auto tail = tailIndex.load(std::memory_order_relaxed);
1681
1.55M
      auto head = headIndex.load(std::memory_order_relaxed);
1682
1.55M
      return details::circular_less_than(head, tail) ? static_cast<size_t>(tail - head) : 0;
1683
1.55M
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::size_approx() const
Line
Count
Source
1679
1.55M
    {
1680
1.55M
      auto tail = tailIndex.load(std::memory_order_relaxed);
1681
1.55M
      auto head = headIndex.load(std::memory_order_relaxed);
1682
1.55M
      return details::circular_less_than(head, tail) ? static_cast<size_t>(tail - head) : 0;
1683
1.55M
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase::size_approx() const
1684
    
1685
    inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); }
1686
  protected:
1687
    std::atomic<index_t> tailIndex;   // Where to enqueue to next
1688
    std::atomic<index_t> headIndex;   // Where to dequeue from next
1689
    
1690
    std::atomic<index_t> dequeueOptimisticCount;
1691
    std::atomic<index_t> dequeueOvercommit;
1692
    
1693
    Block* tailBlock;
1694
    
1695
  public:
1696
    bool isExplicit;
1697
    ConcurrentQueue* parent;
1698
    
1699
  protected:
1700
#ifdef MCDBGQ_TRACKMEM
1701
    friend struct MemStats;
1702
#endif
1703
  };
1704
  
1705
  
1706
  ///////////////////////////
1707
  // Explicit queue
1708
  ///////////////////////////
1709
    
1710
  struct ExplicitProducer : public ProducerBase
1711
  {
1712
    explicit ExplicitProducer(ConcurrentQueue* parent_) :
1713
9.65k
      ProducerBase(parent_, true),
1714
9.65k
      blockIndex(nullptr),
1715
9.65k
      pr_blockIndexSlotsUsed(0),
1716
9.65k
      pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1),
1717
9.65k
      pr_blockIndexFront(0),
1718
9.65k
      pr_blockIndexEntries(nullptr),
1719
9.65k
      pr_blockIndexRaw(nullptr)
1720
9.65k
    {
1721
9.65k
      size_t poolBasedIndexSize = details::ceil_to_pow_2(parent_->initialBlockPoolSize) >> 1;
1722
9.65k
      if (poolBasedIndexSize > pr_blockIndexSize) {
1723
0
        pr_blockIndexSize = poolBasedIndexSize;
1724
0
      }
1725
      
1726
9.65k
      new_block_index(0);   // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE
1727
9.65k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::ExplicitProducer(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
Line
Count
Source
1713
9.65k
      ProducerBase(parent_, true),
1714
9.65k
      blockIndex(nullptr),
1715
9.65k
      pr_blockIndexSlotsUsed(0),
1716
9.65k
      pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1),
1717
9.65k
      pr_blockIndexFront(0),
1718
9.65k
      pr_blockIndexEntries(nullptr),
1719
9.65k
      pr_blockIndexRaw(nullptr)
1720
9.65k
    {
1721
9.65k
      size_t poolBasedIndexSize = details::ceil_to_pow_2(parent_->initialBlockPoolSize) >> 1;
1722
9.65k
      if (poolBasedIndexSize > pr_blockIndexSize) {
1723
0
        pr_blockIndexSize = poolBasedIndexSize;
1724
0
      }
1725
      
1726
9.65k
      new_block_index(0);   // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE
1727
9.65k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::ExplicitProducer(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::ExplicitProducer(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
1728
    
1729
    ~ExplicitProducer()
1730
9.65k
    {
1731
      // Destruct any elements not yet dequeued.
1732
      // Since we're in the destructor, we can assume all elements
1733
      // are either completely dequeued or completely not (no halfways).
1734
9.65k
      if (this->tailBlock != nullptr) {   // Note this means there must be a block index too
1735
        // First find the block that's partially dequeued, if any
1736
9.61k
        Block* halfDequeuedBlock = nullptr;
1737
9.61k
        if ((this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) != 0) {
1738
          // The head's not on a block boundary, meaning a block somewhere is partially dequeued
1739
          // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary)
1740
9.57k
          size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1);
1741
10.7k
          while (details::circular_less_than<index_t>(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) {
1742
1.20k
            i = (i + 1) & (pr_blockIndexSize - 1);
1743
1.20k
          }
1744
9.57k
          assert(details::circular_less_than<index_t>(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed)));
1745
9.57k
          halfDequeuedBlock = pr_blockIndexEntries[i].block;
1746
9.57k
        }
1747
        
1748
        // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration)
1749
9.61k
        auto block = this->tailBlock;
1750
10.8k
        do {
1751
10.8k
          block = block->next;
1752
10.8k
          if (block->ConcurrentQueue::Block::template is_empty<explicit_context>()) {
1753
1.28k
            continue;
1754
1.28k
          }
1755
          
1756
9.57k
          size_t i = 0; // Offset into block
1757
9.57k
          if (block == halfDequeuedBlock) {
1758
9.57k
            i = static_cast<size_t>(this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1));
1759
9.57k
          }
1760
          
1761
          // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index
1762
9.57k
          auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast<size_t>(this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1));
1763
9.57k
          while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) {
1764
0
            (*block)[i++]->~T();
1765
0
          }
1766
10.8k
        } while (block != this->tailBlock);
1767
9.61k
      }
1768
      
1769
      // Destroy all blocks that we own
1770
9.65k
      if (this->tailBlock != nullptr) {
1771
9.61k
        auto block = this->tailBlock;
1772
10.8k
        do {
1773
10.8k
          auto nextBlock = block->next;
1774
10.8k
          if (block->dynamicallyAllocated) {
1775
0
            destroy(block);
1776
0
          }
1777
10.8k
          else {
1778
10.8k
            this->parent->add_block_to_free_list(block);
1779
10.8k
          }
1780
10.8k
          block = nextBlock;
1781
10.8k
        } while (block != this->tailBlock);
1782
9.61k
      }
1783
      
1784
      // Destroy the block indices
1785
9.65k
      auto header = static_cast<BlockIndexHeader*>(pr_blockIndexRaw);
1786
19.3k
      while (header != nullptr) {
1787
9.65k
        auto prev = static_cast<BlockIndexHeader*>(header->prev);
1788
9.65k
        header->~BlockIndexHeader();
1789
9.65k
        (Traits::free)(header);
1790
9.65k
        header = prev;
1791
9.65k
      }
1792
9.65k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::~ExplicitProducer()
Line
Count
Source
1730
9.65k
    {
1731
      // Destruct any elements not yet dequeued.
1732
      // Since we're in the destructor, we can assume all elements
1733
      // are either completely dequeued or completely not (no halfways).
1734
9.65k
      if (this->tailBlock != nullptr) {   // Note this means there must be a block index too
1735
        // First find the block that's partially dequeued, if any
1736
9.61k
        Block* halfDequeuedBlock = nullptr;
1737
9.61k
        if ((this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) != 0) {
1738
          // The head's not on a block boundary, meaning a block somewhere is partially dequeued
1739
          // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary)
1740
9.57k
          size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1);
1741
10.7k
          while (details::circular_less_than<index_t>(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) {
1742
1.20k
            i = (i + 1) & (pr_blockIndexSize - 1);
1743
1.20k
          }
1744
9.57k
          assert(details::circular_less_than<index_t>(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed)));
1745
9.57k
          halfDequeuedBlock = pr_blockIndexEntries[i].block;
1746
9.57k
        }
1747
        
1748
        // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration)
1749
9.61k
        auto block = this->tailBlock;
1750
10.8k
        do {
1751
10.8k
          block = block->next;
1752
10.8k
          if (block->ConcurrentQueue::Block::template is_empty<explicit_context>()) {
1753
1.28k
            continue;
1754
1.28k
          }
1755
          
1756
9.57k
          size_t i = 0; // Offset into block
1757
9.57k
          if (block == halfDequeuedBlock) {
1758
9.57k
            i = static_cast<size_t>(this->headIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1));
1759
9.57k
          }
1760
          
1761
          // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index
1762
9.57k
          auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast<size_t>(this->tailIndex.load(std::memory_order_relaxed) & static_cast<index_t>(BLOCK_SIZE - 1));
1763
9.57k
          while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) {
1764
0
            (*block)[i++]->~T();
1765
0
          }
1766
10.8k
        } while (block != this->tailBlock);
1767
9.61k
      }
1768
      
1769
      // Destroy all blocks that we own
1770
9.65k
      if (this->tailBlock != nullptr) {
1771
9.61k
        auto block = this->tailBlock;
1772
10.8k
        do {
1773
10.8k
          auto nextBlock = block->next;
1774
10.8k
          if (block->dynamicallyAllocated) {
1775
0
            destroy(block);
1776
0
          }
1777
10.8k
          else {
1778
10.8k
            this->parent->add_block_to_free_list(block);
1779
10.8k
          }
1780
10.8k
          block = nextBlock;
1781
10.8k
        } while (block != this->tailBlock);
1782
9.61k
      }
1783
      
1784
      // Destroy the block indices
1785
9.65k
      auto header = static_cast<BlockIndexHeader*>(pr_blockIndexRaw);
1786
19.3k
      while (header != nullptr) {
1787
9.65k
        auto prev = static_cast<BlockIndexHeader*>(header->prev);
1788
9.65k
        header->~BlockIndexHeader();
1789
9.65k
        (Traits::free)(header);
1790
9.65k
        header = prev;
1791
9.65k
      }
1792
9.65k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::~ExplicitProducer()
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::~ExplicitProducer()
1793
    
1794
    template<AllocationMode allocMode, typename U>
1795
    inline bool enqueue(U&& element)
1796
3.60k
    {
1797
3.60k
      index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed);
1798
3.60k
      index_t newTailIndex = 1 + currentTailIndex;
1799
3.60k
      if ((currentTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0) {
1800
        // We reached the end of a block, start a new one
1801
251
        auto startBlock = this->tailBlock;
1802
251
        auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed;
1803
251
        if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty<explicit_context>()) {
1804
          // We can re-use the block ahead of us, it's empty!         
1805
89
          this->tailBlock = this->tailBlock->next;
1806
89
          this->tailBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>();
1807
          
1808
          // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the
1809
          // last block from it first -- except instead of removing then adding, we can just overwrite).
1810
          // Note that there must be a valid block index here, since even if allocation failed in the ctor,
1811
          // it would have been re-attempted when adding the first block to the queue; since there is such
1812
          // a block, a block index must have been successfully allocated.
1813
89
        }
1814
162
        else {
1815
          // Whatever head value we see here is >= the last value we saw here (relatively),
1816
          // and <= its current value. Since we have the most recent tail, the head must be
1817
          // <= to it.
1818
162
          auto head = this->headIndex.load(std::memory_order_relaxed);
1819
162
          assert(!details::circular_less_than<index_t>(currentTailIndex, head));
1820
162
          if (!details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE)
1821
162
            || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) {
1822
            // We can't enqueue in another block because there's not enough leeway -- the
1823
            // tail could surpass the head by the time the block fills up! (Or we'll exceed
1824
            // the size limit, if the second part of the condition was true.)
1825
0
            return false;
1826
0
          }
1827
          // We're going to need a new block; check that the block index has room
1828
162
          if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) {
1829
            // Hmm, the circular block index is already full -- we'll need
1830
            // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if
1831
            // the initial allocation failed in the constructor.
1832
            
1833
0
            MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
1834
              return false;
1835
            }
1836
0
            else if (!new_block_index(pr_blockIndexSlotsUsed)) {
1837
0
              return false;
1838
0
            }
1839
0
          }
1840
          
1841
          // Insert a new block in the circular linked list
1842
0
          auto newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>();
1843
162
          if (newBlock == nullptr) {
1844
0
            return false;
1845
0
          }
1846
#ifdef MCDBGQ_TRACKMEM
1847
          newBlock->owner = this;
1848
#endif
1849
162
          newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>();
1850
162
          if (this->tailBlock == nullptr) {
1851
162
            newBlock->next = newBlock;
1852
162
          }
1853
0
          else {
1854
0
            newBlock->next = this->tailBlock->next;
1855
0
            this->tailBlock->next = newBlock;
1856
0
          }
1857
162
          this->tailBlock = newBlock;
1858
162
          ++pr_blockIndexSlotsUsed;
1859
162
        }
1860
1861
251
        if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
1862
          // The constructor may throw. We want the element not to appear in the queue in
1863
          // that case (without corrupting the queue):
1864
0
          MOODYCAMEL_TRY {
1865
0
            new ((*this->tailBlock)[currentTailIndex]) T(std::forward<U>(element));
1866
0
          }
1867
0
          MOODYCAMEL_CATCH (...) {
1868
            // Revert change to the current block, but leave the new block available
1869
            // for next time
1870
0
            pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
1871
0
            this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock;
1872
0
            MOODYCAMEL_RETHROW;
1873
0
          }
1874
0
        }
1875
251
        else {
1876
251
          (void)startBlock;
1877
251
          (void)originalBlockIndexSlotsUsed;
1878
251
        }
1879
        
1880
        // Add block to block index
1881
251
        auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront];
1882
251
        entry.base = currentTailIndex;
1883
251
        entry.block = this->tailBlock;
1884
251
        blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release);
1885
251
        pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1);
1886
        
1887
251
        if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
1888
0
          this->tailIndex.store(newTailIndex, std::memory_order_release);
1889
0
          return true;
1890
0
        }
1891
251
      }
1892
      
1893
      // Enqueue
1894
3.60k
      new ((*this->tailBlock)[currentTailIndex]) T(std::forward<U>(element));
1895
      
1896
3.60k
      this->tailIndex.store(newTailIndex, std::memory_order_release);
1897
3.60k
      return true;
1898
3.60k
    }
1899
    
1900
    template<typename U>
1901
    bool dequeue(U& element)
1902
163M
    {
1903
163M
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
1904
163M
      auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
1905
163M
      if (details::circular_less_than<index_t>(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
1906
        // Might be something to dequeue, let's give it a try
1907
        
1908
        // Note that this if is purely for performance purposes in the common case when the queue is
1909
        // empty and the values are eventually consistent -- we may enter here spuriously.
1910
        
1911
        // Note that whatever the values of overcommit and tail are, they are not going to change (unless we
1912
        // change them) and must be the same value at this point (inside the if) as when the if condition was
1913
        // evaluated.
1914
1915
        // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below.
1916
        // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in
1917
        // the fetch_add below will result in a value at least as recent as that (and therefore at least as large).
1918
        // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all
1919
        // read-modify-write operations are guaranteed to work on the latest value in the modification order), but
1920
        // unfortunately that can't be shown to be correct using only the C++11 standard.
1921
        // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case
1922
997k
        std::atomic_thread_fence(std::memory_order_acquire);
1923
        
1924
        // Increment optimistic counter, then check if it went over the boundary
1925
997k
        auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
1926
        
1927
        // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever
1928
        // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
1929
        // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
1930
        // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
1931
        // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently)
1932
        // overflow; in such a case, though, the logic still holds since the difference between the two is maintained.
1933
        
1934
        // Note that we reload tail here in case it changed; it will be the same value as before or greater, since
1935
        // this load is sequenced after (happens after) the earlier load above. This is supported by read-read
1936
        // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
1937
997k
        tail = this->tailIndex.load(std::memory_order_acquire);
1938
1.00M
        if ((details::likely)(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
1939
          // Guaranteed to be at least one element to dequeue!
1940
          
1941
          // Get the index. Note that since there's guaranteed to be at least one element, this
1942
          // will never exceed tail. We need to do an acquire-release fence here since it's possible
1943
          // that whatever condition got us to this point was for an earlier enqueued element (that
1944
          // we already see the memory effects for), but that by the time we increment somebody else
1945
          // has incremented it, and we need to see the memory effects for *that* element, which is
1946
          // in such a case is necessarily visible on the thread that incremented it in the first
1947
          // place with the more current condition (they must have acquired a tail that is at least
1948
          // as recent).
1949
1.00M
          auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel);
1950
          
1951
          
1952
          // Determine which block the element is in
1953
          
1954
1.00M
          auto localBlockIndex = blockIndex.load(std::memory_order_acquire);
1955
1.00M
          auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire);
1956
          
1957
          // We need to be careful here about subtracting and dividing because of index wrap-around.
1958
          // When an index wraps, we need to preserve the sign of the offset when dividing it by the
1959
          // block size (in order to get a correct signed block count offset in all cases):
1960
1.00M
          auto headBase = localBlockIndex->entries[localBlockIndexHead].base;
1961
1.00M
          auto blockBaseIndex = index & ~static_cast<index_t>(BLOCK_SIZE - 1);
1962
1.00M
          auto offset = static_cast<size_t>(static_cast<typename std::make_signed<index_t>::type>(blockBaseIndex - headBase) / static_cast<typename std::make_signed<index_t>::type>(BLOCK_SIZE));
1963
1.00M
          auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block;
1964
          
1965
          // Dequeue
1966
1.00M
          auto& el = *((*block)[index]);
1967
1.00M
          if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) {
1968
            // Make sure the element is still fully dequeued and destroyed even if the assignment
1969
            // throws
1970
0
            struct Guard {
1971
0
              Block* block;
1972
0
              index_t index;
1973
              
1974
0
              ~Guard()
1975
0
              {
1976
0
                (*block)[index]->~T();
1977
0
                block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
1978
0
              }
1979
0
            } guard = { block, index };
1980
1981
0
            element = std::move(el); // NOLINT
1982
0
          }
1983
1.00M
          else {
1984
1.00M
            element = std::move(el); // NOLINT
1985
1.00M
            el.~T(); // NOLINT
1986
1.00M
            block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
1987
1.00M
          }
1988
          
1989
1.00M
          return true;
1990
1.00M
        }
1991
18.4E
        else {
1992
          // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent
1993
18.4E
          this->dequeueOvercommit.fetch_add(1, std::memory_order_release);    // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write
1994
18.4E
        }
1995
997k
      }
1996
    
1997
162M
      return false;
1998
163M
    }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::dequeue<duckdb::shared_ptr<duckdb::Task, true> >(duckdb::shared_ptr<duckdb::Task, true>&)
Line
Count
Source
1902
163M
    {
1903
163M
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
1904
163M
      auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
1905
163M
      if (details::circular_less_than<index_t>(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
1906
        // Might be something to dequeue, let's give it a try
1907
        
1908
        // Note that this if is purely for performance purposes in the common case when the queue is
1909
        // empty and the values are eventually consistent -- we may enter here spuriously.
1910
        
1911
        // Note that whatever the values of overcommit and tail are, they are not going to change (unless we
1912
        // change them) and must be the same value at this point (inside the if) as when the if condition was
1913
        // evaluated.
1914
1915
        // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below.
1916
        // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in
1917
        // the fetch_add below will result in a value at least as recent as that (and therefore at least as large).
1918
        // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all
1919
        // read-modify-write operations are guaranteed to work on the latest value in the modification order), but
1920
        // unfortunately that can't be shown to be correct using only the C++11 standard.
1921
        // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case
1922
997k
        std::atomic_thread_fence(std::memory_order_acquire);
1923
        
1924
        // Increment optimistic counter, then check if it went over the boundary
1925
997k
        auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
1926
        
1927
        // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever
1928
        // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
1929
        // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
1930
        // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
1931
        // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently)
1932
        // overflow; in such a case, though, the logic still holds since the difference between the two is maintained.
1933
        
1934
        // Note that we reload tail here in case it changed; it will be the same value as before or greater, since
1935
        // this load is sequenced after (happens after) the earlier load above. This is supported by read-read
1936
        // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
1937
997k
        tail = this->tailIndex.load(std::memory_order_acquire);
1938
1.00M
        if ((details::likely)(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
1939
          // Guaranteed to be at least one element to dequeue!
1940
          
1941
          // Get the index. Note that since there's guaranteed to be at least one element, this
1942
          // will never exceed tail. We need to do an acquire-release fence here since it's possible
1943
          // that whatever condition got us to this point was for an earlier enqueued element (that
1944
          // we already see the memory effects for), but that by the time we increment somebody else
1945
          // has incremented it, and we need to see the memory effects for *that* element, which is
1946
          // in such a case is necessarily visible on the thread that incremented it in the first
1947
          // place with the more current condition (they must have acquired a tail that is at least
1948
          // as recent).
1949
1.00M
          auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel);
1950
          
1951
          
1952
          // Determine which block the element is in
1953
          
1954
1.00M
          auto localBlockIndex = blockIndex.load(std::memory_order_acquire);
1955
1.00M
          auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire);
1956
          
1957
          // We need to be careful here about subtracting and dividing because of index wrap-around.
1958
          // When an index wraps, we need to preserve the sign of the offset when dividing it by the
1959
          // block size (in order to get a correct signed block count offset in all cases):
1960
1.00M
          auto headBase = localBlockIndex->entries[localBlockIndexHead].base;
1961
1.00M
          auto blockBaseIndex = index & ~static_cast<index_t>(BLOCK_SIZE - 1);
1962
1.00M
          auto offset = static_cast<size_t>(static_cast<typename std::make_signed<index_t>::type>(blockBaseIndex - headBase) / static_cast<typename std::make_signed<index_t>::type>(BLOCK_SIZE));
1963
1.00M
          auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block;
1964
          
1965
          // Dequeue
1966
1.00M
          auto& el = *((*block)[index]);
1967
1.00M
          if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) {
1968
            // Make sure the element is still fully dequeued and destroyed even if the assignment
1969
            // throws
1970
0
            struct Guard {
1971
0
              Block* block;
1972
0
              index_t index;
1973
              
1974
0
              ~Guard()
1975
0
              {
1976
0
                (*block)[index]->~T();
1977
0
                block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
1978
0
              }
1979
0
            } guard = { block, index };
1980
1981
0
            element = std::move(el); // NOLINT
1982
0
          }
1983
1.00M
          else {
1984
1.00M
            element = std::move(el); // NOLINT
1985
1.00M
            el.~T(); // NOLINT
1986
1.00M
            block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
1987
1.00M
          }
1988
          
1989
1.00M
          return true;
1990
1.00M
        }
1991
18.4E
        else {
1992
          // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent
1993
18.4E
          this->dequeueOvercommit.fetch_add(1, std::memory_order_release);    // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write
1994
18.4E
        }
1995
997k
      }
1996
    
1997
162M
      return false;
1998
163M
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::dequeue<duckdb::BufferEvictionNode>(duckdb::BufferEvictionNode&)
1999
    
2000
    template<AllocationMode allocMode, typename It>
2001
    bool enqueue_bulk(It itemFirst, size_t count)
2002
855k
    {
2003
      // First, we need to make sure we have enough room to enqueue all of the elements;
2004
      // this means pre-allocating blocks and putting them in the block index (but only if
2005
      // all the allocations succeeded).
2006
855k
      index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed);
2007
855k
      auto startBlock = this->tailBlock;
2008
855k
      auto originalBlockIndexFront = pr_blockIndexFront;
2009
855k
      auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed;
2010
      
2011
855k
      Block* firstAllocatedBlock = nullptr;
2012
      
2013
      // Figure out how many blocks we'll need to allocate, and do so
2014
855k
      size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1));
2015
855k
      index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
2016
855k
      if (blockBaseDiff > 0) {
2017
        // Allocate as many blocks as possible from ahead
2018
61.5k
        while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty<explicit_context>()) {
2019
25.4k
          blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE);
2020
25.4k
          currentTailIndex += static_cast<index_t>(BLOCK_SIZE);
2021
          
2022
25.4k
          this->tailBlock = this->tailBlock->next;
2023
25.4k
          firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock;
2024
          
2025
25.4k
          auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront];
2026
25.4k
          entry.base = currentTailIndex;
2027
25.4k
          entry.block = this->tailBlock;
2028
25.4k
          pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1);
2029
25.4k
        }
2030
        
2031
        // Now allocate as many blocks as necessary from the block pool
2032
46.8k
        while (blockBaseDiff > 0) {
2033
10.6k
          blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE);
2034
10.6k
          currentTailIndex += static_cast<index_t>(BLOCK_SIZE);
2035
          
2036
10.6k
          auto head = this->headIndex.load(std::memory_order_relaxed);
2037
10.6k
          assert(!details::circular_less_than<index_t>(currentTailIndex, head));
2038
10.6k
          bool full = !details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head));
2039
10.6k
          if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) {
2040
0
            MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
2041
              // Failed to allocate, undo changes (but keep injected blocks)
2042
              pr_blockIndexFront = originalBlockIndexFront;
2043
              pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
2044
              this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock;
2045
              return false;
2046
            }
2047
0
            else if (full || !new_block_index(originalBlockIndexSlotsUsed)) {
2048
              // Failed to allocate, undo changes (but keep injected blocks)
2049
0
              pr_blockIndexFront = originalBlockIndexFront;
2050
0
              pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
2051
0
              this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock;
2052
0
              return false;
2053
0
            }
2054
            
2055
            // pr_blockIndexFront is updated inside new_block_index, so we need to
2056
            // update our fallback value too (since we keep the new index even if we
2057
            // later fail)
2058
0
            originalBlockIndexFront = originalBlockIndexSlotsUsed;
2059
0
          }
2060
          
2061
          // Insert a new block in the circular linked list
2062
0
          auto newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>();
2063
10.6k
          if (newBlock == nullptr) {
2064
0
            pr_blockIndexFront = originalBlockIndexFront;
2065
0
            pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
2066
0
            this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock;
2067
0
            return false;
2068
0
          }
2069
          
2070
#ifdef MCDBGQ_TRACKMEM
2071
          newBlock->owner = this;
2072
#endif
2073
10.6k
          newBlock->ConcurrentQueue::Block::template set_all_empty<explicit_context>();
2074
10.6k
          if (this->tailBlock == nullptr) {
2075
9.45k
            newBlock->next = newBlock;
2076
9.45k
          }
2077
1.23k
          else {
2078
1.23k
            newBlock->next = this->tailBlock->next;
2079
1.23k
            this->tailBlock->next = newBlock;
2080
1.23k
          }
2081
10.6k
          this->tailBlock = newBlock;
2082
10.6k
          firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock;
2083
          
2084
10.6k
          ++pr_blockIndexSlotsUsed;
2085
          
2086
10.6k
          auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront];
2087
10.6k
          entry.base = currentTailIndex;
2088
10.6k
          entry.block = this->tailBlock;
2089
10.6k
          pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1);
2090
10.6k
        }
2091
        
2092
        // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and
2093
        // publish the new block index front
2094
36.1k
        auto block = firstAllocatedBlock;
2095
36.1k
        while (true) {
2096
36.1k
          block->ConcurrentQueue::Block::template reset_empty<explicit_context>();
2097
36.1k
          if (block == this->tailBlock) {
2098
36.1k
            break;
2099
36.1k
          }
2100
0
          block = block->next;
2101
0
        }
2102
        
2103
36.1k
        if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
2104
36.1k
          blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
2105
36.1k
        }
2106
36.1k
      }
2107
      
2108
      // Enqueue, one block at a time
2109
855k
      index_t newTailIndex = startTailIndex + static_cast<index_t>(count);
2110
855k
      currentTailIndex = startTailIndex;
2111
855k
      auto endBlock = this->tailBlock;
2112
855k
      this->tailBlock = startBlock;
2113
855k
      assert((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0);
2114
855k
      if ((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) {
2115
31.6k
        this->tailBlock = firstAllocatedBlock;
2116
31.6k
      }
2117
859k
      while (true) {
2118
859k
        auto stopIndex = (currentTailIndex & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2119
859k
        if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
2120
833k
          stopIndex = newTailIndex;
2121
833k
        }
2122
859k
        if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
2123
1.85M
          while (currentTailIndex != stopIndex) {
2124
999k
            new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
2125
999k
          }
2126
859k
        }
2127
0
        else {
2128
0
          MOODYCAMEL_TRY {
2129
0
            while (currentTailIndex != stopIndex) {
2130
              // Must use copy constructor even if move constructor is available
2131
              // because we may have to revert if there's an exception.
2132
              // Sorry about the horrible templated next line, but it was the only way
2133
              // to disable moving *at compile time*, which is important because a type
2134
              // may only define a (noexcept) move constructor, and so calls to the
2135
              // cctor will not compile, even if they are in an if branch that will never
2136
              // be executed
2137
0
              new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
2138
0
              ++currentTailIndex;
2139
0
              ++itemFirst;
2140
0
            }
2141
0
          }
2142
0
          MOODYCAMEL_CATCH (...) {
2143
            // Oh dear, an exception's been thrown -- destroy the elements that
2144
            // were enqueued so far and revert the entire bulk operation (we'll keep
2145
            // any allocated blocks in our linked list for later, though).
2146
0
            auto constructedStopIndex = currentTailIndex;
2147
0
            auto lastBlockEnqueued = this->tailBlock;
2148
            
2149
0
            pr_blockIndexFront = originalBlockIndexFront;
2150
0
            pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed;
2151
0
            this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock;
2152
            
2153
0
            if (!details::is_trivially_destructible<T>::value) {
2154
0
              auto block = startBlock;
2155
0
              if ((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0) {
2156
0
                block = firstAllocatedBlock;
2157
0
              }
2158
0
              currentTailIndex = startTailIndex;
2159
0
              while (true) {
2160
0
                stopIndex = (currentTailIndex & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2161
0
                if (details::circular_less_than<index_t>(constructedStopIndex, stopIndex)) {
2162
0
                  stopIndex = constructedStopIndex;
2163
0
                }
2164
0
                while (currentTailIndex != stopIndex) {
2165
0
                  (*block)[currentTailIndex++]->~T();
2166
0
                }
2167
0
                if (block == lastBlockEnqueued) {
2168
0
                  break;
2169
0
                }
2170
0
                block = block->next;
2171
0
              }
2172
0
            }
2173
0
            MOODYCAMEL_RETHROW;
2174
0
          }
2175
0
        }
2176
        
2177
859k
        if (this->tailBlock == endBlock) {
2178
855k
          assert(currentTailIndex == newTailIndex);
2179
855k
          break;
2180
855k
        }
2181
4.51k
        this->tailBlock = this->tailBlock->next;
2182
4.51k
      }
2183
      
2184
855k
      if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) {
2185
0
        blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
2186
0
      }
2187
      
2188
855k
      this->tailIndex.store(newTailIndex, std::memory_order_release);
2189
855k
      return true;
2190
855k
    }
2191
    
2192
    template<typename It>
2193
    size_t dequeue_bulk(It& itemFirst, size_t max)
2194
0
    {
2195
0
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
2196
0
      auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
2197
0
      auto desiredCount = static_cast<size_t>(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit));
2198
0
      if (details::circular_less_than<size_t>(0, desiredCount)) {
2199
0
        desiredCount = desiredCount < max ? desiredCount : max;
2200
0
        std::atomic_thread_fence(std::memory_order_acquire);
2201
        
2202
0
        auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);;
2203
        
2204
0
        tail = this->tailIndex.load(std::memory_order_acquire);
2205
0
        auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit));
2206
0
        if (details::circular_less_than<size_t>(0, actualCount)) {
2207
0
          actualCount = desiredCount < actualCount ? desiredCount : actualCount;
2208
0
          if (actualCount < desiredCount) {
2209
0
            this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release);
2210
0
          }
2211
          
2212
          // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this
2213
          // will never exceed tail.
2214
0
          auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel);
2215
          
2216
          // Determine which block the first element is in
2217
0
          auto localBlockIndex = blockIndex.load(std::memory_order_acquire);
2218
0
          auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire);
2219
          
2220
0
          auto headBase = localBlockIndex->entries[localBlockIndexHead].base;
2221
0
          auto firstBlockBaseIndex = firstIndex & ~static_cast<index_t>(BLOCK_SIZE - 1);
2222
0
          auto offset = static_cast<size_t>(static_cast<typename std::make_signed<index_t>::type>(firstBlockBaseIndex - headBase) / static_cast<typename std::make_signed<index_t>::type>(BLOCK_SIZE));
2223
0
          auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1);
2224
          
2225
          // Iterate the blocks and dequeue
2226
0
          auto index = firstIndex;
2227
0
          do {
2228
0
            auto firstIndexInBlock = index;
2229
0
            auto endIndex = (index & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2230
0
            endIndex = details::circular_less_than<index_t>(firstIndex + static_cast<index_t>(actualCount), endIndex) ? firstIndex + static_cast<index_t>(actualCount) : endIndex;
2231
0
            auto block = localBlockIndex->entries[indexIndex].block;
2232
0
            if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) {
2233
0
              while (index != endIndex) {
2234
0
                auto& el = *((*block)[index]);
2235
0
                *itemFirst++ = std::move(el);
2236
0
                el.~T();
2237
0
                ++index;
2238
0
              }
2239
0
            }
2240
0
            else {
2241
0
              MOODYCAMEL_TRY {
2242
0
                while (index != endIndex) {
2243
0
                  auto& el = *((*block)[index]);
2244
0
                  *itemFirst = std::move(el);
2245
0
                  ++itemFirst;
2246
0
                  el.~T();
2247
0
                  ++index;
2248
0
                }
2249
0
              }
2250
0
              MOODYCAMEL_CATCH (...) {
2251
                // It's too late to revert the dequeue, but we can make sure that all
2252
                // the dequeued objects are properly destroyed and the block index
2253
                // (and empty count) are properly updated before we propagate the exception
2254
0
                do {
2255
0
                  block = localBlockIndex->entries[indexIndex].block;
2256
0
                  while (index != endIndex) {
2257
0
                    (*block)[index++]->~T();
2258
0
                  }
2259
0
                  block->ConcurrentQueue::Block::template set_many_empty<explicit_context>(firstIndexInBlock, static_cast<size_t>(endIndex - firstIndexInBlock));
2260
0
                  indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1);
2261
                  
2262
0
                  firstIndexInBlock = index;
2263
0
                  endIndex = (index & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2264
0
                  endIndex = details::circular_less_than<index_t>(firstIndex + static_cast<index_t>(actualCount), endIndex) ? firstIndex + static_cast<index_t>(actualCount) : endIndex;
2265
0
                } while (index != firstIndex + actualCount);
2266
                
2267
0
                MOODYCAMEL_RETHROW;
2268
0
              }
2269
0
            }
2270
0
            block->ConcurrentQueue::Block::template set_many_empty<explicit_context>(firstIndexInBlock, static_cast<size_t>(endIndex - firstIndexInBlock));
2271
0
            indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1);
2272
0
          } while (index != firstIndex + actualCount);
2273
          
2274
0
          return actualCount;
2275
0
        }
2276
0
        else {
2277
          // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent
2278
0
          this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release);
2279
0
        }
2280
0
      }
2281
      
2282
0
      return 0;
2283
0
    }
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::dequeue_bulk<std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>&, unsigned long)
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::dequeue_bulk<std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>&, unsigned long)
2284
    
2285
  private:
2286
    struct BlockIndexEntry
2287
    {
2288
      index_t base;
2289
      Block* block;
2290
    };
2291
    
2292
    struct BlockIndexHeader
2293
    {
2294
      size_t size;
2295
      std::atomic<size_t> front;    // Current slot (not next, like pr_blockIndexFront)
2296
      BlockIndexEntry* entries;
2297
      void* prev;
2298
    };
2299
    
2300
    
2301
    bool new_block_index(size_t numberOfFilledSlotsToExpose)
2302
9.65k
    {
2303
9.65k
      auto prevBlockSizeMask = pr_blockIndexSize - 1;
2304
      
2305
      // Create the new block
2306
9.65k
      pr_blockIndexSize <<= 1;
2307
9.65k
      auto newRawPtr = static_cast<char*>((Traits::malloc)(sizeof(BlockIndexHeader) + std::alignment_of<BlockIndexEntry>::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize));
2308
9.65k
      if (newRawPtr == nullptr) {
2309
0
        pr_blockIndexSize >>= 1;    // Reset to allow graceful retry
2310
0
        return false;
2311
0
      }
2312
      
2313
9.65k
      auto newBlockIndexEntries = reinterpret_cast<BlockIndexEntry*>(details::align_for<BlockIndexEntry>(newRawPtr + sizeof(BlockIndexHeader)));
2314
      
2315
      // Copy in all the old indices, if any
2316
9.65k
      size_t j = 0;
2317
9.65k
      if (pr_blockIndexSlotsUsed != 0) {
2318
0
        auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask;
2319
0
        do {
2320
0
          newBlockIndexEntries[j++] = pr_blockIndexEntries[i];
2321
0
          i = (i + 1) & prevBlockSizeMask;
2322
0
        } while (i != pr_blockIndexFront);
2323
0
      }
2324
      
2325
      // Update everything
2326
9.65k
      auto header = new (newRawPtr) BlockIndexHeader;
2327
9.65k
      header->size = pr_blockIndexSize;
2328
9.65k
      header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed);
2329
9.65k
      header->entries = newBlockIndexEntries;
2330
9.65k
      header->prev = pr_blockIndexRaw;    // we link the new block to the old one so we can free it later
2331
      
2332
9.65k
      pr_blockIndexFront = j;
2333
9.65k
      pr_blockIndexEntries = newBlockIndexEntries;
2334
9.65k
      pr_blockIndexRaw = newRawPtr;
2335
9.65k
      blockIndex.store(header, std::memory_order_release);
2336
      
2337
9.65k
      return true;
2338
9.65k
    }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::new_block_index(unsigned long)
Line
Count
Source
2302
9.65k
    {
2303
9.65k
      auto prevBlockSizeMask = pr_blockIndexSize - 1;
2304
      
2305
      // Create the new block
2306
9.65k
      pr_blockIndexSize <<= 1;
2307
9.65k
      auto newRawPtr = static_cast<char*>((Traits::malloc)(sizeof(BlockIndexHeader) + std::alignment_of<BlockIndexEntry>::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize));
2308
9.65k
      if (newRawPtr == nullptr) {
2309
0
        pr_blockIndexSize >>= 1;    // Reset to allow graceful retry
2310
0
        return false;
2311
0
      }
2312
      
2313
9.65k
      auto newBlockIndexEntries = reinterpret_cast<BlockIndexEntry*>(details::align_for<BlockIndexEntry>(newRawPtr + sizeof(BlockIndexHeader)));
2314
      
2315
      // Copy in all the old indices, if any
2316
9.65k
      size_t j = 0;
2317
9.65k
      if (pr_blockIndexSlotsUsed != 0) {
2318
0
        auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask;
2319
0
        do {
2320
0
          newBlockIndexEntries[j++] = pr_blockIndexEntries[i];
2321
0
          i = (i + 1) & prevBlockSizeMask;
2322
0
        } while (i != pr_blockIndexFront);
2323
0
      }
2324
      
2325
      // Update everything
2326
9.65k
      auto header = new (newRawPtr) BlockIndexHeader;
2327
9.65k
      header->size = pr_blockIndexSize;
2328
9.65k
      header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed);
2329
9.65k
      header->entries = newBlockIndexEntries;
2330
9.65k
      header->prev = pr_blockIndexRaw;    // we link the new block to the old one so we can free it later
2331
      
2332
9.65k
      pr_blockIndexFront = j;
2333
9.65k
      pr_blockIndexEntries = newBlockIndexEntries;
2334
9.65k
      pr_blockIndexRaw = newRawPtr;
2335
9.65k
      blockIndex.store(header, std::memory_order_release);
2336
      
2337
9.65k
      return true;
2338
9.65k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::new_block_index(unsigned long)
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer::new_block_index(unsigned long)
2339
    
2340
  private:
2341
    std::atomic<BlockIndexHeader*> blockIndex;
2342
    
2343
    // To be used by producer only -- consumer must use the ones in referenced by blockIndex
2344
    size_t pr_blockIndexSlotsUsed;
2345
    size_t pr_blockIndexSize;
2346
    size_t pr_blockIndexFront;    // Next slot (not current)
2347
    BlockIndexEntry* pr_blockIndexEntries;
2348
    void* pr_blockIndexRaw;
2349
    
2350
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
2351
  public:
2352
    ExplicitProducer* nextExplicitProducer;
2353
  private:
2354
#endif
2355
    
2356
#ifdef MCDBGQ_TRACKMEM
2357
    friend struct MemStats;
2358
#endif
2359
  };
2360
  
2361
  
2362
  //////////////////////////////////
2363
  // Implicit queue
2364
  //////////////////////////////////
2365
  
2366
  struct ImplicitProducer : public ProducerBase
2367
  {     
2368
    ImplicitProducer(ConcurrentQueue* parent_) :
2369
31.5k
      ProducerBase(parent_, false),
2370
31.5k
      nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE),
2371
31.5k
      blockIndex(nullptr)
2372
31.5k
    {
2373
31.5k
      new_block_index();
2374
31.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::ImplicitProducer(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::ImplicitProducer(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
Line
Count
Source
2369
31.5k
      ProducerBase(parent_, false),
2370
31.5k
      nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE),
2371
31.5k
      blockIndex(nullptr)
2372
31.5k
    {
2373
31.5k
      new_block_index();
2374
31.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::ImplicitProducer(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*)
2375
    
2376
    ~ImplicitProducer()
2377
31.5k
    {
2378
      // Note that since we're in the destructor we can assume that all enqueue/dequeue operations
2379
      // completed already; this means that all undequeued elements are placed contiguously across
2380
      // contiguous blocks, and that only the first and last remaining blocks can be only partially
2381
      // empty (all other remaining blocks must be completely full).
2382
      
2383
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
2384
      // Unregister ourselves for thread termination notification
2385
      if (!this->inactive.load(std::memory_order_relaxed)) {
2386
        details::ThreadExitNotifier::unsubscribe(&threadExitListener);
2387
      }
2388
#endif
2389
      
2390
      // Destroy all remaining elements!
2391
31.5k
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
2392
31.5k
      auto index = this->headIndex.load(std::memory_order_relaxed);
2393
31.5k
      Block* block = nullptr;
2394
31.5k
      assert(index == tail || details::circular_less_than(index, tail));
2395
31.5k
      bool forceFreeLastBlock = index != tail;    // If we enter the loop, then the last (tail) block will not be freed
2396
250k
      while (index != tail) {
2397
219k
        if ((index & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 || block == nullptr) {
2398
33.5k
          if (block != nullptr) {
2399
            // Free the old block
2400
1.98k
            this->parent->add_block_to_free_list(block);
2401
1.98k
          }
2402
          
2403
33.5k
          block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed);
2404
33.5k
        }
2405
        
2406
219k
        ((*block)[index])->~T();
2407
219k
        ++index;
2408
219k
      }
2409
      // Even if the queue is empty, there's still one block that's not on the free list
2410
      // (unless the head index reached the end of it, in which case the tail will be poised
2411
      // to create a new block).
2412
31.5k
      if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast<index_t>(BLOCK_SIZE - 1)) != 0)) {
2413
31.5k
        this->parent->add_block_to_free_list(this->tailBlock);
2414
31.5k
      }
2415
      
2416
      // Destroy block index
2417
31.5k
      auto localBlockIndex = blockIndex.load(std::memory_order_relaxed);
2418
31.5k
      if (localBlockIndex != nullptr) {
2419
1.04M
        for (size_t i = 0; i != localBlockIndex->capacity; ++i) {
2420
1.01M
          localBlockIndex->index[i]->~BlockIndexEntry();
2421
1.01M
        }
2422
31.5k
        do {
2423
31.5k
          auto prev = localBlockIndex->prev;
2424
31.5k
          localBlockIndex->~BlockIndexHeader();
2425
31.5k
          (Traits::free)(localBlockIndex);
2426
31.5k
          localBlockIndex = prev;
2427
31.5k
        } while (localBlockIndex != nullptr);
2428
31.5k
      }
2429
31.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::~ImplicitProducer()
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::~ImplicitProducer()
Line
Count
Source
2377
31.5k
    {
2378
      // Note that since we're in the destructor we can assume that all enqueue/dequeue operations
2379
      // completed already; this means that all undequeued elements are placed contiguously across
2380
      // contiguous blocks, and that only the first and last remaining blocks can be only partially
2381
      // empty (all other remaining blocks must be completely full).
2382
      
2383
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
2384
      // Unregister ourselves for thread termination notification
2385
      if (!this->inactive.load(std::memory_order_relaxed)) {
2386
        details::ThreadExitNotifier::unsubscribe(&threadExitListener);
2387
      }
2388
#endif
2389
      
2390
      // Destroy all remaining elements!
2391
31.5k
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
2392
31.5k
      auto index = this->headIndex.load(std::memory_order_relaxed);
2393
31.5k
      Block* block = nullptr;
2394
31.5k
      assert(index == tail || details::circular_less_than(index, tail));
2395
31.5k
      bool forceFreeLastBlock = index != tail;    // If we enter the loop, then the last (tail) block will not be freed
2396
250k
      while (index != tail) {
2397
219k
        if ((index & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 || block == nullptr) {
2398
33.5k
          if (block != nullptr) {
2399
            // Free the old block
2400
1.98k
            this->parent->add_block_to_free_list(block);
2401
1.98k
          }
2402
          
2403
33.5k
          block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed);
2404
33.5k
        }
2405
        
2406
219k
        ((*block)[index])->~T();
2407
219k
        ++index;
2408
219k
      }
2409
      // Even if the queue is empty, there's still one block that's not on the free list
2410
      // (unless the head index reached the end of it, in which case the tail will be poised
2411
      // to create a new block).
2412
31.5k
      if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast<index_t>(BLOCK_SIZE - 1)) != 0)) {
2413
31.5k
        this->parent->add_block_to_free_list(this->tailBlock);
2414
31.5k
      }
2415
      
2416
      // Destroy block index
2417
31.5k
      auto localBlockIndex = blockIndex.load(std::memory_order_relaxed);
2418
31.5k
      if (localBlockIndex != nullptr) {
2419
1.04M
        for (size_t i = 0; i != localBlockIndex->capacity; ++i) {
2420
1.01M
          localBlockIndex->index[i]->~BlockIndexEntry();
2421
1.01M
        }
2422
31.5k
        do {
2423
31.5k
          auto prev = localBlockIndex->prev;
2424
31.5k
          localBlockIndex->~BlockIndexHeader();
2425
31.5k
          (Traits::free)(localBlockIndex);
2426
31.5k
          localBlockIndex = prev;
2427
31.5k
        } while (localBlockIndex != nullptr);
2428
31.5k
      }
2429
31.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::~ImplicitProducer()
2430
    
2431
    template<AllocationMode allocMode, typename U>
2432
    inline bool enqueue(U&& element)
2433
218k
    {
2434
218k
      index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed);
2435
218k
      index_t newTailIndex = 1 + currentTailIndex;
2436
218k
      if ((currentTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0) {
2437
        // We reached the end of a block, start a new one
2438
33.5k
        auto head = this->headIndex.load(std::memory_order_relaxed);
2439
33.5k
        assert(!details::circular_less_than<index_t>(currentTailIndex, head));
2440
33.5k
        if (!details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) {
2441
0
          return false;
2442
0
        }
2443
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2444
        debug::DebugLock lock(mutex);
2445
#endif
2446
        // Find out where we'll be inserting this block in the block index
2447
33.5k
        BlockIndexEntry* idxEntry;
2448
33.5k
        if (!insert_block_index_entry<allocMode>(idxEntry, currentTailIndex)) {
2449
0
          return false;
2450
0
        }
2451
        
2452
        // Get ahold of a new block
2453
33.5k
        auto newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>();
2454
33.5k
        if (newBlock == nullptr) {
2455
0
          rewind_block_index_tail();
2456
0
          idxEntry->value.store(nullptr, std::memory_order_relaxed);
2457
0
          return false;
2458
0
        }
2459
#ifdef MCDBGQ_TRACKMEM
2460
        newBlock->owner = this;
2461
#endif
2462
33.5k
        newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
2463
        
2464
33.5k
        if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
2465
          // May throw, try to insert now before we publish the fact that we have this new block
2466
0
          MOODYCAMEL_TRY {
2467
0
            new ((*newBlock)[currentTailIndex]) T(std::forward<U>(element));
2468
0
          }
2469
0
          MOODYCAMEL_CATCH (...) {
2470
0
            rewind_block_index_tail();
2471
0
            idxEntry->value.store(nullptr, std::memory_order_relaxed);
2472
0
            this->parent->add_block_to_free_list(newBlock);
2473
0
            MOODYCAMEL_RETHROW;
2474
0
          }
2475
0
        }
2476
        
2477
        // Insert the new block into the index
2478
33.5k
        idxEntry->value.store(newBlock, std::memory_order_relaxed);
2479
        
2480
33.5k
        this->tailBlock = newBlock;
2481
        
2482
33.5k
        if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
2483
0
          this->tailIndex.store(newTailIndex, std::memory_order_release);
2484
0
          return true;
2485
0
        }
2486
33.5k
      }
2487
      
2488
      // Enqueue
2489
218k
      new ((*this->tailBlock)[currentTailIndex]) T(std::forward<U>(element));
2490
      
2491
218k
      this->tailIndex.store(newTailIndex, std::memory_order_release);
2492
218k
      return true;
2493
218k
    }
2494
    
2495
    template<typename U>
2496
    bool dequeue(U& element)
2497
0
    {
2498
      // See ExplicitProducer::dequeue for rationale and explanation
2499
0
      index_t tail = this->tailIndex.load(std::memory_order_relaxed);
2500
0
      index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
2501
0
      if (details::circular_less_than<index_t>(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) {
2502
0
        std::atomic_thread_fence(std::memory_order_acquire);
2503
        
2504
0
        index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
2505
0
        tail = this->tailIndex.load(std::memory_order_acquire);
2506
0
        if ((details::likely)(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
2507
0
          index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel);
2508
          
2509
          // Determine which block the element is in
2510
0
          auto entry = get_block_index_entry_for_index(index);
2511
          
2512
          // Dequeue
2513
0
          auto block = entry->value.load(std::memory_order_relaxed);
2514
0
          auto& el = *((*block)[index]);
2515
          
2516
0
          if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) {
2517
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2518
            // Note: Acquiring the mutex with every dequeue instead of only when a block
2519
            // is released is very sub-optimal, but it is, after all, purely debug code.
2520
            debug::DebugLock lock(producer->mutex);
2521
#endif
2522
0
            struct Guard {
2523
0
              Block* block;
2524
0
              index_t index;
2525
0
              BlockIndexEntry* entry;
2526
0
              ConcurrentQueue* parent;
2527
              
2528
0
              ~Guard()
2529
0
              {
2530
0
                (*block)[index]->~T();
2531
0
                if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) {
2532
0
                  entry->value.store(nullptr, std::memory_order_relaxed);
2533
0
                  parent->add_block_to_free_list(block);
2534
0
                }
2535
0
              }
2536
0
            } guard = { block, index, entry, this->parent };
2537
2538
0
            element = std::move(el); // NOLINT
2539
0
          }
2540
0
          else {
2541
0
            element = std::move(el); // NOLINT
2542
0
            el.~T(); // NOLINT
2543
2544
0
            if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) {
2545
0
              {
2546
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2547
                debug::DebugLock lock(mutex);
2548
#endif
2549
                // Add the block back into the global free pool (and remove from block index)
2550
0
                entry->value.store(nullptr, std::memory_order_relaxed);
2551
0
              }
2552
0
              this->parent->add_block_to_free_list(block);    // releases the above store
2553
0
            }
2554
0
          }
2555
          
2556
0
          return true;
2557
0
        }
2558
0
        else {
2559
0
          this->dequeueOvercommit.fetch_add(1, std::memory_order_release);
2560
0
        }
2561
0
      }
2562
    
2563
0
      return false;
2564
0
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::dequeue<duckdb::shared_ptr<duckdb::Task, true> >(duckdb::shared_ptr<duckdb::Task, true>&)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::dequeue<duckdb::BufferEvictionNode>(duckdb::BufferEvictionNode&)
2565
    
2566
    template<AllocationMode allocMode, typename It>
2567
    bool enqueue_bulk(It itemFirst, size_t count)
2568
0
    {
2569
      // First, we need to make sure we have enough room to enqueue all of the elements;
2570
      // this means pre-allocating blocks and putting them in the block index (but only if
2571
      // all the allocations succeeded).
2572
      
2573
      // Note that the tailBlock we start off with may not be owned by us any more;
2574
      // this happens if it was filled up exactly to the top (setting tailIndex to
2575
      // the first index of the next block which is not yet allocated), then dequeued
2576
      // completely (putting it on the free list) before we enqueue again.
2577
      
2578
0
      index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed);
2579
0
      auto startBlock = this->tailBlock;
2580
0
      Block* firstAllocatedBlock = nullptr;
2581
0
      auto endBlock = this->tailBlock;
2582
      
2583
      // Figure out how many blocks we'll need to allocate, and do so
2584
0
      size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1));
2585
0
      index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
2586
0
      if (blockBaseDiff > 0) {
2587
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2588
        debug::DebugLock lock(mutex);
2589
#endif
2590
0
        do {
2591
0
          blockBaseDiff -= static_cast<index_t>(BLOCK_SIZE);
2592
0
          currentTailIndex += static_cast<index_t>(BLOCK_SIZE);
2593
          
2594
          // Find out where we'll be inserting this block in the block index
2595
0
          BlockIndexEntry* idxEntry = nullptr;  // initialization here unnecessary but compiler can't always tell
2596
0
          Block* newBlock;
2597
0
          bool indexInserted = false;
2598
0
          auto head = this->headIndex.load(std::memory_order_relaxed);
2599
0
          assert(!details::circular_less_than<index_t>(currentTailIndex, head));
2600
0
          bool full = !details::circular_less_than<index_t>(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max<size_t>::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head));
2601
0
          if (full || !(indexInserted = insert_block_index_entry<allocMode>(idxEntry, currentTailIndex)) || (newBlock = this->parent->ConcurrentQueue::template requisition_block<allocMode>()) == nullptr) {
2602
            // Index allocation or block allocation failed; revert any other allocations
2603
            // and index insertions done so far for this operation
2604
0
            if (indexInserted) {
2605
0
              rewind_block_index_tail();
2606
0
              idxEntry->value.store(nullptr, std::memory_order_relaxed);
2607
0
            }
2608
0
            currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
2609
0
            for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) {
2610
0
              currentTailIndex += static_cast<index_t>(BLOCK_SIZE);
2611
0
              idxEntry = get_block_index_entry_for_index(currentTailIndex);
2612
0
              idxEntry->value.store(nullptr, std::memory_order_relaxed);
2613
0
              rewind_block_index_tail();
2614
0
            }
2615
0
            this->parent->add_blocks_to_free_list(firstAllocatedBlock);
2616
0
            this->tailBlock = startBlock;
2617
            
2618
0
            return false;
2619
0
          }
2620
          
2621
#ifdef MCDBGQ_TRACKMEM
2622
          newBlock->owner = this;
2623
#endif
2624
0
          newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
2625
0
          newBlock->next = nullptr;
2626
          
2627
          // Insert the new block into the index
2628
0
          idxEntry->value.store(newBlock, std::memory_order_relaxed);
2629
          
2630
          // Store the chain of blocks so that we can undo if later allocations fail,
2631
          // and so that we can find the blocks when we do the actual enqueueing
2632
0
          if ((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr) {
2633
0
            assert(this->tailBlock != nullptr);
2634
0
            this->tailBlock->next = newBlock;
2635
0
          }
2636
0
          this->tailBlock = newBlock;
2637
0
          endBlock = newBlock;
2638
0
          firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock;
2639
0
        } while (blockBaseDiff > 0);
2640
0
      }
2641
      
2642
      // Enqueue, one block at a time
2643
0
      index_t newTailIndex = startTailIndex + static_cast<index_t>(count);
2644
0
      currentTailIndex = startTailIndex;
2645
0
      this->tailBlock = startBlock;
2646
0
      assert((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0);
2647
0
      if ((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) {
2648
0
        this->tailBlock = firstAllocatedBlock;
2649
0
      }
2650
0
      while (true) {
2651
0
        auto stopIndex = (currentTailIndex & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2652
0
        if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
2653
0
          stopIndex = newTailIndex;
2654
0
        }
2655
0
        if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
2656
0
          while (currentTailIndex != stopIndex) {
2657
0
            new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
2658
0
          }
2659
0
        }
2660
0
        else {
2661
0
          MOODYCAMEL_TRY {
2662
0
            while (currentTailIndex != stopIndex) {
2663
0
              new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
2664
0
              ++currentTailIndex;
2665
0
              ++itemFirst;
2666
0
            }
2667
0
          }
2668
0
          MOODYCAMEL_CATCH (...) {
2669
0
            auto constructedStopIndex = currentTailIndex;
2670
0
            auto lastBlockEnqueued = this->tailBlock;
2671
            
2672
0
            if (!details::is_trivially_destructible<T>::value) {
2673
0
              auto block = startBlock;
2674
0
              if ((startTailIndex & static_cast<index_t>(BLOCK_SIZE - 1)) == 0) {
2675
0
                block = firstAllocatedBlock;
2676
0
              }
2677
0
              currentTailIndex = startTailIndex;
2678
0
              while (true) {
2679
0
                stopIndex = (currentTailIndex & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2680
0
                if (details::circular_less_than<index_t>(constructedStopIndex, stopIndex)) {
2681
0
                  stopIndex = constructedStopIndex;
2682
0
                }
2683
0
                while (currentTailIndex != stopIndex) {
2684
0
                  (*block)[currentTailIndex++]->~T();
2685
0
                }
2686
0
                if (block == lastBlockEnqueued) {
2687
0
                  break;
2688
0
                }
2689
0
                block = block->next;
2690
0
              }
2691
0
            }
2692
            
2693
0
            currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
2694
0
            for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) {
2695
0
              currentTailIndex += static_cast<index_t>(BLOCK_SIZE);
2696
0
              auto idxEntry = get_block_index_entry_for_index(currentTailIndex);
2697
0
              idxEntry->value.store(nullptr, std::memory_order_relaxed);
2698
0
              rewind_block_index_tail();
2699
0
            }
2700
0
            this->parent->add_blocks_to_free_list(firstAllocatedBlock);
2701
0
            this->tailBlock = startBlock;
2702
0
            MOODYCAMEL_RETHROW;
2703
0
          }
2704
0
        }
2705
        
2706
0
        if (this->tailBlock == endBlock) {
2707
0
          assert(currentTailIndex == newTailIndex);
2708
0
          break;
2709
0
        }
2710
0
        this->tailBlock = this->tailBlock->next;
2711
0
      }
2712
0
      this->tailIndex.store(newTailIndex, std::memory_order_release);
2713
0
      return true;
2714
0
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>, unsigned long)
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::enqueue_bulk<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0, unsigned int*>(unsigned int*, unsigned long)
2715
    
2716
    template<typename It>
2717
    size_t dequeue_bulk(It& itemFirst, size_t max)
2718
0
    {
2719
0
      auto tail = this->tailIndex.load(std::memory_order_relaxed);
2720
0
      auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed);
2721
0
      auto desiredCount = static_cast<size_t>(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit));
2722
0
      if (details::circular_less_than<size_t>(0, desiredCount)) {
2723
0
        desiredCount = desiredCount < max ? desiredCount : max;
2724
0
        std::atomic_thread_fence(std::memory_order_acquire);
2725
        
2726
0
        auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);
2727
        
2728
0
        tail = this->tailIndex.load(std::memory_order_acquire);
2729
0
        auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit));
2730
0
        if (details::circular_less_than<size_t>(0, actualCount)) {
2731
0
          actualCount = desiredCount < actualCount ? desiredCount : actualCount;
2732
0
          if (actualCount < desiredCount) {
2733
0
            this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release);
2734
0
          }
2735
          
2736
          // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this
2737
          // will never exceed tail.
2738
0
          auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel);
2739
          
2740
          // Iterate the blocks and dequeue
2741
0
          auto index = firstIndex;
2742
0
          BlockIndexHeader* localBlockIndex;
2743
0
          auto indexIndex = get_block_index_index_for_index(index, localBlockIndex);
2744
0
          do {
2745
0
            auto blockStartIndex = index;
2746
0
            auto endIndex = (index & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2747
0
            endIndex = details::circular_less_than<index_t>(firstIndex + static_cast<index_t>(actualCount), endIndex) ? firstIndex + static_cast<index_t>(actualCount) : endIndex;
2748
            
2749
0
            auto entry = localBlockIndex->index[indexIndex];
2750
0
            auto block = entry->value.load(std::memory_order_relaxed);
2751
0
            if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) {
2752
0
              while (index != endIndex) {
2753
0
                auto& el = *((*block)[index]);
2754
0
                *itemFirst++ = std::move(el);
2755
0
                el.~T();
2756
0
                ++index;
2757
0
              }
2758
0
            }
2759
0
            else {
2760
0
              MOODYCAMEL_TRY {
2761
0
                while (index != endIndex) {
2762
0
                  auto& el = *((*block)[index]);
2763
0
                  *itemFirst = std::move(el);
2764
0
                  ++itemFirst;
2765
0
                  el.~T();
2766
0
                  ++index;
2767
0
                }
2768
0
              }
2769
0
              MOODYCAMEL_CATCH (...) {
2770
0
                do {
2771
0
                  entry = localBlockIndex->index[indexIndex];
2772
0
                  block = entry->value.load(std::memory_order_relaxed);
2773
0
                  while (index != endIndex) {
2774
0
                    (*block)[index++]->~T();
2775
0
                  }
2776
                  
2777
0
                  if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
2778
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2779
                    debug::DebugLock lock(mutex);
2780
#endif
2781
0
                    entry->value.store(nullptr, std::memory_order_relaxed);
2782
0
                    this->parent->add_block_to_free_list(block);
2783
0
                  }
2784
0
                  indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1);
2785
                  
2786
0
                  blockStartIndex = index;
2787
0
                  endIndex = (index & ~static_cast<index_t>(BLOCK_SIZE - 1)) + static_cast<index_t>(BLOCK_SIZE);
2788
0
                  endIndex = details::circular_less_than<index_t>(firstIndex + static_cast<index_t>(actualCount), endIndex) ? firstIndex + static_cast<index_t>(actualCount) : endIndex;
2789
0
                } while (index != firstIndex + actualCount);
2790
                
2791
0
                MOODYCAMEL_RETHROW;
2792
0
              }
2793
0
            }
2794
0
            if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
2795
0
              {
2796
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2797
                debug::DebugLock lock(mutex);
2798
#endif
2799
                // Note that the set_many_empty above did a release, meaning that anybody who acquires the block
2800
                // we're about to free can use it safely since our writes (and reads!) will have happened-before then.
2801
0
                entry->value.store(nullptr, std::memory_order_relaxed);
2802
0
              }
2803
0
              this->parent->add_block_to_free_list(block);    // releases the above store
2804
0
            }
2805
0
            indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1);
2806
0
          } while (index != firstIndex + actualCount);
2807
          
2808
0
          return actualCount;
2809
0
        }
2810
0
        else {
2811
0
          this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release);
2812
0
        }
2813
0
      }
2814
      
2815
0
      return 0;
2816
0
    }
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::dequeue_bulk<std::__1::__wrap_iter<duckdb::BufferEvictionNode*> >(std::__1::__wrap_iter<duckdb::BufferEvictionNode*>&, unsigned long)
Unexecuted instantiation: unsigned long duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::dequeue_bulk<std::__1::__wrap_iter<unsigned int*> >(std::__1::__wrap_iter<unsigned int*>&, unsigned long)
2817
    
2818
  private:
2819
    // The block size must be > 1, so any number with the low bit set is an invalid block base index
2820
    static const index_t INVALID_BLOCK_BASE = 1;
2821
    
2822
    struct BlockIndexEntry
2823
    {
2824
      std::atomic<index_t> key;
2825
      std::atomic<Block*> value;
2826
    };
2827
    
2828
    struct BlockIndexHeader
2829
    {
2830
      size_t capacity;
2831
      std::atomic<size_t> tail;
2832
      BlockIndexEntry* entries;
2833
      BlockIndexEntry** index;
2834
      BlockIndexHeader* prev;
2835
    };
2836
    
2837
    template<AllocationMode allocMode>
2838
    inline bool insert_block_index_entry(BlockIndexEntry*& idxEntry, index_t blockStartIndex)
2839
33.5k
    {
2840
33.5k
      auto localBlockIndex = blockIndex.load(std::memory_order_relaxed);    // We're the only writer thread, relaxed is OK
2841
33.5k
      if (localBlockIndex == nullptr) {
2842
0
        return false;  // this can happen if new_block_index failed in the constructor
2843
0
      }
2844
33.5k
      auto newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1);
2845
33.5k
      idxEntry = localBlockIndex->index[newTail];
2846
33.5k
      if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE ||
2847
33.5k
        idxEntry->value.load(std::memory_order_relaxed) == nullptr) {
2848
        
2849
33.5k
        idxEntry->key.store(blockStartIndex, std::memory_order_relaxed);
2850
33.5k
        localBlockIndex->tail.store(newTail, std::memory_order_release);
2851
33.5k
        return true;
2852
33.5k
      }
2853
      
2854
      // No room in the old block index, try to allocate another one!
2855
18.4E
      MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
2856
18.4E
        return false;
2857
18.4E
      }
2858
18.4E
      else if (!new_block_index()) {
2859
0
        return false;
2860
0
      }
2861
18.4E
      localBlockIndex = blockIndex.load(std::memory_order_relaxed);
2862
18.4E
      newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1);
2863
18.4E
      idxEntry = localBlockIndex->index[newTail];
2864
18.4E
      assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE);
2865
18.4E
      idxEntry->key.store(blockStartIndex, std::memory_order_relaxed);
2866
18.4E
      localBlockIndex->tail.store(newTail, std::memory_order_release);
2867
18.4E
      return true;
2868
33.5k
    }
bool duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::insert_block_index_entry<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry*&, unsigned long)
Line
Count
Source
2839
33.5k
    {
2840
33.5k
      auto localBlockIndex = blockIndex.load(std::memory_order_relaxed);    // We're the only writer thread, relaxed is OK
2841
33.5k
      if (localBlockIndex == nullptr) {
2842
0
        return false;  // this can happen if new_block_index failed in the constructor
2843
0
      }
2844
33.5k
      auto newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1);
2845
33.5k
      idxEntry = localBlockIndex->index[newTail];
2846
33.5k
      if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE ||
2847
33.5k
        idxEntry->value.load(std::memory_order_relaxed) == nullptr) {
2848
        
2849
33.5k
        idxEntry->key.store(blockStartIndex, std::memory_order_relaxed);
2850
33.5k
        localBlockIndex->tail.store(newTail, std::memory_order_release);
2851
33.5k
        return true;
2852
33.5k
      }
2853
      
2854
      // No room in the old block index, try to allocate another one!
2855
18.4E
      MOODYCAMEL_CONSTEXPR_IF (allocMode == CannotAlloc) {
2856
18.4E
        return false;
2857
18.4E
      }
2858
18.4E
      else if (!new_block_index()) {
2859
0
        return false;
2860
0
      }
2861
18.4E
      localBlockIndex = blockIndex.load(std::memory_order_relaxed);
2862
18.4E
      newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1);
2863
18.4E
      idxEntry = localBlockIndex->index[newTail];
2864
      assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE);
2865
18.4E
      idxEntry->key.store(blockStartIndex, std::memory_order_relaxed);
2866
18.4E
      localBlockIndex->tail.store(newTail, std::memory_order_release);
2867
18.4E
      return true;
2868
33.5k
    }
Unexecuted instantiation: bool duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::insert_block_index_entry<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexEntry*&, unsigned long)
2869
    
2870
    inline void rewind_block_index_tail()
2871
0
    {
2872
0
      auto localBlockIndex = blockIndex.load(std::memory_order_relaxed);
2873
0
      localBlockIndex->tail.store((localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), std::memory_order_relaxed);
2874
0
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::rewind_block_index_tail()
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::rewind_block_index_tail()
2875
    
2876
    inline BlockIndexEntry* get_block_index_entry_for_index(index_t index) const
2877
33.5k
    {
2878
33.5k
      BlockIndexHeader* localBlockIndex;
2879
33.5k
      auto idx = get_block_index_index_for_index(index, localBlockIndex);
2880
33.5k
      return localBlockIndex->index[idx];
2881
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_entry_for_index(unsigned long) const
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_entry_for_index(unsigned long) const
Line
Count
Source
2877
33.5k
    {
2878
33.5k
      BlockIndexHeader* localBlockIndex;
2879
33.5k
      auto idx = get_block_index_index_for_index(index, localBlockIndex);
2880
33.5k
      return localBlockIndex->index[idx];
2881
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_entry_for_index(unsigned long) const
2882
    
2883
    inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const
2884
33.5k
    {
2885
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2886
      debug::DebugLock lock(mutex);
2887
#endif
2888
33.5k
      index &= ~static_cast<index_t>(BLOCK_SIZE - 1);
2889
33.5k
      localBlockIndex = blockIndex.load(std::memory_order_acquire);
2890
33.5k
      auto tail = localBlockIndex->tail.load(std::memory_order_acquire);
2891
33.5k
      auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed);
2892
33.5k
      assert(tailBase != INVALID_BLOCK_BASE);
2893
      // Note: Must use division instead of shift because the index may wrap around, causing a negative
2894
      // offset, whose negativity we want to preserve
2895
33.5k
      auto offset = static_cast<size_t>(static_cast<typename std::make_signed<index_t>::type>(index - tailBase) / static_cast<typename std::make_signed<index_t>::type>(BLOCK_SIZE));
2896
33.5k
      size_t idx = (tail + offset) & (localBlockIndex->capacity - 1);
2897
33.5k
      assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr);
2898
33.5k
      return idx;
2899
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_index_for_index(unsigned long, duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexHeader*&) const
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_index_for_index(unsigned long, duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexHeader*&) const
Line
Count
Source
2884
33.5k
    {
2885
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2886
      debug::DebugLock lock(mutex);
2887
#endif
2888
33.5k
      index &= ~static_cast<index_t>(BLOCK_SIZE - 1);
2889
33.5k
      localBlockIndex = blockIndex.load(std::memory_order_acquire);
2890
33.5k
      auto tail = localBlockIndex->tail.load(std::memory_order_acquire);
2891
33.5k
      auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed);
2892
33.5k
      assert(tailBase != INVALID_BLOCK_BASE);
2893
      // Note: Must use division instead of shift because the index may wrap around, causing a negative
2894
      // offset, whose negativity we want to preserve
2895
33.5k
      auto offset = static_cast<size_t>(static_cast<typename std::make_signed<index_t>::type>(index - tailBase) / static_cast<typename std::make_signed<index_t>::type>(BLOCK_SIZE));
2896
33.5k
      size_t idx = (tail + offset) & (localBlockIndex->capacity - 1);
2897
      assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr);
2898
33.5k
      return idx;
2899
33.5k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::get_block_index_index_for_index(unsigned long, duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::BlockIndexHeader*&) const
2900
    
2901
    bool new_block_index()
2902
31.4k
    {
2903
31.4k
      auto prev = blockIndex.load(std::memory_order_relaxed);
2904
31.4k
      size_t prevCapacity = prev == nullptr ? 0 : prev->capacity;
2905
31.4k
      auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity;
2906
31.4k
      auto raw = static_cast<char*>((Traits::malloc)(
2907
31.4k
        sizeof(BlockIndexHeader) +
2908
31.4k
        std::alignment_of<BlockIndexEntry>::value - 1 + sizeof(BlockIndexEntry) * entryCount +
2909
31.4k
        std::alignment_of<BlockIndexEntry*>::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity));
2910
31.4k
      if (raw == nullptr) {
2911
0
        return false;
2912
0
      }
2913
      
2914
31.4k
      auto header = new (raw) BlockIndexHeader;
2915
31.4k
      auto entries = reinterpret_cast<BlockIndexEntry*>(details::align_for<BlockIndexEntry>(raw + sizeof(BlockIndexHeader)));
2916
31.4k
      auto index = reinterpret_cast<BlockIndexEntry**>(details::align_for<BlockIndexEntry*>(reinterpret_cast<char*>(entries) + sizeof(BlockIndexEntry) * entryCount));
2917
31.4k
      if (prev != nullptr) {
2918
0
        auto prevTail = prev->tail.load(std::memory_order_relaxed);
2919
0
        auto prevPos = prevTail;
2920
0
        size_t i = 0;
2921
0
        do {
2922
0
          prevPos = (prevPos + 1) & (prev->capacity - 1);
2923
0
          index[i++] = prev->index[prevPos];
2924
0
        } while (prevPos != prevTail);
2925
0
        assert(i == prevCapacity);
2926
0
      }
2927
1.03M
      for (size_t i = 0; i != entryCount; ++i) {
2928
1.00M
        new (entries + i) BlockIndexEntry;
2929
1.00M
        entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed);
2930
1.00M
        index[prevCapacity + i] = entries + i;
2931
1.00M
      }
2932
31.4k
      header->prev = prev;
2933
31.4k
      header->entries = entries;
2934
31.4k
      header->index = index;
2935
31.4k
      header->capacity = nextBlockIndexCapacity;
2936
31.4k
      header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed);
2937
      
2938
31.4k
      blockIndex.store(header, std::memory_order_release);
2939
      
2940
31.4k
      nextBlockIndexCapacity <<= 1;
2941
      
2942
31.4k
      return true;
2943
31.4k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::new_block_index()
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::new_block_index()
Line
Count
Source
2902
31.4k
    {
2903
31.4k
      auto prev = blockIndex.load(std::memory_order_relaxed);
2904
31.4k
      size_t prevCapacity = prev == nullptr ? 0 : prev->capacity;
2905
31.4k
      auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity;
2906
31.4k
      auto raw = static_cast<char*>((Traits::malloc)(
2907
31.4k
        sizeof(BlockIndexHeader) +
2908
31.4k
        std::alignment_of<BlockIndexEntry>::value - 1 + sizeof(BlockIndexEntry) * entryCount +
2909
31.4k
        std::alignment_of<BlockIndexEntry*>::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity));
2910
31.4k
      if (raw == nullptr) {
2911
0
        return false;
2912
0
      }
2913
      
2914
31.4k
      auto header = new (raw) BlockIndexHeader;
2915
31.4k
      auto entries = reinterpret_cast<BlockIndexEntry*>(details::align_for<BlockIndexEntry>(raw + sizeof(BlockIndexHeader)));
2916
31.4k
      auto index = reinterpret_cast<BlockIndexEntry**>(details::align_for<BlockIndexEntry*>(reinterpret_cast<char*>(entries) + sizeof(BlockIndexEntry) * entryCount));
2917
31.4k
      if (prev != nullptr) {
2918
0
        auto prevTail = prev->tail.load(std::memory_order_relaxed);
2919
0
        auto prevPos = prevTail;
2920
0
        size_t i = 0;
2921
0
        do {
2922
0
          prevPos = (prevPos + 1) & (prev->capacity - 1);
2923
0
          index[i++] = prev->index[prevPos];
2924
0
        } while (prevPos != prevTail);
2925
0
        assert(i == prevCapacity);
2926
0
      }
2927
1.03M
      for (size_t i = 0; i != entryCount; ++i) {
2928
1.00M
        new (entries + i) BlockIndexEntry;
2929
1.00M
        entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed);
2930
1.00M
        index[prevCapacity + i] = entries + i;
2931
1.00M
      }
2932
31.4k
      header->prev = prev;
2933
31.4k
      header->entries = entries;
2934
31.4k
      header->index = index;
2935
31.4k
      header->capacity = nextBlockIndexCapacity;
2936
31.4k
      header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed);
2937
      
2938
31.4k
      blockIndex.store(header, std::memory_order_release);
2939
      
2940
31.4k
      nextBlockIndexCapacity <<= 1;
2941
      
2942
31.4k
      return true;
2943
31.4k
    }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer::new_block_index()
2944
    
2945
  private:
2946
    size_t nextBlockIndexCapacity;
2947
    std::atomic<BlockIndexHeader*> blockIndex;
2948
2949
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
2950
  public:
2951
    details::ThreadExitListener threadExitListener;
2952
  private:
2953
#endif
2954
    
2955
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
2956
  public:
2957
    ImplicitProducer* nextImplicitProducer;
2958
  private:
2959
#endif
2960
2961
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
2962
    mutable debug::DebugMutex mutex;
2963
#endif
2964
#ifdef MCDBGQ_TRACKMEM
2965
    friend struct MemStats;
2966
#endif
2967
  };
2968
  
2969
  
2970
  //////////////////////////////////
2971
  // Block pool manipulation
2972
  //////////////////////////////////
2973
  
2974
  void populate_initial_block_list(size_t blockCount)
2975
98.0k
  {
2976
98.0k
    initialBlockPoolSize = blockCount;
2977
98.0k
    if (initialBlockPoolSize == 0) {
2978
0
      initialBlockPool = nullptr;
2979
0
      return;
2980
0
    }
2981
    
2982
98.0k
    initialBlockPool = create_array<Block>(blockCount);
2983
98.0k
    if (initialBlockPool == nullptr) {
2984
0
      initialBlockPoolSize = 0;
2985
0
    }
2986
686k
    for (size_t i = 0; i < initialBlockPoolSize; ++i) {
2987
588k
      initialBlockPool[i].dynamicallyAllocated = false;
2988
588k
    }
2989
98.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_block_list(unsigned long)
Line
Count
Source
2975
8.91k
  {
2976
8.91k
    initialBlockPoolSize = blockCount;
2977
8.91k
    if (initialBlockPoolSize == 0) {
2978
0
      initialBlockPool = nullptr;
2979
0
      return;
2980
0
    }
2981
    
2982
8.91k
    initialBlockPool = create_array<Block>(blockCount);
2983
8.91k
    if (initialBlockPool == nullptr) {
2984
0
      initialBlockPoolSize = 0;
2985
0
    }
2986
62.3k
    for (size_t i = 0; i < initialBlockPoolSize; ++i) {
2987
53.4k
      initialBlockPool[i].dynamicallyAllocated = false;
2988
53.4k
    }
2989
8.91k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_block_list(unsigned long)
Line
Count
Source
2975
71.2k
  {
2976
71.2k
    initialBlockPoolSize = blockCount;
2977
71.2k
    if (initialBlockPoolSize == 0) {
2978
0
      initialBlockPool = nullptr;
2979
0
      return;
2980
0
    }
2981
    
2982
71.2k
    initialBlockPool = create_array<Block>(blockCount);
2983
71.2k
    if (initialBlockPool == nullptr) {
2984
0
      initialBlockPoolSize = 0;
2985
0
    }
2986
498k
    for (size_t i = 0; i < initialBlockPoolSize; ++i) {
2987
427k
      initialBlockPool[i].dynamicallyAllocated = false;
2988
427k
    }
2989
71.2k
  }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_block_list(unsigned long)
Line
Count
Source
2975
17.8k
  {
2976
17.8k
    initialBlockPoolSize = blockCount;
2977
17.8k
    if (initialBlockPoolSize == 0) {
2978
0
      initialBlockPool = nullptr;
2979
0
      return;
2980
0
    }
2981
    
2982
17.8k
    initialBlockPool = create_array<Block>(blockCount);
2983
17.8k
    if (initialBlockPool == nullptr) {
2984
0
      initialBlockPoolSize = 0;
2985
0
    }
2986
124k
    for (size_t i = 0; i < initialBlockPoolSize; ++i) {
2987
106k
      initialBlockPool[i].dynamicallyAllocated = false;
2988
106k
    }
2989
17.8k
  }
2990
  
2991
  inline Block* try_get_block_from_initial_pool()
2992
44.3k
  {
2993
44.3k
    if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) {
2994
25.0k
      return nullptr;
2995
25.0k
    }
2996
    
2997
19.3k
    auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed);
2998
    
2999
18.4E
    return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr;
3000
44.3k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_initial_pool()
Line
Count
Source
2992
10.8k
  {
2993
10.8k
    if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) {
2994
0
      return nullptr;
2995
0
    }
2996
    
2997
10.8k
    auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed);
2998
    
2999
10.8k
    return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr;
3000
10.8k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_initial_pool()
Line
Count
Source
2992
33.5k
  {
2993
33.5k
    if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) {
2994
25.0k
      return nullptr;
2995
25.0k
    }
2996
    
2997
8.48k
    auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed);
2998
    
2999
18.4E
    return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr;
3000
33.5k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_initial_pool()
3001
  
3002
  inline void add_block_to_free_list(Block* block)
3003
44.4k
  {
3004
#ifdef MCDBGQ_TRACKMEM
3005
    block->owner = nullptr;
3006
#endif
3007
44.4k
    freeList.add(block);
3008
44.4k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_block_to_free_list(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
3003
10.8k
  {
3004
#ifdef MCDBGQ_TRACKMEM
3005
    block->owner = nullptr;
3006
#endif
3007
10.8k
    freeList.add(block);
3008
10.8k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_block_to_free_list(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
3003
33.5k
  {
3004
#ifdef MCDBGQ_TRACKMEM
3005
    block->owner = nullptr;
3006
#endif
3007
33.5k
    freeList.add(block);
3008
33.5k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_block_to_free_list(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
3009
  
3010
  inline void add_blocks_to_free_list(Block* block)
3011
0
  {
3012
0
    while (block != nullptr) {
3013
0
      auto next = block->next;
3014
0
      add_block_to_free_list(block);
3015
0
      block = next;
3016
0
    }
3017
0
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_blocks_to_free_list(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_blocks_to_free_list(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
3018
  
3019
  inline Block* try_get_block_from_free_list()
3020
25.0k
  {
3021
25.0k
    return freeList.try_get();
3022
25.0k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_free_list()
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_free_list()
Line
Count
Source
3020
25.0k
  {
3021
25.0k
    return freeList.try_get();
3022
25.0k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::try_get_block_from_free_list()
3023
  
3024
  // Gets a free block from one of the memory pools, or allocates a new one (if applicable)
3025
  template<AllocationMode canAlloc>
3026
  Block* requisition_block()
3027
44.3k
  {
3028
44.3k
    auto block = try_get_block_from_initial_pool();
3029
44.3k
    if (block != nullptr) {
3030
19.3k
      return block;
3031
19.3k
    }
3032
    
3033
25.0k
    block = try_get_block_from_free_list();
3034
25.0k
    if (block != nullptr) {
3035
0
      return block;
3036
0
    }
3037
    
3038
25.0k
    MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) {
3039
25.0k
      return create<Block>();
3040
    }
3041
    else {
3042
      return nullptr;
3043
    }
3044
25.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::requisition_block<(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0>()
Line
Count
Source
3027
10.8k
  {
3028
10.8k
    auto block = try_get_block_from_initial_pool();
3029
10.8k
    if (block != nullptr) {
3030
10.8k
      return block;
3031
10.8k
    }
3032
    
3033
0
    block = try_get_block_from_free_list();
3034
0
    if (block != nullptr) {
3035
0
      return block;
3036
0
    }
3037
    
3038
0
    MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) {
3039
0
      return create<Block>();
3040
    }
3041
    else {
3042
      return nullptr;
3043
    }
3044
0
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::requisition_block<(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0>()
Line
Count
Source
3027
33.5k
  {
3028
33.5k
    auto block = try_get_block_from_initial_pool();
3029
33.5k
    if (block != nullptr) {
3030
8.48k
      return block;
3031
8.48k
    }
3032
    
3033
25.0k
    block = try_get_block_from_free_list();
3034
25.0k
    if (block != nullptr) {
3035
0
      return block;
3036
0
    }
3037
    
3038
25.0k
    MOODYCAMEL_CONSTEXPR_IF (canAlloc == CanAlloc) {
3039
25.0k
      return create<Block>();
3040
    }
3041
    else {
3042
      return nullptr;
3043
    }
3044
25.0k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::requisition_block<(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::AllocationMode)0>()
3045
  
3046
3047
#ifdef MCDBGQ_TRACKMEM
3048
  public:
3049
    struct MemStats {
3050
      size_t allocatedBlocks;
3051
      size_t usedBlocks;
3052
      size_t freeBlocks;
3053
      size_t ownedBlocksExplicit;
3054
      size_t ownedBlocksImplicit;
3055
      size_t implicitProducers;
3056
      size_t explicitProducers;
3057
      size_t elementsEnqueued;
3058
      size_t blockClassBytes;
3059
      size_t queueClassBytes;
3060
      size_t implicitBlockIndexBytes;
3061
      size_t explicitBlockIndexBytes;
3062
      
3063
      friend class ConcurrentQueue;
3064
      
3065
    private:
3066
      static MemStats getFor(ConcurrentQueue* q)
3067
      {
3068
        MemStats stats = { 0 };
3069
        
3070
        stats.elementsEnqueued = q->size_approx();
3071
      
3072
        auto block = q->freeList.head_unsafe();
3073
        while (block != nullptr) {
3074
          ++stats.allocatedBlocks;
3075
          ++stats.freeBlocks;
3076
          block = block->freeListNext.load(std::memory_order_relaxed);
3077
        }
3078
        
3079
        for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
3080
          bool implicit = dynamic_cast<ImplicitProducer*>(ptr) != nullptr;
3081
          stats.implicitProducers += implicit ? 1 : 0;
3082
          stats.explicitProducers += implicit ? 0 : 1;
3083
          
3084
          if (implicit) {
3085
            auto prod = static_cast<ImplicitProducer*>(ptr);
3086
            stats.queueClassBytes += sizeof(ImplicitProducer);
3087
            auto head = prod->headIndex.load(std::memory_order_relaxed);
3088
            auto tail = prod->tailIndex.load(std::memory_order_relaxed);
3089
            auto hash = prod->blockIndex.load(std::memory_order_relaxed);
3090
            if (hash != nullptr) {
3091
              for (size_t i = 0; i != hash->capacity; ++i) {
3092
                if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) {
3093
                  ++stats.allocatedBlocks;
3094
                  ++stats.ownedBlocksImplicit;
3095
                }
3096
              }
3097
              stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry);
3098
              for (; hash != nullptr; hash = hash->prev) {
3099
                stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*);
3100
              }
3101
            }
3102
            for (; details::circular_less_than<index_t>(head, tail); head += BLOCK_SIZE) {
3103
              //auto block = prod->get_block_index_entry_for_index(head);
3104
              ++stats.usedBlocks;
3105
            }
3106
          }
3107
          else {
3108
            auto prod = static_cast<ExplicitProducer*>(ptr);
3109
            stats.queueClassBytes += sizeof(ExplicitProducer);
3110
            auto tailBlock = prod->tailBlock;
3111
            bool wasNonEmpty = false;
3112
            if (tailBlock != nullptr) {
3113
              auto block = tailBlock;
3114
              do {
3115
                ++stats.allocatedBlocks;
3116
                if (!block->ConcurrentQueue::Block::template is_empty<explicit_context>() || wasNonEmpty) {
3117
                  ++stats.usedBlocks;
3118
                  wasNonEmpty = wasNonEmpty || block != tailBlock;
3119
                }
3120
                ++stats.ownedBlocksExplicit;
3121
                block = block->next;
3122
              } while (block != tailBlock);
3123
            }
3124
            auto index = prod->blockIndex.load(std::memory_order_relaxed);
3125
            while (index != nullptr) {
3126
              stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry);
3127
              index = static_cast<typename ExplicitProducer::BlockIndexHeader*>(index->prev);
3128
            }
3129
          }
3130
        }
3131
        
3132
        auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed);
3133
        stats.allocatedBlocks += freeOnInitialPool;
3134
        stats.freeBlocks += freeOnInitialPool;
3135
        
3136
        stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks;
3137
        stats.queueClassBytes += sizeof(ConcurrentQueue);
3138
        
3139
        return stats;
3140
      }
3141
    };
3142
    
3143
    // For debugging only. Not thread-safe.
3144
    MemStats getMemStats()
3145
    {
3146
      return MemStats::getFor(this);
3147
    }
3148
  private:
3149
    friend struct MemStats;
3150
#endif
3151
  
3152
  
3153
  //////////////////////////////////
3154
  // Producer list manipulation
3155
  //////////////////////////////////  
3156
  
3157
  ProducerBase* recycle_or_create_producer(bool isExplicit)
3158
86.4k
  {
3159
86.4k
    bool recycled;
3160
86.4k
    return recycle_or_create_producer(isExplicit, recycled);
3161
86.4k
  }
3162
  
3163
  ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled)
3164
117k
  {
3165
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3166
    debug::DebugLock lock(implicitProdMutex);
3167
#endif
3168
    // Try to re-use one first
3169
518k
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
3170
477k
      if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) {
3171
76.7k
        bool expected = true;
3172
76.7k
        if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) {
3173
          // We caught one! It's been marked as activated, the caller can have it
3174
76.7k
          recycled = true;
3175
76.7k
          return ptr;
3176
76.7k
        }
3177
76.7k
      }
3178
477k
    }
3179
    
3180
41.1k
    recycled = false;
3181
41.1k
    return add_producer(isExplicit ? static_cast<ProducerBase*>(create<ExplicitProducer>(this)) : create<ImplicitProducer>(this));
3182
117k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::recycle_or_create_producer(bool, bool&)
Line
Count
Source
3164
86.4k
  {
3165
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3166
    debug::DebugLock lock(implicitProdMutex);
3167
#endif
3168
    // Try to re-use one first
3169
95.8k
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
3170
86.1k
      if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) {
3171
76.7k
        bool expected = true;
3172
76.7k
        if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) {
3173
          // We caught one! It's been marked as activated, the caller can have it
3174
76.7k
          recycled = true;
3175
76.7k
          return ptr;
3176
76.7k
        }
3177
76.7k
      }
3178
86.1k
    }
3179
    
3180
9.65k
    recycled = false;
3181
9.65k
    return add_producer(isExplicit ? static_cast<ProducerBase*>(create<ExplicitProducer>(this)) : create<ImplicitProducer>(this));
3182
86.4k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::recycle_or_create_producer(bool, bool&)
Line
Count
Source
3164
31.5k
  {
3165
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3166
    debug::DebugLock lock(implicitProdMutex);
3167
#endif
3168
    // Try to re-use one first
3169
422k
    for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
3170
391k
      if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) {
3171
0
        bool expected = true;
3172
0
        if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) {
3173
          // We caught one! It's been marked as activated, the caller can have it
3174
0
          recycled = true;
3175
0
          return ptr;
3176
0
        }
3177
0
      }
3178
391k
    }
3179
    
3180
31.5k
    recycled = false;
3181
31.5k
    return add_producer(isExplicit ? static_cast<ProducerBase*>(create<ExplicitProducer>(this)) : create<ImplicitProducer>(this));
3182
31.5k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::recycle_or_create_producer(bool, bool&)
3183
  
3184
  ProducerBase* add_producer(ProducerBase* producer)
3185
41.1k
  {
3186
    // Handle failed memory allocation
3187
41.1k
    if (producer == nullptr) {
3188
0
      return nullptr;
3189
0
    }
3190
    
3191
41.1k
    producerCount.fetch_add(1, std::memory_order_relaxed);
3192
    
3193
    // Add it to the lock-free list
3194
41.1k
    auto prevTail = producerListTail.load(std::memory_order_relaxed);
3195
41.1k
    do {
3196
41.1k
      producer->next = prevTail;
3197
41.1k
    } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed));
3198
    
3199
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
3200
    if (producer->isExplicit) {
3201
      auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed);
3202
      do {
3203
        static_cast<ExplicitProducer*>(producer)->nextExplicitProducer = prevTailExplicit;
3204
      } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast<ExplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3205
    }
3206
    else {
3207
      auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed);
3208
      do {
3209
        static_cast<ImplicitProducer*>(producer)->nextImplicitProducer = prevTailImplicit;
3210
      } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast<ImplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3211
    }
3212
#endif
3213
    
3214
41.1k
    return producer;
3215
41.1k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_producer(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
Line
Count
Source
3185
9.65k
  {
3186
    // Handle failed memory allocation
3187
9.65k
    if (producer == nullptr) {
3188
0
      return nullptr;
3189
0
    }
3190
    
3191
9.65k
    producerCount.fetch_add(1, std::memory_order_relaxed);
3192
    
3193
    // Add it to the lock-free list
3194
9.65k
    auto prevTail = producerListTail.load(std::memory_order_relaxed);
3195
9.65k
    do {
3196
9.65k
      producer->next = prevTail;
3197
9.65k
    } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed));
3198
    
3199
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
3200
    if (producer->isExplicit) {
3201
      auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed);
3202
      do {
3203
        static_cast<ExplicitProducer*>(producer)->nextExplicitProducer = prevTailExplicit;
3204
      } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast<ExplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3205
    }
3206
    else {
3207
      auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed);
3208
      do {
3209
        static_cast<ImplicitProducer*>(producer)->nextImplicitProducer = prevTailImplicit;
3210
      } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast<ImplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3211
    }
3212
#endif
3213
    
3214
9.65k
    return producer;
3215
9.65k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_producer(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
Line
Count
Source
3185
31.5k
  {
3186
    // Handle failed memory allocation
3187
31.5k
    if (producer == nullptr) {
3188
0
      return nullptr;
3189
0
    }
3190
    
3191
31.5k
    producerCount.fetch_add(1, std::memory_order_relaxed);
3192
    
3193
    // Add it to the lock-free list
3194
31.5k
    auto prevTail = producerListTail.load(std::memory_order_relaxed);
3195
31.5k
    do {
3196
31.5k
      producer->next = prevTail;
3197
31.5k
    } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed));
3198
    
3199
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
3200
    if (producer->isExplicit) {
3201
      auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed);
3202
      do {
3203
        static_cast<ExplicitProducer*>(producer)->nextExplicitProducer = prevTailExplicit;
3204
      } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast<ExplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3205
    }
3206
    else {
3207
      auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed);
3208
      do {
3209
        static_cast<ImplicitProducer*>(producer)->nextImplicitProducer = prevTailImplicit;
3210
      } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast<ImplicitProducer*>(producer), std::memory_order_release, std::memory_order_relaxed));
3211
    }
3212
#endif
3213
    
3214
31.5k
    return producer;
3215
31.5k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::add_producer(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
3216
  
3217
  void reown_producers()
3218
  {
3219
    // After another instance is moved-into/swapped-with this one, all the
3220
    // producers we stole still think their parents are the other queue.
3221
    // So fix them up!
3222
    for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) {
3223
      ptr->parent = this;
3224
    }
3225
  }
3226
  
3227
  
3228
  //////////////////////////////////
3229
  // Implicit producer hash
3230
  //////////////////////////////////
3231
  
3232
  struct ImplicitProducerKVP
3233
  {
3234
    std::atomic<details::thread_id_t> key;
3235
    ImplicitProducer* value;    // No need for atomicity since it's only read by the thread that sets it in the first place
3236
    
3237
3.24M
    ImplicitProducerKVP() : value(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducerKVP::ImplicitProducerKVP()
Line
Count
Source
3237
285k
    ImplicitProducerKVP() : value(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducerKVP::ImplicitProducerKVP()
Line
Count
Source
3237
2.39M
    ImplicitProducerKVP() : value(nullptr) { }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducerKVP::ImplicitProducerKVP()
Line
Count
Source
3237
570k
    ImplicitProducerKVP() : value(nullptr) { }
3238
    
3239
    ImplicitProducerKVP(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT
3240
    {
3241
      key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed);
3242
      value = other.value;
3243
    }
3244
    
3245
    inline ImplicitProducerKVP& operator=(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT
3246
    {
3247
      swap(other);
3248
      return *this;
3249
    }
3250
    
3251
    inline void swap(ImplicitProducerKVP& other) MOODYCAMEL_NOEXCEPT
3252
    {
3253
      if (this != &other) {
3254
        details::swap_relaxed(key, other.key);
3255
        std::swap(value, other.value);
3256
      }
3257
    }
3258
  };
3259
  
3260
  template<typename XT, typename XTraits>
3261
  friend void duckdb_moodycamel::swap(typename ConcurrentQueue<XT, XTraits>::ImplicitProducerKVP&, typename ConcurrentQueue<XT, XTraits>::ImplicitProducerKVP&) MOODYCAMEL_NOEXCEPT;
3262
  
3263
  struct ImplicitProducerHash
3264
  {
3265
    size_t capacity;
3266
    ImplicitProducerKVP* entries;
3267
    ImplicitProducerHash* prev;
3268
  };
3269
  
3270
  inline void populate_initial_implicit_producer_hash()
3271
98.0k
  {
3272
98.0k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
3273
      return;
3274
    }
3275
98.0k
    else {
3276
98.0k
      implicitProducerHashCount.store(0, std::memory_order_relaxed);
3277
98.0k
      auto hash = &initialImplicitProducerHash;
3278
98.0k
      hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
3279
98.0k
      hash->entries = &initialImplicitProducerHashEntries[0];
3280
3.23M
      for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
3281
3.13M
        initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3282
3.13M
      }
3283
98.0k
      hash->prev = nullptr;
3284
98.0k
      implicitProducerHash.store(hash, std::memory_order_relaxed);
3285
98.0k
    }
3286
98.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_implicit_producer_hash()
Line
Count
Source
3271
8.91k
  {
3272
8.91k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
3273
      return;
3274
    }
3275
8.91k
    else {
3276
8.91k
      implicitProducerHashCount.store(0, std::memory_order_relaxed);
3277
8.91k
      auto hash = &initialImplicitProducerHash;
3278
8.91k
      hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
3279
8.91k
      hash->entries = &initialImplicitProducerHashEntries[0];
3280
294k
      for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
3281
285k
        initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3282
285k
      }
3283
8.91k
      hash->prev = nullptr;
3284
8.91k
      implicitProducerHash.store(hash, std::memory_order_relaxed);
3285
8.91k
    }
3286
8.91k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_implicit_producer_hash()
Line
Count
Source
3271
71.2k
  {
3272
71.2k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
3273
      return;
3274
    }
3275
71.2k
    else {
3276
71.2k
      implicitProducerHashCount.store(0, std::memory_order_relaxed);
3277
71.2k
      auto hash = &initialImplicitProducerHash;
3278
71.2k
      hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
3279
71.2k
      hash->entries = &initialImplicitProducerHashEntries[0];
3280
2.35M
      for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
3281
2.28M
        initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3282
2.28M
      }
3283
71.2k
      hash->prev = nullptr;
3284
71.2k
      implicitProducerHash.store(hash, std::memory_order_relaxed);
3285
71.2k
    }
3286
71.2k
  }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::populate_initial_implicit_producer_hash()
Line
Count
Source
3271
17.8k
  {
3272
17.8k
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
3273
      return;
3274
    }
3275
17.8k
    else {
3276
17.8k
      implicitProducerHashCount.store(0, std::memory_order_relaxed);
3277
17.8k
      auto hash = &initialImplicitProducerHash;
3278
17.8k
      hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE;
3279
17.8k
      hash->entries = &initialImplicitProducerHashEntries[0];
3280
588k
      for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) {
3281
570k
        initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3282
570k
      }
3283
17.8k
      hash->prev = nullptr;
3284
17.8k
      implicitProducerHash.store(hash, std::memory_order_relaxed);
3285
17.8k
    }
3286
17.8k
  }
3287
  
3288
  void swap_implicit_producer_hashes(ConcurrentQueue& other)
3289
  {
3290
    MOODYCAMEL_CONSTEXPR_IF (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) {
3291
      return;
3292
    }
3293
    else {
3294
      // Swap (assumes our implicit producer hash is initialized)
3295
      initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries);
3296
      initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0];
3297
      other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0];
3298
      
3299
      details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount);
3300
      
3301
      details::swap_relaxed(implicitProducerHash, other.implicitProducerHash);
3302
      if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) {
3303
        implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed);
3304
      }
3305
      else {
3306
        ImplicitProducerHash* hash;
3307
        for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) {
3308
          continue;
3309
        }
3310
        hash->prev = &initialImplicitProducerHash;
3311
      }
3312
      if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) {
3313
        other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed);
3314
      }
3315
      else {
3316
        ImplicitProducerHash* hash;
3317
        for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) {
3318
          continue;
3319
        }
3320
        hash->prev = &other.initialImplicitProducerHash;
3321
      }
3322
    }
3323
  }
3324
  
3325
  // Only fails (returns nullptr) if memory allocation fails
3326
  ImplicitProducer* get_or_add_implicit_producer()
3327
218k
  {
3328
    // Note that since the data is essentially thread-local (key is thread ID),
3329
    // there's a reduced need for fences (memory ordering is already consistent
3330
    // for any individual thread), except for the current table itself.
3331
    
3332
    // Start by looking for the thread ID in the current and all previous hash tables.
3333
    // If it's not found, it must not be in there yet, since this same thread would
3334
    // have added it previously to one of the tables that we traversed.
3335
    
3336
    // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
3337
    
3338
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3339
    debug::DebugLock lock(implicitProdMutex);
3340
#endif
3341
    
3342
218k
    auto id = details::thread_id();
3343
218k
    auto hashedId = details::hash_thread_id(id);
3344
    
3345
218k
    auto mainHash = implicitProducerHash.load(std::memory_order_acquire);
3346
218k
    assert(mainHash != nullptr);  // silence clang-tidy and MSVC warnings (hash cannot be null)
3347
280k
    for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
3348
      // Look for the id in this hash
3349
249k
      auto index = hashedId;
3350
339k
      while (true) {   // Not an infinite loop because at least one slot is free in the hash table
3351
339k
        index &= hash->capacity - 1;
3352
        
3353
339k
        auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
3354
339k
        if (probedKey == id) {
3355
          // Found it! If we had to search several hashes deep, though, we should lazily add it
3356
          // to the current main hash table to avoid the extended search next time.
3357
          // Note there's guaranteed to be room in the current hash table since every subsequent
3358
          // table implicitly reserves space for all previous tables (there's only one
3359
          // implicitProducerHashCount).
3360
186k
          auto value = hash->entries[index].value;
3361
186k
          if (hash != mainHash) {
3362
19.8k
            index = hashedId;
3363
26.1k
            while (true) {
3364
26.1k
              index &= mainHash->capacity - 1;
3365
26.1k
              probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
3366
26.1k
              auto empty = details::invalid_thread_id;
3367
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3368
              auto reusable = details::invalid_thread_id2;
3369
              if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed)) ||
3370
                (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) {
3371
#else
3372
26.1k
              if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed))) {
3373
19.8k
#endif
3374
19.8k
                mainHash->entries[index].value = value;
3375
19.8k
                break;
3376
19.8k
              }
3377
6.28k
              ++index;
3378
6.28k
            }
3379
19.8k
          }
3380
          
3381
186k
          return value;
3382
186k
        }
3383
152k
        if (probedKey == details::invalid_thread_id) {
3384
62.9k
          break;    // Not in this hash table
3385
62.9k
        }
3386
89.3k
        ++index;
3387
89.3k
      }
3388
249k
    }
3389
    
3390
    // Insert!
3391
31.2k
    auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed);
3392
31.5k
    while (true) {
3393
      // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
3394
31.5k
      if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) {
3395
        // We've acquired the resize lock, try to allocate a bigger hash table.
3396
        // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when
3397
        // we reload implicitProducerHash it must be the most recent version (it only gets changed within this
3398
        // locked block).
3399
1.36k
        mainHash = implicitProducerHash.load(std::memory_order_acquire);
3400
1.36k
        if (newCount >= (mainHash->capacity >> 1)) {
3401
1.36k
          auto newCapacity = mainHash->capacity << 1;
3402
1.36k
          while (newCount >= (newCapacity >> 1)) {
3403
0
            newCapacity <<= 1;
3404
0
          }
3405
1.36k
          auto raw = static_cast<char*>((Traits::malloc)(sizeof(ImplicitProducerHash) + std::alignment_of<ImplicitProducerKVP>::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity));
3406
1.36k
          if (raw == nullptr) {
3407
            // Allocation failed
3408
0
            implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3409
0
            implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
3410
0
            return nullptr;
3411
0
          }
3412
          
3413
1.36k
          auto newHash = new (raw) ImplicitProducerHash;
3414
1.36k
          newHash->capacity = newCapacity;
3415
1.36k
          newHash->entries = reinterpret_cast<ImplicitProducerKVP*>(details::align_for<ImplicitProducerKVP>(raw + sizeof(ImplicitProducerHash)));
3416
111k
          for (size_t i = 0; i != newCapacity; ++i) {
3417
110k
            new (newHash->entries + i) ImplicitProducerKVP;
3418
110k
            newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3419
110k
          }
3420
1.36k
          newHash->prev = mainHash;
3421
1.36k
          implicitProducerHash.store(newHash, std::memory_order_release);
3422
1.36k
          implicitProducerHashResizeInProgress.clear(std::memory_order_release);
3423
1.36k
          mainHash = newHash;
3424
1.36k
        }
3425
2
        else {
3426
2
          implicitProducerHashResizeInProgress.clear(std::memory_order_release);
3427
2
        }
3428
1.36k
      }
3429
      
3430
      // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table
3431
      // to finish being allocated by another thread (and if we just finished allocating above, the condition will
3432
      // always be true)
3433
31.5k
      if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) {
3434
31.5k
        bool recycled;
3435
31.5k
        auto producer = static_cast<ImplicitProducer*>(recycle_or_create_producer(false, recycled));
3436
31.5k
        if (producer == nullptr) {
3437
0
          implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3438
0
          return nullptr;
3439
0
        }
3440
31.5k
        if (recycled) {
3441
0
          implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3442
0
        }
3443
        
3444
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3445
        producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback;
3446
        producer->threadExitListener.userData = producer;
3447
        details::ThreadExitNotifier::subscribe(&producer->threadExitListener);
3448
#endif
3449
        
3450
31.5k
        auto index = hashedId;
3451
41.5k
        while (true) {
3452
41.5k
          index &= mainHash->capacity - 1;
3453
41.5k
          auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
3454
          
3455
41.5k
          auto empty = details::invalid_thread_id;
3456
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3457
          auto reusable = details::invalid_thread_id2;
3458
          if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed)) ||
3459
            (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) {
3460
#else
3461
41.5k
          if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed))) {
3462
31.5k
#endif
3463
31.5k
            mainHash->entries[index].value = producer;
3464
31.5k
            break;
3465
31.5k
          }
3466
9.94k
          ++index;
3467
9.94k
        }
3468
31.5k
        return producer;
3469
31.5k
      }
3470
      
3471
      // Hmm, the old hash is quite full and somebody else is busy allocating a new one.
3472
      // We need to wait for the allocating thread to finish (if it succeeds, we add, if not,
3473
      // we try to allocate ourselves).
3474
2
      mainHash = implicitProducerHash.load(std::memory_order_acquire);
3475
2
    }
3476
31.2k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::get_or_add_implicit_producer()
Line
Count
Source
3327
218k
  {
3328
    // Note that since the data is essentially thread-local (key is thread ID),
3329
    // there's a reduced need for fences (memory ordering is already consistent
3330
    // for any individual thread), except for the current table itself.
3331
    
3332
    // Start by looking for the thread ID in the current and all previous hash tables.
3333
    // If it's not found, it must not be in there yet, since this same thread would
3334
    // have added it previously to one of the tables that we traversed.
3335
    
3336
    // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
3337
    
3338
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3339
    debug::DebugLock lock(implicitProdMutex);
3340
#endif
3341
    
3342
218k
    auto id = details::thread_id();
3343
218k
    auto hashedId = details::hash_thread_id(id);
3344
    
3345
218k
    auto mainHash = implicitProducerHash.load(std::memory_order_acquire);
3346
218k
    assert(mainHash != nullptr);  // silence clang-tidy and MSVC warnings (hash cannot be null)
3347
280k
    for (auto hash = mainHash; hash != nullptr; hash = hash->prev) {
3348
      // Look for the id in this hash
3349
249k
      auto index = hashedId;
3350
339k
      while (true) {   // Not an infinite loop because at least one slot is free in the hash table
3351
339k
        index &= hash->capacity - 1;
3352
        
3353
339k
        auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
3354
339k
        if (probedKey == id) {
3355
          // Found it! If we had to search several hashes deep, though, we should lazily add it
3356
          // to the current main hash table to avoid the extended search next time.
3357
          // Note there's guaranteed to be room in the current hash table since every subsequent
3358
          // table implicitly reserves space for all previous tables (there's only one
3359
          // implicitProducerHashCount).
3360
186k
          auto value = hash->entries[index].value;
3361
186k
          if (hash != mainHash) {
3362
19.8k
            index = hashedId;
3363
26.1k
            while (true) {
3364
26.1k
              index &= mainHash->capacity - 1;
3365
26.1k
              probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
3366
26.1k
              auto empty = details::invalid_thread_id;
3367
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3368
              auto reusable = details::invalid_thread_id2;
3369
              if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed)) ||
3370
                (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) {
3371
#else
3372
26.1k
              if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed))) {
3373
19.8k
#endif
3374
19.8k
                mainHash->entries[index].value = value;
3375
19.8k
                break;
3376
19.8k
              }
3377
6.28k
              ++index;
3378
6.28k
            }
3379
19.8k
          }
3380
          
3381
186k
          return value;
3382
186k
        }
3383
152k
        if (probedKey == details::invalid_thread_id) {
3384
62.9k
          break;    // Not in this hash table
3385
62.9k
        }
3386
89.3k
        ++index;
3387
89.3k
      }
3388
249k
    }
3389
    
3390
    // Insert!
3391
31.2k
    auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed);
3392
31.5k
    while (true) {
3393
      // NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
3394
31.5k
      if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) {
3395
        // We've acquired the resize lock, try to allocate a bigger hash table.
3396
        // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when
3397
        // we reload implicitProducerHash it must be the most recent version (it only gets changed within this
3398
        // locked block).
3399
1.36k
        mainHash = implicitProducerHash.load(std::memory_order_acquire);
3400
1.36k
        if (newCount >= (mainHash->capacity >> 1)) {
3401
1.36k
          auto newCapacity = mainHash->capacity << 1;
3402
1.36k
          while (newCount >= (newCapacity >> 1)) {
3403
0
            newCapacity <<= 1;
3404
0
          }
3405
1.36k
          auto raw = static_cast<char*>((Traits::malloc)(sizeof(ImplicitProducerHash) + std::alignment_of<ImplicitProducerKVP>::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity));
3406
1.36k
          if (raw == nullptr) {
3407
            // Allocation failed
3408
0
            implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3409
0
            implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed);
3410
0
            return nullptr;
3411
0
          }
3412
          
3413
1.36k
          auto newHash = new (raw) ImplicitProducerHash;
3414
1.36k
          newHash->capacity = newCapacity;
3415
1.36k
          newHash->entries = reinterpret_cast<ImplicitProducerKVP*>(details::align_for<ImplicitProducerKVP>(raw + sizeof(ImplicitProducerHash)));
3416
111k
          for (size_t i = 0; i != newCapacity; ++i) {
3417
110k
            new (newHash->entries + i) ImplicitProducerKVP;
3418
110k
            newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed);
3419
110k
          }
3420
1.36k
          newHash->prev = mainHash;
3421
1.36k
          implicitProducerHash.store(newHash, std::memory_order_release);
3422
1.36k
          implicitProducerHashResizeInProgress.clear(std::memory_order_release);
3423
1.36k
          mainHash = newHash;
3424
1.36k
        }
3425
2
        else {
3426
2
          implicitProducerHashResizeInProgress.clear(std::memory_order_release);
3427
2
        }
3428
1.36k
      }
3429
      
3430
      // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table
3431
      // to finish being allocated by another thread (and if we just finished allocating above, the condition will
3432
      // always be true)
3433
31.5k
      if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) {
3434
31.5k
        bool recycled;
3435
31.5k
        auto producer = static_cast<ImplicitProducer*>(recycle_or_create_producer(false, recycled));
3436
31.5k
        if (producer == nullptr) {
3437
0
          implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3438
0
          return nullptr;
3439
0
        }
3440
31.5k
        if (recycled) {
3441
0
          implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed);
3442
0
        }
3443
        
3444
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3445
        producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback;
3446
        producer->threadExitListener.userData = producer;
3447
        details::ThreadExitNotifier::subscribe(&producer->threadExitListener);
3448
#endif
3449
        
3450
31.5k
        auto index = hashedId;
3451
41.5k
        while (true) {
3452
41.5k
          index &= mainHash->capacity - 1;
3453
41.5k
          auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed);
3454
          
3455
41.5k
          auto empty = details::invalid_thread_id;
3456
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3457
          auto reusable = details::invalid_thread_id2;
3458
          if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed)) ||
3459
            (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) {
3460
#else
3461
41.5k
          if ((probedKey == empty    && mainHash->entries[index].key.compare_exchange_strong(empty,    id, std::memory_order_relaxed, std::memory_order_relaxed))) {
3462
31.5k
#endif
3463
31.5k
            mainHash->entries[index].value = producer;
3464
31.5k
            break;
3465
31.5k
          }
3466
9.94k
          ++index;
3467
9.94k
        }
3468
31.5k
        return producer;
3469
31.5k
      }
3470
      
3471
      // Hmm, the old hash is quite full and somebody else is busy allocating a new one.
3472
      // We need to wait for the allocating thread to finish (if it succeeds, we add, if not,
3473
      // we try to allocate ourselves).
3474
2
      mainHash = implicitProducerHash.load(std::memory_order_acquire);
3475
2
    }
3476
31.2k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::get_or_add_implicit_producer()
3477
  
3478
#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED
3479
  void implicit_producer_thread_exited(ImplicitProducer* producer)
3480
  {
3481
    // Remove from thread exit listeners
3482
    details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener);
3483
    
3484
    // Remove from hash
3485
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3486
    debug::DebugLock lock(implicitProdMutex);
3487
#endif
3488
    auto hash = implicitProducerHash.load(std::memory_order_acquire);
3489
    assert(hash != nullptr);    // The thread exit listener is only registered if we were added to a hash in the first place
3490
    auto id = details::thread_id();
3491
    auto hashedId = details::hash_thread_id(id);
3492
    details::thread_id_t probedKey;
3493
    
3494
    // We need to traverse all the hashes just in case other threads aren't on the current one yet and are
3495
    // trying to add an entry thinking there's a free slot (because they reused a producer)
3496
    for (; hash != nullptr; hash = hash->prev) {
3497
      auto index = hashedId;
3498
      do {
3499
        index &= hash->capacity - 1;
3500
        probedKey = hash->entries[index].key.load(std::memory_order_relaxed);
3501
        if (probedKey == id) {
3502
          hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release);
3503
          break;
3504
        }
3505
        ++index;
3506
      } while (probedKey != details::invalid_thread_id);    // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place
3507
    }
3508
    
3509
    // Mark the queue as being recyclable
3510
    producer->inactive.store(true, std::memory_order_release);
3511
  }
3512
  
3513
  static void implicit_producer_thread_exited_callback(void* userData)
3514
  {
3515
    auto producer = static_cast<ImplicitProducer*>(userData);
3516
    auto queue = producer->parent;
3517
    queue->implicit_producer_thread_exited(producer);
3518
  }
3519
#endif
3520
  
3521
  //////////////////////////////////
3522
  // Utility functions
3523
  //////////////////////////////////
3524
3525
  template<typename TAlign>
3526
  static inline void* aligned_malloc(size_t size)
3527
164k
  {
3528
164k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
164k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
void* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3527
8.91k
  {
3528
8.91k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
8.91k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
void* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(unsigned long)
Line
Count
Source
3527
9.65k
  {
3528
9.65k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
9.65k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
Unexecuted instantiation: void* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(unsigned long)
Unexecuted instantiation: void* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(unsigned long)
void* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(unsigned long)
Line
Count
Source
3527
31.4k
  {
3528
31.4k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
31.4k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
void* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3527
96.3k
  {
3528
96.3k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
96.3k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
Unexecuted instantiation: void* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer>(unsigned long)
Unexecuted instantiation: void* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer>(unsigned long)
void* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_malloc<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3527
17.8k
  {
3528
17.8k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3529
17.8k
      return (Traits::malloc)(size);
3530
0
    size_t alignment = std::alignment_of<TAlign>::value;
3531
0
    void* raw = (Traits::malloc)(size + alignment - 1 + sizeof(void*));
3532
0
    if (!raw)
3533
0
      return nullptr;
3534
0
    char* ptr = details::align_for<TAlign>(reinterpret_cast<char*>(raw) + sizeof(void*));
3535
0
    *(reinterpret_cast<void**>(ptr) - 1) = raw;
3536
0
    return ptr;
3537
0
  }
3538
3539
  template<typename TAlign>
3540
  static inline void aligned_free(void* ptr)
3541
164k
  {
3542
164k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
164k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(void*)
Line
Count
Source
3541
9.65k
  {
3542
9.65k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
9.65k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(void*)
Line
Count
Source
3541
8.91k
  {
3542
8.91k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
8.91k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(void*)
Line
Count
Source
3541
31.5k
  {
3542
31.5k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
31.5k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(void*)
Line
Count
Source
3541
96.3k
  {
3542
96.3k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
96.3k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
Unexecuted instantiation: void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(void*)
void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::aligned_free<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(void*)
Line
Count
Source
3541
17.8k
  {
3542
17.8k
    if (std::alignment_of<TAlign>::value <= std::alignment_of<details::max_align_t>::value)
3543
17.8k
      return (Traits::free)(ptr);
3544
0
    (Traits::free)(ptr ? *(reinterpret_cast<void**>(ptr) - 1) : nullptr);
3545
0
  }
3546
3547
  template<typename U>
3548
  static inline U* create_array(size_t count)
3549
98.0k
  {
3550
98.0k
    assert(count > 0);
3551
98.0k
    U* p = static_cast<U*>(aligned_malloc<U>(sizeof(U) * count));
3552
98.0k
    if (p == nullptr)
3553
0
      return nullptr;
3554
3555
686k
    for (size_t i = 0; i != count; ++i)
3556
588k
      new (p + i) U();
3557
98.0k
    return p;
3558
98.0k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create_array<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3549
8.91k
  {
3550
8.91k
    assert(count > 0);
3551
8.91k
    U* p = static_cast<U*>(aligned_malloc<U>(sizeof(U) * count));
3552
8.91k
    if (p == nullptr)
3553
0
      return nullptr;
3554
3555
62.3k
    for (size_t i = 0; i != count; ++i)
3556
53.4k
      new (p + i) U();
3557
8.91k
    return p;
3558
8.91k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create_array<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3549
71.2k
  {
3550
71.2k
    assert(count > 0);
3551
71.2k
    U* p = static_cast<U*>(aligned_malloc<U>(sizeof(U) * count));
3552
71.2k
    if (p == nullptr)
3553
0
      return nullptr;
3554
3555
498k
    for (size_t i = 0; i != count; ++i)
3556
427k
      new (p + i) U();
3557
71.2k
    return p;
3558
71.2k
  }
duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create_array<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(unsigned long)
Line
Count
Source
3549
17.8k
  {
3550
17.8k
    assert(count > 0);
3551
17.8k
    U* p = static_cast<U*>(aligned_malloc<U>(sizeof(U) * count));
3552
17.8k
    if (p == nullptr)
3553
0
      return nullptr;
3554
3555
124k
    for (size_t i = 0; i != count; ++i)
3556
106k
      new (p + i) U();
3557
17.8k
    return p;
3558
17.8k
  }
3559
3560
  template<typename U>
3561
  static inline void destroy_array(U* p, size_t count)
3562
98.0k
  {
3563
98.0k
    if (p != nullptr) {
3564
98.0k
      assert(count > 0);
3565
686k
      for (size_t i = count; i != 0; )
3566
588k
        (p + --i)->~U();
3567
98.0k
    }
3568
98.0k
    aligned_free<U>(p);
3569
98.0k
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy_array<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*, unsigned long)
Line
Count
Source
3562
8.91k
  {
3563
8.91k
    if (p != nullptr) {
3564
8.91k
      assert(count > 0);
3565
62.3k
      for (size_t i = count; i != 0; )
3566
53.4k
        (p + --i)->~U();
3567
8.91k
    }
3568
8.91k
    aligned_free<U>(p);
3569
8.91k
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy_array<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*, unsigned long)
Line
Count
Source
3562
71.2k
  {
3563
71.2k
    if (p != nullptr) {
3564
71.2k
      assert(count > 0);
3565
498k
      for (size_t i = count; i != 0; )
3566
427k
        (p + --i)->~U();
3567
71.2k
    }
3568
71.2k
    aligned_free<U>(p);
3569
71.2k
  }
void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy_array<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*, unsigned long)
Line
Count
Source
3562
17.8k
  {
3563
17.8k
    if (p != nullptr) {
3564
17.8k
      assert(count > 0);
3565
124k
      for (size_t i = count; i != 0; )
3566
106k
        (p + --i)->~U();
3567
17.8k
    }
3568
17.8k
    aligned_free<U>(p);
3569
17.8k
  }
3570
3571
  template<typename U>
3572
  static inline U* create()
3573
25.0k
  {
3574
25.0k
    void* p = aligned_malloc<U>(sizeof(U));
3575
18.4E
    return p != nullptr ? new (p) U : nullptr;
3576
25.0k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>()
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>()
Line
Count
Source
3573
25.0k
  {
3574
25.0k
    void* p = aligned_malloc<U>(sizeof(U));
3575
18.4E
    return p != nullptr ? new (p) U : nullptr;
3576
25.0k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>()
3577
3578
  template<typename U, typename A1>
3579
  static inline U* create(A1&& a1)
3580
41.1k
  {
3581
41.1k
    void* p = aligned_malloc<U>(sizeof(U));
3582
18.4E
    return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
3583
41.1k
  }
duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer, duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
Line
Count
Source
3580
9.65k
  {
3581
9.65k
    void* p = aligned_malloc<U>(sizeof(U));
3582
9.65k
    return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
3583
9.65k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer* duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer, duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer, duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer* duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer, duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
Line
Count
Source
3580
31.4k
  {
3581
31.4k
    void* p = aligned_malloc<U>(sizeof(U));
3582
18.4E
    return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
3583
31.4k
  }
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ExplicitProducer, duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
Unexecuted instantiation: duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer* duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::create<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ImplicitProducer, duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>*&&)
3584
3585
  template<typename U>
3586
  static inline void destroy(U* p)
3587
66.2k
  {
3588
66.2k
    if (p != nullptr)
3589
66.2k
      p->~U();
3590
66.2k
    aligned_free<U>(p);
3591
66.2k
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
Line
Count
Source
3587
9.65k
  {
3588
9.65k
    if (p != nullptr)
3589
9.65k
      p->~U();
3590
9.65k
    aligned_free<U>(p);
3591
9.65k
  }
Unexecuted instantiation: void duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<duckdb::shared_ptr<duckdb::Task, true>, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
Line
Count
Source
3587
31.5k
  {
3588
31.5k
    if (p != nullptr)
3589
31.5k
      p->~U();
3590
31.5k
    aligned_free<U>(p);
3591
31.5k
  }
void duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<duckdb::BufferEvictionNode, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
Line
Count
Source
3587
25.0k
  {
3588
25.0k
    if (p != nullptr)
3589
25.0k
      p->~U();
3590
25.0k
    aligned_free<U>(p);
3591
25.0k
  }
Unexecuted instantiation: void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::ProducerBase*)
Unexecuted instantiation: void duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::destroy<duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block>(duckdb_moodycamel::ConcurrentQueue<unsigned int, duckdb_moodycamel::ConcurrentQueueDefaultTraits>::Block*)
3592
3593
private:
3594
  std::atomic<ProducerBase*> producerListTail;
3595
  std::atomic<std::uint32_t> producerCount;
3596
  
3597
  std::atomic<size_t> initialBlockPoolIndex;
3598
  Block* initialBlockPool;
3599
  size_t initialBlockPoolSize;
3600
  
3601
#ifndef MCDBGQ_USEDEBUGFREELIST
3602
  FreeList<Block> freeList;
3603
#else
3604
  debug::DebugFreeList<Block> freeList;
3605
#endif
3606
  
3607
  std::atomic<ImplicitProducerHash*> implicitProducerHash;
3608
  std::atomic<size_t> implicitProducerHashCount;    // Number of slots logically used
3609
  ImplicitProducerHash initialImplicitProducerHash;
3610
  std::array<ImplicitProducerKVP, INITIAL_IMPLICIT_PRODUCER_HASH_SIZE> initialImplicitProducerHashEntries;
3611
  std::atomic_flag implicitProducerHashResizeInProgress;
3612
  
3613
  std::atomic<std::uint32_t> nextExplicitConsumerId;
3614
  std::atomic<std::uint32_t> globalExplicitConsumerOffset;
3615
  
3616
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
3617
  debug::DebugMutex implicitProdMutex;
3618
#endif
3619
  
3620
#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG
3621
  std::atomic<ExplicitProducer*> explicitProducers;
3622
  std::atomic<ImplicitProducer*> implicitProducers;
3623
#endif
3624
};
3625
3626
3627
template<typename T, typename Traits>
3628
ProducerToken::ProducerToken(ConcurrentQueue<T, Traits>& queue)
3629
86.4k
  : producer(queue.recycle_or_create_producer(true))
3630
86.4k
{
3631
86.4k
  if (producer != nullptr) {
3632
86.4k
    producer->token = this;
3633
86.4k
  }
3634
86.4k
}
3635
3636
template<typename T, typename Traits>
3637
ProducerToken::ProducerToken(BlockingConcurrentQueue<T, Traits>& queue)
3638
  : producer(reinterpret_cast<ConcurrentQueue<T, Traits>*>(&queue)->recycle_or_create_producer(true))
3639
{
3640
  if (producer != nullptr) {
3641
    producer->token = this;
3642
  }
3643
}
3644
3645
template<typename T, typename Traits>
3646
ConsumerToken::ConsumerToken(ConcurrentQueue<T, Traits>& queue)
3647
  : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr)
3648
{
3649
  initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release);
3650
  lastKnownGlobalOffset = uint32_t(-1);
3651
}
3652
3653
template<typename T, typename Traits>
3654
ConsumerToken::ConsumerToken(BlockingConcurrentQueue<T, Traits>& queue)
3655
  : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr)
3656
{
3657
  initialOffset = reinterpret_cast<ConcurrentQueue<T, Traits>*>(&queue)->nextExplicitConsumerId.fetch_add(1, std::memory_order_release);
3658
  lastKnownGlobalOffset = uint32_t(-1);
3659
}
3660
3661
template<typename T, typename Traits>
3662
inline void swap(ConcurrentQueue<T, Traits>& a, ConcurrentQueue<T, Traits>& b) MOODYCAMEL_NOEXCEPT
3663
{
3664
  a.swap(b);
3665
}
3666
3667
inline void swap(ProducerToken& a, ProducerToken& b) MOODYCAMEL_NOEXCEPT
3668
0
{
3669
0
  a.swap(b);
3670
0
}
3671
3672
inline void swap(ConsumerToken& a, ConsumerToken& b) MOODYCAMEL_NOEXCEPT
3673
0
{
3674
0
  a.swap(b);
3675
0
}
3676
3677
template<typename T, typename Traits>
3678
inline void swap(typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& a, typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT
3679
{
3680
  a.swap(b);
3681
}
3682
3683
}
3684