• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NeonGE / geEngineSDK / c205df6d-fac3-4706-bdb2-5cd6ce1878bb

05 Mar 2026 06:08PM UTC coverage: 57.868% (-0.04%) from 57.909%
c205df6d-fac3-4706-bdb2-5cd6ce1878bb

push

circleci

NeonGE
Improve type safety and noexcept guarantees in core types

Replaced unsafe reinterpret_casts with bit_cast for safer type punning in math functions. Marked move constructors, assignment operators, and swap methods as _NOEXCEPT in StdAlloc, SmallVector, StackMemory, and Vector2I to ensure exception safety and better compatibility with standard containers.

5 of 5 new or added lines in 1 file covered. (100.0%)

7 existing lines in 2 files now uncovered.

5608 of 9691 relevant lines covered (57.87%)

9104.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.91
/sdk/geUtilities/src/geThreadPool.cpp
1
/*****************************************************************************/
2
/**
3
 * @file    geThreadPool.cpp
4
 * @author  Samuel Prince (samuel.prince.quezada@gmail.com)
5
 * @date    2017/06/05
6
 * @brief   Class that maintains a pool of threads we can easily use.
7
 *
8
 * Class that maintains a pool of threads we can easily retrieve and use for
9
 * any task. This saves on the cost of creating and destroying threads.
10
 *
11
 * @bug     No known bugs.
12
 */
13
/*****************************************************************************/
14

15
/*****************************************************************************/
16
/**
17
 * Includes
18
 */
19
/*****************************************************************************/
20
#include "geThreadPool.h"
21
#include "geDebug.h"
22
#include "geMath.h"
23

24
#if USING(GE_PLATFORM_WINDOWS)
25
# include "Win32/geMinWindows.h"
26
# if USING(GE_COMPILER_MSVC)
27
  //disable: nonstandard extension used: 'X' uses SEH and 'Y' has destructor
28
  //We don't care about this as any exception is meant to crash the program.
29
#   pragma warning(disable: 4509)
30
# endif
31
#endif
32

33
namespace geEngineSDK {
34
  using std::bind;
35
  using std::function;
36
  using std::time;
37

38
  /**
39
   * The thread pool will check for unused threads every UNUSED_CHECK_PERIOD
40
   * getThread() calls
41
   */
42
  static CONSTEXPR int32 UNUSED_CHECK_PERIOD = 32;
43

44
  HThread::HThread(ThreadPool* pool, uint32 threadId)
83✔
45
    : m_threadId(threadId),
83✔
46
      m_pool(pool) {}
83✔
47

48
  void
49
  HThread::blockUntilComplete() {
14✔
50
    PooledThread* parentThread = nullptr;
14✔
51
    {//Scope for the Lock operation
52
      Lock lock(m_pool->m_mutex);
14✔
53

54
      for (auto& refThread : m_pool->m_threads) {
16✔
55
        if (refThread->getId() == m_threadId) {
16✔
56
          parentThread = refThread;
14✔
57
          break;
14✔
58
        }
59
      }
60
    }
14✔
61

62
    if (nullptr != parentThread) {
14✔
63
      Lock lock(parentThread->m_mutex);
14✔
64
      if (parentThread->m_id == m_threadId) {
14✔
65
        parentThread->m_workerEndedCond.wait(lock, [&]
14✔
66
        {
67
          return parentThread->m_idle || parentThread->m_id != m_threadId;
26✔
68
        });
69
      }
70
    }
14✔
71
  }
14✔
72

73
  void
74
  PooledThread::initialize() {
33✔
75
    m_thread = ge_new<Thread>(bind(&PooledThread::run, this));
33✔
76
    Lock lock(m_mutex);
33✔
77
    m_startedCond.wait(lock, [this] { return m_threadStarted; });
99✔
78
  }
33✔
79

80
  void
81
  PooledThread::start(const function<void()>& workerMethod, uint32 id) {
83✔
82
    {//Scope for the Lock operation
83
      Lock lock(m_mutex);
83✔
84

85
      m_workerMethod = workerMethod;
83✔
86
      m_idle = false;
83✔
87
      m_idleTime = time(nullptr);
83✔
88
      m_threadReady = true;
83✔
89
      m_id = id;
83✔
90
    }
83✔
91
    m_readyCond.notify_one();
83✔
92
  }
83✔
93

94
  void
95
  PooledThread::run() {
33✔
96
    onThreadStarted(m_name);
33✔
97
    
98
    {
99
      Lock lock(m_mutex);
33✔
100
      m_threadStarted = true;
33✔
101
    }
33✔
102

103
    m_startedCond.notify_one();
33✔
104

105
    while (true) {
106
      function<void()> worker = nullptr;
116✔
107

108
      {
109
        {
110
          Lock lock(m_mutex);
115✔
111
          m_readyCond.wait(lock, [this] { return m_threadReady; });
323✔
112
          worker = m_workerMethod;
91✔
113
        }
91✔
114

115
        if (nullptr == worker) {
91✔
116
          onThreadEnded(m_name);
8✔
117
          return;
16✔
118
        }
119
      }
120

121
      workingMethodRun(worker);
83✔
122

123
      {
124
        Lock lock(m_mutex);
83✔
125

126
        m_idle = true;
83✔
127
        m_idleTime = time(nullptr);
83✔
128
        m_threadReady = false;
83✔
129
        m_workerMethod = nullptr; //Make sure to clear as it could have bound shared pointers
83✔
130

131
        m_workerEndedCond.notify_one();
83✔
132
      }
83✔
133
    }
174✔
134
  }
135

136
  void
137
  PooledThread::workingMethodRun(const function<void()>& worker) {
83✔
138
#if USING(GE_PLATFORM_WINDOWS)
139
    __try {
140
      worker();
141
    }
142
    __except (g_crashHandler().reportCrash(GetExceptionInformation())) {
143
      PlatformUtility::terminate(true);
144
    }
145
#else
146
    worker();
83✔
147
    GE_LOG(kWarning, Generic, "Starting a thread with no error handling.");
83✔
148
#endif
149
  }
83✔
150

151
  void
152
  PooledThread::destroy() {
8✔
153
    blockUntilComplete();
8✔
154
    {
155
      Lock lock(m_mutex);
8✔
156
      m_workerMethod = nullptr;
8✔
157
      m_threadReady = true;
8✔
158
    }
8✔
159
    m_readyCond.notify_one();
8✔
160
    m_thread->join();
8✔
161
    ge_delete(m_thread);
8✔
162
  }
8✔
163

164
  void
165
  PooledThread::blockUntilComplete() {
8✔
166
    Lock lock(m_mutex);
8✔
167
    m_workerEndedCond.wait(lock, [this] { return m_idle; });
16✔
168
  }
8✔
169

170
  bool
171
  PooledThread::isIdle() {
2,785✔
172
    Lock lock(m_mutex);
2,785✔
173
    return m_idle;
2,785✔
174
  }
2,785✔
175

176
  time_t
177
  PooledThread::idleTime() {
4✔
178
    Lock lock(m_mutex);
4✔
179
    return (time(nullptr) - m_idleTime);
8✔
180
  }
4✔
181

182
  void
183
  PooledThread::setName(const String& name) {
50✔
184
    m_name = name;
50✔
185
  }
50✔
186

187
  uint32
188
  PooledThread::getId() const {
99✔
189
    Lock lock(m_mutex);
99✔
190
    return m_id;
99✔
191
  }
99✔
192

193
  ThreadPool::ThreadPool(SIZE_T threadCapacity, SIZE_T maxCapacity, uint32 idleTimeout)
11✔
194
    : m_defaultCapacity(threadCapacity),
11✔
195
      m_maxCapacity(maxCapacity),
11✔
196
      m_idleTimeout(idleTimeout)
11✔
197
  {}
11✔
198

199
  ThreadPool::~ThreadPool() {
6✔
200
    stopAll();
6✔
201
  }
6✔
202

203
  HThread
204
  ThreadPool::run(const String& name, const function<void()>& workerMethod) {
84✔
205
    PooledThread* pThread = getThread(name);
84✔
206
    pThread->start(workerMethod, ++m_uniqueId);
83✔
207
    return HThread(this, pThread->getId());
83✔
208
  }
209

210
  void
211
  ThreadPool::stopAll() {
6✔
212
    Lock lock(m_mutex);
6✔
213
    for (auto& myThread : m_threads) {
11✔
214
      destroyThread(myThread);
5✔
215
    }
216
    m_threads.clear();
6✔
217
  }
6✔
218

219
  void
220
  ThreadPool::clearUnused() {
4✔
221
    Lock lock(m_mutex);
4✔
222
    m_age = 0;
4✔
223

224
    if (m_threads.size() <= m_defaultCapacity) {
4✔
225
      return;
2✔
226
    }
227

228
    Vector<PooledThread*> idleThreads;
2✔
229
    Vector<PooledThread*> expiredThreads;
2✔
230
    Vector<PooledThread*> activeThreads;
2✔
231

232
    idleThreads.reserve(m_threads.size());
2✔
233
    expiredThreads.reserve(m_threads.size());
2✔
234
    activeThreads.reserve(m_threads.size());
2✔
235

236
    for (auto& pThread : m_threads) {
6✔
237
      if (pThread->isIdle()) {
4✔
238
        if (pThread->idleTime() >= m_idleTimeout) {
4✔
239
          expiredThreads.push_back(pThread);
4✔
240
        }
241
        else {
UNCOV
242
          idleThreads.push_back(pThread);
×
243
        }
244
      }
245
      else {
UNCOV
246
        activeThreads.push_back(pThread);
×
247
      }
248
    }
249

250
    idleThreads.insert(idleThreads.end(), expiredThreads.begin(), expiredThreads.end());
2✔
251
    SIZE_T limit = Math::min(idleThreads.size(), m_defaultCapacity);
2✔
252
    m_threads.clear();
2✔
253

254
    for (SIZE_T i = 0; i < idleThreads.size(); ++i) {
6✔
255
      PooledThread* pThread = idleThreads[i];
4✔
256
      if (i < limit) {
4✔
257
        m_threads.push_back(pThread);
1✔
258
      }
259
      else {
260
        destroyThread(pThread);
3✔
261
      }
262
    }
263

264
    m_threads.insert(m_threads.end(), activeThreads.begin(), activeThreads.end());
2✔
265
  }
4✔
266

267
  void
268
  ThreadPool::destroyThread(PooledThread* thread) {
8✔
269
    thread->destroy();
8✔
270
    ge_delete(thread);
8✔
271
  }
8✔
272

273
  PooledThread*
274
  ThreadPool::getThread(const String& name) {
84✔
275
    uint32 age = 0;
84✔
276
    {
277
      Lock lock(m_mutex);
84✔
278
      age = ++m_age;
84✔
279
    }
84✔
280

281
    if (UNUSED_CHECK_PERIOD == age) {
84✔
282
      clearUnused();
2✔
283
    }
284

285
    Lock lock(m_mutex);
84✔
286

287
    for (auto& pThread : m_threads) {
590✔
288
      if (pThread->isIdle()) {
556✔
289
        pThread->setName(name);
50✔
290
        return pThread;
50✔
291
      }
292
    }
293

294
    if (m_threads.size() >= m_maxCapacity) {
34✔
295
      //This version avoids a crash
296
      throw std::runtime_error("ThreadPool exhausted");
1✔
297
      /*
298
      GE_EXCEPT(InvalidStateException,
299
                "Unable to create a new thread in the pool because "          \
300
                "maximum capacity has been reached.");
301
      */
302
    }
303

304
    PooledThread* newThread = createThread(name);
33✔
305
    m_threads.push_back(newThread);
33✔
306

307
    return newThread;
33✔
308
  }
84✔
309

310
  SIZE_T
311
  ThreadPool::getNumAvailable() const {
151✔
312
    Vector<PooledThread*> threadsCopy;
151✔
313
    {
314
      Lock lock(m_mutex);
151✔
315
      threadsCopy = m_threads;
151✔
316
    }
151✔
317

318
    SIZE_T numAvailable = m_maxCapacity;
151✔
319
    for (auto* pThread : threadsCopy) {
2,374✔
320
      if (!pThread->isIdle()) {
2,223✔
321
        --numAvailable;
2,082✔
322
      }
323
    }
324
    return numAvailable;
151✔
325
  }
151✔
326

327
  SIZE_T
328
  ThreadPool::getNumActive() const {
2✔
329
    Vector<PooledThread*> threadsCopy;
2✔
330
    {
331
      Lock lock(m_mutex);
2✔
332
      threadsCopy = m_threads;
2✔
333
    }
2✔
334

335
    SIZE_T numActive = 0;
2✔
336
    Lock lock(m_mutex);
2✔
337
    for (auto* pThread : threadsCopy) {
4✔
338
      if (!pThread->isIdle()) {
2✔
339
        ++numActive;
1✔
340
      }
341
    }
342
    return numActive;
2✔
343
  }
2✔
344

345
  SIZE_T
346
  ThreadPool::getNumAllocated() const {
7✔
347
    Lock lock(m_mutex);
7✔
348
    return m_threads.size();
14✔
349
  }
7✔
350
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc