File: worker.cpp

package info (click to toggle)
sight 25.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 43,252 kB
  • sloc: cpp: 310,629; xml: 17,622; ansic: 9,960; python: 1,379; sh: 144; makefile: 33
file content (351 lines) | stat: -rw-r--r-- 10,693 bytes parent folder | download | duplicates (2)
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/************************************************************************
 *
 * Copyright (C) 2009-2024 IRCAD France
 * Copyright (C) 2012-2017 IHU Strasbourg
 *
 * This file is part of Sight.
 *
 * Sight is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sight is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
 *
 ***********************************************************************/

// cspell:ignore HRESULT PWSTR

#include "core/thread/worker.hpp"

#include "core/lazy_instantiator.hpp"
#include "core/mt/types.hpp"

#ifdef _WIN32
#include <windows.h>
#include <processthreadsapi.h>

#include <winerror.h>
#else
#include <pthread.h>
#endif

#include <codecvt>
#include <map>
#include <boost/locale/encoding_utf.hpp>

#include "core/spy_log.hpp"

namespace sight::core::thread
{

//------------------------------------------------------------------------------

thread_id_t get_current_thread_id()
{
    return std::this_thread::get_id();
}

//------------------------------------------------------------------------------

thread_native_id_t get_current_thread_native_id()
{
#ifdef _WIN32
    void* thread_id = GetCurrentThread();
    return thread_id;
#else
    pthread_t thread_id = pthread_self();
    return thread_id;
#endif
}

//------------------------------------------------------------------------------

void set_thread_name(const std::string& _thread_name, std::optional<std::thread::native_handle_type> _thread_id)
{
    SIGHT_WARN_IF(
        "Thread name '" << _thread_name << "' is too long. It must be restricted to "
        << get_max_length_of_thread_name() << " characters including the terminating null byte.",
        _thread_name.size()
        >= get_max_length_of_thread_name()
    );

    const std::string restricted_thread_name = _thread_name.substr(0, get_max_length_of_thread_name() - 1);

#ifdef _WIN32
    const std::wstring wide_thread_name(restricted_thread_name.begin(), restricted_thread_name.end()); // only works for
                                                                                                       // ascii
                                                                                                       // characters
    const auto thread_id_to_use = _thread_id.has_value() ? _thread_id.value() : GetCurrentThread();

    const HRESULT hr = SetThreadDescription(thread_id_to_use, wide_thread_name.c_str());

    SIGHT_WARN_IF("Unable to set the name of the thread. Error code: " << hr << std::endl, FAILED(hr));
#else
    const auto thread_id_to_use = _thread_id.has_value() ? _thread_id.value() : pthread_self();
    const auto success          = pthread_setname_np(thread_id_to_use, restricted_thread_name.c_str());

    SIGHT_WARN_IF(
        "Unable to set the name of the thread. Error code: "
        << "The length of the string specified pointed to by name exceeds the allowed limit." << std::endl,
        success != 0
    );
#endif
}

//------------------------------------------------------------------------------

std::string get_thread_name(std::optional<std::thread::native_handle_type> _thread_id)
{
#ifdef _WIN32
    PWSTR data                  = nullptr;
    const auto thread_id_to_use = _thread_id.has_value() ? _thread_id.value() : GetCurrentThread();

    const HRESULT hr = GetThreadDescription(thread_id_to_use, &data);

    if(FAILED(hr))
    {
        SIGHT_WARN("Failed to get thread name. Error code: " << hr << std::endl);
        return {};
    }

    std::wstring _wide_thread_name_(data);
    std::string thread_name;
    std::transform(
        _wide_thread_name_.begin(),
        _wide_thread_name_.end(),
        std::back_inserter(thread_name),
        [](const wchar_t c)
        {
            return static_cast<char>(c);
        });

    LocalFree(data);
    return thread_name;
#else
    const auto thread_id_to_use = _thread_id.has_value() ? _thread_id.value() : pthread_self();
    std::array<char, get_max_length_of_thread_name()> thread_name {};

    const auto success = pthread_getname_np(thread_id_to_use, thread_name.data(), sizeof(thread_name));
    if(success != 0)
    {
        SIGHT_WARN(
            "Failed to get thread name. Error code: The buffer specified by name and size is too small to hold the thread name."
            << std::endl
        );
        return {};
    }
    return {thread_name.data()};
#endif
}

//------------------------------------------------------------------------------

/**
 * @brief This internal class registers worker threads in the system. It creates a default worker.
 * The life cycle of registered workers should be handled by the creator of the workers, but to avoid unneeded crashes,
 * we do stop and destroy them if this not done outside. A non breaking error message is sent.
 */
class active_workers
{
public:

