For University I have to write a program like space Invaders.
Now we have to write the "bullet" - Class for the main spaceShip, to shoot bullets.
Every bullet has to create its own thread. The thread has to execute the run method.
Bullet.hpp
ifndef BULLET_HPP_
define BULLET_HPP_
include "TriangleMesh.hpp"
include <GL/glu.h>
include <GL/glut.h>
include <thread>
include <chrono>
namespace asteroids
{
class Bullet : public TriangleMesh
{
public:
/**
* #brief Contructor. Build a bullet on the given Fighter's
* position. The bullet will move on the
* given axis.
* #param fighter_position Position of the fighter that shoots this bullet
* #param fighter_axis Axis the bullet will move on
*/
Bullet(Vertex<float> fighter_position, Vertex<float> fighter_axis);
~Bullet();
/**
* #brief Moves the bullet until it's lifetime is over.
*/
void run();
/**
* #brief Starts bullet movement
*/
void start();
/*
* #brief Stops bullet movement
*/
void stop();
/**
* #brief Renders the bullet via glutSolidSphere.
*/
void render();
/**
* #brief Returns the status of this bullet.
* #return false, if the bullet's lifetime is over and true otherwise
*/
bool isAlive();
private:
// Lifetime, i.e., how many timesteps the bullet visible
static const int m_lifetime = 9000;
// True, if the bullet's lifetime isn't over yet
bool m_alive;
// Flight direction of the bullet
Vertex<float> m_fighterAxis;
//position of the fighter
Vertex<float> m_fighterPosition;
static void staticRun(Bullet * bullet);
bool isRunning;
/// TODO: ADD TIMING AND THREAD FUNCTIONALITY
void stopThread();
void startThread();
void sleep(int n)
{
std::this_thread::sleep_for(std::chrono::milliseconds(n));
}
};
} // namespace asteroids
#endif /* BULLET_HPP_ */
/**
* Bullet.cpp
*
*/
BULLET.CPP
#include "Bullet.hpp"
using namespace std;
namespace asteroids
{
Bullet::Bullet(Vertex<float> fighter_position, Vertex<float> fighter_axis)
: m_alive(true)
{
// TODO: Implement Ctor logic
m_fighterAxis = fighter_axis;
m_fighterPosition = fighter_position;
m_position = fighter_position;
startThread();
}
void Bullet::stopThread()
{
currentThread->join();
}
//creates a static-member option for starting the thread with an argument.
void Bullet::staticRun(Bullet* bullet)
{
bullet->run();
}
//Logic to create and start a Thread with out static function to execute
void Bullet::startThread()
{
Bullet* bullet = this;
std::thread t1(&Bullet::staticRun, bullet);
currentThread = t1; //Here i wanted to set the pointer equal the existing thread
//But compiler says that i cant convert a thread type to a //pointer type
}
Bullet::~Bullet() {}
bool Bullet::isAlive()
{
return m_alive;
}
void Bullet::stop()
{
m_alive = false;
}
void Bullet::start()
{
if (isAlive())
{
// TODO: Implement thread start logic
startThread();
}
}
void Bullet::run()
{
Vertex<float> move = Vertex<float>(1, 1, 1);
for (int i = 0; i < m_lifetime; i++)
{
m_position = m_fighterAxis + move;
render();
sleep(1000);
//this->sleep(1000);
}
stop();
}
void Bullet::render()
{
// Compute transformation matrix
computeMatrix();
// Push old transformation of the OpenGL matrix stack and
// start rendering the bullet in according to the
// internal transformation matrix
glPushMatrix();
glMultMatrixf(m_transformation);
glutSolidSphere(10, 16, 16);
// Pop transformation matrix of this object
// to restore the previous state of the OpenGL
// matrix stack
glPopMatrix();
}
} // namespace asreroids
Now my question is: is it the right way to create the thread in a separate method and create a pointer which holds it?
Or how would you implement a thread-logic without a thread-parent-class?
Thank you for help guys, and excuse me if the layout is not the right way to post a commend here, its my first post ever ^^
You should bind the function to the variable that you're trying to pass, in this case I suggest you to bind it to the instance that you're creating and then the arguments that the function will receive, usually you declare the function as a member function and then bind the instance to it, then pass it to the thread that you're creating.
We're using mutex below in order to protect the running boolean from multiple thread access. You should use a mutex to protect the variables that could be accessed by multiple threads.
currentThread.join(); awaits for the thread to finish.
#include <thread>
#include <mutex>
#include <chrono>
class Bullet{
public:
void startThread(){
currentThread = std::thread(std::bind(&Bullet::run, this));
}
void run(){
while (isRunning()){
std::this_thread::sleep_for(std::chrono::milliseconds(200));
//Do something.
}
}
void Bullet::stopThread()
{
runMutex.lock();
running = false;
runMutex.unlock();
currentThread.join();
}
bool isRunning(){
bool isRunning = false;
runMutex.lock();
isRunning = running;
runMutex.unlock();
return isRunning;
}
private:
std::thread currentThread;
std::mutex runMutex;
bool running;
};
int main()
{
Bullet bullet;
bullet.startThread();
bullet.stopThread();
return 0; //return.
}
Having too many "bullet" objects and each running it's own thread would make it that harder to debug later on if the need arises....One way that this could be done could be through the use of ThreadPool pattern..A single thread pool can be used not just for bullets, but other entities in the system too...You will have a central repository of all threads and you can monitor them if the need arises...
https://codereview.stackexchange.com/questions/40536/simple-thread-pool-in-c
Bullet* bullet = this;
std::thread t1(&Bullet::staticRun, bullet);
currentThread = t1;
As far as the above code goes, is currentThread a class member??I havent used much of c++11 threading, but, doesn't std::thread t1(&Bullet::staticRun, bullet); go out of scope once the method execution completes??
Related
I have created a class using condition variable where I can create an object and use it instead of writing the implementation of the condition variable.
Below is my code. Now the functionality works as expected.
I see some issues if I run valgrind.
So I have two requests.
Please check the class implementation of the class and confirm if there can be any issues.
There is a "still reachable" error in valgrind if I run a gtest with this code. I checked online and found that "still reachable" issue can be ignored and moreover this problem is shown even if I do not add any code inside the test case (empty test case). So I am not too much worried about this error.
here is my code.
//ConditionalVariable.h
#ifndef SOURCE_MBUTILS_CONDITIONALVARIABLE_H_
#define SOURCE_MBUTILS_CONDITIONALVARIABLE_H_
#include <mutex>
#include <condition_variable>
#include <chrono>
class ConditionalVariable {
public:
ConditionalVariable();
virtual ~ConditionalVariable();
/**
* wait for the condition to reset from thread 1
*/
void waitForCondition();
/**
* wait for the condition to reset from thread 1 for timeout (msec)
* returns false if timeout, true otherwise
*/
bool waitForCondtion(std::uint32_t timeout);
/**
* reset the condition from thread 1, normally after the wait is over!
*/
void resetCondition();
/**
* set the condition from thread 2
*/
void setTheCondition();
/**
* get current condition state
*/
bool isConditionNowSet();
private:
std::mutex _mtx;
std::condition_variable _cv;
bool _condition;
};
#endif /* SOURCE_MBUTILS_CONDITIONALVARIABLE_H_ */
//ConditionalVariable.cpp
#include "ConditionalVariable.h"
ConditionalVariable::ConditionalVariable(): _condition(false)
{
// TODO Auto-generated constructor stub
}
ConditionalVariable::~ConditionalVariable() {
// TODO Auto-generated destructor stub
}
void ConditionalVariable::waitForCondition()
{
std::unique_lock<std::mutex> lock(_mtx);
_cv.wait(lock, [this]{ return _condition;});
}
bool ConditionalVariable::waitForCondtion(std::uint32_t timeout)
{
auto timePoint = std::chrono::system_clock::now() + (std::chrono::milliseconds) timeout;
std::unique_lock<std::mutex> lock(_mtx);
return _cv.wait_until(lock, timePoint, [this]{return _condition;});
}
void ConditionalVariable::resetCondition() {
//make the condition false to reset!
std::lock_guard<std::mutex> lock(_mtx);
_condition = false;
}
void ConditionalVariable::setTheCondition()
{
std::lock_guard<std::mutex> lock(_mtx);
_condition = true;
_cv.notify_one();
}
bool ConditionalVariable::isConditionNowSet()
{
return _condition;
}
Below error is from valgrind
So, I create a State class. In that State class's create function, it creates a StateVisual class by calling the StateVisual class's create function, passing itself as a parameter. (The parameter is a reference so that there is no copying). The StateVisual then sets the parameter (The State Instance), as it's parent variable, which is a pointer of a State type.
In the StateVisual's create function, everything works fine. However, when you get to it's update method, and try to print it's parent size, it prints some weird value.
#ifndef STATE_H
#define STATE_H
#include "cocos2d.h"
class StateVisual;
class State {
public:
State();
~State();
static State create();
StateVisual *visual;
float size;
void setSize(float);
void update(float);
private:
cocos2d::Scheduler* _scheduler;
};
#endif
StateVisual.cpp
#ifndef STATE_VIS_H
#define STATE_VIS_H
#include "cocos2d.h"
#include "State.h"
class StateVisual : public cocos2d::Sprite {
public:
StateVisual();
~StateVisual();
// create a visual
State* parent;
static StateVisual* create(State& parent);
cocos2d::Label* label;
void setSize(float);
void update(float);
private:
bool _activated;
float _size;
};
#endif
State.cpp
#include "State.h"
#include "GameScene.h"
State::State() : size(0) {
CCLOG("Created");
}
State::~State() {
}
void State::setSize(float newSize) {
size = newSize;
CCLOG("%f, %f", newSize, size);
}
void State::update(float dt) {
}
State State::create() {
State state;
state.visual = StateVisual::create(state);
cocos2d::SEL_SCHEDULE ss;
return state;
}
StateVisual.cpp
#include "StateVisual.h"
#include "GameScene.h"
using namespace cocos2d;
StateVisual::StateVisual() : parent(nullptr) {
CCLOG("New STATE VISUAL!");
}
StateVisual::~StateVisual() {
}
void StateVisual::setSize(float size) {
setContentSize(Size(size, size));
if (size > 30) {
label->setSystemFontSize(size*.1);
label->setOpacity(255);
}
else {
label->setOpacity(0);
}
}
void StateVisual::update(float dt) {
cocos2d::MathUtil::smooth(&_size, parent->size, dt, .2);
setSize(_size);
CCLOG("%f, %f", _size, (*this->parent).size);
}
StateVisual* StateVisual::create(State &parent) {
StateVisual* visual(new StateVisual());
if (visual->initWithFile("Circle.png"))
{
visual->setSize(200);
visual->_size = 200;
visual->parent = &parent;
visual->parent->setSize(20);
CCLOG("PARENT SIZE: %f", visual->parent->size);
visual->autorelease();
visual->scheduleUpdate();
return visual;
}
CC_SAFE_DELETE(visual);
return NULL;
}
It outputs:
Created
New STATE VISUAL!
cocos2d: fullPathForFilename: No file found at /cc_2x2_white_image. Possible
missing file.
20.000000, 20.000000
PARENT SIZE: 20.000000
500.000000, 500.000000
cocos2d: QuadCommand: resizing index size from [-1] to [2560]
168.058044, -107374176.000000 <-- Those are the weird values it prints
155.130508, -107374176.000000
The program '[1464] State.exe' has exited with code 0 (0x0).
0xCCCCCCCC, a typical value used to fill uninitialized memory in Debug builds, interpreted as a 32-bit float, equals -107374176.000000. You're printing an uninitialized float value.
State State::create() {
State state;
state.visual = StateVisual::create(state);
cocos2d::SEL_SCHEDULE ss;
return state;
}
At the end of this function the local object state is destroyed a copy ois made (or moved to a new object). Which means any pointer to the address &state is invalid after the end of State::create
visual->parent = &parent; // parent = state object on stack
Because of this line visual->parent is now dangling.
Those weird values are the random content of you thread stack...
What you should do is use shared pointers for state\parent.
void StateVisual::update(float dt) {
cocos2d::MathUtil::smooth(&_size, parent->size, dt, .2);
setSize(_size);
CCLOG("%f, %f", _size, (*this->parent).size);
Why do you use parent->size in 1 row of function and (*this->parent).size in third?
Why don't you use getters and setters?
Why haven't you shown header files?
Why haven't you prepared minimal example (like just parent-child logic and sizes without labels, positions etc.?
What is this suposed to do? StateVisual* visual(new StateVisual());
If visual is automatic variable, i think it should look loke StateVisual visual(new StateVisual()); otherwise shouldn't it be StateVisual* visual = new StateVisual(new StateVisual()); Also are you sure you want to pass new StateVisual object into constructor of StateVisual?
I'm trying to use an RtosTimer within a class but the mbed locks up. I think this is because I'm calling threadHelper each tick and its creating a new pointer whereas I actually want to call threadMethod each tick or call threadHeper each tick but use the same pointer.
Can anyone show me how I should be doing this?
The code below works for an RtosThread because threadHelper is only called once, but I need to use an Rtos Timer.
.h
#ifndef TEST_CLASS_H
#define TEST_CLASS_H
#include "mbed.h"
#include "rtos.h"
/** TestClass class.
* Used for demonstrating stuff.
*/
class TestClass
{
public:
/** Create a TestClass object with the specified specifics
*
* #param led The LED pin.
* #param flashRate The rate to flash the LED at in Hz.
*/
TestClass(PinName led, float flashRate);
/** Start flashing the LED using a Thread
*/
void start();
private:
//Member variables
DigitalOut m_Led;
float m_FlashRate;
RtosTimer *m_rtosTimer;
//Internal methods
static void threadHelper(const void* arg);
void threadMethod();
};
#endif
cpp
#include "TestClass.h"
TestClass::TestClass(PinName led, float flashRate) : m_Led(led, 0), m_FlashRate(flashRate)
{
//NOTE: The RTOS hasn't started yet, so we can't create the internal thread here
}
void TestClass::start()
{
m_rtosTimer = new RtosTimer(&TestClass::threadHelper, osTimerPeriodic, (void *)0);
int updateTime = (1.0 / m_FlashRate) * 1000;
m_rtosTimer->start(updateTime);
}
void TestClass::threadHelper(const void* arg)
{
//Cast the argument to a TestClass instance pointer
TestClass* instance = (TestClass*)arg;
//Call the thread method for the TestClass instance
instance ->threadMethod();
}
void TestClass::threadMethod()
{
//Do some stuff
}
Thanks
The arg pointer passed to threadHelper() is the argument you passed ot the RtosTimer constructor - in this case a null-pointer. threadHelper() dereferences this null-pointer causing a runtime error.
Instead:
m_rtosTimer = new RtosTimer(&TestClass::threadHelper, osTimerPeriodic, (void*)this);
I was trying to compile a simple Helloworld program for BADA but through command prompt.After compiling i ma getting the errors as
c:/Helloworld/src/Helloworld.cpp:12: error: prototype for 'Osp::App::Application HelloWorld::CreateInstance()' does not match any in class 'HelloWorld'
C:/Helloworld/inc/HelloWorld.h:21: error: candidate is: static Osp::App::Application* HelloWorld::CreateInstance()
could any body help that what needs to be done with it.
Thanks
Code for Helloworld.h
#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__
#include <FBase.h>
#include <FGraphics.h>
#include <FLocales.h>
#include <FSystem.h>
#include <FApp.h>
using namespace Osp::Base;
using namespace Osp::Graphics;
using namespace Osp::Locales;
using namespace Osp::System;
using namespace Osp::App;
class HelloWorld :
public Application // must inherit from Application class
{
public:
// The application must have a factory method that creates an instance of the application.
static Application* CreateInstance(void);
public:
HelloWorld();
~HelloWorld();
public:
// The application must provide its name.
String GetAppName(void) const;
protected:
// The application must provide its ID.
AppId GetAppId(void) const;
AppSecret GetAppSecret(void) const;
public:
// This method is called when the application is initializing.
bool OnAppInitializing(AppRegistry& appRegistry);
// This method is called when the application is terminating.
bool OnAppTerminating(AppRegistry& appRegistry);
// Thie method is called when the application is brought to the foreground
void OnForeground(void);
// This method is called when the application is sent to the background.
void OnBackground(void);
// This method is called when the application has little available memory.
void OnLowMemory(void);
// This method is called when the device's battery level changes.
void OnBatteryLevelChanged(BatteryLevel batteryLevel);
};
#endif
Code for Helloworld.cpp
#include "HelloWorld.h"
HelloWorld::HelloWorld()
{
}
HelloWorld::~HelloWorld()
{
}
Application*
HelloWorld::CreateInstance(void)
{
// You can create the instance through another constructor.
return new HelloWorld();
}
String
HelloWorld::GetAppName(void) const
{
static String appName(L"HelloWorld");
return appName;
}
AppId
HelloWorld::GetAppId(void) const
{
static AppId appId(L"93bt1p123e");
return appId;
}
AppSecret
HelloWorld::GetAppSecret(void) const
{
static AppSecret appSecret(L"9C645DDBA19C71BAD1204DA4DAA7A0B9");
return appSecret;
}
bool
HelloWorld::OnAppInitializing(AppRegistry& appRegistry)
{
// TODO:
// Initialization including UI construction can be done here.
// Load the application's latest data, if necessary.
// If this method is successful, return true; otherwise, return false.
return true;
}
bool
HelloWorld::OnAppTerminating(AppRegistry& appRegistry)
{
// TODO:
// Deallocate or close any resources still alive.
// Save the application's current states, if applicable.
// If this method is successful, return true; otherwise, return false.
return true;
}
void
HelloWorld::OnForeground(void)
{
result r = E_SUCCESS;
Canvas* pCanvas = GetAppFrame()->GetCanvasN();
if(pCanvas == null)
return;
Font* pFont = new Font();
pFont->Construct(FONT_STYLE_PLAIN | FONT_STYLE_BOLD, 50);
pCanvas->SetFont(*pFont);
r = pCanvas->DrawText(Point(30, 30), GetAppName());
if (IsFailed(r))
{
AppLog("pCanvas->DrawText() failed.\n");
delete pCanvas;
return;
}
r = pCanvas->Show();
if (IsFailed(r))
{ AppLog("pCanvas->Show() failed.\n");
delete pCanvas;
return;
}
delete pCanvas;
}
void
HelloWorld::OnBackground(void)
{
}
void
HelloWorld::OnLowMemory(void)
{
// TODO:
// Deallocate as many resources as possible.
}
void
HelloWorld::OnBatteryLevelChanged(BatteryLevel batteryLevel)
{
// TODO:
// It is recommended that the application save its data,
// and terminate itself if the application consumes much battery
}
Code for Helloworldentry.cpp
/**
* OSP Application entry point(OspMain) introduced.
*/
#include <fapp.h>
#include "HelloWorld.h"
using namespace Osp::Base::Collection;
extern "C"
{
__declspec(dllexport) void OspMain(int hInstance, int argc, char *argv[]);
}
/**
* Entry function of OSP Application which is called by the operating system.
*/
extern "C" {
void OspMain(int hInstance, int argc, char *argv[])
{
AppLog("OspMain() Started. \n");
result r = E_SUCCESS;
ArrayList* pArgs = new ArrayList();
pArgs->Construct();
for (int i = 0; i < argc; i++)
{
String* pEachArg = new String(argv[i]);
pArgs->Add(*pEachArg);
}
r = Osp::App::Application::Execute(HelloWorld::CreateInstance, pArgs);
if (IsFailed(r))
{
AppLog("Application execution has failed.\n");
}
if (pArgs)
{
pArgs->RemoveAll(true);
delete pArgs;
}
AppLog("OspMain() Ended. \n");
}
}
The compiler is complaining that you have defined a method with this signature:
Osp::App::Application HelloWorld::CreateInstance()
whereas the HelloWorld class declares that its CreateInstance method has this signature:
Osp::App::Application* HelloWorld::CreateInstance()
Note the difference in the return type. The class definition says that the method with this name returns an Application pointer but you have implemented a method that returns an Application object.
In the future, please post code along with compiler errors. It's rarely possible to adequately explain compiler errors in isolation from the code that produced them. For example, I can't tell you which return type is correct in this case; I can only tell you that the return types don't match (which is exactly what the compiler already said).
I would like to create a class whose methods can be called from multiple threads. but instead of executing the method in the thread from which it was called, it should perform them all in it's own thread. No result needs to be returned and It shouldn't block the calling thread.
A first attempt Implementation I have included below. The public methods insert a function pointer and data into a job Queue, which the worker thread then picks up. However it's not particularily nice code and adding new methods is cumbersome.
Ideally I would like to use this as a base class which I can easy add methods (with a variable number of arguments) with minimum hastle and code duplication.
What is a better way to do this? Is there any existing code available which does something similar? Thanks
#include <queue>
using namespace std;
class GThreadObject
{
class event
{
public:
void (GThreadObject::*funcPtr)(void *);
void * data;
};
public:
void functionOne(char * argOne, int argTwo);
private:
void workerThread();
queue<GThreadObject::event*> jobQueue;
void functionOneProxy(void * buffer);
void functionOneInternal(char * argOne, int argTwo);
};
#include <iostream>
#include "GThreadObject.h"
using namespace std;
/* On a continuous loop, reading tasks from queue
* When a new event is received it executes the attached function pointer
* It should block on a condition, but Thread code removed to decrease clutter
*/
void GThreadObject::workerThread()
{
//New Event added, process it
GThreadObject::event * receivedEvent = jobQueue.front();
//Execute the function pointer with the attached data
(*this.*receivedEvent->funcPtr)(receivedEvent->data);
}
/*
* This is the public interface, Can be called from child threads
* Instead of executing the event directly it adds it to a job queue
* Then the workerThread picks it up and executes all tasks on the same thread
*/
void GThreadObject::functionOne(char * argOne, int argTwo)
{
//Malloc an object the size of the function arguments
int argumentSize = sizeof(char*)+sizeof(int);
void * myData = malloc(argumentSize);
//Copy the data passed to this function into the buffer
memcpy(myData, &argOne, argumentSize);
//Create the event and push it on to the queue
GThreadObject::event * myEvent = new event;
myEvent->data = myData;
myEvent->funcPtr = >hreadObject::functionOneProxy;
jobQueue.push(myEvent);
//This would be send a thread condition signal, replaced with a simple call here
this->workerThread();
}
/*
* This handles the actual event
*/
void GThreadObject::functionOneInternal(char * argOne, int argTwo)
{
cout << "We've made it to functionTwo char*:" << argOne << " int:" << argTwo << endl;
//Now do the work
}
/*
* This is the function I would like to remove if possible
* Split the void * buffer into arguments for the internal Function
*/
void GThreadObject::functionOneProxy(void * buffer)
{
char * cBuff = (char*)buffer;
functionOneInternal((char*)*((unsigned int*)cBuff), (int)*(cBuff+sizeof(char*)));
};
int main()
{
GThreadObject myObj;
myObj.functionOne("My Message", 23);
return 0;
}
There's Futures library making its way into Boost and the C++ standard library. There's also something of the same sort in ACE, but I would hate to recommend it to anyone (as #lothar already pointed out, it's Active Object.)
Below is an implementation which doesn't require a "functionProxy" method. Even though it is easier to add new methods, it's still messy.
Boost::Bind and "Futures" do seem like they would tidy a lot of this up. I guess I'll have a look at the boost code and see how it works. Thanks for your suggestions everyone.
GThreadObject.h
#include <queue>
using namespace std;
class GThreadObject
{
template <int size>
class VariableSizeContainter
{
char data[size];
};
class event
{
public:
void (GThreadObject::*funcPtr)(void *);
int dataSize;
char * data;
};
public:
void functionOne(char * argOne, int argTwo);
void functionTwo(int argTwo, int arg2);
private:
void newEvent(void (GThreadObject::*)(void*), unsigned int argStart, int argSize);
void workerThread();
queue<GThreadObject::event*> jobQueue;
void functionTwoInternal(int argTwo, int arg2);
void functionOneInternal(char * argOne, int argTwo);
};
GThreadObject.cpp
#include <iostream>
#include "GThreadObject.h"
using namespace std;
/* On a continuous loop, reading tasks from queue
* When a new event is received it executes the attached function pointer
* Thread code removed to decrease clutter
*/
void GThreadObject::workerThread()
{
//New Event added, process it
GThreadObject::event * receivedEvent = jobQueue.front();
/* Create an object the size of the stack the function is expecting, then cast the function to accept this object as an argument.
* This is the bit i would like to remove
* Only supports 8 byte argument size e.g 2 int's OR pointer + int OR myObject8bytesSize
* Subsequent data sizes would need to be added with an else if
* */
if (receivedEvent->dataSize == 8)
{
const int size = 8;
void (GThreadObject::*newFuncPtr)(VariableSizeContainter<size>);
newFuncPtr = (void (GThreadObject::*)(VariableSizeContainter<size>))receivedEvent->funcPtr;
//Execute the function
(*this.*newFuncPtr)(*((VariableSizeContainter<size>*)receivedEvent->data));
}
//Clean up
free(receivedEvent->data);
delete receivedEvent;
}
void GThreadObject::newEvent(void (GThreadObject::*funcPtr)(void*), unsigned int argStart, int argSize)
{
//Malloc an object the size of the function arguments
void * myData = malloc(argSize);
//Copy the data passed to this function into the buffer
memcpy(myData, (char*)argStart, argSize);
//Create the event and push it on to the queue
GThreadObject::event * myEvent = new event;
myEvent->data = (char*)myData;
myEvent->dataSize = argSize;
myEvent->funcPtr = funcPtr;
jobQueue.push(myEvent);
//This would be send a thread condition signal, replaced with a simple call here
this->workerThread();
}
/*
* This is the public interface, Can be called from child threads
* Instead of executing the event directly it adds it to a job queue
* Then the workerThread picks it up and executes all tasks on the same thread
*/
void GThreadObject::functionOne(char * argOne, int argTwo)
{
newEvent((void (GThreadObject::*)(void*))>hreadObject::functionOneInternal, (unsigned int)&argOne, sizeof(char*)+sizeof(int));
}
/*
* This handles the actual event
*/
void GThreadObject::functionOneInternal(char * argOne, int argTwo)
{
cout << "We've made it to functionOne Internal char*:" << argOne << " int:" << argTwo << endl;
//Now do the work
}
void GThreadObject::functionTwo(int argOne, int argTwo)
{
newEvent((void (GThreadObject::*)(void*))>hreadObject::functionTwoInternal, (unsigned int)&argOne, sizeof(int)+sizeof(int));
}
/*
* This handles the actual event
*/
void GThreadObject::functionTwoInternal(int argOne, int argTwo)
{
cout << "We've made it to functionTwo Internal arg1:" << argOne << " int:" << argTwo << endl;
}
main.cpp
#include <iostream>
#include "GThreadObject.h"
int main()
{
GThreadObject myObj;
myObj.functionOne("My Message", 23);
myObj.functionTwo(456, 23);
return 0;
}
Edit: Just for completeness I did an implementation with Boost::bind. Key Differences:
queue<boost::function<void ()> > jobQueue;
void GThreadObjectBoost::functionOne(char * argOne, int argTwo)
{
jobQueue.push(boost::bind(>hreadObjectBoost::functionOneInternal, this, argOne, argTwo));
workerThread();
}
void GThreadObjectBoost::workerThread()
{
boost::function<void ()> func = jobQueue.front();
func();
}
Using the boost implementation for 10,000,000 Iterations of functionOne() it took ~19sec. However the non boost implementation took only ~6.5 sec. So Approx 3x slower. I'm guessing finding a good non-locking queue will be the biggest performance bottle neck here. But it's still quite a big difference.
The POCO library has something along the same lines called ActiveMethod (along with some related functionality e.g. ActiveResult) in the threading section. The source code is readily available and easily understood.
You might be interested in Active Object one of the ACE Patterns of the ACE framework.
As Nikolai pointed out futures are planned for standard C++ some time in the future (pun intended).
For extensibility and maintainability (and other -bilities) you could define an abstract class (or interface) for the "job" that thread is to perform. Then user(s) of your thread pool would implement this interface and give reference to the object to the thread pool. This is very similar to Symbian Active Object design: every AO subclasses CActive and have to implement methods such as Run() and Cancel().
For simplicity your interface (abstract class) might be as simple as:
class IJob
{
virtual Run()=0;
};
Then the thread pool, or single thread accepting requests would have something like:
class CThread
{
<...>
public:
void AddJob(IJob* iTask);
<...>
};
Naturally you would have multiple tasks that can have all kinds of extra setters / getters / attributes and whatever you need in any walk of life. However, the only must is to implement method Run(), which would perform the lengthy calculations:
class CDumbLoop : public IJob
{
public:
CDumbJob(int iCount) : m_Count(iCount) {};
~CDumbJob() {};
void Run()
{
// Do anything you want here
}
private:
int m_Count;
};
You can solve this by using Boost's Thread -library. Something like this (half-pseudo):
class GThreadObject
{
...
public:
GThreadObject()
: _done(false)
, _newJob(false)
, _thread(boost::bind(>hreadObject::workerThread, this))
{
}
~GThreadObject()
{
_done = true;
_thread.join();
}
void functionOne(char *argOne, int argTwo)
{
...
_jobQueue.push(myEvent);
{
boost::lock_guard l(_mutex);
_newJob = true;
}
_cond.notify_one();
}
private:
void workerThread()
{
while (!_done) {
boost::unique_lock l(_mutex);
while (!_newJob) {
cond.wait(l);
}
Event *receivedEvent = _jobQueue.front();
...
}
}
private:
volatile bool _done;
volatile bool _newJob;
boost::thread _thread;
boost::mutex _mutex;
boost::condition_variable _cond;
std::queue<Event*> _jobQueue;
};
Also, please note how RAII allow us to get this code smaller and better to manage.
Here's a class I wrote for a similar purpose (I use it for event handling but you could of course rename it to ActionQueue -- and rename its methods).
You use it like this:
With function you want to call: void foo (const int x, const int y) { /*...*/ }
And: EventQueue q;
q.AddEvent (boost::bind (foo, 10, 20));
In the worker thread
q.PlayOutEvents ();
Note: It should be fairly easy to add code to block on condition to avoid using up CPU cycles.
The code (Visual Studio 2003 with boost 1.34.1):
#pragma once
#include <boost/thread/recursive_mutex.hpp>
#include <boost/function.hpp>
#include <boost/signals.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <string>
using std::string;
// Records & plays out actions (closures) in a safe-thread manner.
class EventQueue
{
typedef boost::function <void ()> Event;
public:
const bool PlayOutEvents ()
{
// The copy is there to ensure there are no deadlocks.
const std::vector<Event> eventsCopy = PopEvents ();
BOOST_FOREACH (const Event& e, eventsCopy)
{
e ();
Sleep (0);
}
return eventsCopy.size () > 0;
}
void AddEvent (const Event& event)
{
Mutex::scoped_lock lock (myMutex);
myEvents.push_back (event);
}
protected:
const std::vector<Event> PopEvents ()
{
Mutex::scoped_lock lock (myMutex);
const std::vector<Event> eventsCopy = myEvents;
myEvents.clear ();
return eventsCopy;
}
private:
typedef boost::recursive_mutex Mutex;
Mutex myMutex;
std::vector <Event> myEvents;
};
I hope this helps. :)
Martin Bilski
You should take a look at the Boost ASIO library. It is designed to dispatch events asynchronously. It can be paired with the Boost Thread library to build the system that you described.
You would need to instantiate a single boost::asio::io_service object and schedule a series of asynchronous events (boost::asio::io_service::post or boost::asio::io_service::dispatch). Next, you call the run member function from n threads. The io_service object is thread-safe and guarantees that your asynchronous handlers will only be dispatched in a thread from which you called io_service::run.
The boost::asio::strand object is also useful for simple thread synchronization.
For what it is worth, I think that the ASIO library is a very elegant solution to this problem.