/src/log4cplus/src/global-init.cxx
Line | Count | Source |
1 | | // Module: Log4CPLUS |
2 | | // File: global-init.cxx |
3 | | // Created: 5/2003 |
4 | | // Author: Tad E. Smith |
5 | | // |
6 | | // |
7 | | // Copyright 2003-2017 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 | | |
23 | | #if defined (LOG4CPLUS_WITH_UNIT_TESTS) |
24 | | // Include full Windows.h early for Catch. |
25 | | # include <log4cplus/config/windowsh-inc-full.h> |
26 | | # define CATCH_CONFIG_RUNNER |
27 | | # include <catch.hpp> |
28 | | #endif |
29 | | |
30 | | #include <log4cplus/initializer.h> |
31 | | #include <log4cplus/config/windowsh-inc.h> |
32 | | #include <log4cplus/logger.h> |
33 | | #include <log4cplus/ndc.h> |
34 | | #include <log4cplus/mdc.h> |
35 | | #include <log4cplus/helpers/eventcounter.h> |
36 | | #include <log4cplus/helpers/loglog.h> |
37 | | #include <log4cplus/internal/customloglevelmanager.h> |
38 | | #include <log4cplus/internal/internal.h> |
39 | | #include <log4cplus/thread/impl/tls.h> |
40 | | #include <log4cplus/thread/syncprims-pub-impl.h> |
41 | | #include <log4cplus/helpers/loglog.h> |
42 | | #include <log4cplus/spi/factory.h> |
43 | | #include <log4cplus/hierarchy.h> |
44 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
45 | | #include "ThreadPool.h" |
46 | | #endif |
47 | | #include <cstdio> |
48 | | #include <iostream> |
49 | | #include <stdexcept> |
50 | | #include <chrono> |
51 | | |
52 | | |
53 | | // Forward Declarations |
54 | | namespace log4cplus |
55 | | { |
56 | | |
57 | | #ifdef UNICODE |
58 | | LOG4CPLUS_EXPORT tostream & tcout = std::wcout; |
59 | | LOG4CPLUS_EXPORT tostream & tcerr = std::wcerr; |
60 | | |
61 | | #else |
62 | | LOG4CPLUS_EXPORT tostream & tcout = std::cout; |
63 | | LOG4CPLUS_EXPORT tostream & tcerr = std::cerr; |
64 | | |
65 | | #endif // UNICODE |
66 | | |
67 | | |
68 | | struct InitializerImpl |
69 | | { |
70 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
71 | | std::mutex mtx; |
72 | | |
73 | | static std::once_flag flag; |
74 | | #endif |
75 | | |
76 | | unsigned count = 0; |
77 | | |
78 | | static InitializerImpl * instance; |
79 | | }; |
80 | | |
81 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
82 | | std::once_flag InitializerImpl::flag; |
83 | | #endif |
84 | | InitializerImpl * InitializerImpl::instance; |
85 | | |
86 | | |
87 | | Initializer::Initializer () |
88 | 70 | { |
89 | 70 | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
90 | 70 | std::call_once (InitializerImpl::flag, |
91 | 70 | [&] { |
92 | 70 | InitializerImpl::instance = new InitializerImpl; |
93 | 70 | }); |
94 | | #else |
95 | | InitializerImpl::instance = new InitializerImpl; |
96 | | #endif |
97 | | |
98 | 70 | LOG4CPLUS_THREADED ( |
99 | 70 | std::unique_lock<std::mutex> guard ( |
100 | 70 | InitializerImpl::instance->mtx)); |
101 | 70 | if (InitializerImpl::instance->count == 0) |
102 | 70 | initialize (); |
103 | | |
104 | 70 | ++InitializerImpl::instance->count; |
105 | 70 | } |
106 | | |
107 | | |
108 | | // Forward declaration. Defined in this file. |
109 | | void shutdownThreadPool(); |
110 | | |
111 | | Initializer::~Initializer () |
112 | 0 | { |
113 | 0 | bool destroy = false; |
114 | 0 | { |
115 | 0 | LOG4CPLUS_THREADED ( |
116 | 0 | std::unique_lock<std::mutex> guard ( |
117 | 0 | InitializerImpl::instance->mtx)); |
118 | 0 | --InitializerImpl::instance->count; |
119 | 0 | if (InitializerImpl::instance->count == 0) |
120 | 0 | { |
121 | 0 | destroy = true; |
122 | 0 | deinitialize (); |
123 | 0 | } |
124 | 0 | } |
125 | 0 | if (destroy) |
126 | 0 | { |
127 | 0 | delete InitializerImpl::instance; |
128 | 0 | InitializerImpl::instance = nullptr; |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | |
133 | | namespace |
134 | | { |
135 | | |
136 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
137 | | static |
138 | | std::unique_ptr<progschj::ThreadPool> |
139 | | instantiate_thread_pool () |
140 | 36 | { |
141 | 36 | log4cplus::thread::SignalsBlocker sb; |
142 | 36 | #if defined (LOG4CPLUS_ENABLE_THREAD_POOL) |
143 | 36 | return std::unique_ptr<progschj::ThreadPool>(new progschj::ThreadPool (4)); |
144 | | #else |
145 | | return std::unique_ptr<progschj::ThreadPool>(); |
146 | | #endif |
147 | 36 | } |
148 | | #endif |
149 | | |
150 | | |
151 | | //! Helper structure for holding progschj::ThreadPool pointer. |
152 | | //! It is necessary to have this so that we can correctly order |
153 | | //! destructors between Hierarchy and the progschj::ThreadPool. |
154 | | //! Hierarchy wants to wait for outstading logging to finish |
155 | | //! therefore the ThreadPool can only be destroyed after that. |
156 | | struct ThreadPoolHolder |
157 | | { |
158 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
159 | | std::atomic<progschj::ThreadPool*> thread_pool{}; |
160 | | #endif |
161 | | |
162 | 70 | ThreadPoolHolder () = default; |
163 | | ThreadPoolHolder (ThreadPoolHolder const&) = delete; |
164 | | ~ThreadPoolHolder () |
165 | 0 | { |
166 | 0 | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
167 | 0 | auto const tp = thread_pool.exchange(nullptr, std::memory_order_release); |
168 | 0 | delete tp; |
169 | 0 | #endif |
170 | 0 | } |
171 | | }; |
172 | | |
173 | | |
174 | | //! Default context. |
175 | | struct DefaultContext |
176 | | { |
177 | | log4cplus::thread::Mutex console_mutex; |
178 | | helpers::LogLog loglog; |
179 | | LogLevelManager log_level_manager; |
180 | | internal::CustomLogLevelManager custom_log_level_manager; |
181 | | helpers::Time TTCCLayout_time_base; |
182 | | NDC ndc; |
183 | | MDC mdc; |
184 | | spi::AppenderFactoryRegistry appender_factory_registry; |
185 | | spi::LayoutFactoryRegistry layout_factory_registry; |
186 | | spi::FilterFactoryRegistry filter_factory_registry; |
187 | | spi::LocaleFactoryRegistry locale_factory_registry; |
188 | | Hierarchy hierarchy; |
189 | | ThreadPoolHolder thread_pool; |
190 | | std::atomic<bool> block_on_full {true}; |
191 | | |
192 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
193 | | progschj::ThreadPool * |
194 | | get_thread_pool (bool init) |
195 | 448k | { |
196 | 448k | if (init) { |
197 | 123k | std::call_once (thread_pool_once, [&] { |
198 | 36 | thread_pool.thread_pool.store (instantiate_thread_pool ().release (), std::memory_order_release); |
199 | 36 | }); |
200 | 123k | } |
201 | | // cppreference.com says: The specification of release-consume ordering |
202 | | // is being revised, and the use of memory_order_consume is temporarily |
203 | | // discouraged. Thus, let's use memory_order_acquire. |
204 | 448k | return thread_pool.thread_pool.load (std::memory_order_acquire); |
205 | 448k | } |
206 | | |
207 | | void |
208 | | shutdown_thread_pool () |
209 | 0 | { |
210 | 0 | auto const tp = thread_pool.thread_pool.exchange(nullptr, std::memory_order_release); |
211 | 0 | delete tp; |
212 | 0 | } |
213 | | |
214 | | private: |
215 | | std::once_flag thread_pool_once; |
216 | | #endif |
217 | | }; |
218 | | |
219 | | |
220 | | enum DCState |
221 | | { |
222 | | DC_UNINITIALIZED, |
223 | | DC_INITIALIZED, |
224 | | DC_DESTROYED |
225 | | }; |
226 | | |
227 | | |
228 | | static DCState default_context_state = DC_UNINITIALIZED; |
229 | | static DefaultContext * default_context = nullptr; |
230 | | |
231 | | |
232 | | struct destroy_default_context |
233 | | { |
234 | | ~destroy_default_context () |
235 | 0 | { |
236 | 0 | delete default_context; |
237 | 0 | default_context = nullptr; |
238 | 0 | default_context_state = DC_DESTROYED; |
239 | 0 | } |
240 | | } static destroy_default_context_ |
241 | | LOG4CPLUS_INIT_PRIORITY (LOG4CPLUS_INIT_PRIORITY_BASE + 1); |
242 | | |
243 | | |
244 | | static |
245 | | void |
246 | | alloc_dc () |
247 | 70 | { |
248 | 70 | assert (! default_context); |
249 | 70 | assert (default_context_state == DC_UNINITIALIZED); |
250 | | |
251 | 70 | if (default_context) |
252 | 0 | throw std::logic_error ( |
253 | 0 | "alloc_dc() called with non-NULL default_context."); |
254 | | |
255 | 70 | if (default_context_state == DC_INITIALIZED) |
256 | 0 | throw std::logic_error ("alloc_dc() called in DC_INITIALIZED state."); |
257 | | |
258 | 70 | default_context = new DefaultContext; |
259 | | |
260 | 70 | if (default_context_state == DC_DESTROYED) |
261 | 0 | default_context->loglog.error ( |
262 | 0 | LOG4CPLUS_TEXT ("Re-initializing default context after it has") |
263 | 0 | LOG4CPLUS_TEXT (" already been destroyed.\n") |
264 | 0 | LOG4CPLUS_TEXT ("The memory will be leaked.")); |
265 | | |
266 | 70 | default_context_state = DC_INITIALIZED; |
267 | 70 | } |
268 | | |
269 | | |
270 | | static |
271 | | DefaultContext * |
272 | | get_dc( |
273 | | #ifdef LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION |
274 | | bool alloc = false |
275 | | #else |
276 | | bool alloc = true |
277 | | #endif |
278 | | ) |
279 | 5.10M | { |
280 | 5.10M | if (LOG4CPLUS_UNLIKELY(!default_context)) |
281 | 70 | { |
282 | 70 | if (alloc) |
283 | 70 | { |
284 | 70 | alloc_dc(); |
285 | 70 | } |
286 | 0 | else |
287 | 0 | { |
288 | | #ifdef LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION |
289 | | throw std::logic_error("log4cplus is not initialized" |
290 | | " and implicit initialization is turned off"); |
291 | | #endif |
292 | 0 | } |
293 | 70 | } |
294 | 5.10M | return default_context; |
295 | 5.10M | } |
296 | | |
297 | | |
298 | | } // namespace |
299 | | |
300 | | |
301 | | namespace internal { |
302 | | |
303 | | CustomLogLevelManager & getCustomLogLevelManager () |
304 | 0 | { |
305 | 0 | return get_dc ()->custom_log_level_manager; |
306 | 0 | } |
307 | | |
308 | | } // namespace internal |
309 | | |
310 | | |
311 | | namespace helpers |
312 | | { |
313 | | |
314 | | |
315 | | log4cplus::thread::Mutex const & |
316 | | getConsoleOutputMutex () |
317 | 373k | { |
318 | 373k | return get_dc ()->console_mutex; |
319 | 373k | } |
320 | | |
321 | | |
322 | | LogLog & |
323 | | getLogLog () |
324 | 847k | { |
325 | 847k | return get_dc ()->loglog; |
326 | 847k | } |
327 | | |
328 | | |
329 | | } // namespace helpers |
330 | | |
331 | | |
332 | | helpers::Time const & |
333 | | getTTCCLayoutTimeBase () |
334 | 0 | { |
335 | 0 | return get_dc ()->TTCCLayout_time_base; |
336 | 0 | } |
337 | | |
338 | | |
339 | | LogLevelManager & |
340 | | getLogLevelManager () |
341 | 2.17M | { |
342 | 2.17M | return get_dc ()->log_level_manager; |
343 | 2.17M | } |
344 | | |
345 | | |
346 | | Hierarchy & |
347 | | getDefaultHierarchy () |
348 | 1.07M | { |
349 | 1.07M | return get_dc ()->hierarchy; |
350 | 1.07M | } |
351 | | |
352 | | |
353 | | NDC & |
354 | | getNDC () |
355 | 0 | { |
356 | 0 | return get_dc ()->ndc; |
357 | 0 | } |
358 | | |
359 | | |
360 | | MDC & |
361 | | getMDC () |
362 | 0 | { |
363 | 0 | return get_dc ()->mdc; |
364 | 0 | } |
365 | | |
366 | | |
367 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) \ |
368 | | && defined (LOG4CPLUS_ENABLE_THREAD_POOL) |
369 | | void |
370 | | enqueueAsyncDoAppend (SharedAppenderPtr const & appender, |
371 | | spi::InternalLoggingEvent const & event) |
372 | 0 | { |
373 | 0 | static helpers::SteadyClockGate gate (helpers::SteadyClockGate::Duration {std::chrono::minutes (5)}); |
374 | |
|
375 | 0 | DefaultContext * dc = get_dc (); |
376 | 0 | progschj::ThreadPool * tp = dc->get_thread_pool (true); |
377 | 0 | if (dc->block_on_full) |
378 | 0 | tp->enqueue_block ([=] () { appender->asyncDoAppend (event); }); |
379 | 0 | else |
380 | 0 | { |
381 | 0 | std::future<void> future = tp->enqueue ([=] () { appender->asyncDoAppend (event); }); |
382 | 0 | if (future.wait_for (std::chrono::seconds (0)) == std::future_status::ready) |
383 | 0 | { |
384 | 0 | try |
385 | 0 | { |
386 | 0 | future.get (); |
387 | 0 | } |
388 | 0 | catch (const progschj::would_block &) |
389 | 0 | { |
390 | 0 | gate.record_event (); |
391 | 0 | helpers::SteadyClockGate::Info info; |
392 | 0 | if (gate.latch_open (info)) |
393 | 0 | { |
394 | 0 | helpers::LogLog & loglog = helpers::getLogLog (); |
395 | 0 | log4cplus::tostringstream oss; |
396 | 0 | oss << LOG4CPLUS_TEXT ("Asynchronous logging queue is full. Dropped ") |
397 | 0 | << info.count << LOG4CPLUS_TEXT (" events in last ") |
398 | 0 | << std::chrono::duration_cast<std::chrono::seconds> (info.time_span).count () |
399 | 0 | << LOG4CPLUS_TEXT (" seconds"); |
400 | 0 | loglog.warn (oss.str ()); |
401 | 0 | } |
402 | 0 | } |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | #endif |
408 | | |
409 | | void |
410 | | shutdownThreadPool () |
411 | 0 | { |
412 | 0 | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
413 | 0 | DefaultContext * const dc = get_dc (false); |
414 | 0 | if (dc) |
415 | 0 | { |
416 | 0 | dc->shutdown_thread_pool (); |
417 | 0 | } |
418 | 0 | #endif |
419 | 0 | } |
420 | | |
421 | | |
422 | | void |
423 | | waitUntilEmptyThreadPoolQueue () |
424 | 324k | { |
425 | 324k | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
426 | 324k | DefaultContext * const dc = get_dc (false); |
427 | 324k | progschj::ThreadPool * tp; |
428 | 324k | if (dc && (tp = dc->get_thread_pool (false))) |
429 | 324k | { |
430 | 324k | tp->wait_until_empty (); |
431 | 324k | tp->wait_until_nothing_in_flight (); |
432 | 324k | } |
433 | 324k | #endif |
434 | 324k | } |
435 | | |
436 | | |
437 | | namespace spi |
438 | | { |
439 | | |
440 | | |
441 | | AppenderFactoryRegistry & |
442 | | getAppenderFactoryRegistry () |
443 | 123k | { |
444 | 123k | return get_dc ()->appender_factory_registry; |
445 | 123k | } |
446 | | |
447 | | |
448 | | LayoutFactoryRegistry & |
449 | | getLayoutFactoryRegistry () |
450 | 70 | { |
451 | 70 | return get_dc ()->layout_factory_registry; |
452 | 70 | } |
453 | | |
454 | | |
455 | | FilterFactoryRegistry & |
456 | | getFilterFactoryRegistry () |
457 | 70 | { |
458 | 70 | return get_dc ()->filter_factory_registry; |
459 | 70 | } |
460 | | |
461 | | |
462 | | LocaleFactoryRegistry & |
463 | | getLocaleFactoryRegistry() |
464 | 62.0k | { |
465 | 62.0k | return get_dc ()->locale_factory_registry; |
466 | 62.0k | } |
467 | | |
468 | | |
469 | | } // namespace spi |
470 | | |
471 | | |
472 | | namespace internal |
473 | | { |
474 | | |
475 | | |
476 | | gft_scratch_pad::gft_scratch_pad () |
477 | 70 | : uc_q_str_valid (false) |
478 | 70 | , q_str_valid (false) |
479 | 70 | , s_str_valid (false) |
480 | 70 | { } |
481 | | |
482 | | |
483 | | gft_scratch_pad::~gft_scratch_pad () |
484 | 0 | { } |
485 | | |
486 | | |
487 | | appender_sratch_pad::appender_sratch_pad () |
488 | 70 | { } |
489 | | |
490 | | |
491 | | appender_sratch_pad::~appender_sratch_pad () |
492 | 0 | { } |
493 | | |
494 | | |
495 | | per_thread_data::per_thread_data () |
496 | 70 | : fnull (nullptr) |
497 | 70 | { } |
498 | | |
499 | | |
500 | | per_thread_data::~per_thread_data () |
501 | 0 | { |
502 | 0 | if (fnull) |
503 | 0 | std::fclose (fnull); |
504 | 0 | } |
505 | | |
506 | | |
507 | | log4cplus::thread::impl::tls_key_type tls_storage_key; |
508 | | |
509 | | |
510 | | #if ! defined (LOG4CPLUS_SINGLE_THREADED) \ |
511 | | && defined (LOG4CPLUS_THREAD_LOCAL_VAR) |
512 | | |
513 | | LOG4CPLUS_THREAD_LOCAL_VAR per_thread_data * ptd = nullptr; |
514 | | |
515 | | |
516 | | per_thread_data * |
517 | | alloc_ptd () |
518 | 70 | { |
519 | 70 | auto * tmp = new per_thread_data; |
520 | 70 | set_ptd (tmp); |
521 | | // This is a special hack. We set the keys' value to non-NULL to |
522 | | // get the ptd_cleanup_func to execute when this thread ends. The |
523 | | // cast is safe; the associated value will never be used if read |
524 | | // again using the key. |
525 | 70 | thread::impl::tls_set_value (tls_storage_key, |
526 | 70 | reinterpret_cast<void *>(1)); |
527 | | |
528 | 70 | return tmp; |
529 | 70 | } |
530 | | |
531 | | # else |
532 | | |
533 | | per_thread_data * |
534 | | alloc_ptd () |
535 | | { |
536 | | auto * tmp = new per_thread_data; |
537 | | set_ptd (tmp); |
538 | | return tmp; |
539 | | } |
540 | | |
541 | | # endif |
542 | | |
543 | | } // namespace internal |
544 | | |
545 | | |
546 | | void initializeFactoryRegistry (); |
547 | | |
548 | | |
549 | | //! Thread local storage clean up function for POSIX threads. |
550 | | #if defined (LOG4CPLUS_USE_WIN32_THREADS) |
551 | | static |
552 | | void NTAPI |
553 | | #else |
554 | | static |
555 | | void |
556 | | #endif |
557 | | ptd_cleanup_func (void * arg) |
558 | 0 | { |
559 | 0 | internal::per_thread_data * const arg_ptd |
560 | 0 | = static_cast<internal::per_thread_data *>(arg); |
561 | 0 | internal::per_thread_data * const ptd = internal::get_ptd (false); |
562 | 0 | (void) ptd; |
563 | | |
564 | | // Either it is a dummy value or it should be the per thread data |
565 | | // pointer we get from internal::get_ptd(). |
566 | 0 | assert (arg == reinterpret_cast<void *>(1) |
567 | 0 | || arg_ptd == ptd |
568 | 0 | || (! ptd && arg_ptd)); |
569 | |
|
570 | 0 | if (arg == reinterpret_cast<void *>(1)) |
571 | | // Setting the value through the key here is necessary in case |
572 | | // we are using TLS using __thread or __declspec(thread) or |
573 | | // similar constructs with POSIX threads. Otherwise POSIX |
574 | | // calls this cleanup routine more than once if the value |
575 | | // stays non-NULL after it returns. |
576 | 0 | thread::impl::tls_set_value (internal::tls_storage_key, nullptr); |
577 | 0 | else if (arg) |
578 | 0 | { |
579 | | // Instead of using internal::get_ptd(false) here we are using |
580 | | // the value passed to this function directly. This is |
581 | | // necessary because of the following (from SUSv4): |
582 | | // |
583 | | // A call to pthread_getspecific() for the thread-specific |
584 | | // data key being destroyed shall return the value NULL, |
585 | | // unless the value is changed (after the destructor starts) |
586 | | // by a call to pthread_setspecific(). |
587 | 0 | delete arg_ptd; |
588 | 0 | thread::impl::tls_set_value (internal::tls_storage_key, nullptr); |
589 | 0 | } |
590 | 0 | else |
591 | 0 | { |
592 | | // In this case we fall through to threadCleanup() and it does |
593 | | // all the necessary work itself. |
594 | 0 | ; |
595 | 0 | } |
596 | |
|
597 | 0 | threadCleanup (); |
598 | 0 | } |
599 | | |
600 | | |
601 | | static |
602 | | void |
603 | | threadSetup () |
604 | 70 | { |
605 | 70 | internal::get_ptd (true); |
606 | 70 | } |
607 | | |
608 | | |
609 | | void |
610 | | initializeLog4cplus() |
611 | 124k | { |
612 | 124k | static bool initialized = false; |
613 | 124k | if (initialized) |
614 | 123k | return; |
615 | | |
616 | 70 | internal::tls_storage_key = thread::impl::tls_init (ptd_cleanup_func); |
617 | 70 | threadSetup (); |
618 | | |
619 | 70 | DefaultContext * dc = get_dc (true); |
620 | 70 | dc->TTCCLayout_time_base = helpers::now (); |
621 | 70 | Logger::getRoot(); |
622 | 70 | initializeFactoryRegistry(); |
623 | | |
624 | 70 | initialized = true; |
625 | 70 | } |
626 | | |
627 | | |
628 | | void |
629 | | initialize () |
630 | 70 | { |
631 | 70 | initializeLog4cplus (); |
632 | 70 | } |
633 | | |
634 | | |
635 | | void |
636 | | deinitialize () |
637 | 0 | { |
638 | 0 | Logger::shutdown (); |
639 | 0 | shutdownThreadPool(); |
640 | 0 | } |
641 | | |
642 | | |
643 | | void |
644 | | threadCleanup () |
645 | 0 | { |
646 | | // Here we check that we can get CRT's heap handle because if we do not |
647 | | // then the following `delete` will fail with access violation in |
648 | | // `RtlFreeHeap()`. |
649 | | // |
650 | | // How is it possible that the CRT heap handle is NULL? |
651 | | // |
652 | | // This function can be called from TLS initializer/terminator by loader |
653 | | // when log4cplus is compiled and linked to as a static library. In case of |
654 | | // other threads termination, it should do its job and free per-thread |
655 | | // data. However, when the whole process is being terminated, it is called |
656 | | // after the CRT has been uninitialized and the CRT heap is not available |
657 | | // any more. In such case, instead of crashing, we just give up and leak |
658 | | // the memory for the short while before the process terminates anyway. |
659 | | // |
660 | | // It is possible to work around this situation in user application by |
661 | | // calling `threadCleanup()` manually before `main()` exits. |
662 | | #if defined (_WIN32) |
663 | | if (_get_heap_handle() != 0) |
664 | | { |
665 | | #endif |
666 | | // Do thread-specific cleanup. |
667 | 0 | internal::per_thread_data * ptd = internal::get_ptd (false); |
668 | 0 | delete ptd; |
669 | | #if defined (_WIN32) |
670 | | } |
671 | | else |
672 | | { |
673 | | OutputDebugString ( |
674 | | LOG4CPLUS_TEXT ("log4cplus: ") |
675 | | LOG4CPLUS_TEXT ("CRT heap is already gone in threadCleanup()\n")); |
676 | | } |
677 | | #endif |
678 | 0 | internal::set_ptd (nullptr); |
679 | 0 | } |
680 | | |
681 | | |
682 | | void |
683 | | setThreadPoolSize (std::size_t LOG4CPLUS_THREADED (pool_size)) |
684 | 123k | { |
685 | 123k | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
686 | 123k | auto const thread_pool = get_dc ()->get_thread_pool (true); |
687 | 123k | if (thread_pool) |
688 | 123k | thread_pool->set_pool_size (pool_size); |
689 | | |
690 | 123k | #endif |
691 | 123k | } |
692 | | |
693 | | |
694 | | void |
695 | | setThreadPoolQueueSizeLimit (std::size_t LOG4CPLUS_THREADED (queue_size_limit)) |
696 | 0 | { |
697 | 0 | #if ! defined (LOG4CPLUS_SINGLE_THREADED) |
698 | 0 | auto const thread_pool = get_dc ()->get_thread_pool (true); |
699 | 0 | if (thread_pool) |
700 | 0 | thread_pool->set_queue_size_limit (queue_size_limit); |
701 | |
|
702 | 0 | #endif |
703 | 0 | } |
704 | | |
705 | | |
706 | | void |
707 | | setThreadPoolBlockOnFull (bool block) |
708 | 0 | { |
709 | 0 | get_dc ()->block_on_full.store (block); |
710 | 0 | } |
711 | | |
712 | | static |
713 | | void |
714 | | freeTLSSlot () |
715 | 0 | { |
716 | 0 | if (internal::tls_storage_key != thread::impl::tls_key_type ()) |
717 | 0 | { |
718 | 0 | thread::impl::tls_cleanup(internal::tls_storage_key); |
719 | 0 | internal::tls_storage_key = thread::impl::tls_key_type(); |
720 | 0 | } |
721 | 0 | } |
722 | | |
723 | | |
724 | | #if defined (_WIN32) |
725 | | static |
726 | | VOID CALLBACK |
727 | | initializeLog4cplusApcProc (ULONG_PTR /*dwParam*/) |
728 | | { |
729 | | initializeLog4cplus (); |
730 | | threadSetup (); |
731 | | } |
732 | | |
733 | | |
734 | | static |
735 | | void |
736 | | queueLog4cplusInitializationThroughAPC () |
737 | | { |
738 | | #if defined (LOG4CPLUS_BUILD_DLL) |
739 | | if (! QueueUserAPC (initializeLog4cplusApcProc, GetCurrentThread (), |
740 | | 0)) |
741 | | throw std::runtime_error ("QueueUserAPC() has failed"); |
742 | | #endif |
743 | | } |
744 | | |
745 | | |
746 | | static |
747 | | void NTAPI |
748 | | thread_callback (LPVOID /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpReserved*/) |
749 | | { |
750 | | // Perform actions based on the reason for calling. |
751 | | switch (fdwReason) |
752 | | { |
753 | | case DLL_PROCESS_ATTACH: |
754 | | { |
755 | | #if !defined(LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
756 | | // We cannot initialize log4cplus directly here. This is because |
757 | | // DllMain() is called under loader lock. When we are using C++11 |
758 | | // threads and synchronization primitives then there is a deadlock |
759 | | // somewhere in internals of std::mutex::lock(). |
760 | | queueLog4cplusInitializationThroughAPC (); |
761 | | #endif |
762 | | break; |
763 | | } |
764 | | |
765 | | case DLL_THREAD_ATTACH: |
766 | | { |
767 | | // We could call threadSetup() here but that imposes overhead |
768 | | // on threads that do not use log4cplus. Thread local data will |
769 | | // be initialized lazily instead. |
770 | | break; |
771 | | } |
772 | | |
773 | | case DLL_THREAD_DETACH: |
774 | | { |
775 | | // Do thread-specific cleanup. |
776 | | log4cplus::threadCleanup (); |
777 | | |
778 | | break; |
779 | | } |
780 | | |
781 | | case DLL_PROCESS_DETACH: |
782 | | { |
783 | | // Perform any necessary cleanup. |
784 | | |
785 | | // Do thread-specific cleanup. |
786 | | log4cplus::threadCleanup (); |
787 | | log4cplus::freeTLSSlot(); |
788 | | |
789 | | break; |
790 | | } |
791 | | |
792 | | } // switch |
793 | | } |
794 | | |
795 | | |
796 | | static |
797 | | void NTAPI |
798 | | thread_callback_initializer(LPVOID hinstDLL, DWORD fdwReason, LPVOID lpReserved) |
799 | | { |
800 | | if (fdwReason == DLL_PROCESS_ATTACH |
801 | | || fdwReason == DLL_THREAD_ATTACH) |
802 | | thread_callback(hinstDLL, fdwReason, lpReserved); |
803 | | } |
804 | | |
805 | | static |
806 | | void NTAPI |
807 | | thread_callback_terminator(LPVOID hinstDLL, DWORD fdwReason, LPVOID lpReserved) |
808 | | { |
809 | | if (fdwReason == DLL_THREAD_DETACH |
810 | | || fdwReason == DLL_PROCESS_DETACH) |
811 | | thread_callback(hinstDLL, fdwReason, lpReserved); |
812 | | } |
813 | | |
814 | | #endif |
815 | | |
816 | | |
817 | | #if defined (LOG4CPLUS_WITH_UNIT_TESTS) |
818 | | LOG4CPLUS_EXPORT int unit_tests_main (int argc, char* argv[]); |
819 | | int |
820 | | unit_tests_main (int argc, char * argv[]) |
821 | | { |
822 | | return Catch::Session ().run (argc, argv); |
823 | | } |
824 | | |
825 | | #endif // defined (LOG4CPLUS_WITH_UNIT_TESTS) |
826 | | |
827 | | } // namespace log4cplus |
828 | | |
829 | | |
830 | | #if defined (_WIN32) |
831 | | #if defined (LOG4CPLUS_BUILD_DLL) && defined (_DLL) |
832 | | extern "C" |
833 | | BOOL |
834 | | WINAPI |
835 | | DllMain (LOG4CPLUS_DLLMAIN_HINSTANCE hinstDLL, DWORD fdwReason, |
836 | | LPVOID lpReserved) |
837 | | { |
838 | | log4cplus::thread_callback (hinstDLL, fdwReason, lpReserved); |
839 | | |
840 | | return TRUE; // Successful DLL_PROCESS_ATTACH. |
841 | | } |
842 | | |
843 | | #elif defined (_MSC_VER) && _MSC_VER >= 1400 && defined (_DLL) |
844 | | extern "C" |
845 | | { |
846 | | |
847 | | // This magic has been pieced together from several sources: |
848 | | // - <http://www.nynaeve.net/?p=183> |
849 | | // - <http://lists.llvm.org/pipermail/cfe-dev/2011-November/018818.html> |
850 | | // - `internal_shared.h` in CRT source in Visual Studio 2015 |
851 | | |
852 | | #pragma data_seg (push, old_seg) |
853 | | #ifdef _WIN64 |
854 | | #pragma const_seg (".CRT$XLY") |
855 | | extern const |
856 | | #else |
857 | | #pragma data_seg (".CRT$XLY") |
858 | | #endif |
859 | | PIMAGE_TLS_CALLBACK log4cplus_p_thread_callback_initializer = log4cplus::thread_callback_initializer; |
860 | | #pragma data_seg (pop, old_seg) |
861 | | |
862 | | #pragma data_seg (push, old_seg) |
863 | | #ifdef _WIN64 |
864 | | #pragma const_seg (".CRT$XLAA") |
865 | | extern const |
866 | | #else |
867 | | #pragma data_seg (".CRT$XLAA") |
868 | | #endif |
869 | | PIMAGE_TLS_CALLBACK log4cplus_p_thread_callback_terminator = log4cplus::thread_callback_terminator; |
870 | | #pragma data_seg (pop, old_seg) |
871 | | |
872 | | #ifdef _WIN64 |
873 | | #pragma comment (linker, "/INCLUDE:_tls_used") |
874 | | #if ! defined (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
875 | | #pragma comment (linker, "/INCLUDE:log4cplus_p_thread_callback_initializer") |
876 | | #endif |
877 | | #pragma comment (linker, "/INCLUDE:log4cplus_p_thread_callback_terminator") |
878 | | #else |
879 | | #pragma comment (linker, "/INCLUDE:__tls_used") |
880 | | #if ! defined (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
881 | | #pragma comment (linker, "/INCLUDE:_log4cplus_p_thread_callback_initializer") |
882 | | #endif |
883 | | #pragma comment (linker, "/INCLUDE:_log4cplus_p_thread_callback_terminator") |
884 | | #endif |
885 | | |
886 | | } // extern "C" |
887 | | |
888 | | #endif |
889 | | |
890 | | namespace { |
891 | | |
892 | | struct _static_log4cplus_initializer |
893 | | { |
894 | | #if ! defined (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
895 | | _static_log4cplus_initializer () |
896 | | { |
897 | | // It is not possible to reliably call initializeLog4cplus() here |
898 | | // when we are using Visual Studio and C++11 threads |
899 | | // and synchronization primitives. It would result into a deadlock |
900 | | // on loader lock. |
901 | | #if ! defined (_MSC_VER) |
902 | | log4cplus::initializeLog4cplus (); |
903 | | #endif |
904 | | } |
905 | | #endif |
906 | | |
907 | | ~_static_log4cplus_initializer () |
908 | | { |
909 | | // Last thread cleanup. |
910 | | log4cplus::threadCleanup (); |
911 | | log4cplus::freeTLSSlot(); |
912 | | } |
913 | | } static initializer; |
914 | | |
915 | | } // namespace |
916 | | |
917 | | |
918 | | #else // defined (WIN32) |
919 | | namespace { |
920 | | |
921 | | #if ! defined (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
922 | | static void |
923 | | _log4cplus_initializer_func () |
924 | | LOG4CPLUS_CONSTRUCTOR_FUNC (LOG4CPLUS_INIT_PRIORITY_BASE); |
925 | | static void |
926 | | _log4cplus_initializer_func () |
927 | 0 | { |
928 | 0 | log4cplus::initializeLog4cplus(); |
929 | 0 | } |
930 | | #endif |
931 | | |
932 | | struct _static_log4cplus_initializer |
933 | | { |
934 | | #if ! defined (LOG4CPLUS_REQUIRE_EXPLICIT_INITIALIZATION) |
935 | | _static_log4cplus_initializer () |
936 | 70 | { |
937 | 70 | log4cplus::initializeLog4cplus(); |
938 | 70 | } |
939 | | #endif |
940 | | |
941 | | ~_static_log4cplus_initializer () |
942 | 0 | { |
943 | | // Last thread cleanup. |
944 | 0 | log4cplus::threadCleanup (); |
945 | 0 | log4cplus::freeTLSSlot(); |
946 | 0 | } |
947 | | } static initializer |
948 | | LOG4CPLUS_INIT_PRIORITY (LOG4CPLUS_INIT_PRIORITY_BASE); |
949 | | |
950 | | } // namespace |
951 | | |
952 | | #endif |