Coverage Report

Created: 2026-04-27 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/threadutility.cpp
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#include "log4cxx/helpers/threadutility.h"
19
#if !defined(LOG4CXX)
20
  #define LOG4CXX 1
21
#endif
22
#include "log4cxx/private/log4cxx_private.h"
23
#include "log4cxx/helpers/loglog.h"
24
#include "log4cxx/helpers/transcoder.h"
25
26
#include <signal.h>
27
#include <mutex>
28
#include <list>
29
#include <condition_variable>
30
#include <algorithm>
31
32
#ifdef _WIN32
33
  #include <windows.h>
34
  #include <processthreadsapi.h>
35
#endif
36
37
#if LOG4CXX_EVENTS_AT_EXIT
38
#include <log4cxx/private/atexitregistry.h>
39
#endif
40
#if !defined(LOG4CXX)
41
  #define LOG4CXX 1
42
#endif
43
#include <log4cxx/helpers/aprinitializer.h>
44
45
namespace LOG4CXX_NS
46
{
47
namespace helpers
48
{
49
50
struct ThreadUtility::priv_data
51
{
52
  priv_data()
53
#if LOG4CXX_EVENTS_AT_EXIT
54
    : atExitRegistryRaii{ [this]{ stopThread(); } }
55
#endif
56
2
  {
57
2
  }
58
59
  ~priv_data()
60
2
  { stopThread(); }
61
62
  ThreadStartPre  start_pre{nullptr};
63
  ThreadStarted   started{nullptr};
64
  ThreadStartPost start_post{nullptr};
65
66
  using TimePoint = std::chrono::time_point<std::chrono::system_clock>;
67
  struct NamedPeriodicFunction
68
  {
69
    LogString             name;
70
    Period                delay;
71
    TimePoint             nextRun;
72
    std::function<void()> f;
73
    int                   errorCount;
74
    bool                  removed;
75
  };
76
  using JobStore = std::list<NamedPeriodicFunction>;
77
  JobStore                  jobs;
78
  std::recursive_mutex      job_mutex;
79
  std::thread               thread;
80
  std::condition_variable   interrupt;
81
  std::mutex                interrupt_mutex;
82
  bool                      terminated{ false };
83
  int                       retryCount{ 2 };
84
  Period                    maxDelay{ 0 };
85
  bool                      threadIsActive{ false };
86
87
  void doPeriodicTasks();
88
89
  void setTerminated()
90
451
  {
91
451
    std::lock_guard<std::mutex> lock(interrupt_mutex);
92
451
    terminated = true;
93
451
  }
94
95
  void stopThread()
96
451
  {
97
451
    setTerminated();
98
451
    interrupt.notify_all();
99
451
    if (thread.joinable())
100
0
      thread.join();
101
451
  }
102
103
#if LOG4CXX_EVENTS_AT_EXIT
104
  helpers::AtExitRegistry::Raii atExitRegistryRaii;
105
#endif
106
};
107
108
#if LOG4CXX_HAS_PTHREAD_SIGMASK
109
  static thread_local sigset_t old_mask;
110
  static thread_local bool sigmask_valid;
111
#endif
112
113
ThreadUtility::ThreadUtility()
114
2
  : m_priv( std::make_unique<priv_data>() )
115
2
{
116
  // Block signals by default.
117
2
  configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, this ),
118
2
    nullptr,
119
2
    std::bind( &ThreadUtility::postThreadUnblockSignals, this ) );
120
2
}
121
122
2
ThreadUtility::~ThreadUtility() {}
123
124
auto ThreadUtility::instancePtr() -> ManagerPtr
125
451
{
126
451
  auto result = APRInitializer::getOrAddUnique<Manager>
127
451
    ( []() -> ObjectPtr
128
451
      { return std::make_shared<Manager>(); }
129
451
    );
130
451
  return result;
131
451
}
132
133
ThreadUtility* ThreadUtility::instance()
134
451
{
135
451
  return &instancePtr()->value();
136
451
}
137
138
void ThreadUtility::configure( ThreadConfigurationType type )
139
2
{
140
2
  auto utility = instance();
141
142
2
  if ( type == ThreadConfigurationType::NoConfiguration )
143
1
  {
144
1
    utility->configureFuncs( nullptr, nullptr, nullptr );
145
1
  }
146
1
  else if ( type == ThreadConfigurationType::NameThreadOnly )
147
0
  {
148
0
    utility->configureFuncs( nullptr,
149
0
      std::bind( &ThreadUtility::threadStartedNameThread, utility,
150
0
        std::placeholders::_1,
151
0
        std::placeholders::_2,
152
0
        std::placeholders::_3 ),
153
0
      nullptr );
154
0
  }
155
1
  else if ( type == ThreadConfigurationType::BlockSignalsOnly )
156
1
  {
157
1
    utility->configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, utility ),
158
1
      nullptr,
159
1
      std::bind( &ThreadUtility::postThreadUnblockSignals, utility ) );
160
1
  }
161
0
  else if ( type == ThreadConfigurationType::BlockSignalsAndNameThread )
162
0
  {
163
0
    utility->configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, utility ),
164
0
      std::bind( &ThreadUtility::threadStartedNameThread, utility,
165
0
        std::placeholders::_1,
166
0
        std::placeholders::_2,
167
0
        std::placeholders::_3 ),
168
0
      std::bind( &ThreadUtility::postThreadUnblockSignals, utility ) );
169
0
  }
170
2
}
171
172
void ThreadUtility::configureFuncs( ThreadStartPre pre_start,
173
  ThreadStarted started,
174
  ThreadStartPost post_start )
175
4
{
176
4
  m_priv->start_pre = pre_start;
177
4
  m_priv->started = started;
178
4
  m_priv->start_post = post_start;
179
4
}
log4cxx::helpers::ThreadUtility::configureFuncs(std::__1::function<void ()>, std::__1::function<void (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__thread_id, unsigned long)>, std::__1::function<void ()>)
Line
Count
Source
175
4
{
176
4
  m_priv->start_pre = pre_start;
177
4
  m_priv->started = started;
178
4
  m_priv->start_post = post_start;
179
4
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::configureFuncs(std::__1::function<void ()>, std::__1::function<void (std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::__thread_id, unsigned long)>, std::__1::function<void ()>)
180
181
void ThreadUtility::preThreadBlockSignals()
182
0
{
183
0
#if LOG4CXX_HAS_PTHREAD_SIGMASK
184
0
  sigset_t set;
185
0
  sigfillset(&set);
186
187
0
  if ( pthread_sigmask(SIG_SETMASK, &set, &old_mask) < 0 )
188
0
  {
189
0
    LOGLOG_ERROR( LOG4CXX_STR("Unable to set thread sigmask") );
190
0
    sigmask_valid = false;
191
0
  }
192
0
  else
193
0
  {
194
0
    sigmask_valid = true;
195
0
  }
196
197
0
#endif /* LOG4CXX_HAS_PTHREAD_SIGMASK */
198
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::preThreadBlockSignals()
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::preThreadBlockSignals()
199
200
void ThreadUtility::threadStartedNameThread(LogString threadName,
201
  std::thread::id /*threadId*/,
202
  std::thread::native_handle_type nativeHandle)
203
0
{
204
0
#if LOG4CXX_HAS_PTHREAD_SETNAME && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
205
0
  LOG4CXX_ENCODE_CHAR(sthreadName, threadName);
206
0
  if (pthread_setname_np(static_cast<pthread_t>(nativeHandle), sthreadName.c_str()) < 0) {
207
0
    LOGLOG_ERROR(LOG4CXX_STR("unable to set thread name"));
208
0
  }
209
#elif defined(_WIN32)
210
  typedef HRESULT (WINAPI *TSetThreadDescription)(HANDLE, PCWSTR);
211
  static struct initialiser
212
  {
213
    HMODULE hKernelBase;
214
    TSetThreadDescription SetThreadDescription;
215
    initialiser()
216
      : hKernelBase(GetModuleHandleA("KernelBase.dll"))
217
      , SetThreadDescription(nullptr)
218
    {
219
      if (hKernelBase)
220
        SetThreadDescription = reinterpret_cast<TSetThreadDescription>(GetProcAddress(hKernelBase, "SetThreadDescription"));
221
    }
222
  } win32Func;
223
  if (win32Func.SetThreadDescription)
224
  {
225
    LOG4CXX_ENCODE_WCHAR(wthreadName, threadName);
226
    if(FAILED(win32Func.SetThreadDescription(static_cast<HANDLE>(nativeHandle), wthreadName.c_str())))
227
      LOGLOG_ERROR( LOG4CXX_STR("unable to set thread name") );
228
  }
229
#endif
230
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::threadStartedNameThread(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::__thread_id, unsigned long)
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::threadStartedNameThread(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::__thread_id, unsigned long)
231
232
void ThreadUtility::postThreadUnblockSignals()
233
0
{
234
0
#if LOG4CXX_HAS_PTHREAD_SIGMASK
235
236
  // Only restore the signal mask if we were able to set it in the first place.
237
0
  if ( sigmask_valid )
238
0
  {
239
0
    if ( pthread_sigmask(SIG_SETMASK, &old_mask, nullptr) < 0 )
240
0
    {
241
0
      LOGLOG_ERROR( LOG4CXX_STR("Unable to set thread sigmask") );
242
0
    }
243
0
  }
244
245
0
#endif /* LOG4CXX_HAS_PTHREAD_SIGMASK */
246
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::postThreadUnblockSignals()
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::postThreadUnblockSignals()
247
248
249
ThreadStartPre ThreadUtility::preStartFunction()
250
0
{
251
0
  return m_priv->start_pre;
252
0
}
253
254
ThreadStarted ThreadUtility::threadStartedFunction()
255
0
{
256
0
  return m_priv->started;
257
0
}
258
259
ThreadStartPost ThreadUtility::postStartFunction()
260
0
{
261
0
  return m_priv->start_post;
262
0
}
263
264
/**
265
 * Add a periodic task
266
 */
267
void ThreadUtility::addPeriodicTask(const LogString& name, std::function<void()> f, const Period& delay)
268
0
{
269
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
270
0
  if (m_priv->maxDelay < delay)
271
0
    m_priv->maxDelay = delay;
272
0
  auto currentTime = std::chrono::system_clock::now();
273
0
  m_priv->jobs.push_back( priv_data::NamedPeriodicFunction{name, delay, currentTime + delay, f, 0, false} );
274
275
  // Restart thread if it has stopped.
276
0
  if (!m_priv->threadIsActive && m_priv->thread.joinable())
277
0
    m_priv->thread.join();
278
279
0
  if (!m_priv->thread.joinable())
280
0
  {
281
0
    m_priv->terminated = false;
282
0
    m_priv->threadIsActive = true;
283
0
    m_priv->thread = createThread(LOG4CXX_STR("log4cxx"), std::bind(&priv_data::doPeriodicTasks, m_priv.get()));
284
0
  }
285
0
  else
286
0
    m_priv->interrupt.notify_one();
287
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::addPeriodicTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::function<void ()>, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l> > const&)
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::addPeriodicTask(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, std::__1::function<void ()>, std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l> > const&)
288
289
/**
290
 * Is this already running a \c taskName periodic task?
291
 */
292
bool ThreadUtility::hasPeriodicTask(const LogString& name)
293
0
{
294
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
295
0
  auto pItem = std::find_if(m_priv->jobs.begin(), m_priv->jobs.end()
296
0
    , [&name](const priv_data::NamedPeriodicFunction& item)
297
0
    { return !item.removed && name == item.name; }
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::hasPeriodicTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::hasPeriodicTask(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
298
0
    );
299
0
  return m_priv->jobs.end() != pItem;
300
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::hasPeriodicTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::hasPeriodicTask(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
301
302
/**
303
 * Remove all periodic tasks and stop the processing thread
304
 */
305
void ThreadUtility::removeAllPeriodicTasks()
306
449
{
307
449
  {
308
449
    std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
309
449
    while (!m_priv->jobs.empty())
310
0
      m_priv->jobs.pop_back();
311
449
  }
312
449
  m_priv->stopThread();
313
449
}
314
315
/**
316
 * Remove the \c taskName periodic task
317
 */
318
void ThreadUtility::removePeriodicTask(const LogString& name)
319
0
{
320
0
  std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
321
0
  auto pItem = std::find_if(m_priv->jobs.begin(), m_priv->jobs.end()
322
0
    , [&name](const priv_data::NamedPeriodicFunction& item)
323
0
    { return !item.removed && name == item.name; }
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::removePeriodicTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::removePeriodicTask(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
324
0
    );
325
0
  if (m_priv->jobs.end() != pItem)
326
0
  {
327
0
    pItem->removed = true;
328
0
    m_priv->interrupt.notify_one();
329
0
  }
330
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::removePeriodicTask(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::removePeriodicTask(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
331
332
/**
333
 * Remove any periodic task matching \c namePrefix
334
 */
335
void ThreadUtility::removePeriodicTasksMatching(const LogString& namePrefix)
336
0
{
337
0
  while (1)
338
0
  {
339
0
    std::lock_guard<std::recursive_mutex> lock(m_priv->job_mutex);
340
0
    auto pItem = std::find_if(m_priv->jobs.begin(), m_priv->jobs.end()
341
0
      , [&namePrefix](const priv_data::NamedPeriodicFunction& item)
342
0
      { return !item.removed && namePrefix.size() <= item.name.size() && item.name.substr(0, namePrefix.size()) == namePrefix; }
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::removePeriodicTasksMatching(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
Unexecuted instantiation: threadutility.cpp:log4cxx::helpers::ThreadUtility::removePeriodicTasksMatching(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)::$_0::operator()(log4cxx::helpers::ThreadUtility::priv_data::NamedPeriodicFunction const&) const
343
0
      );
344
0
    if (m_priv->jobs.end() == pItem)
345
0
      break;
346
0
    pItem->removed = true;
347
0
  }
348
0
  m_priv->interrupt.notify_one();
349
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::removePeriodicTasksMatching(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: log4cxx::helpers::ThreadUtility::removePeriodicTasksMatching(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
350
351
// Run ready tasks
352
void ThreadUtility::priv_data::doPeriodicTasks()
353
0
{
354
0
  while (!this->terminated)
355
0
  {
356
0
    auto currentTime = std::chrono::system_clock::now();
357
0
    TimePoint nextOperationTime = currentTime + this->maxDelay;
358
0
    {
359
0
      std::lock_guard<std::recursive_mutex> lock(this->job_mutex);
360
0
      for (auto& item : this->jobs)
361
0
      {
362
0
        if (this->terminated)
363
0
          return;
364
0
        if (item.removed)
365
0
          ;
366
0
        else if (item.nextRun <= currentTime)
367
0
        {
368
0
          try
369
0
          {
370
0
            item.f();
371
0
            item.nextRun = std::chrono::system_clock::now() + item.delay;
372
0
            if (item.nextRun < nextOperationTime)
373
0
              nextOperationTime = item.nextRun;
374
0
            item.errorCount = 0;
375
0
          }
376
0
          catch (std::exception& ex)
377
0
          {
378
0
            LogLog::warn(item.name, ex);
379
0
            ++item.errorCount;
380
0
          }
381
0
          catch (...)
382
0
          {
383
0
            LogLog::warn(item.name + LOG4CXX_STR(" threw an exception"));
384
0
            ++item.errorCount;
385
0
          }
386
0
        }
387
0
        else if (item.nextRun < nextOperationTime)
388
0
          nextOperationTime = item.nextRun;
389
0
      }
390
0
    }
391
    // Delete removed and faulty tasks
392
0
    while (1)
393
0
    {
394
0
      std::lock_guard<std::recursive_mutex> lock(this->job_mutex);
395
0
      auto pItem = std::find_if(this->jobs.begin(), this->jobs.end()
396
0
        , [this](const NamedPeriodicFunction& item)
397
0
        { return item.removed || this->retryCount < item.errorCount; }
398
0
        );
399
0
      if (this->jobs.end() == pItem)
400
0
        break;
401
0
      this->jobs.erase(pItem);
402
0
      if (this->jobs.empty())
403
0
        return;
404
0
    }
405
406
0
    std::unique_lock<std::mutex> lock(this->interrupt_mutex);
407
0
    this->interrupt.wait_until(lock, nextOperationTime);
408
0
  }
409
0
    this->threadIsActive = false;
410
0
}
411
412
} //namespace helpers
413
} //namespace log4cxx