    /// Constructor, creates the default worker
    active_workers()
    = default;

    /// Destructor, destroys the default worker and the registered ones if necessary (this sends an error in this case)
    virtual ~active_workers()
    {
        core::mt::write_lock lock(m_registry_mutex);

        // Avoid double stop
        if(m_default_worker)
        {
            m_default_worker->stop();
        }

        SIGHT_ERROR_IF(
            "Workers are still registered, this is abnormal unless the application crashed before.",
            !m_workers.empty()
        );

        for(const auto& elt : m_workers)
        {
            elt.second->stop();
        }

        m_workers.clear();
    }

    //------------------------------------------------------------------------------

    void add_worker(const worker_key_type& _key, core::thread::worker::sptr _worker)
    {
        core::mt::write_lock lock(m_registry_mutex);
        m_workers.insert(worker_map_type::value_type(_key, _worker));
    }

    //------------------------------------------------------------------------------

    void remove_worker(const worker_key_type& _key)
    {
        core::mt::write_lock lock(m_registry_mutex);

        auto it = m_workers.find(_key);

        if(it != m_workers.end())
        {
            it->second->stop();
            m_workers.erase(_key);
        }
    }

    //------------------------------------------------------------------------------

    void remove_worker(core::thread::worker::sptr _worker)
    {
        core::mt::write_lock lock(m_registry_mutex);
        for(const auto& [key, value] : m_workers)
        {
            if(value == _worker)
            {
                _worker->stop();

                m_workers.erase(key);
                return;
            }
        }

        SIGHT_WARN("A worker was requested to be removed, but it could not be found");
    }

    //------------------------------------------------------------------------------

    core::thread::worker::sptr get_worker(const worker_key_type& _key) const
    {
        core::mt::read_lock lock(m_registry_mutex);

        if(auto it = m_workers.find(_key); it != m_workers.end())
        {
            return it->second;
        }

        return {};
    }

    //------------------------------------------------------------------------------

    static std::shared_ptr<active_workers> get()
    {
        return core::lazy_instantiator<active_workers>::get_instance();
    }

    //------------------------------------------------------------------------------

    core::thread::worker::sptr get_default_worker() const
    {
        return m_default_worker;
    }

    //------------------------------------------------------------------------------

    void set_default_worker(const core::thread::worker::sptr& _worker)
    {
        SIGHT_THROW_IF("default worker can not be null", _worker == nullptr);

        SIGHT_THROW_IF(
            "Can not switch the default worker as the initial one is already used in the application",
            m_default_worker.use_count() > 1
        );

        if(m_default_worker)
        {
            m_default_worker->stop();
        }

        m_default_worker = _worker;
    }

    //------------------------------------------------------------------------------

    void reset_default_worker()
    {
        m_default_worker->stop();
        m_default_worker.reset();
    }

private:

    /// Specific pointer for the default worker
    core::thread::worker::sptr m_default_worker {core::thread::worker::make()};

    using worker_map_type = std::map<worker_key_type, core::thread::worker::sptr>;

    /// Association key <=> worker
    worker_map_type m_workers;

    /// Used to protect the registry access.
    mutable core::mt::read_write_mutex m_registry_mutex;
};

static auto active_workers = core::thread::active_workers::get();

//-----------------------------------------------------------------------------

core::thread::worker::sptr get_worker(const worker_key_type& _key)
{
    return active_workers::get()->get_worker(_key);
}

//-----------------------------------------------------------------------------

void add_worker(const worker_key_type& _key, core::thread::worker::sptr _worker)
{
    active_workers::get()->add_worker(_key, _worker);
}

//-----------------------------------------------------------------------------

void remove_worker(const worker_key_type& _key)
{
    active_workers::get()->remove_worker(_key);
}

//-----------------------------------------------------------------------------

void remove_worker(core::thread::worker::sptr _worker)
{
    active_workers::get()->remove_worker(_worker);
}

//-----------------------------------------------------------------------------

core::thread::worker::sptr get_default_worker()
{
    return active_workers::get()->get_default_worker();
}

//-----------------------------------------------------------------------------

void set_default_worker(core::thread::worker::sptr _worker)
{
    active_workers::get()->set_default_worker(_worker);
}

//------------------------------------------------------------------------------

void reset_default_worker()
{
    active_workers::get()->reset_default_worker();
}

//-----------------------------------------------------------------------------

} //namespace sight::core::thread