#pragma once
#include "ofConstants.h"
#ifndef TARGET_NO_THREADS
#include <atomic>
#include <thread>
#include <mutex>
#include <condition_variable>
/// \class ofThread
/// \brief A threaded base class with a built in mutex for convenience.
///
/// Users can extend this base class by public inheritance like this:
///
/// class MyThreadedClass: public ofThread
/// {
/// public:
/// /// ...
/// void threadedFunction()
/// {
/// while(isThreadRunning())
/// {
/// /// Threaded function here.
/// }
/// }
/// };
///
/// ofThread is a convenient wrapper for Poco::Thread, Poco::Runnable and
/// Poco::Mutex. It represents a simplified (sometimes overly simplified -
/// or simplified in ways that might not make sense for your project)
/// pathway for quickly writing threaded classes. Poco::Runnable represents
/// a class that can be "run" via its void run() method. Poco::Thread is
/// able to spawn a thread and "run" the contents of a class that extends
/// the Poco::Runnable interface (which ofThread does). Poco::FastMutex,
/// (aka ofMutex) is a "mutual exclusion" object that prevents two threads
/// from accessing the same data at the same time. It is important to know
/// that Poco::FastMutex (aka ofMutex) is not "recursive" while Poco::Mutex
/// is. This means that if the same thread attempts to lock a thread while
/// it ALREADY has a lock on the mutex, the program will lock up and go
/// nowhere. Thus, it is important that ofThread subclasses carefully
/// their use of the mutex. Currently ofThread does not lock its own mutex
/// at any point (e.g. ofThread's internal variables are not thread safe).
/// This is a somewhat dangerous convenience that is (theoretically)
/// supposed to make it easier for subclasses to avoid the recursive mutex
/// "problem". The situation that arises from two threads simultanously
/// reading or writing from the same shared data (shared data
/// occupies the same physical location in memory) leads to something
/// called a "race condition", which can lead to deadlocks.
/// A deadlock is as bad as it sounds. It means your program
/// just stops. ofMutex prevents race conditions, deadlocks and crashes by
/// permitting only one thread access to shared data at a time. When using
/// mutexes to protect data, the trick is to always be sure to unlock the
/// mutex when finished. This problem can often be avoided by using
/// an Poco::FastMutex::ScopedLock (aka ofScopedLock). See the
/// the documentation for more information. Finally, there are many cases
/// where it might make more sense to use Poco::Thread, Poco::Runnable and
/// Poco::FastMutex directly rather than using ofThread. Further, cross
/// platform thread management will be alleviated with the std::thread
/// support library included with C++11.
///
/// Uncaught Exceptions throw from within ofThread will cause the thread to stop
/// and the Exception will be delivered to the default ofBaseThreadErrorHandler.
/// The ofBaseThreadErrorHandler will print the exception details, if available.
/// The ofBaseThreadErrorHandler offers no opportunity to take corrective action
/// and only allows the user to receive more valuable debugging information
/// about the uncaught exception. Users should design ofThread subclasses to
/// catch and respond to all anticipated exceptions.
class ofThread {
public:
/// \brief Create an ofThread.
ofThread();
/// \brief Check the running status of the thread.
/// \returns true iff the thread is currently running.
bool isThreadRunning() const;
/// \brief Get the unique thread id.
/// \note This is NOT the the same as the operating thread id!
std::thread::id getThreadId() const;
/// \brief Get the unique thread name, in the form of "Thread id#"
/// \returns the Thread ID string.
std::string getThreadName() const;
void setThreadName(const std::string & name);
/// \brief Start the thread.
/// \note Subclasses can directly access the mutex and employ thier
/// own locking strategy.
void startThread();
/// \brief Start the thread with options.
/// \param mutexBlocks Set blocking to true if you want the mutex to
/// block when lock() is called.
/// \note Subclasses can directly access the mutex and employ thier
/// own locking strategy.
OF_DEPRECATED_MSG("Use tryLock instead of setting the type of lock on startThread",
void startThread(bool mutexBlocks));
/// \brief Lock the mutex.
///
/// If the thread was started startThread(true), then this call will wait
/// until the mutex is available and return true. If the thread was started
/// startThread(false), this call will return true iff the mutex is
/// was successfully acquired.
///
/// \returns true if the lock was successfully acquired.
bool lock();
/// \brief Tries to lock the mutex.
///
/// If the thread was started startThread(true), then this call will wait
/// until the mutex is available and return true. If the thread was started
/// startThread(false), this call will return true iff the mutex is
/// was successfully acquired.
///
/// \returns true if the lock was successfully acquired.
bool tryLock();
/// \brief Unlock the mutex.
///
/// This will only unlocks the mutex if it was previously by the same
/// calling thread.
void unlock();
/// \brief Stop the thread.
///
/// This does immediately stop the thread from processing, but
/// will only set a flag that must be checked from within your
/// threadedFunction() by calling isThreadRunning(). If the user wants
/// to both stop the thread AND wait for the thread to finish
/// processing, the user should call waitForThread(true, ...).
void stopThread();
/// \brief Wait for the thread to exit (aka "joining" the thread).
///
/// This method waits for a thread will "block" and wait for the
/// thread (aka "join" the thread) before it returns. This allows the
/// user to be sure that the thread is properly cleaned up. An example
/// of when this might be particularly important is if the
/// threadedFunction() is opening a set of network sockets, or
/// downloading data from the web. Destroying an ofThread subclass
/// without releasing those sockets (or other resources), may result in
/// segmentation faults, error signals or other undefined behaviors.
///
/// \param callStopThread Set stop to true if you want to signal the thread
/// to exit before waiting. This is the equivalent to calling
/// stopThread(). If you your threadedFunction uses a while-loop that
/// depends on isThreadRunning() and you do not call stopThread() or set
/// stop == true, waitForThread will hang indefinitely. Set stop ==
/// false ONLY if you have already called stopThread() and you simply
/// need to be sure your thread has finished its tasks.
///
/// \param milliseconds If millseconds is set to INFINITE_JOIN_TIMEOUT, the
/// waitForThread will wait indefinitely for the thread to complete. If
/// milliseconds is set to a lower number (e.g. 10000 for 10 seconds),
/// waitForThread will wait for 10000 milliseconds and if the thread has
/// not yet stopped it will return and log an error message. Users are
/// encouraged to use the default INFINITE_JOIN_TIMEOUT. If the user is
/// unhappy with the amount of time it takes to join a thread, the user
/// is encouraged to seek more expedient ways of signalling their desire
/// for a thread to complete via other signalling methods such as
/// Poco::Event, Poco::Condition, or Poco::Semaphore.
/// \sa http://pocoproject.org/slides/090-NotificationsEvents.pdf
/// \sa http://pocoproject.org/docs/Poco.Condition.html
/// \sa http://pocoproject.org/docs/Poco.Event.html
/// \sa http://pocoproject.org/docs/Poco.Semaphore.html
void waitForThread(bool callStopThread = true,
long milliseconds = INFINITE_JOIN_TIMEOUT);
/// \brief Tell the thread to sleep for a certain amount of milliseconds.
///
/// This is useful inside the threadedFunction() when a thread is waiting
/// for input to process:
///
/// void MyThreadedClass::threadedFunction()
/// {
/// // start
/// while(isThreadRunning())
/// {
/// // bReadyToProcess can be set from outside the threadedFuntion.
/// // perhaps by another thread that downloads data, or loads
/// // some media, etc.
///
/// if(bReadyToProcess == true)
/// {
/// // do some time intensive processing
/// bReadyToProcess = false;
/// }
/// else
/// {
/// // sleep the thread to give up some cpu
/// sleep(20);
/// }
/// }
/// // done
/// }
///
/// If the user does not give the thread a chance to sleep, the
/// thread may take 100% of the CPU core while it's looping as it
/// waits for something to do. This may lead to poor application
/// performance.
///
/// \param milliseconds The number of milliseconds to sleep.
void sleep(long milliseconds);
/// \brief Tell the thread to give up its CPU time other threads.
///
/// This method is similar to sleep() and can often be used in
/// the same way. The main difference is that 1 millisecond
/// (the minimum sleep time available with sleep()) is a very
/// long time on modern processors and yield() simply gives up
/// processing time to the next thread, instead of waiting for
/// number of milliseconds. In some cases, this behavior will
/// be preferred.
void yield();
/// \brief Query whether the current thread is active.
///
/// In multithreaded situations, it can be useful to know which
/// thread is currently running some code in order to make sure
/// only certain threads can do certain things. For example,
/// OpenGL can only run in the main execution thread. Thus,
/// situations where a thread is responsible for interacting
/// with graphics resources may need to prevent graphics updates
/// unless the main thread is accessing or updating resources
/// shared with this ofThread (or its subclass).
///
/// if(myThread.isCurrentThread())
/// {
/// // do some myThread things,
/// // but keep your hands off my resources!
/// }
/// else if(ofThread::isMainThread())
/// {
/// // pheew! ok, update those graphics resources
/// }
///
/// By way of another example, a subclass of ofThread may have
/// an update() method that is called from ofBaseApp during the
/// execution of the main application thread. In these cases,
/// the ofThread subclass might want to ask itself whether it
/// can, for instance, call update() on an ofImage, in order to
/// send copy some ofPixels to an ofTexture on the graphics
/// card.
///
/// \returns True iff this ofThread the currently active thread.
bool isCurrentThread() const;
/// \brief Get a reference to the underlying Poco thread.
///
/// Poco::Thread provides a clean cross-platform wrapper for
/// threads. On occasion, it may be useful to interact with the
/// underlying Poco::Thread directly.
///
/// \returns A reference to the backing Poco thread.
std::thread& getNativeThread();
/// \brief Get a const reference to the underlying Poco thread.
///
/// Poco::Thread provides a clean cross-platform wrapper for
/// threads. On occasion, it may be useful to interact with the
/// underlying Poco::Thread directly.
///
/// \returns A reference to the backing Poco thread.
const std::thread & getNativeThread() const;
enum {
INFINITE_JOIN_TIMEOUT = -1
///< \brief A sentinal value for an infinite join timeout.
///<
///< Primarily used with the waitForThread() method.
};
protected:
/// \brief The thread's run function.
///
/// Users must overide this in your their derived class
/// and then implement their threaded activity inside the loop.
/// If the the users's threadedFunction does not have a loop,
/// the contents of the threadedFunction will be executed once
/// and the thread will then exit.
///
/// For tasks that must be repeated, the user can use a while
/// loop that will run repeatedly until the thread's
/// threadRunning is set to false via the stopThread() method.
///
/// void MyThreadedClass::threadedFunction()
/// {
/// // Start the loop and continue until
/// // isThreadRunning() returns false.
/// while(isThreadRunning())
/// {
/// // Do activity repeatedly here:
///
/// // int j = 1 + 1;
///
/// // This while loop will run as fast as it possibly
/// // can, consuming as much processor speed as it can.
/// // To help the processor stay cool, users are
/// // encouraged to let the while loop sleep via the
/// // sleep() method, or call the yield() method to let
/// // other threads have a turn. See the sleep() and
/// // yield() methods for more information.
///
/// // sleep(100);
/// }
/// }
///
virtual void threadedFunction();
/// \brief The Poco::Thread that runs the Poco::Runnable.
std::thread thread;
/// \brief The internal mutex called through lock() & unlock().
///
/// This mutext can also be used with std::unique_lock or lock_guard
/// within the threaded function by calling:
///
/// std::unique_lock<std::mutex> lock(mutex);
///
mutable std::mutex mutex;
private:
///< \brief Implements Poco::Runnable::run().
void run();
///< \brief Is the thread running?
std::atomic<bool> threadRunning;
std::atomic<bool> threadDone;
///< \brief Should the mutex block?
std::atomic<bool> mutexBlocks;
std::string name;
std::condition_variable condition;
};
#else
class ofThread{
public:
void lock(){}
void unlock(){}
void startThread(){}
void stopThread(){};
bool isThreadRunning(){return false;}
enum {
INFINITE_JOIN_TIMEOUT = LONG_MAX
};
};
#endif
Comments