Coverage Report

Created: 2026-02-26 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/logging-log4cxx/src/main/cpp/threadspecificdata.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/log4cxx.h>
19
#include <log4cxx/logstring.h>
20
#include <log4cxx/helpers/threadspecificdata.h>
21
#include <log4cxx/helpers/exception.h>
22
#include <log4cxx/helpers/stringhelper.h>
23
#include <log4cxx/helpers/transcoder.h>
24
#include <apr_thread_proc.h>
25
#include <apr_strings.h>
26
#if !defined(LOG4CXX)
27
  #define LOG4CXX 1
28
#endif
29
#include <log4cxx/private/log4cxx_private.h>
30
#include <log4cxx/helpers/aprinitializer.h>
31
#include <sstream>
32
#include <algorithm>
33
#include <thread>
34
#include <mutex>
35
#include <list>
36
37
using namespace LOG4CXX_NS;
38
using namespace LOG4CXX_NS::helpers;
39
40
struct ThreadSpecificData::ThreadSpecificDataPrivate{
41
  ThreadSpecificDataPrivate()
42
13
    : pNamePair(std::make_shared<NamePair>())
43
13
  {
44
13
    setThreadIdName();
45
13
    setThreadUserName();
46
13
  }
47
  NDC::Stack ndcStack;
48
  MDC::Map mdcMap;
49
50
  std::shared_ptr<NamePair> pNamePair;
51
52
  template <typename T>
53
  struct CountedStringStream
54
  {
55
    int usage_count{ 0 };
56
    std::basic_ostringstream<T> ss;
57
  };
58
59
  // Find an unused stream buffer or add a new stream buffer in the collection \c store
60
  template <typename T>
61
  std::basic_ostringstream<T>& getStream(std::list<CountedStringStream<T> >& store)
62
0
  {
63
0
    CountedStringStream<T>* pItem{ nullptr };
64
0
    for (auto& item : store)
65
0
    {
66
0
      if (0 == item.usage_count)
67
0
      {
68
0
        pItem = &item;
69
0
        break;
70
0
      }
71
0
    }
72
0
    if (!pItem)
73
0
    {
74
0
      store.emplace_back();
75
0
      pItem = &store.back();
76
0
    }
77
0
    ++pItem->usage_count;
78
0
    return pItem->ss;
79
0
  }
Unexecuted instantiation: std::__1::basic_ostringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >& log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::getStream<char>(std::__1::list<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<char>, std::__1::allocator<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<char> > >&)
Unexecuted instantiation: std::__1::basic_ostringstream<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >& log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::getStream<wchar_t>(std::__1::list<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<wchar_t>, std::__1::allocator<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<wchar_t> > >&)
80
81
  // Decrement the usage count associated with the stream buffer \c ss in the collection \c store
82
  template <typename T>
83
  void releaseStream(std::list<CountedStringStream<T> >& store, std::basic_ostringstream<T>& ss)
84
0
  {
85
0
    for (auto& item : store)
86
0
    {
87
0
      if (&item.ss == &ss)
88
0
      {
89
0
        --item.usage_count;
90
0
        break;
91
0
      }
92
0
    }
93
0
  }
Unexecuted instantiation: void log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::releaseStream<char>(std::__1::list<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<char>, std::__1::allocator<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<char> > >&, std::__1::basic_ostringstream<char, std::__1::char_traits<char>, std::__1::allocator<char> >&)
Unexecuted instantiation: void log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::releaseStream<wchar_t>(std::__1::list<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<wchar_t>, std::__1::allocator<log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::CountedStringStream<wchar_t> > >&, std::__1::basic_ostringstream<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >&)
94
95
  std::list<CountedStringStream<char> > char_stringstream;
96
97
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
98
  std::list<CountedStringStream<wchar_t> > wchar_stringstream;
99
#endif
100
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
101
  std::list<CountedStringStream<UniChar> > unichar_stringstream;
102
#endif
103
104
  void setThreadIdName();
105
  void setThreadUserName();
106
};
107
108
/* Generate an identifier for the current thread
109
*/
110
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadIdName()
111
26
{
112
26
#if LOG4CXX_HAS_PTHREAD_SELF && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
113
  // pthread_t encoded in HEX takes needs as many characters
114
  // as two times the size of the type, plus an additional null byte.
115
26
  auto threadId = pthread_self();
116
26
  char result[sizeof(pthread_t) * 3 + 10];
117
26
  apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId);
118
26
  this->pNamePair->idString = Transcoder::decode(result);
119
#elif defined(_WIN32)
120
  char result[20];
121
  apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, GetCurrentThreadId());
122
  this->pNamePair->idString = Transcoder::decode(result);
123
#else
124
  std::stringstream ss;
125
  ss << std::hex << "0x" << std::this_thread::get_id();
126
  this->pNamePair->idString = Transcoder::decode(ss.str().c_str());
127
#endif
128
26
}
log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::setThreadIdName()
Line
Count
Source
111
13
{
112
13
#if LOG4CXX_HAS_PTHREAD_SELF && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
113
  // pthread_t encoded in HEX takes needs as many characters
114
  // as two times the size of the type, plus an additional null byte.
115
13
  auto threadId = pthread_self();
116
13
  char result[sizeof(pthread_t) * 3 + 10];
117
13
  apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId);
118
13
  this->pNamePair->idString = Transcoder::decode(result);
119
#elif defined(_WIN32)
120
  char result[20];
121
  apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, GetCurrentThreadId());
122
  this->pNamePair->idString = Transcoder::decode(result);
123
#else
124
  std::stringstream ss;
125
  ss << std::hex << "0x" << std::this_thread::get_id();
126
  this->pNamePair->idString = Transcoder::decode(ss.str().c_str());
127
#endif
128
13
}
log4cxx::helpers::ThreadSpecificData::ThreadSpecificDataPrivate::setThreadIdName()
Line
Count
Source
111
13
{
112
13
#if LOG4CXX_HAS_PTHREAD_SELF && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
113
  // pthread_t encoded in HEX takes needs as many characters
114
  // as two times the size of the type, plus an additional null byte.
115
13
  auto threadId = pthread_self();
116
13
  char result[sizeof(pthread_t) * 3 + 10];
117
13
  apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId);
118
13
  this->pNamePair->idString = Transcoder::decode(result);
119
#elif defined(_WIN32)
120
  char result[20];
121
  apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, GetCurrentThreadId());
122
  this->pNamePair->idString = Transcoder::decode(result);
123
#else
124
  std::stringstream ss;
125
  ss << std::hex << "0x" << std::this_thread::get_id();
126
  this->pNamePair->idString = Transcoder::decode(ss.str().c_str());
127
#endif
128
13
}
129
130
/*
131
 * Get the user-specified name of the current thread (on a per-platform basis).
132
 * This is set using a method such as pthread_setname_np on POSIX
133
 * systems or SetThreadDescription on Windows.
134
 */
135
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadUserName()
136
13
{
137
13
#if LOG4CXX_HAS_PTHREAD_GETNAME && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
138
13
  char result[16];
139
13
  pthread_t current_thread = pthread_self();
140
13
  if (pthread_getname_np(current_thread, result, sizeof(result)) < 0 || 0 == result[0])
141
0
    this->pNamePair->threadName = this->pNamePair->idString;
142
13
  else
143
13
    this->pNamePair->threadName = Transcoder::decode(result);
144
#elif defined(_WIN32)
145
  typedef HRESULT (WINAPI *TGetThreadDescription)(HANDLE, PWSTR*);
146
  static struct initialiser
147
  {
148
    HMODULE hKernelBase;
149
    TGetThreadDescription GetThreadDescription;
150
    initialiser()
151
      : hKernelBase(GetModuleHandleA("KernelBase.dll"))
152
      , GetThreadDescription(nullptr)
153
    {
154
      if (hKernelBase)
155
        GetThreadDescription = reinterpret_cast<TGetThreadDescription>(GetProcAddress(hKernelBase, "GetThreadDescription"));
156
    }
157
  } win32func;
158
  if (win32func.GetThreadDescription)
159
  {
160
    PWSTR result = 0;
161
    HRESULT hr = win32func.GetThreadDescription(GetCurrentThread(), &result);
162
    if (SUCCEEDED(hr) && result)
163
    {
164
      std::wstring wresult = result;
165
      LOG4CXX_DECODE_WCHAR(decoded, wresult);
166
      LocalFree(result);
167
      this->pNamePair->threadName = decoded;
168
    }
169
  }
170
  if (this->pNamePair->threadName.empty())
171
    this->pNamePair->threadName = this->pNamePair->idString;
172
#else
173
  this->pNamePair->threadName = this->pNamePair->idString;
174
#endif
175
13
}
176
177
ThreadSpecificData::ThreadSpecificData()
178
13
  : m_priv(std::make_unique<ThreadSpecificDataPrivate>())
179
13
{
180
13
}
181
182
ThreadSpecificData::ThreadSpecificData(ThreadSpecificData&& other)
183
0
  : m_priv(std::move(other.m_priv))
184
0
{
185
0
}
186
187
ThreadSpecificData::~ThreadSpecificData()
188
13
{
189
13
  m_priv.reset();
190
13
}
191
192
NDC::Stack& ThreadSpecificData::getStack()
193
67.5k
{
194
67.5k
  return m_priv->ndcStack;
195
67.5k
}
196
197
MDC::Map& ThreadSpecificData::getMap()
198
67.7k
{
199
67.7k
  return m_priv->mdcMap;
200
67.7k
}
201
202
auto ThreadSpecificData::getNames() -> NamePairPtr
203
142k
{
204
142k
  auto p = getCurrentData();
205
142k
  return p ? p->m_priv->pNamePair : std::make_shared<NamePair>();
206
142k
}
207
208
std::basic_ostringstream<char>& ThreadSpecificData::getStream(const char&)
209
0
{
210
0
  auto p = getCurrentData();
211
0
  return p->m_priv->getStream(p->m_priv->char_stringstream);
212
0
}
213
214
void ThreadSpecificData::releaseStream(std::basic_ostringstream<char>& ss)
215
0
{
216
0
  auto p = getCurrentData();
217
0
  p->m_priv->releaseStream(p->m_priv->char_stringstream, ss);
218
0
}
219
220
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
221
std::basic_ostringstream<wchar_t>& ThreadSpecificData::getStream(const wchar_t&)
222
0
{
223
0
  auto p = getCurrentData();
224
0
  return p->m_priv->getStream(p->m_priv->wchar_stringstream);
225
0
}
226
227
void ThreadSpecificData::releaseStream(std::basic_ostringstream<wchar_t>& ss)
228
0
{
229
0
  auto p = getCurrentData();
230
0
  p->m_priv->releaseStream(p->m_priv->wchar_stringstream, ss);
231
0
}
232
#endif
233
234
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
235
std::basic_ostringstream<UniChar>& ThreadSpecificData::getStream(const UniChar&)
236
{
237
  auto p = getCurrentData();
238
  return p->m_priv->getStream(p->m_priv->unichar_stringstream);
239
}
240
241
void ThreadSpecificData::releaseStream(std::basic_ostringstream<UniChar>& ss)
242
{
243
  auto p = getCurrentData();
244
  p->m_priv->releaseStream(p->m_priv->unichar_stringstream, ss);
245
}
246
#endif
247
248
ThreadSpecificData* ThreadSpecificData::getCurrentData()
249
277k
{
250
277k
#if LOG4CXX_HAS_THREAD_LOCAL
251
277k
  thread_local ThreadSpecificData data;
252
277k
  return data.m_priv ? &data : NULL;
253
#elif APR_HAS_THREADS
254
  void* pData = NULL;
255
  if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
256
    && !pData)
257
  {
258
    pData = new ThreadSpecificData();
259
    if (APR_SUCCESS != apr_threadkey_private_set(pData, APRInitializer::getTlsKey()))
260
    {
261
      delete (ThreadSpecificData*)pData;
262
      pData = NULL;
263
    }
264
  }
265
  if (pData)
266
    return (ThreadSpecificData*) pData;
267
#endif
268
269
  // Fallback implementation that is not expected to be used
270
0
  using TaggedData = std::pair<std::thread::id, ThreadSpecificData>;
271
0
  static std::list<TaggedData> thread_id_map;
272
0
  static std::mutex mutex;
273
0
  std::lock_guard<std::mutex> lock(mutex);
274
0
  auto threadId = std::this_thread::get_id();
275
0
  auto pThreadId = std::find_if(thread_id_map.begin(), thread_id_map.end()
276
0
    , [threadId](const TaggedData& item) { return threadId == item.first; });
277
0
  if (thread_id_map.end() == pThreadId)
278
0
    pThreadId = thread_id_map.emplace(thread_id_map.begin(), threadId, ThreadSpecificData());
279
0
  return &pThreadId->second;
280
277k
}
281
282
void ThreadSpecificData::recycle()
283
50.8k
{
284
#if !LOG4CXX_HAS_THREAD_LOCAL && APR_HAS_THREADS
285
  if (m_priv->ndcStack.empty() && m_priv->mdcMap.empty())
286
  {
287
    void* pData = NULL;
288
    if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
289
      && pData == this
290
      && APR_SUCCESS == apr_threadkey_private_set(0, APRInitializer::getTlsKey()))
291
        delete this;
292
  }
293
#endif
294
50.8k
}
295
296
void ThreadSpecificData::put(const LogString& key, const LogString& val)
297
29.2k
{
298
29.2k
  if (auto p = getCurrentData())
299
29.2k
    p->getMap()[key] = val;
300
29.2k
}
log4cxx::helpers::ThreadSpecificData::put(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
297
13.6k
{
298
13.6k
  if (auto p = getCurrentData())
299
13.6k
    p->getMap()[key] = val;
300
13.6k
}
log4cxx::helpers::ThreadSpecificData::put(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
Line
Count
Source
297
15.6k
{
298
15.6k
  if (auto p = getCurrentData())
299
15.6k
    p->getMap()[key] = val;
300
15.6k
}
301
302
void ThreadSpecificData::push(const LogString& val)
303
17.0k
{
304
17.0k
  auto p = getCurrentData();
305
17.0k
  if (!p)
306
0
    return;
307
17.0k
  NDC::Stack& stack = p->getStack();
308
17.0k
  if (stack.empty())
309
17.0k
  {
310
17.0k
    stack.push(NDC::DiagnosticContext(val, val));
311
17.0k
  }
312
0
  else
313
0
  {
314
0
    LogString fullMessage(stack.top().second);
315
0
    fullMessage.append(1, (logchar) 0x20);
316
0
    fullMessage.append(val);
317
0
    stack.push(NDC::DiagnosticContext(val, fullMessage));
318
0
  }
319
17.0k
}
log4cxx::helpers::ThreadSpecificData::push(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Line
Count
Source
303
7.90k
{
304
7.90k
  auto p = getCurrentData();
305
7.90k
  if (!p)
306
0
    return;
307
7.90k
  NDC::Stack& stack = p->getStack();
308
7.90k
  if (stack.empty())
309
7.90k
  {
310
7.90k
    stack.push(NDC::DiagnosticContext(val, val));
311
7.90k
  }
312
0
  else
313
0
  {
314
0
    LogString fullMessage(stack.top().second);
315
0
    fullMessage.append(1, (logchar) 0x20);
316
0
    fullMessage.append(val);
317
0
    stack.push(NDC::DiagnosticContext(val, fullMessage));
318
0
  }
319
7.90k
}
log4cxx::helpers::ThreadSpecificData::push(std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > const&)
Line
Count
Source
303
9.09k
{
304
9.09k
  auto p = getCurrentData();
305
9.09k
  if (!p)
306
0
    return;
307
9.09k
  NDC::Stack& stack = p->getStack();
308
9.09k
  if (stack.empty())
309
9.09k
  {
310
9.09k
    stack.push(NDC::DiagnosticContext(val, val));
311
9.09k
  }
312
0
  else
313
0
  {
314
0
    LogString fullMessage(stack.top().second);
315
0
    fullMessage.append(1, (logchar) 0x20);
316
0
    fullMessage.append(val);
317
0
    stack.push(NDC::DiagnosticContext(val, fullMessage));
318
0
  }
319
9.09k
}
320
321
void ThreadSpecificData::inherit(const NDC::Stack& src)
322
0
{
323
0
  if (auto p = getCurrentData())
324
0
    p->getStack() = src;
325
0
}
Unexecuted instantiation: log4cxx::helpers::ThreadSpecificData::inherit(std::__1::stack<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > > > const&)
Unexecuted instantiation: log4cxx::helpers::ThreadSpecificData::inherit(std::__1::stack<std::__1::pair<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > >, std::__1::deque<std::__1::pair<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> >, std::__1::basic_string<wchar_t, std::__1::char_traits<wchar_t>, std::__1::allocator<wchar_t> > > > > > const&)
326