aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/thread.h
blob: 2a666fb9c2dbeab44b166dd40bd900a328ff42a1 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#pragma once

/// This code should not be used directly unless you really understand what you do.
/// If you need threads, use thread pool functionality in <util/thread/factory.h> 
/// @see SystemThreadFactory() 

#include <util/generic/ptr.h>
#include <util/generic/string.h>

#include "defaults.h"
#include "progname.h"

bool SetHighestThreadPriority();

class TThread {
    template <typename Callable>
    struct TCallableParams;
    struct TPrivateCtor {};

public:
    using TThreadProc = void* (*)(void*);
    using TId = size_t;

    struct TParams {
        TThreadProc Proc;
        void* Data;
        size_t StackSize;
        void* StackPointer;
        // See comments for `SetCurrentThreadName` 
        TString Name = GetProgramName();

        inline TParams()
            : Proc(nullptr)
            , Data(nullptr)
            , StackSize(0)
            , StackPointer(nullptr)
        {
        }

        inline TParams(TThreadProc proc, void* data)
            : Proc(proc)
            , Data(data)
            , StackSize(0)
            , StackPointer(nullptr)
        {
        }

        inline TParams(TThreadProc proc, void* data, size_t stackSize)
            : Proc(proc)
            , Data(data)
            , StackSize(stackSize)
            , StackPointer(nullptr)
        {
        }

        inline TParams& SetName(const TString& name) noexcept {
            Name = name;

            return *this;
        }

        inline TParams& SetStackSize(size_t size) noexcept {
            StackSize = size;

            return *this;
        }

        inline TParams& SetStackPointer(void* ptr) noexcept {
            StackPointer = ptr;

            return *this;
        }
    };

    TThread(const TParams& params);
    TThread(TThreadProc threadProc, void* param);

    template <typename Callable>
    TThread(Callable&& callable)
        : TThread(TPrivateCtor{},
                  MakeHolder<TCallableParams<Callable>>(std::forward<Callable>(callable)))
    {
    }

    TThread(TParams&& params)
        : TThread((const TParams&)params)
    {
    }

    TThread(TParams& params)
        : TThread((const TParams&)params)
    {
    }

    ~TThread();

    void Start();

    void* Join();
    void Detach();
    bool Running() const noexcept;
    TId Id() const noexcept;

    static TId ImpossibleThreadId() noexcept;
    static TId CurrentThreadId() noexcept;

    /*
     * Returns numeric thread id, as visible in e. g. htop.
     * Consider using this value for logging.
     */
    static TId CurrentThreadNumericId() noexcept;

    // NOTE: Content of `name` will be copied.
    //
    // NOTE: On Linux thread name is limited to 15 symbols which is probably the smallest one among
    // all platforms. If you provide a name longer than 15 symbols it will be cut. So if you expect
    // `CurrentThreadName` to return the same name as `name` make sure it's not longer than 15 
    // symbols.
    static void SetCurrentThreadName(const char* name); 

    // NOTE: Will return empty string where CanGetCurrentThreadName() returns false.
    static TString CurrentThreadName(); 

    // NOTE: Depends on a platform version.
    // Will return true for Darwin, Linux or fresh Windows 10.
    static bool CanGetCurrentThreadName();

private:
    struct TCallableBase {
        virtual ~TCallableBase() = default;
        virtual void run() = 0;

        static void* ThreadWorker(void* arg) {
            static_cast<TCallableBase*>(arg)->run();
            return nullptr;
        }
    };

    template <typename Callable>
    struct TCallableParams: public TCallableBase {
        TCallableParams(Callable&& callable)
            : Callable_(std::forward<Callable>(callable))
        {
        }

        Callable Callable_;

        void run() override {
            Callable_();
        }
    };

    TThread(TPrivateCtor, THolder<TCallableBase> callable);

private:
    class TImpl;
    THolder<TImpl> Impl_;
};

class ISimpleThread: public TThread {
public:
    ISimpleThread(size_t stackSize = 0);

    virtual ~ISimpleThread() = default;

    virtual void* ThreadProc() = 0;
};

struct TCurrentThreadLimits {
    TCurrentThreadLimits() noexcept;

    const void* StackBegin;
    size_t StackLength;
};