Thread.h 6.2 KB
Newer Older
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
/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#pragma once

#include "config.h"

#if CXX11_THREADS && !defined(__ANDROID__)
#include <thread>
namespace medialibrary
{
namespace compat
{
using Thread = std::thread;
namespace this_thread = std::this_thread;
}
}

#else

#include <functional>
#include <memory>
#include <system_error>
#include <unistd.h>

45
46
47
48
49
50
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
namespace medialibrary
{
namespace compat
{

// temporary forward declarations, so we can use thread_id from this_thread::get_id
// and have this_thread::get_id declared before thread_id itself, in order to friend it.
namespace details { class thread_id; }
namespace this_thread { details::thread_id get_id(); }
class Thread;

namespace details
{

class thread_id
{
public:
68
69
70
71
72
73
74
75

#ifdef _WIN32
    using native_thread_id_type = HANDLE;
#else
    using native_thread_id_type = pthread_t;
#endif

    constexpr thread_id() noexcept : m_id{} {}
76
77
78
79
80
81
82
83
84
85
86
87
88
    thread_id( const thread_id& ) = default;
    thread_id& operator=( const thread_id& ) = default;
    thread_id( thread_id&& r ) : m_id( r.m_id ) { r.m_id = 0; }
    thread_id& operator=( thread_id&& r ) { m_id = r.m_id; r.m_id = 0; return *this; }

    bool operator==(const thread_id& r) const noexcept { return m_id == r.m_id; }
    bool operator!=(const thread_id& r) const noexcept { return m_id != r.m_id; }
    bool operator< (const thread_id& r) const noexcept { return m_id <  r.m_id; }
    bool operator<=(const thread_id& r) const noexcept { return m_id <= r.m_id; }
    bool operator> (const thread_id& r) const noexcept { return m_id >  r.m_id; }
    bool operator>=(const thread_id& r) const noexcept { return m_id >= r.m_id; }

private:
89
    thread_id( native_thread_id_type id ) : m_id( id ) {}
90

91
    native_thread_id_type m_id;
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

    friend thread_id this_thread::get_id();
    friend Thread;
    friend std::hash<thread_id>;
};
}

// Compatibility thread class, for platforms that don't implement C++11 (or do it incorrectly)
// This handles the very basic usage we need for the medialibrary
class Thread
{
    template <typename T>
    struct Invoker
    {
        void (T::*func)();
        T* inst;
    };

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#ifdef _WIN32
    template <typename T>
    static __attribute__((__stdcall__)) DWORD threadRoutine( void* opaque )
    {
        auto invoker = std::unique_ptr<Invoker<T>>( reinterpret_cast<Invoker<T>*>( opaque ) );
        (invoker->inst->*(invoker->func))();
        return 0;
    }
#else
    template <typename T>
    static void* threadRoutine( void* opaque )
    {
        auto invoker = std::unique_ptr<Invoker<T>>( reinterpret_cast<Invoker<T>*>( opaque ) );
        (invoker->inst->*(invoker->func))();
        return nullptr;
    }
#endif

128
129
130
131
132
133
134
135
136
public:
    using id = details::thread_id;

    template <typename T>
    Thread( void (T::*entryPoint)(), T* inst )
    {
        auto i = std::unique_ptr<Invoker<T>>( new Invoker<T>{
            entryPoint, inst
        });
137
138
139
140
141
#ifdef _WIN32
        if ( ( m_id = CreateThread( nullptr, 0, &threadRoutine<T>, i.get(), 0, nullptr ) ) == nullptr )
#else
        if ( pthread_create( &m_id.m_id, nullptr, &threadRoutine<T>, i.get() ) != 0 )
#endif
142
143
144
145
            throw std::system_error{ std::make_error_code( std::errc::resource_unavailable_try_again ) };
        i.release();
    }

146
    static unsigned hardware_concurrency() noexcept
147
    {
148
149
150
151
152
153
154
#ifdef _WIN32
#if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP
        return GetCurrentProcessorNumber();
#else
        return 0;
#endif
#else
155
        return sysconf( _SC_NPROCESSORS_ONLN );
156
#endif
157
158
159
160
161
162
163
164
    }

    void join()
    {
        if ( !joinable() )
            throw std::system_error{ std::make_error_code( std::errc::invalid_argument ) };
        if ( this_thread::get_id() == m_id )
            throw std::system_error{ std::make_error_code( std::errc::resource_deadlock_would_occur ) };
165
166
167
168
#ifdef _WIN32
        WaitForSingleObjectEx( m_id.m_id, INFINITE, TRUE );
        CloseHandle( m_id.m_id );
#else
169
        pthread_join( m_id.m_id, nullptr );
170
#endif
171
172
    }

173
174
175
    // Platform agnostic methods:

    Thread() = default;
176
177
178
179
180
181
    ~Thread()
    {
        if ( joinable() == true )
            std::terminate();
    }

182
183
184
185
186
187
    Thread( Thread&& ) = default;
    Thread& operator=( Thread&& ) = default;
    Thread( const Thread& ) = delete;
    Thread& operator=( const Thread& ) = delete;

    bool joinable()
188
    {
189
        return m_id != id{};
190
191
    }

192
    id get_id()
193
    {
194
        return m_id;
195
196
197
198
199
200
201
202
203
204
    }

private:
    id m_id;
};

namespace this_thread
{
    inline details::thread_id get_id()
    {
205
206
207
#ifdef _WIN32
        return GetCurrentThread();
#else
208
        return { pthread_self() };
209
210
211
212
213
214
215
216
217
218
219
#endif
    }

    template <typename Rep, typename Period>
    inline void sleep_for( const std::chrono::duration<Rep, Period>& duration )
    {
        auto d = std::chrono::duration_cast<std::chrono::milliseconds>( duration );
#ifdef _WIN32
        Sleep( d.count() );
#else
        usleep( d.count() * 1000 );
220
#endif
221
222
223
224
225
226
227
228
229
230
231
232
233
    }
}

}
}

namespace std
{
template <>
struct hash<medialibrary::compat::Thread::id>
{
    size_t operator()( const medialibrary::compat::Thread::id& id ) const noexcept
    {
234
        static_assert( sizeof( id.m_id ) <= sizeof( size_t ), "thread handle is too big" );
235
        return (size_t)id.m_id;
236
237
238
239
240
    }
};
}

#endif