Coverage Report

Created: 2025-12-08 07:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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