summaryrefslogtreecommitdiff
path: root/zen/globals.h
blob: 109754148a97ae9ac895240948c3b4afd3697f7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// *****************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under    *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0          *
// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
// *****************************************************************************

#ifndef GLOBALS_H_8013740213748021573485
#define GLOBALS_H_8013740213748021573485

#include <atomic>
#include <memory>
#include "scope_guard.h"


namespace zen
{
//solve static destruction order fiasco by providing shared ownership and serialized access to global variables
template <class T>
class Global
{
public:
    Global()
    {
        static_assert(std::is_trivially_destructible_v<Pod>, "this memory needs to live forever");
        assert(!pod_.inst && !pod_.spinLock); //we depend on static zero-initialization!
    }

    explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }

    ~Global() { set(nullptr); }

    std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!)
    {
        while (pod_.spinLock.exchange(true)) ;
        ZEN_ON_SCOPE_EXIT(pod_.spinLock = false);
        if (pod_.inst)
            return *pod_.inst;
        return nullptr;
    }

    void set(std::unique_ptr<T>&& newInst)
    {
        std::shared_ptr<T>* tmpInst = nullptr;
        if (newInst)
            tmpInst = new std::shared_ptr<T>(std::move(newInst));
        {
            while (pod_.spinLock.exchange(true)) ;
            ZEN_ON_SCOPE_EXIT(pod_.spinLock = false);
            std::swap(pod_.inst, tmpInst);
        }
        delete tmpInst;
    }

private:
    //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown
    //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message!
    //=> use trivially-destructible POD only!!!
    struct Pod
    {
        std::shared_ptr<T>* inst;   // = nullptr;
        std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction!
        //serialize access; can't use std::mutex: has non-trival destructor
    } pod_;
};
}

#endif //GLOBALS_H_8013740213748021573485
bgstack15