Commit ea809a28 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

Add a thread compatibility layer

parent 625b74c3
......@@ -140,6 +140,7 @@ noinst_HEADERS = \
src/utils/SWMRLock.h \
src/utils/VLCInstance.h \
src/VideoTrack.h \
src/compat/Thread.h \
$(NULL)
......
......@@ -219,6 +219,15 @@ AM_CONDITIONAL(HAVE_IOS, test "${HAVE_IOS}" = "1")
AM_CONDITIONAL(HAVE_OSX, test "${HAVE_OSX}" = "1")
AM_CONDITIONAL(HAVE_TVOS, test "${HAVE_TVOS}" = "1")
AC_LANG_PUSH([C++])
AC_MSG_CHECKING([c++11 thread support])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([#include <thread>
int main() { std::thread t; }])], [
AC_MSG_RESULT([ok])
AC_DEFINE(CXX11_THREADS, 1, [Defined to 1 if C++11 threads are supported])
],[ AC_MSG_RESULT([no])])
AC_LANG_PUSH([C++])
dnl Android is linux, but a bit different
AS_IF([test "$SYS" = linux],[
AC_MSG_CHECKING([for an Android system])
......
/*****************************************************************************
* 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 <pthread.h>
#include <system_error>
#include <unistd.h>
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:
constexpr thread_id() noexcept : m_id( 0 ) {}
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:
thread_id( pthread_t id ) : m_id( id ) {}
pthread_t m_id;
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;
};
public:
using id = details::thread_id;
Thread() = default;
template <typename T>
Thread( void (T::*entryPoint)(), T* inst )
{
auto i = std::unique_ptr<Invoker<T>>( new Invoker<T>{
entryPoint, inst
});
if ( pthread_create( &m_id.m_id, nullptr, []( void* opaque ) -> void* {
auto invoker = std::unique_ptr<Invoker<T>>( reinterpret_cast<Invoker<T>*>( opaque ) );
(invoker->inst->*(invoker->func))();
return nullptr;
}, i.get() ) != 0 )
throw std::system_error{ std::make_error_code( std::errc::resource_unavailable_try_again ) };
i.release();
}
Thread( Thread&& ) = default;
Thread& operator=( Thread&& ) = default;
Thread( const Thread& ) = delete;
Thread& operator=( const Thread& ) = delete;
bool joinable()
{
return m_id != id{};
}
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 ) };
pthread_join( m_id.m_id, nullptr );
}
id get_id()
{
return m_id;
}
static unsigned hardware_concurrency()
{
return sysconf( _SC_NPROCESSORS_ONLN );
}
private:
id m_id;
};
namespace this_thread
{
inline details::thread_id get_id()
{
return { pthread_self() };
}
}
}
}
namespace std
{
template <>
struct hash<medialibrary::compat::Thread::id>
{
size_t operator()( const medialibrary::compat::Thread::id& id ) const noexcept
{
static_assert( sizeof( id.m_id <= sizeof(size_t) ), "pthread_t is too big" );
return id.m_id;
}
};
}
#endif
......@@ -47,7 +47,7 @@ SqliteConnection::Handle SqliteConnection::getConn()
{
std::unique_lock<std::mutex> lock( m_connMutex );
sqlite3* dbConnection;
auto it = m_conns.find( std::this_thread::get_id() );
auto it = m_conns.find( compat::this_thread::get_id() );
if ( it == end( m_conns ) )
{
auto res = sqlite3_open( m_dbPath.c_str(), &dbConnection );
......@@ -62,7 +62,7 @@ SqliteConnection::Handle SqliteConnection::getConn()
s.execute();
while ( s.row() != nullptr )
;
m_conns.emplace( std::this_thread::get_id(), std::move( dbConn ) );
m_conns.emplace( compat::this_thread::get_id(), std::move( dbConn ) );
sqlite3_update_hook( dbConnection, &updateHook, this );
return dbConnection;
}
......
......@@ -28,11 +28,11 @@
#include <sqlite3.h>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <string>
#include "utils/SWMRLock.h"
#include "compat/Thread.h"
namespace medialibrary
{
......@@ -75,7 +75,7 @@ private:
using ConnPtr = std::unique_ptr<sqlite3, int(*)(sqlite3*)>;
const std::string m_dbPath;
std::mutex m_connMutex;
std::unordered_map<std::thread::id, ConnPtr> m_conns;
std::unordered_map<compat::Thread::id, ConnPtr> m_conns;
utils::SWMRLock m_contextLock;
utils::ReadLocker m_readLock;
utils::WriteLocker m_writeLock;
......
......@@ -82,10 +82,10 @@ void DiscovererWorker::enqueue( const std::string& entryPoint, bool reload )
std::unique_lock<std::mutex> lock( m_mutex );
m_tasks.emplace( entryPoint, reload );
if ( m_thread.get_id() == std::thread::id{} )
if ( m_thread.get_id() == compat::Thread::id{} )
{
m_run = true;
m_thread = std::thread( &DiscovererWorker::run, this );
m_thread = compat::Thread( &DiscovererWorker::run, this );
}
// Since we just added an element, let's not check for size == 0 :)
else if ( m_tasks.size() == 1 )
......
......@@ -28,9 +28,9 @@
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>
#include "compat/Thread.h"
#include "discoverer/IDiscoverer.h"
namespace medialibrary
......@@ -62,7 +62,7 @@ private:
bool reload;
};
std::thread m_thread;
compat::Thread m_thread;
std::queue<Task> m_tasks;
std::mutex m_mutex;
std::condition_variable m_cond;
......
......@@ -99,7 +99,7 @@ void ParserService::initialize( MediaLibrary* ml, IParserCb* parserCb )
uint8_t ParserService::nbNativeThreads() const
{
auto nbProcs = std::thread::hardware_concurrency();
auto nbProcs = compat::Thread::hardware_concurrency();
if ( nbProcs == 0 )
return 1;
return nbProcs;
......
......@@ -25,11 +25,11 @@
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <queue>
#include "Task.h"
#include "medialibrary/Types.h"
#include "compat/Thread.h"
namespace medialibrary
{
......@@ -85,7 +85,7 @@ private:
bool m_paused;
std::condition_variable m_cond;
std::queue<std::unique_ptr<parser::Task>> m_tasks;
std::vector<std::thread> m_threads;
std::vector<compat::Thread> m_threads;
std::mutex m_lock;
};
......
......@@ -47,8 +47,8 @@ ModificationNotifier::~ModificationNotifier()
void ModificationNotifier::start()
{
assert( m_notifierThread.get_id() == std::thread::id{} );
m_notifierThread = std::thread{ &ModificationNotifier::run, this };
assert( m_notifierThread.get_id() == compat::Thread::id{} );
m_notifierThread = compat::Thread{ &ModificationNotifier::run, this };
}
void ModificationNotifier::notifyMediaCreation( MediaPtr media )
......
......@@ -26,12 +26,12 @@
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>
#include "medialibrary/Types.h"
#include "Types.h"
#include "compat/Thread.h"
namespace medialibrary
{
......@@ -151,7 +151,7 @@ private:
// Notifier thread
std::mutex m_lock;
std::condition_variable m_cond;
std::thread m_notifierThread;
compat::Thread m_notifierThread;
std::atomic_bool m_stop;
std::chrono::time_point<std::chrono::steady_clock> m_timeout;
};
......
......@@ -26,6 +26,8 @@
#include "History.h"
#include "Media.h"
#include <thread>
class HistoryTest : public Tests
{
......
......@@ -30,6 +30,8 @@
#include "AlbumTrack.h"
#include "mocks/FileSystem.h"
#include <thread>
class Medias : public Tests
{
};
......
......@@ -61,8 +61,8 @@ TEST_F( VideoTracks, FetchTracks )
ASSERT_EQ( ts.size(), 2u );
t2 = ts[0];
ASSERT_EQ( t2->codec(), "H264" );
ASSERT_EQ( t2->width(), 1920 );
ASSERT_EQ( t2->height(), 1080 );
ASSERT_EQ( t2->width(), 1920u );
ASSERT_EQ( t2->height(), 1080u );
ASSERT_EQ( t2->fps(), 29.97f );
ASSERT_EQ( t2->language(), "l1" );
ASSERT_EQ( t2->description(), "d1" );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment