1 // Module: Log4CPLUS
2 // File: global-init.cxx
3 // Created: 5/2003
4 // Author: Tad E. Smith
5 //
6 //
7 // Copyright 2003-2013 Tad E. Smith
8 //
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 //
13 // http://www.apache.org/licenses/LICENSE-2.0
14 //
15 // Unless required by applicable law or agreed to in writing, software
16 // distributed under the License is distributed on an "AS IS" BASIS,
17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 // See the License for the specific language governing permissions and
19 // limitations under the License.
20
21 #include <log4cplus/config.hxx>
22 #include <log4cplus/config/windowsh-inc.h>
23 #include <log4cplus/logger.h>
24 #include <log4cplus/ndc.h>
25 #include <log4cplus/mdc.h>
26 #include <log4cplus/helpers/loglog.h>
27 #include <log4cplus/internal/internal.h>
28 #include <log4cplus/thread/impl/tls.h>
29 #include <log4cplus/thread/syncprims-pub-impl.h>
30 #include <log4cplus/helpers/loglog.h>
31 #include <log4cplus/spi/factory.h>
32 #include <log4cplus/hierarchy.h>
33 #include <cstdio>
34 #include <iostream>
35 #include <stdexcept>
36
37
38 // Forward Declarations
39 namespace log4cplus
40 {
41
42 #ifdef UNICODE
43 LOG4CPLUS_EXPORT tostream & tcout = std::wcout;
44 LOG4CPLUS_EXPORT tostream & tcerr = std::wcerr;
45
46 #else
47 LOG4CPLUS_EXPORT tostream & tcout = std::cout;
48 LOG4CPLUS_EXPORT tostream & tcerr = std::cerr;
49
50 #endif // UNICODE
51
52
53 namespace
54 {
55
56
57 //! Default context.
58 struct DefaultContext
59 {
60 log4cplus::thread::Mutex console_mutex;
61 helpers::LogLog loglog;
62 LogLevelManager log_level_manager;
63 helpers::Time TTCCLayout_time_base;
64 NDC ndc;
65 MDC mdc;
66 Hierarchy hierarchy;
67 spi::AppenderFactoryRegistry appender_factory_registry;
68 spi::LayoutFactoryRegistry layout_factory_registry;
69 spi::FilterFactoryRegistry filter_factory_registry;
70 spi::LocaleFactoryRegistry locale_factory_registry;
71 };
72
73
74 enum DCState
75 {
76 DC_UNINITIALIZED,
77 DC_INITIALIZED,
78 DC_DESTROYED
79 };
80
81
82 static DCState default_context_state;
83 static DefaultContext * default_context;
84
85
86 struct destroy_default_context
87 {
~destroy_default_contextlog4cplus::__anonf74f6c680111::destroy_default_context88 ~destroy_default_context ()
89 {
90 delete default_context;
91 default_context = 0;
92 default_context_state = DC_DESTROYED;
93 }
94 } static destroy_default_context_;
95
96
97 static
98 void
alloc_dc()99 alloc_dc ()
100 {
101 assert (! default_context);
102 assert (default_context_state == DC_UNINITIALIZED);
103
104 if (default_context)
105 throw std::logic_error (
106 "alloc_dc() called with non-NULL default_context.");
107
108 if (default_context_state == DC_INITIALIZED)
109 throw std::logic_error ("alloc_dc() called in DC_INITIALIZED state.");
110
111 default_context = new DefaultContext;
112
113 if (default_context_state == DC_DESTROYED)
114 default_context->loglog.error (
115 LOG4CPLUS_TEXT ("Re-initializing default context after it has")
116 LOG4CPLUS_TEXT (" already been destroyed.\n")
117 LOG4CPLUS_TEXT ("The memory will be leaked."));
118
119 default_context_state = DC_INITIALIZED;
120 }
121
122
123 static
124 DefaultContext *
get_dc(bool alloc=true)125 get_dc (bool alloc = true)
126 {
127 if (LOG4CPLUS_UNLIKELY (! default_context && alloc))
128 alloc_dc ();
129 return default_context;
130 }
131
132
133 } // namespace
134
135
136 namespace helpers
137 {
138
139
140 log4cplus::thread::Mutex const &
getConsoleOutputMutex()141 getConsoleOutputMutex ()
142 {
143 return get_dc ()->console_mutex;
144 }
145
146
147 LogLog &
getLogLog()148 getLogLog ()
149 {
150 return get_dc ()->loglog;
151 }
152
153
154 } // namespace helpers
155
156
157 helpers::Time const &
getTTCCLayoutTimeBase()158 getTTCCLayoutTimeBase ()
159 {
160 return get_dc ()->TTCCLayout_time_base;
161 }
162
163
164 LogLevelManager &
getLogLevelManager()165 getLogLevelManager ()
166 {
167 return get_dc ()->log_level_manager;
168 }
169
170
171 Hierarchy &
getDefaultHierarchy()172 getDefaultHierarchy ()
173 {
174 return get_dc ()->hierarchy;
175 }
176
177
178 NDC &
getNDC()179 getNDC ()
180 {
181 return get_dc ()->ndc;
182 }
183
184
185 MDC &
getMDC()186 getMDC ()
187 {
188 return get_dc ()->mdc;
189 }
190
191
192 namespace spi
193 {
194
195
196 AppenderFactoryRegistry &
getAppenderFactoryRegistry()197 getAppenderFactoryRegistry ()
198 {
199 return get_dc ()->appender_factory_registry;
200 }
201
202
203 LayoutFactoryRegistry &
getLayoutFactoryRegistry()204 getLayoutFactoryRegistry ()
205 {
206 return get_dc ()->layout_factory_registry;
207 }
208
209
210 FilterFactoryRegistry &
getFilterFactoryRegistry()211 getFilterFactoryRegistry ()
212 {
213 return get_dc ()->filter_factory_registry;
214 }
215
216
217 LocaleFactoryRegistry &
getLocaleFactoryRegistry()218 getLocaleFactoryRegistry()
219 {
220 return get_dc ()->locale_factory_registry;
221 }
222
223
224 } // namespace spi
225
226
227 namespace internal
228 {
229
230
gft_scratch_pad()231 gft_scratch_pad::gft_scratch_pad ()
232 : uc_q_str_valid (false)
233 , q_str_valid (false)
234 , s_str_valid (false)
235 { }
236
237
~gft_scratch_pad()238 gft_scratch_pad::~gft_scratch_pad ()
239 { }
240
241
appender_sratch_pad()242 appender_sratch_pad::appender_sratch_pad ()
243 { }
244
245
~appender_sratch_pad()246 appender_sratch_pad::~appender_sratch_pad ()
247 { }
248
249
per_thread_data()250 per_thread_data::per_thread_data ()
251 : fnull (0)
252 { }
253
254
~per_thread_data()255 per_thread_data::~per_thread_data ()
256 {
257 if (fnull)
258 std::fclose (fnull);
259 }
260
261
262 log4cplus::thread::impl::tls_key_type tls_storage_key;
263
264
265 #if ! defined (LOG4CPLUS_SINGLE_THREADED) \
266 && defined (LOG4CPLUS_THREAD_LOCAL_VAR)
267
268 LOG4CPLUS_THREAD_LOCAL_VAR per_thread_data * ptd = 0;
269
270
271 per_thread_data *
alloc_ptd()272 alloc_ptd ()
273 {
274 per_thread_data * tmp = new per_thread_data;
275 set_ptd (tmp);
276 // This is a special hack. We set the keys' value to non-NULL to
277 // get the ptd_cleanup_func to execute when this thread ends. The
278 // cast is safe; the associated value will never be used if read
279 // again using the key.
280 thread::impl::tls_set_value (tls_storage_key,
281 reinterpret_cast<void *>(1));
282
283 return tmp;
284 }
285
286 # else
287
288 per_thread_data *
alloc_ptd()289 alloc_ptd ()
290 {
291 per_thread_data * tmp = new per_thread_data;
292 set_ptd (tmp);
293 return tmp;
294 }
295
296 # endif
297
298
299 } // namespace internal
300
301
302 void initializeFactoryRegistry();
303
304
305 //! Thread local storage clean up function for POSIX threads.
306 static
307 void
ptd_cleanup_func(void * arg)308 ptd_cleanup_func (void * arg)
309 {
310 internal::per_thread_data * const arg_ptd
311 = static_cast<internal::per_thread_data *>(arg);
312 internal::per_thread_data * const ptd = internal::get_ptd (false);
313 (void) ptd;
314
315 // Either it is a dummy value or it should be the per thread data
316 // pointer we get from internal::get_ptd().
317 assert (arg == reinterpret_cast<void *>(1)
318 || arg_ptd == ptd
319 || (! ptd && arg_ptd));
320
321 if (arg == reinterpret_cast<void *>(1))
322 // Setting the value through the key here is necessary in case
323 // we are using TLS using __thread or __declspec(thread) or
324 // similar constructs with POSIX threads. Otherwise POSIX
325 // calls this cleanup routine more than once if the value
326 // stays non-NULL after it returns.
327 thread::impl::tls_set_value (internal::tls_storage_key, 0);
328 else if (arg)
329 {
330 // Instead of using internal::get_ptd(false) here we are using
331 // the value passed to this function directly. This is
332 // necessary because of the following (from SUSv4):
333 //
334 // A call to pthread_getspecific() for the thread-specific
335 // data key being destroyed shall return the value NULL,
336 // unless the value is changed (after the destructor starts)
337 // by a call to pthread_setspecific().
338 delete arg_ptd;
339 thread::impl::tls_set_value (internal::tls_storage_key, 0);
340 }
341 else
342 {
343 // In this case we fall through to threadCleanup() and it does
344 // all the necessary work itself.
345 ;
346 }
347
348 threadCleanup ();
349 }
350
351
352 static
353 void
threadSetup()354 threadSetup ()
355 {
356 internal::get_ptd (true);
357 }
358
359
360 void
initializeLog4cplus()361 initializeLog4cplus()
362 {
363 static bool initialized = false;
364 if (initialized)
365 return;
366
367 internal::tls_storage_key = thread::impl::tls_init (ptd_cleanup_func);
368 threadSetup ();
369
370 DefaultContext * dc = get_dc (true);
371 dc->TTCCLayout_time_base = helpers::Time::gettimeofday ();
372 Logger::getRoot();
373 initializeFactoryRegistry();
374
375 initialized = true;
376 }
377
378
379 void
initialize()380 initialize ()
381 {
382 initializeLog4cplus ();
383 }
384
385
386 void
threadCleanup()387 threadCleanup ()
388 {
389 // Do thread-specific cleanup.
390 internal::per_thread_data * ptd = internal::get_ptd (false);
391 delete ptd;
392 internal::set_ptd (0);
393 }
394
395
396 #if defined (_WIN32)
397 static
398 VOID CALLBACK
initializeLog4cplusApcProc(ULONG_PTR)399 initializeLog4cplusApcProc (ULONG_PTR /*dwParam*/)
400 {
401 initializeLog4cplus ();
402 threadSetup ();
403 }
404
405
406 static
407 void
queueLog4cplusInitializationThroughAPC()408 queueLog4cplusInitializationThroughAPC ()
409 {
410 if (! QueueUserAPC (initializeLog4cplusApcProc, GetCurrentThread (),
411 0))
412 throw std::runtime_error ("QueueUserAPC() has failed");
413 }
414
415
416 static
417 void NTAPI
thread_callback(LPVOID,DWORD fdwReason,LPVOID)418 thread_callback (LPVOID /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpReserved*/)
419 {
420 // Perform actions based on the reason for calling.
421 switch (fdwReason)
422 {
423 case DLL_PROCESS_ATTACH:
424 {
425 // We cannot initialize log4cplus directly here. This is because
426 // DllMain() is called under loader lock. When we are using C++11
427 // threads and synchronization primitives then there is a deadlock
428 // somewhere in internals of std::mutex::lock().
429 queueLog4cplusInitializationThroughAPC ();
430 break;
431 }
432
433 case DLL_THREAD_ATTACH:
434 {
435 // We could call threadSetup() here but that imposes overhead
436 // on threads that do not use log4cplus. Thread local data will
437 // be initialized lazily instead.
438 break;
439 }
440
441 case DLL_THREAD_DETACH:
442 {
443 // Do thread-specific cleanup.
444 log4cplus::threadCleanup ();
445
446 break;
447 }
448
449 case DLL_PROCESS_DETACH:
450 {
451 // Perform any necessary cleanup.
452
453 // Do thread-specific cleanup.
454 log4cplus::threadCleanup ();
455 #if ! defined (LOG4CPLUS_THREAD_LOCAL_VAR)
456 log4cplus::thread::impl::tls_cleanup (
457 log4cplus::internal::tls_storage_key);
458 #endif
459 break;
460 }
461
462 } // switch
463 }
464
465 #endif
466
467
468 } // namespace log4cplus
469
470
471 #if defined (_WIN32) && defined (LOG4CPLUS_BUILD_DLL) && defined (_DLL)
472 extern "C"
473 BOOL
474 WINAPI
DllMain(LOG4CPLUS_DLLMAIN_HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)475 DllMain (LOG4CPLUS_DLLMAIN_HINSTANCE hinstDLL, DWORD fdwReason,
476 LPVOID lpReserved)
477 {
478 log4cplus::thread_callback (hinstDLL, fdwReason, lpReserved);
479
480 return TRUE; // Successful DLL_PROCESS_ATTACH.
481 }
482
483 #elif defined (_WIN32) \
484 && defined (_MSC_VER) && _MSC_VER >= 1400 && defined (_DLL)
485 extern "C"
486 {
487
488 // This magic has been pieced together from several sources:
489 // - <http://www.nynaeve.net/?p=183>
490 // - <http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-November/018818.html>
491
492 #pragma data_seg (push, old_seg)
493 #ifdef _WIN64
494 #pragma const_seg (".CRT$XLX")
495 extern const
496 #else
497 #pragma data_seg (".CRT$XLX")
498 #endif
499 PIMAGE_TLS_CALLBACK log4cplus_p_thread_callback = log4cplus::thread_callback;
500 #pragma data_seg (pop, old_seg)
501 #ifdef _WIN64
502 #pragma comment (linker, "/INCLUDE:_tls_used")
503 #pragma comment (linker, "/INCLUDE:log4cplus_p_thread_callback")
504 #else
505 #pragma comment (linker, "/INCLUDE:__tls_used")
506 #pragma comment (linker, "/INCLUDE:_log4cplus_p_thread_callback")
507 #endif
508
509 } // extern "C"
510
511 #elif defined (_WIN32)
512 namespace {
513
514 struct _static_log4cplus_initializer
515 {
_static_log4cplus_initializer__anonf74f6c680211::_static_log4cplus_initializer516 _static_log4cplus_initializer ()
517 {
518 // It is not possible to reliably call initializeLog4cplus() here
519 // when we are using Visual Studio and C++11 threads
520 // and synchronization primitives. It would result into a deadlock
521 // on loader lock.
522 #if ! (defined (LOG4CPLUS_WITH_CXX11_THREADS) && defined (_MSC_VER))
523 log4cplus::initializeLog4cplus ();
524 #endif
525 }
526
~_static_log4cplus_initializer__anonf74f6c680211::_static_log4cplus_initializer527 ~_static_log4cplus_initializer ()
528 {
529 // Last thread cleanup.
530 log4cplus::threadCleanup ();
531 #if ! defined (LOG4CPLUS_THREAD_LOCAL_VAR)
532 log4cplus::thread::impl::tls_cleanup (
533 log4cplus::internal::tls_storage_key);
534 #endif
535 }
536 } static initializer;
537
538 } // namespace
539
540 #else
541 namespace {
542
543 struct _static_log4cplus_initializer
544 {
_static_log4cplus_initializer__anonf74f6c680311::_static_log4cplus_initializer545 _static_log4cplus_initializer ()
546 {
547 log4cplus::initializeLog4cplus();
548 }
549
~_static_log4cplus_initializer__anonf74f6c680311::_static_log4cplus_initializer550 ~_static_log4cplus_initializer ()
551 {
552 // Last thread cleanup.
553 log4cplus::threadCleanup ();
554
555 log4cplus::thread::impl::tls_cleanup (
556 log4cplus::internal::tls_storage_key);
557 }
558 } static initializer;
559
560 } // namespace
561
562 #endif
563