Skip to content

Instantly share code, notes, and snippets.

@bakercp
Last active December 11, 2015 01:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bakercp/4522874 to your computer and use it in GitHub Desktop.
Save bakercp/4522874 to your computer and use it in GitHub Desktop.
ofThread updates ...
#include "ofThread.h"
#include "ofLog.h"
#include "ofUtils.h"
#ifdef TARGET_ANDROID
#include <jni.h>
#include "ofxAndroidUtils.h"
#endif
//-------------------------------------------------
ofThread::ofThread(){
threadRunning = false;
verbose = false;
thread.setName("Thread "+ofToString(thread.id()));
blocking = true;
oldErrorHandler = NULL;
}
//-------------------------------------------------
ofThread::~ofThread(){
stopThread();
}
//-------------------------------------------------
bool ofThread::isThreadRunning(){
return threadRunning;
}
//-------------------------------------------------
int ofThread::getThreadId(){
return thread.id();
}
//-------------------------------------------------
string ofThread::getThreadName(){
return thread.name();
}
//-------------------------------------------------
void ofThread::startThread(bool blocking, bool verbose){
if(thread.isRunning()){
ofLogWarning(thread.name()) << "cannot start, thread already running";
return;
}
// have to put this here because the thread can be running
// before the call to create it returns
threadRunning = true;
this->blocking = blocking;
this->verbose = verbose;
if(verbose){
ofSetLogLevel(thread.name(), OF_LOG_VERBOSE);
}
else{
ofSetLogLevel(thread.name(), OF_LOG_NOTICE);
}
errorHandler.setName(getThreadName());
oldErrorHandler = Poco::ErrorHandler::set(&errorHandler);
thread.start(*this);
}
//-------------------------------------------------
bool ofThread::lock(){
if(blocking){
if(verbose){
if(Poco::Thread::current() == &thread){
ofLogVerbose(thread.name()) << "thread waiting for own mutex to be unlocked";
}
else{
ofLogVerbose(thread.name()) << "external waiting for thread mutex to be unlocked";
}
}
mutex.lock();
}
else{
if(!mutex.tryLock()){
ofLogVerbose(thread.name()) << "mutex is busy - already locked";
return false;
}
}
if(verbose){
if(Poco::Thread::current() == &thread){
ofLogVerbose(thread.name()) << "thread locked own mutex";
}
else{
ofLogVerbose(thread.name()) << "external locked thread mutex";
}
}
return true;
}
//-------------------------------------------------
void ofThread::unlock(){
mutex.unlock();
if(verbose){
if(Poco::Thread::current() == &thread){
ofLogVerbose(thread.name()) << "thread unlocked own mutex";
}
else{
ofLogVerbose(thread.name()) << "external unlocked thread mutex";
}
}
return;
}
//-------------------------------------------------
void ofThread::stopThread(){
if(thread.isRunning()) {
threadRunning = false;
}
}
//-------------------------------------------------
void ofThread::waitForThread(bool stop){
if(thread.isRunning()){
// tell thread to stop
if(stop){
threadRunning = false;
ofLogVerbose(thread.name()) << "signaled to stop";
}
// wait for the thread to finish
ofLogVerbose(thread.name()) << "waiting to stop";
if(Poco::Thread::current() == &thread){
ofLogWarning(thread.name()) << "waitForThread should only be called from outside the thread";
return;
}
thread.join();
Poco::ErrorHandler::set(oldErrorHandler);
}
}
//-------------------------------------------------
void ofThread::sleep(int sleepMS){
Poco::Thread::sleep(sleepMS);
}
//-------------------------------------------------
void ofThread::yield(){
Poco::Thread::yield();
}
//-------------------------------------------------
bool ofThread::isCurrentThread(){
if(ofThread::getCurrentThread() == this)
return true;
return false;
}
//-------------------------------------------------
Poco::Thread & ofThread::getPocoThread(){
return thread;
}
//-------------------------------------------------
bool ofThread::isMainThread(){
if(Poco::Thread::current() == NULL)
return true;
return false;
}
//-------------------------------------------------
ofThread * ofThread::getCurrentThread(){
// assumes all created threads are ofThreads ...
// might be dangerous if people are using Poco::Threads directly
return (ofThread *) Poco::Thread::current();
}
// PROTECTED
//-------------------------------------------------
void ofThread::threadedFunction(){
ofLogWarning(thread.name()) << "override threadedFunction with your own";
}
// PRIVATE
//-------------------------------------------------
void ofThread::run(){
ofLogVerbose(thread.name()) << "started";
#ifdef TARGET_ANDROID
JNIEnv * env;
jint attachResult = ofGetJavaVMPtr()->AttachCurrentThread(&env,NULL);
#endif
// user function
threadedFunction();
#ifdef TARGET_ANDROID
attachResult = ofGetJavaVMPtr()->DetachCurrentThread();
#endif
threadRunning = false;
ofLogVerbose(thread.name()) << "stopped";
}
#pragma once
#include "ofConstants.h"
#include "ofTypes.h"
#include "ofThreadErrorHandler.h"
#include "Poco/Thread.h"
#include "Poco/Runnable.h"
/// a thread base class with a built in mutex
///
/// derive this class and implement threadedFunction()
class ofThread : protected Poco::Runnable{
public:
ofThread();
virtual ~ofThread();
/// returns true if the thread is currently running
bool isThreadRunning();
/// get the unique thread id
///
/// note: this is *not* the OS thread id!
int getThreadId();
/// get the unique thread name, in the form of "Thread id#"
string getThreadName();
/// start the thread
///
/// set blocking to true if you want the mutex to block on lock()
///
/// set verbose to true if you want detailed logging on thread and mutex
/// events
void startThread(bool blocking = true, bool verbose = false);
/// try to lock the mutex
///
/// if the thread is blocking, this call will wait until the mutex is
/// available
///
/// if the thread is non-blocking, this call will return a true or false
/// if the mutex is available
bool lock();
/// unlock the mutex
///
/// only unlocks the mutex if it had been locked previously by the
/// calling thread
void unlock();
/// stop the thread
void stopThread();
/// wait for the thread to exit
///
/// this function waits for the thread to exit before it returns to make
/// sure the thread is cleaned up, otherwise you will get errors on exit
///
/// set stop to true if you want to signal the thread to exit before
/// waiting, this is the equivalent to calling stopThread(false)
///
/// set stop to false if you have already signalled the thread to exit
/// by calling stopThread(false) and only need to wait for it to finish
///
void waitForThread(bool stop = true);
/// 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 myClass::threadedFunction(){
///
/// // start
///
/// while(isThreadRunning()){
///
/// if(bReadyToProcess == true){
///
/// // do some time intensive processing
///
/// bReadyToProcess = false;
/// }
/// else{
///
/// // sleep the thread to give up some cpu
/// sleep(20);
/// }
/// }
///
/// // done
/// }
///
/// not sleeping the thread means the thread will take 100% of the cpu
/// while it's waiting and will impact performance of your app
///
void sleep(int sleepMS);
/// tell the thread to give up the cpu to other threads
///
/// this function is similar to sleep() and can be used in the same way,
/// the main difference is that 1 ms is a long time on modern processors
/// and yield() simply gives up processing time to the next thread
/// instead of waiting for a certain amount of time
///
/// this can be faster in some circumstances
///
void yield();
/// 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 ...
///
/// this is especially useful with graphics as resources must
/// be allocated and updated inside the main app thread only:
///
/// if(myThread.isCurrentThread()){
/// // do some myThread things, keep your hands off my resources!
/// }
/// else if(ofThread::isMainThread()){
/// // pheew! ok, update those graphics resources
/// }
///
/// returns true if this the currently active thread
bool isCurrentThread();
Poco::Thread & getPocoThread();
/// returns true if the main app thread is the currently active thread
static bool isMainThread();
/// get the current thread, returns NULL if in the main app thread
///
/// this is useful if you want to access the currently active thread:
///
/// ofThread* myThread = ofThread::getCurrentThread();
/// if(myThread != NULL){
/// ofLog() << "Current thread is " << myThread->getThreadName();
/// }
/// else{
/// ofLog() << "Current thread is the main app thread";
/// }
///
static ofThread * getCurrentThread();
protected:
/// this is the thread run function
///
/// you need to overide this in your derived class and implement your
/// thread stuff inside
///
/// if you do not have a loop inside this function, it will run once
/// then exit
///
/// if you want the thread to run until you signal it to stop, use a
/// while loop inside that checks if the thread is should keep running:
///
/// void myClass::threadedFunction(){
///
/// // start
///
/// while(isThreadRunning()){
///
/// // do stuff
/// }
///
/// // done
/// }
///
virtual void threadedFunction();
/// the internal mutex called through lock() & unlock()
ofMutex mutex;
bool threadRunning; ///< is the thread running?
bool blocking; ///< should the mutex block?
bool verbose; ///< print detailed run/mutex info?
private:
/// runs the user thread function
void run();
Poco::Thread thread;
ofThreadErrorHandler errorHandler;
Poco::ErrorHandler* oldErrorHandler;
};
//
// ofThreadErrorHandler.h
// example
//
// Created by Christopher P. Baker on 1/13/13.
//
//
#pragma once
#include "ofLog.h"
#include "Poco/ErrorHandler.h"
class ofThreadErrorHandler : public Poco::ErrorHandler {
public:
ofThreadErrorHandler(const string& _threadName = "NONE") {
threadName = _threadName;
}
virtual ~ofThreadErrorHandler() { }
void exception(const Poco::Exception& exc) {
ofLogError("ofThreadErrorHandler::exception") << "Uncaught thread exception: " << exc.displayText();
}
void exception(const std::exception& exc) {
ofLogError("ofThreadErrorHandler::exception") << "Uncaught thread exception: " << exc.what();
}
void exception() {
ofLogError("ofThreadErrorHandler::exception") << "Uncaught thread exception: Unknown exception.";
}
void setName(const string& name) {
threadName = name;
}
string getName() const {
return threadName;
}
private:
string threadName;
};
...
ofTheadSubClass::threadedFunction() {
int count = 0;
while( isThreadRunning() ) {
if(count > 10) {
throw Poco::ApplicationException("Exception that is invisible without the Error Handler!");
}
// pretend like you're doing something
count++
sleep(1000);
}
}
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment