From e73d274d86b7652f58c5c9d07df293b4f913f8ca Mon Sep 17 00:00:00 2001 From: LaurentGom Date: Sat, 20 Nov 2010 13:00:19 +0000 Subject: [PATCH] New API for sf::Thread (more flexible) git-svn-id: https://sfml.svn.sourceforge.net/svnroot/sfml/branches/sfml2@1685 4e206d99-4929-0410-ac5d-dfc041789085 --- include/SFML/Audio/SoundRecorder.hpp | 5 +- include/SFML/Audio/SoundStream.hpp | 5 +- include/SFML/System/Thread.hpp | 197 +++++++++++++++++++-------- include/SFML/System/Thread.inl | 90 ++++++++++++ src/SFML/Audio/SoundRecorder.cpp | 11 +- src/SFML/Audio/SoundStream.cpp | 13 +- src/SFML/System/CMakeLists.txt | 1 + src/SFML/System/Thread.cpp | 46 ++----- 8 files changed, 263 insertions(+), 105 deletions(-) create mode 100644 include/SFML/System/Thread.inl diff --git a/include/SFML/Audio/SoundRecorder.hpp b/include/SFML/Audio/SoundRecorder.hpp index f7e99790..2a3a0e92 100644 --- a/include/SFML/Audio/SoundRecorder.hpp +++ b/include/SFML/Audio/SoundRecorder.hpp @@ -38,7 +38,7 @@ namespace sf /// \brief Abstract base class for capturing sound data /// //////////////////////////////////////////////////////////// -class SFML_API SoundRecorder : private Thread +class SFML_API SoundRecorder { public : @@ -157,7 +157,7 @@ private : /// only when the capture is stopped. /// //////////////////////////////////////////////////////////// - virtual void Run(); + void Record(); //////////////////////////////////////////////////////////// /// \brief Get the new available audio samples and process them @@ -180,6 +180,7 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// + Thread myThread; ///< Thread running the background recording task std::vector mySamples; ///< Buffer to store captured samples unsigned int mySampleRate; ///< Sample rate bool myIsCapturing; ///< Capturing state diff --git a/include/SFML/Audio/SoundStream.hpp b/include/SFML/Audio/SoundStream.hpp index 7c33f779..2717ce2b 100644 --- a/include/SFML/Audio/SoundStream.hpp +++ b/include/SFML/Audio/SoundStream.hpp @@ -39,7 +39,7 @@ namespace sf /// \brief Abstract base class for streamed audio sources /// //////////////////////////////////////////////////////////// -class SFML_API SoundStream : public SoundSource, private Thread +class SFML_API SoundStream : public SoundSource { public : @@ -208,7 +208,7 @@ private : /// only when the sound is stopped. /// //////////////////////////////////////////////////////////// - virtual void Run(); + void Stream(); //////////////////////////////////////////////////////////// /// \brief Request a new chunk of audio samples from the stream source @@ -279,6 +279,7 @@ private : //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// + Thread myThread; ///< Thread running the background tasks bool myIsStreaming; ///< Streaming state (true = playing, false = stopped) unsigned int myBuffers[BuffersCount]; ///< Sound buffers used to store temporary audio data unsigned int myChannelsCount; ///< Number of channels (1 = mono, 2 = stereo, ...) diff --git a/include/SFML/System/Thread.hpp b/include/SFML/System/Thread.hpp index 8978e89a..8ec56b17 100644 --- a/include/SFML/System/Thread.hpp +++ b/include/SFML/System/Thread.hpp @@ -38,6 +38,7 @@ namespace sf namespace priv { class ThreadImpl; + struct ThreadFunc; } //////////////////////////////////////////////////////////// @@ -48,29 +49,99 @@ class SFML_API Thread : NonCopyable { public : - typedef void (*FuncType)(void*); ///< Type of functions that can be used as thread entry points + //////////////////////////////////////////////////////////// + /// \brief Construct the thread from a functor with no argument + /// + /// This constructor works for function objects, as well + /// as free function. + /// + /// Use this constructor for this kind of function: + /// \code + /// void function(); + /// + /// // --- or ---- + /// + /// struct Functor + /// { + /// void operator()(); + /// }; + /// \endcode + /// Note: this does *not* run the thread, use Run(). + /// + /// \param function Functor or free function to use as the entry point of the thread + /// + //////////////////////////////////////////////////////////// + template + Thread(F function); //////////////////////////////////////////////////////////// - /// \brief Construct the thread from a function pointer + /// \brief Construct the thread from a functor with an argument + /// + /// This constructor works for function objects, as well + /// as free function. + /// It is a template, which means that the argument can + /// have any type (int, std::string, void*, Toto, ...). + /// + /// Use this constructor for this kind of function: + /// \code + /// void function(int arg); + /// + /// // --- or ---- + /// + /// struct Functor + /// { + /// void operator()(std::string arg); + /// }; + /// \endcode + /// Note: this does *not* run the thread, use Run(). + /// + /// \param function Functor or free function to use as the entry point of the thread + /// \param argument argument to forward to the function + /// + //////////////////////////////////////////////////////////// + template + Thread(F function, A argument); + + //////////////////////////////////////////////////////////// + /// \brief Construct the thread from a member function and an object + /// + /// This constructor is template, which means that you can + /// use it with any class. + /// Use this constructor for this kind of function: + /// \code + /// class MyClass + /// { + /// public : + /// + /// void function(); + /// }; + /// \endcode + /// Note: this does *not* run the thread, use Run(). /// /// \param function Entry point of the thread - /// \param userData Data to pass to the thread function + /// \param object Pointer to the object to use /// //////////////////////////////////////////////////////////// - Thread(FuncType function, void* userData = NULL); + template + Thread(void(C::*function)(), C* object); //////////////////////////////////////////////////////////// - /// \brief Virtual destructor + /// \brief destructor /// - /// This destructor calls Wait(), so that the thread cannot - /// survive when its sf::Thread instance is destroyed. + /// This destructor calls Wait(), so that the internal thread + /// cannot survive after its sf::Thread instance is destroyed. /// //////////////////////////////////////////////////////////// - virtual ~Thread(); + ~Thread(); //////////////////////////////////////////////////////////// /// \brief Run the thread /// + /// This function starts the entry point passed to the + /// thread's constructor, and returns immediately. + /// After this function returns, the thread's function is + /// running in parallel to the calling code. + /// //////////////////////////////////////////////////////////// void Launch(); @@ -79,6 +150,8 @@ public : /// /// This function will block the execution until the /// thread's function ends. + /// Warning: if the thread function never ends, the calling + /// thread will block forever. /// //////////////////////////////////////////////////////////// void Wait(); @@ -96,39 +169,28 @@ public : //////////////////////////////////////////////////////////// void Terminate(); -protected : - - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - /// This constructor is only accessible from derived classes - /// - //////////////////////////////////////////////////////////// - Thread(); - private : friend class priv::ThreadImpl; //////////////////////////////////////////////////////////// - /// \brief Function called as the entry point of the thread + /// \brief Internal entry point of the thread /// - /// You must override this function when inheriting from - /// sf::Thread. + /// This function is called by the thread implementation. /// //////////////////////////////////////////////////////////// - virtual void Run(); + void Run(); //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - priv::ThreadImpl* myThreadImpl; ///< OS-specific implementation of the thread - FuncType myFunction; ///< Function to call as the thread entry point - void* myUserData; ///< Data to pass to the thread function + priv::ThreadImpl* myImpl; ///< OS-specific implementation of the thread + priv::ThreadFunc* myFunction; ///< Abstraction of the function to run }; -} // namespace sf +#include +} // namespace sf #endif // SFML_THREAD_HPP @@ -142,52 +204,69 @@ private : /// is split and both the new thread and the caller run /// in parallel. /// -/// There are two ways to use sf::Thread. The first (and -/// preferred) way of using it is to created derived classes. -/// When inheriting from sf::Thread, the virtual function -/// Run() has to be overriden, and defines the entry point -/// of the thread. The thread automatically stops when -/// this function ends. +/// To use a sf::Thread, you construct it directly with the +/// function to execute as the entry point of the thread. +/// sf::Thread has multiple template constructors, which means +/// that you can use several types of entry points: +/// \li non-member functions with no argument +/// \li non-member functions with one argument of any type +/// \li functors with no argument (this one is particularly useful for compatibility with boost/std::bind) +/// \li functors with one argument of any type +/// \li member functions from any class with no argument /// -/// Usage example: +/// The function argument, if any, is copied in the sf::Thread +/// instance, as well as the functor (if the corresponding +/// constructor is used). Class instances, however, are passed +/// by pointer to you must make sure that the object won't be +/// destroyed while the thread is still using it. +/// +/// The thread ends when its function is terminated. If the +/// owner sf::Thread instance is destroyed before the +/// thread is finished, the destructor will wait (see Wait()) +/// +/// Usage examples: /// \code -/// class MyThread : public sf::Thread +/// // example 1: non member function with one argument +/// +/// void ThreadFunc(int argument) /// { -/// private : -/// -/// virtual void Run() +/// ... +/// } +/// +/// sf::Thread thread(&ThreadFunc, 5); +/// thread.Launch(); // start the thread (internally calls ThreadFunc(5)) +/// \endcode +/// +/// \code +/// // example 2: member function +/// +/// class Task +/// { +/// public : +/// void Run() /// { -/// // beginning of the thread /// ... -/// // end of the thread /// } /// }; /// -/// MyThread mythread; -/// mythread.Launch(); // start the thread (internally calls thread.Run()) -/// -/// // from now on, both mythread and the main thread run in parallel +/// Task task; +/// sf::Thread thread(&Task::Run, &task); +/// thread.Launch(); // start the thread (internally calls task.run()) /// \endcode /// -/// The second way of using sf::Thread uses a non-member function -/// as the entry point and thus doesn't involve creating -/// a new class. However, this method is only provided for -/// compatibility with C-style code or for fast prototyping; -/// you are strongly encouraged to use the first method. -/// -/// Usage example: /// \code -/// void ThreadFunc(void* data) +/// // example 3: functor +/// +/// struct Task /// { -/// // beginning of the thread -/// ... -/// // end of the thread -/// } +/// void operator()() +/// { +/// ... +/// } +/// }; /// -/// sf::Thread mythread(&ThreadFunc, NULL); -/// mythread.Launch(); // start the thread (internally calls ThreadFunc(NULL)) -/// -/// // from now on, both mythread and the main thread run in parallel +/// sf::Thread thread(Task()); +/// thread.Launch(); // start the thread (internally calls operator() on the Task instance) /// \endcode /// /// Creating parallel threads of execution can be dangerous: diff --git a/include/SFML/System/Thread.inl b/include/SFML/System/Thread.inl new file mode 100644 index 00000000..462bfb1b --- /dev/null +++ b/include/SFML/System/Thread.inl @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +namespace priv +{ +// Base class for abstract thread functions +struct ThreadFunc +{ + virtual ~ThreadFunc() {} + virtual void Run() = 0; +}; + +// Specialization using a functor (including free functions) with no argument +template +struct ThreadFunctor : ThreadFunc +{ + ThreadFunctor(T functor) : myFunctor(functor) {} + virtual void Run() {myFunctor();} + T myFunctor; +}; + +// Specialization using a functor (including free functions) with one argument +template +struct ThreadFunctorWithArg : ThreadFunc +{ + ThreadFunctorWithArg(F function, A arg) : myFunction(function), myArg(arg) {} + virtual void Run() {myFunction(myArg);} + F myFunction; + A myArg; +}; + +// Specialization using a member function +template +struct ThreadMemberFunc : ThreadFunc +{ + ThreadMemberFunc(void(C::*function)(), C* object) : myFunction(function), myObject(object) {} + virtual void Run() {(myObject->*myFunction)();} + void(C::*myFunction)(); + C* myObject; +}; + +} // namespace priv + + +//////////////////////////////////////////////////////////// +template +Thread::Thread(F functor) : +myImpl (NULL), +myFunction(new priv::ThreadFunctor(functor)) +{ +} + + +//////////////////////////////////////////////////////////// +template +Thread::Thread(F function, A argument) : +myImpl (NULL), +myFunction(new priv::ThreadFunctorWithArg(function, argument)) +{ +} + + +//////////////////////////////////////////////////////////// +template +Thread::Thread(void(C::*function)(), C* object) : +myImpl (NULL), +myFunction(new priv::ThreadMemberFunc(function, object)) +{ +} diff --git a/src/SFML/Audio/SoundRecorder.cpp b/src/SFML/Audio/SoundRecorder.cpp index 295c2f49..1c8f918a 100644 --- a/src/SFML/Audio/SoundRecorder.cpp +++ b/src/SFML/Audio/SoundRecorder.cpp @@ -31,6 +31,10 @@ #include #include +#ifdef _MSC_VER + #pragma warning(disable : 4355) // 'this' used in base member initializer list +#endif + //////////////////////////////////////////////////////////// // Private data @@ -44,6 +48,7 @@ namespace sf { //////////////////////////////////////////////////////////// SoundRecorder::SoundRecorder() : +myThread (&SoundRecorder::Record, this), mySampleRate (0), myIsCapturing(false) { @@ -97,7 +102,7 @@ void SoundRecorder::Start(unsigned int sampleRate) // Start the capture in a new thread, to avoid blocking the main thread myIsCapturing = true; - Launch(); + myThread.Launch(); } } @@ -107,7 +112,7 @@ void SoundRecorder::Stop() { // Stop the capturing thread myIsCapturing = false; - Wait(); + myThread.Wait(); } @@ -142,7 +147,7 @@ void SoundRecorder::OnStop() //////////////////////////////////////////////////////////// -void SoundRecorder::Run() +void SoundRecorder::Record() { while (myIsCapturing) { diff --git a/src/SFML/Audio/SoundStream.cpp b/src/SFML/Audio/SoundStream.cpp index d873f687..0a2503ef 100644 --- a/src/SFML/Audio/SoundStream.cpp +++ b/src/SFML/Audio/SoundStream.cpp @@ -31,11 +31,16 @@ #include #include +#ifdef _MSC_VER + #pragma warning(disable : 4355) // 'this' used in base member initializer list +#endif + namespace sf { //////////////////////////////////////////////////////////// SoundStream::SoundStream() : +myThread (&SoundStream::Stream, this), myIsStreaming (false), myChannelsCount (0), mySampleRate (0), @@ -97,7 +102,7 @@ void SoundStream::Play() // Start updating the stream in a separate thread to avoid blocking the application mySamplesProcessed = 0; myIsStreaming = true; - Launch(); + myThread.Launch(); } @@ -113,7 +118,7 @@ void SoundStream::Stop() { // Wait for the thread to terminate myIsStreaming = false; - Wait(); + myThread.Wait(); } @@ -156,7 +161,7 @@ void SoundStream::SetPlayingOffset(float timeOffset) // Restart streaming mySamplesProcessed = static_cast(timeOffset * mySampleRate * myChannelsCount); myIsStreaming = true; - Launch(); + myThread.Launch(); } @@ -185,7 +190,7 @@ bool SoundStream::GetLoop() const //////////////////////////////////////////////////////////// -void SoundStream::Run() +void SoundStream::Stream() { // Create the buffers ALCheck(alGenBuffers(BuffersCount, myBuffers)); diff --git a/src/SFML/System/CMakeLists.txt b/src/SFML/System/CMakeLists.txt index 945b1267..d9a4737c 100644 --- a/src/SFML/System/CMakeLists.txt +++ b/src/SFML/System/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRC ${INCROOT}/String.hpp ${SRCROOT}/Thread.cpp ${INCROOT}/Thread.hpp + ${INCROOT}/Thread.inl ${SRCROOT}/ThreadLocal.cpp ${INCROOT}/ThreadLocal.hpp ${INCROOT}/ThreadLocalPtr.hpp diff --git a/src/SFML/System/Thread.cpp b/src/SFML/System/Thread.cpp index 80bf590f..d521fe4c 100644 --- a/src/SFML/System/Thread.cpp +++ b/src/SFML/System/Thread.cpp @@ -29,42 +29,19 @@ #if defined(SFML_SYSTEM_WINDOWS) - #include - #else - #include - #endif namespace sf { -//////////////////////////////////////////////////////////// -Thread::Thread() : -myThreadImpl(NULL), -myFunction (NULL), -myUserData (NULL) -{ - -} - - -//////////////////////////////////////////////////////////// -Thread::Thread(Thread::FuncType function, void* userData) : -myThreadImpl(NULL), -myFunction (function), -myUserData (userData) -{ - -} - - //////////////////////////////////////////////////////////// Thread::~Thread() { Wait(); + delete myFunction; } @@ -72,18 +49,18 @@ Thread::~Thread() void Thread::Launch() { Wait(); - myThreadImpl = new priv::ThreadImpl(this); + myImpl = new priv::ThreadImpl(this); } //////////////////////////////////////////////////////////// void Thread::Wait() { - if (myThreadImpl) + if (myImpl) { - myThreadImpl->Wait(); - delete myThreadImpl; - myThreadImpl = NULL; + myImpl->Wait(); + delete myImpl; + myImpl = NULL; } } @@ -91,11 +68,11 @@ void Thread::Wait() //////////////////////////////////////////////////////////// void Thread::Terminate() { - if (myThreadImpl) + if (myImpl) { - myThreadImpl->Terminate(); - delete myThreadImpl; - myThreadImpl = NULL; + myImpl->Terminate(); + delete myImpl; + myImpl = NULL; } } @@ -103,8 +80,7 @@ void Thread::Terminate() //////////////////////////////////////////////////////////// void Thread::Run() { - if (myFunction) - myFunction(myUserData); + myFunction->Run(); } } // namespace sf