• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
You are now the owner of this repo.

NeonGE / geEngineSDK / 7493b2d6-7677-4acb-8720-c03b9f3d0381

05 Mar 2026 07:07PM UTC coverage: 57.844% (+0.01%) from 57.834%
7493b2d6-7677-4acb-8720-c03b9f3d0381

push

circleci

NeonGE
Add SonarCloud quality gate badge to README

Added a SonarCloud Quality Gate Status badge to the README to display code quality and analysis results directly in the project documentation. This helps track code health and maintain high standards.

5608 of 9695 relevant lines covered (57.84%)

9101.61 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);
116✔
111
          m_readyCond.wait(lock, [this] { return m_threadReady; });
321✔
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.");
82✔
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,659✔
172
    Lock lock(m_mutex);
2,659✔
173
    return m_idle;
2,659✔
174
  }
2,659✔
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 {
242
          idleThreads.push_back(pThread);
×
243
        }
244
      }
245
      else {
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) {
608✔
288
      if (pThread->isIdle()) {
574✔
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 {
142✔
312
    Vector<PooledThread*> threadsCopy;
142✔
313
    {
314
      Lock lock(m_mutex);
142✔
315
      threadsCopy = m_threads;
142✔
316
    }
142✔
317

318
    SIZE_T numAvailable = m_maxCapacity;
142✔
319
    for (auto* pThread : threadsCopy) {
2,221✔
320
      if (!pThread->isIdle()) {
2,079✔
321
        --numAvailable;
1,906✔
322
      }
323
    }
324
    return numAvailable;
142✔
325
  }
142✔
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