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