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

NeonGE / geEngineSDK / a1b152f2-f09f-4c89-b291-28b6f61c8e39

04 Mar 2026 08:52PM UTC coverage: 57.821% (+0.01%) from 57.811%
a1b152f2-f09f-4c89-b291-28b6f61c8e39

push

circleci

NeonGE
Set GIT_LFS_SKIP_SMUDGE for ktx_software fetch

Set the GIT_LFS_SKIP_SMUDGE environment variable to "1" before declaring FetchContent for ktx_software. This prevents automatic downloading of Git LFS files during the initial clone, improving fetch speed and avoiding unnecessary downloads.

5604 of 9692 relevant lines covered (57.82%)

9102.32 hits per line

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

98.32
/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) { //Check again in case it changed
14✔
65
        while (!parentThread->m_idle) {
26✔
66
          parentThread->m_workerEndedCond.wait(lock);
12✔
67
        }
68
      }
69
    }
14✔
70
  }
14✔
71

72
  void
73
  PooledThread::initialize() {
33✔
74
    m_thread = ge_new<Thread>(bind(&PooledThread::run, this));
33✔
75
    Lock lock(m_mutex);
33✔
76

77
    while (!m_threadStarted) {
66✔
78
      m_startedCond.wait(lock);
33✔
79
    }
80
  }
33✔
81

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

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

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

105
    m_startedCond.notify_one();
33✔
106

107
    while (true) {
108
      function<void()> worker = nullptr;
116✔
109
      
110
      {
111
        {
112
          Lock lock(m_mutex);
116✔
113

114
          while (!m_threadReady) {
205✔
115
            m_readyCond.wait(lock);
114✔
116
          }
117
          worker = m_workerMethod;
91✔
118
        }
91✔
119

120
        if (nullptr == worker) {
91✔
121
          onThreadEnded(m_name);
8✔
122
          return;
16✔
123
        }
124
      }
125

126
      workingMethodRun(worker);
83✔
127

128
      {
129
        Lock lock(m_mutex);
83✔
130

131
        m_idle = true;
83✔
132
        m_idleTime = time(nullptr);
83✔
133
        m_threadReady = false;
83✔
134
        m_workerMethod = nullptr; //Make sure to clear as it could have bound shared pointers
83✔
135

136
        m_workerEndedCond.notify_one();
83✔
137
      }
83✔
138
    }
174✔
139
  }
140

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

156
  void
157
  PooledThread::destroy() {
8✔
158
    blockUntilComplete();
8✔
159
    {
160
      Lock lock(m_mutex);
8✔
161
      m_workerMethod = nullptr;
8✔
162
      m_threadReady = true;
8✔
163
    }
8✔
164
    m_readyCond.notify_one();
8✔
165
    m_thread->join();
8✔
166
    ge_delete(m_thread);
8✔
167
  }
8✔
168

169
  void
170
  PooledThread::blockUntilComplete() {
8✔
171
    Lock lock(m_mutex);
8✔
172
    while (!m_idle) {
8✔
173
      m_workerEndedCond.wait(lock);
×
174
    }
175
  }
8✔
176

177
  bool
178
  PooledThread::isIdle() {
3,862✔
179
    Lock lock(m_mutex);
3,862✔
180
    return m_idle;
3,862✔
181
  }
3,862✔
182

183
  time_t
184
  PooledThread::idleTime() {
4✔
185
    Lock lock(m_mutex);
4✔
186
    return (time(nullptr) - m_idleTime);
8✔
187
  }
4✔
188

189
  void
190
  PooledThread::setName(const String& name) {
50✔
191
    m_name = name;
50✔
192
  }
50✔
193

194
  uint32
195
  PooledThread::getId() const {
99✔
196
    Lock lock(m_mutex);
99✔
197
    return m_id;
99✔
198
  }
99✔
199

200
  ThreadPool::ThreadPool(SIZE_T threadCapacity, SIZE_T maxCapacity, uint32 idleTimeout)
11✔
201
    : m_defaultCapacity(threadCapacity),
11✔
202
      m_maxCapacity(maxCapacity),
11✔
203
      m_idleTimeout(idleTimeout)
11✔
204
  {}
11✔
205

206
  ThreadPool::~ThreadPool() {
6✔
207
    stopAll();
6✔
208
  }
6✔
209

210
  HThread
211
  ThreadPool::run(const String& name, const function<void()>& workerMethod) {
84✔
212
    PooledThread* pThread = getThread(name);
84✔
213
    pThread->start(workerMethod, ++m_uniqueId);
83✔
214
    return HThread(this, pThread->getId());
83✔
215
  }
216

217
  void
218
  ThreadPool::stopAll() {
6✔
219
    Lock lock(m_mutex);
6✔
220
    for (auto& myThread : m_threads) {
11✔
221
      destroyThread(myThread);
5✔
222
    }
223
    m_threads.clear();
6✔
224
  }
6✔
225

226
  void
227
  ThreadPool::clearUnused() {
4✔
228
    Lock lock(m_mutex);
4✔
229
    m_age = 0;
4✔
230

231
    if (m_threads.size() <= m_defaultCapacity) {
4✔
232
      return;
2✔
233
    }
234

235
    Vector<PooledThread*> idleThreads;
2✔
236
    Vector<PooledThread*> expiredThreads;
2✔
237
    Vector<PooledThread*> activeThreads;
2✔
238

239
    idleThreads.reserve(m_threads.size());
2✔
240
    expiredThreads.reserve(m_threads.size());
2✔
241
    activeThreads.reserve(m_threads.size());
2✔
242

243
    for (auto& pThread : m_threads) {
6✔
244
      if (pThread->isIdle()) {
4✔
245
        if (pThread->idleTime() >= m_idleTimeout) {
4✔
246
          expiredThreads.push_back(pThread);
4✔
247
        }
248
        else {
249
          idleThreads.push_back(pThread);
×
250
        }
251
      }
252
      else {
253
        activeThreads.push_back(pThread);
×
254
      }
255
    }
256

257
    idleThreads.insert(idleThreads.end(), expiredThreads.begin(), expiredThreads.end());
2✔
258
    SIZE_T limit = Math::min(idleThreads.size(), m_defaultCapacity);
2✔
259
    m_threads.clear();
2✔
260

261
    for (SIZE_T i = 0; i < idleThreads.size(); ++i) {
6✔
262
      PooledThread* pThread = idleThreads[i];
4✔
263
      if (i < limit) {
4✔
264
        m_threads.push_back(pThread);
1✔
265
      }
266
      else {
267
        destroyThread(pThread);
3✔
268
      }
269
    }
270

271
    m_threads.insert(m_threads.end(), activeThreads.begin(), activeThreads.end());
2✔
272
  }
4✔
273

274
  void
275
  ThreadPool::destroyThread(PooledThread* thread) {
8✔
276
    thread->destroy();
8✔
277
    ge_delete(thread);
8✔
278
  }
8✔
279

280
  PooledThread*
281
  ThreadPool::getThread(const String& name) {
84✔
282
    uint32 age = 0;
84✔
283
    {
284
      Lock lock(m_mutex);
84✔
285
      age = ++m_age;
84✔
286
    }
84✔
287

288
    if (UNUSED_CHECK_PERIOD == age) {
84✔
289
      clearUnused();
2✔
290
    }
291

292
    Lock lock(m_mutex);
84✔
293

294
    for (auto& pThread : m_threads) {
611✔
295
      if (pThread->isIdle()) {
577✔
296
        pThread->setName(name);
50✔
297
        return pThread;
50✔
298
      }
299
    }
300

301
    if (m_threads.size() >= m_maxCapacity) {
34✔
302
      //This version avoids a crash
303
      throw std::runtime_error("ThreadPool exhausted");
1✔
304
      /*
305
      GE_EXCEPT(InvalidStateException,
306
                "Unable to create a new thread in the pool because "          \
307
                "maximum capacity has been reached.");
308
      */
309
    }
310

311
    PooledThread* newThread = createThread(name);
33✔
312
    m_threads.push_back(newThread);
33✔
313

314
    return newThread;
33✔
315
  }
84✔
316

317
  SIZE_T
318
  ThreadPool::getNumAvailable() const {
217✔
319
    SIZE_T numAvailable = m_maxCapacity;
217✔
320

321
    Lock lock(m_mutex);
217✔
322
    for (auto& pThread : m_threads) {
3,496✔
323
      if (!pThread->isIdle()) {
3,279✔
324
        --numAvailable;
3,147✔
325
      }
326
    }
327
    return numAvailable;
217✔
328
  }
217✔
329

330
  SIZE_T
331
  ThreadPool::getNumActive() const {
2✔
332
    SIZE_T numActive = 0;
2✔
333

334
    Lock lock(m_mutex);
2✔
335
    for (auto& pThread : m_threads) {
4✔
336
      if (!pThread->isIdle()) {
2✔
337
        ++numActive;
1✔
338
      }
339
    }
340
    return numActive;
2✔
341
  }
2✔
342

343
  SIZE_T
344
  ThreadPool::getNumAllocated() const {
7✔
345
    Lock lock(m_mutex);
7✔
346
    return m_threads.size();
14✔
347
  }
7✔
348
}
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