aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/atexit.cpp
blob: 4ae0d9c3e77c3c64ae7d4975ca252c85544a52b1 (plain) (blame)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "atexit.h"
#include "atomic.h"
#include "yassert.h"
#include "spinlock.h"
#include "thread.h"

#include <util/generic/ylimits.h>
#include <util/generic/utility.h>
#include <util/generic/deque.h>
#include <util/generic/queue.h>

#include <tuple>

#include <cstdlib>

namespace {
    class TAtExit {
        struct TFunc {
            TAtExitFunc Func;
            void* Ctx;
            size_t Priority;
            size_t Number;
        };

        struct TCmp {
            inline bool operator()(const TFunc* l, const TFunc* r) const noexcept {
                return std::tie(l->Priority, l->Number) < std::tie(r->Priority, r->Number);
            }
        };

    public:
        inline TAtExit() noexcept
            : FinishStarted_(0)
        {
        }

        inline void Finish() noexcept { 
            AtomicSet(FinishStarted_, 1);

            auto guard = Guard(Lock_);

            while (Items_) {
                auto c = Items_.top();

                Y_ASSERT(c);

                Items_.pop();

                {
                    auto unguard = Unguard(guard);

                    try {
                        c->Func(c->Ctx);
                    } catch (...) {
                        // ¯\_(ツ)_/¯
                    }
                }
            }
        }

        inline void Register(TAtExitFunc func, void* ctx, size_t priority) {
            with_lock (Lock_) {
                Store_.push_back({func, ctx, priority, Store_.size()});
                Items_.push(&Store_.back());
            }
        }

        inline bool FinishStarted() const {
            return AtomicGet(FinishStarted_);
        }

    private:
        TAdaptiveLock Lock_;
        TAtomic FinishStarted_;
        TDeque<TFunc> Store_; 
        TPriorityQueue<TFunc*, TVector<TFunc*>, TCmp> Items_; 
    };

    static TAtomic atExitLock = 0;
    static TAtExit* volatile atExitPtr = nullptr;
    alignas(TAtExit) static char atExitMem[sizeof(TAtExit)];

    static void OnExit() {
        if (TAtExit* const atExit = AtomicGet(atExitPtr)) {
            atExit->Finish();
            atExit->~TAtExit();
            AtomicSet(atExitPtr, nullptr);
        }
    }

    static inline TAtExit* Instance() {
        if (TAtExit* const atExit = AtomicGet(atExitPtr)) {
            return atExit;
        }
        with_lock (atExitLock) {
            if (TAtExit* const atExit = AtomicGet(atExitPtr)) {
                return atExit;
            }
            atexit(OnExit);
            TAtExit* const atExit = new (atExitMem) TAtExit;
            AtomicSet(atExitPtr, atExit);
            return atExit;
        }
    }
}

void ManualRunAtExitFinalizers() {
    OnExit();
}

bool ExitStarted() {
    if (TAtExit* const atExit = AtomicGet(atExitPtr)) {
        return atExit->FinishStarted();
    }
    return false;
}

void AtExit(TAtExitFunc func, void* ctx, size_t priority) {
    Instance()->Register(func, ctx, priority);
}

void AtExit(TAtExitFunc func, void* ctx) {
    AtExit(func, ctx, Max<size_t>());
}

static void TraditionalCloser(void* ctx) {
    reinterpret_cast<TTraditionalAtExitFunc>(ctx)();
}

void AtExit(TTraditionalAtExitFunc func) {
    AtExit(TraditionalCloser, reinterpret_cast<void*>(func));
}

void AtExit(TTraditionalAtExitFunc func, size_t priority) {
    AtExit(TraditionalCloser, reinterpret_cast<void*>(func), priority);
}