See the open issues for a full list of proposed features (and known issues).
Getting Started
This is an example of how you may give instructions on setting up your project locally
To get a local copy up and running follow these simple example steps.
Prerequisites
The following are required to get the project up and running.
git clone https://github.com/google/googletest.git # Dowload the Google Test repositorycd googletest # Main directory of the cloned repository.
mkdir -p build # Create a directory to hold the build output.cd build # Move into the build directory.
cmake .. # Generate native build scripts for GoogleTest.
make # Compile
sudo make install # Install in
Then in the main function you can create a thread object and pass it the runnable object.
main.cpp
#include"MyRunnable.hpp"intmain(){MyRunnablemyRunnable;// Create a runnable objectMTL::MTLThreadthread(myRunnable);// Create a thread object and pass it the runnable object.thread.run();//Start the Threadstd::this_thread::sleep_for(std::chrono::milliseconds(10000));//Sleep for 10 secondsthread.suspend();//Suspend the threadstd::this_thread::sleep_for(std::chrono::milliseconds(5000));//Sleep for 5 secondsthread.resume();//Resume the threadintcounter=0;while(thread.isRunning()){//Wait 10 seconds in the main threadif(counter==10){break;}std::this_thread::sleep_for(std::chrono::seconds(1));std::cout<<"Counter: "<<counter<<std::endl;counter++;}return0;}
Worker Threads Class
The Worker Thread class is a runnable with 2 more features:
- It contains a message queue
- Has to implement the function to process the messages
This class can be used to create a runnable to pass to a Thread object that receive an input message and elaborate it.
A simple example of implementation of the Worker Thread Class can be the following:
MyWorkerThread.hpp
classMyWorker:publicMTL::MTLWorkerThread{public:MyWorker()=default;virtual~MyWorker()=default;virtualvoidprocessMessage(MTL::Messagemessage)override{//When the message is dequeued it is passed to this functionint*message_casted=static_cast<int*>(message.get());//Cast the message to the correct typestd::cout<<"MyWorker::processMessage("<<*message_casted<<")"<<std::endl;//Do somenthings with the message}};
Thread Manager Class
The Thread Manager class is a Runnable that can be used to manage threads.
If you want to manage a group of threads all togheter you can create a Thread Manager object and pass it to a Thread object.
The threads can be passed to the Thread Manager object and the Thread Manager object will manage the threads.
So if you have multiple tasks or multiple workers, you can collect them in a thread manager and all the action performed to the thread manager(start, stop, suspend, resume, etc) will be performed to all the threads.
A Simple example of usage can be the following:
#include"MTL.h"
#include"MyWorker.hpp"intmain(){// Three different kind of workersMyWorker1myWorker1;MyWorker2myWorker2;MyWorker3myWorker3;MTL::MTLThreadManagerthreadManager;//Create a thread manager objectthreadManager.addThread(std::make_unique<MTL::MTLThread>(myWorker1));//Add a thread to the thread managerthreadManager.addThread(std::make_unique<MTL::MTLThread>(myWorker2));//Add a thread to the thread managerMTL::MTLThreadthread(threadManager);//Create a thread object and pass it the thread managerthread.run();//Start the thread manager that will start all the threadsstd::this_thread::sleep_for(std::chrono::milliseconds(5000));//Let him work for 5 secondsthread.suspend();//Suspend the thread manager that will suspend all the threadsstd::this_thread::sleep_for(std::chrono::milliseconds(5000));//Let suspended for 5 secondsthread.resume();//Resume the thread manager that will resume all the threadsthreadManager.addThread(std::make_unique<MTL::MTLThread>(myWorker3));//Add a thread to the thread managerthread.clean_exit();// Clean Exit the thread manager that will clean exit all the threads while(thread.isRunning())//Wait for the thread manager to exit{std::this_thread::sleep_for(std::chrono::seconds(1));std::cout<<"Counter: "<<counter<<std::endl;counter++;}std::cout<<"Thread Manager Terminated"<<std::endl;return0;}
Thread Pool Class
The Thread Pool class is a Thread Manager that can be used to create a fixed number of workers that execute the same task. When a message is enqueued to the thread pool, the message is passed to a workers.
This kind of thread manager is useful when you have to implement a multi threaded consumer.
An example of usage can be the following:
#include"MTL.h"intmain(){MyWorker1myWorker1;//Create a workerMTL::MTLThreadPoolthreadPool(myWorker1,4);//Create a thread pool with 4 workersMTL::MTLThreadthread(threadPool);//Create a thread object and pass it the thread poolthread.run();//Start the thread pool that will start all the workersfor(inti=0;i<10;++i)// Enqueue 10 messages{std::cout<<"Inject Message "<<i<<std::endl;MTL::Messagemessage(newint(i));threadPool.onMessage(message);}std::this_thread::sleep_for(std::chrono::milliseconds(10000));//Let him work for 10 secondsstd::cout<<"Suspend"<<std::endl;thread.suspend();// Suspend the thread pool that will suspend all the workersfor(inti=10;i<20;++i)// Enqueue 10 messages{std::cout<<"Inject Message "<<i<<std::endl;MTL::Messagemessage(newint(i));threadPool.onMessage(message);}std::this_thread::sleep_for(std::chrono::milliseconds(5000));//Let Suspeded for 5 secondsstd::cout<<"Resume"<<std::endl;thread.resume();// Resume the thread pool that will resume all the workersstd::cout<<"Exit"<<std::endl;thread.clean_exit();// Clean Exit the thread pool that will clean exit all the workers while(thread.isRunning())//Wait for the thread pool to exit{std::this_thread::sleep_for(std::chrono::seconds(1));std::cout<<"Counter: "<<counter<<std::endl;counter++;}std::cout<<"Thread Pool Terminated"<<std::endl;return0;}
Shared Object and Shared Memory Classes
The Shared Object class is a class that manage a shared memory object and can be used in a thread safe manner.
The Shared Memory class is a class that manage shared objects and can be used in a thread safe manner.
The two classes can be used to create a thread safe memory to share data between threads.
The following is a simple example of application:
MySharedObject.hpp
#include"MTL.h"classMySharedObject:publicMTL::MTLSharedObject{public:MySharedObject(unsignedintid):MTLSharedObject(id){std::cout<<"MySharedObject::MySharedObject()"<<std::endl;value=0;}~MySharedObject(){std::cout<<"MySharedObject::~MySharedObject()"<<std::endl;}intgetValue(){returnvalue;}voidsetValue(intv){value=v;}private:intvalue;//The value of the shared object};
MyRunnable.hpp
#include"MTL.h"classMyRunnable:publicMTL::MTLRunnable{public:MyRunnable(MTL::MTLSharedMemory*sharedMemory):m_sharedMemory(sharedMemory){};virtual~MyRunnable()=default;voidrun(MTL::MTLThreadInterface*threadIf){std::cout<<"Hello World!"<<std::endl;intcounter=0;while(true){if(threadIf->getThreadState()==MTL::E_MTLThreadState::STOPPED){std::cout<<"Stopped Thread Id: "<<std::this_thread::get_id()<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(1000));}elseif(threadIf->getThreadState()==MTL::E_MTLThreadState::SUSPENDED){std::cout<<"Suspended Thread Id: "<<std::this_thread::get_id()<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(1000));}elseif(threadIf->getThreadState()==MTL::E_MTLThreadState::EXITED){std::cout<<"Exited Thread Id: "<<std::this_thread::get_id()<<std::endl;break;}elseif(threadIf->getThreadState()==MTL::E_MTLThreadState::RUNNING){// when running this runnable increment the shared memory value by 1MySharedObject&myObj=dynamic_cast<MySharedObject&>(m_sharedMemory->getSharedObjectById(1));std::cout<<"Thread Id: "<<std::this_thread::get_id()<<" starting Value: "<<myObj.getValue()<<" end Value: "<<myObj.getValue()+1<<std::endl;myObj.setValue(myObj.getValue()+1);m_sharedMemory->releaseSharedObject(myObj);std::this_thread::sleep_for(std::chrono::milliseconds(10));counter++;}}}voidstop(){std::cout<<"Stopping"<<std::endl;}voidsuspend(){std::cout<<"Suspending"<<std::endl;}voidresume(){std::cout<<"Resuming"<<std::endl;}voidclean_exit(){std::cout<<"Exiting"<<std::endl;}voidforce_exit(){std::cout<<"Force Exiting"<<std::endl;}private:MTL::MTLSharedMemory*m_sharedMemory;};
main.cpp
#include"MTL.h"
#include"MySharedObject.hpp"
#include"MyRunnable.hpp"intmain(){/**
* This example demostrate how the shared memory and shared object classes
* can be used to share data between threads. Without alter the atomicity
* of the execution over the memory objects
**/std::unique_ptr<MySharedObject>myObj(newMySharedObject(1));//Create a shared objectMTL::MTLSharedMemorysharedMemory;//Create a shared memorysharedMemory.addSharedObject(std::move(myObj));//Add the shared object to the shared memoryMyRunnablemyRunnable1(&sharedMemory);//Create a runnableMyRunnablemyRunnable2(&sharedMemory);//Create another runnableMTL::MTLThreadthread1(myRunnable1);//Create a thread from the runnableMTL::MTLThreadthread2(myRunnable2);//Create another thread from the runnablethread1.run();//Run the threadthread2.run();//Run the threadstd::this_thread::sleep_for(std::chrono::milliseconds(1000));//Let the threads run for 1 secondstd::cout<<"Suspend Thread 1"<<std::endl;thread1.suspend();//Suspend the thread 1std::this_thread::sleep_for(std::chrono::milliseconds(1000));//Let the thread 2 runs for 1 secondstd::cout<<"Resume Thread 1"<<std::endl;thread1.resume();//Resume the thread 1std::this_thread::sleep_for(std::chrono::milliseconds(1000));//Let the threads run for 1 secondstd::cout<<"Exit the Threads"<<std::endl;thread1.clean_exit();//Exit the thread 1thread2.clean_exit();//Exit the thread 2thread1.join();//Wait for the thread 1 to finishthread2.join();//Wait for the thread 2 to finishstd::cout<<"Threads Joined"<<std::endl;return0;}
Task Class
The task class is a wrapper of C++ task class.
It needs a runnable task to be executed.
A simple example can be the following one:
MyRunnableTask.hpp
#include<iostream>
#include"MTL.h"classMyRunnableTask:publicMTL::MTLRunnableTask{public:MyRunnableTask()=default;virtual~MyRunnableTask()=default;std::shared_ptr<void>run(MTL::MTLTaskInterface*interface=nullptr){std::cout<<"Hello World!"<<std::endl;std::cout<<"Simulating Working for 3 seconds"<<std::endl;for(inti=0;i<3;i++){std::cout<<"."<<std::flush;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout<<std::endl;std::cout<<"This is a Task that return 1"<<std::endl;inti=1;std::shared_ptr<void>result(newint(i));returnresult;}};
main.cpp
#include"MyRunnableTask.hpp"intmain(){MyRunnableTaskmyRunnableTask;//Create a runnable taskMTL::MTLTasktask(myRunnableTask);//Create a task from the runnable tasktask.run();//Run the taskstd::shared_ptr<void>result=task.getResult();//Get the result of the taskstd::cout<<"Result: "<<*(static_cast<int*>(result.get()))<<std::endl;//Print the resultreturn0;}
Ordered Task and Task Flow Classes
The Ordered Task class is derived from Task class and has a list of predecessors and a list of successors.
The Task Flow class is a class that allow to execute ordered task in a consistent manner and return the final result.
A simple example can be the following one:
DerivedTasks.hpp
#include<iostream>
#include"MTL.h"//Return 2classVar2Task:publicMTL::MTLRunnableTask{public:Var2Task()=default;virtual~Var2Task()=default;std::shared_ptr<void>run(MTL::MTLTaskInterface*interface=nullptr){MTL::MTLOrderedTaskInterface*orderedTaskIf=dynamic_cast<MTL::MTLOrderedTaskInterface*>(interface);std::cout<<orderedTaskIf->getTaskName()<<": "<<"Start Task "<<std::endl<<std::flush;std::cout<<orderedTaskIf->getTaskName()<<": "<<"Simulating Working for 3 seconds"<<std::endl<<std::flush;for(inti=0;i<3;i++){std::cout<<"."<<std::flush;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout<<std::endl;autopreviousResult=orderedTaskIf->getPredecessorsResults();std::shared_ptr<void>result(newint(2));std::cout<<orderedTaskIf->getTaskName()<<": "<<"Result = "<<*((int*)(result.get()))<<std::endl<<std::flush;returnresult;}};// Sum predecessorsclassSumTask:publicMTL::MTLRunnableTask{public:SumTask()=default;virtual~SumTask()=default;std::shared_ptr<void>run(MTL::MTLTaskInterface*interface=nullptr){MTL::MTLOrderedTaskInterface*orderedTaskIf=dynamic_cast<MTL::MTLOrderedTaskInterface*>(interface);std::cout<<orderedTaskIf->getTaskName()<<": "<<"Start Task "<<std::endl<<std::flush;std::cout<<orderedTaskIf->getTaskName()<<": "<<"Simulating Working for 3 seconds"<<std::endl<<std::flush;for(inti=0;i<3;i++){std::cout<<"."<<std::flush;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout<<std::endl;autopredecessorsResults=orderedTaskIf->getPredecessorsResults();std::shared_ptr<void>result(newint(0));if(predecessorsResults.empty()){std::shared_ptr<void>result(newint(0));returnresult;}else{autoit=predecessorsResults.begin();std::shared_ptr<void>result((int*)it->second.get());++it;for(it;it!=predecessorsResults.end();++it){int*previousResult=(int*)(it->second.get());int*currentResult=(int*)(result.get());*currentResult=(*currentResult)+(*previousResult);}std::cout<<orderedTaskIf->getTaskName()<<": "<<"Result = "<<*((int*)(result.get()))<<std::endl<<std::flush;returnresult;}}};// Multiply predecessorsclassMulTask:publicMTL::MTLRunnableTask{public:MulTask()=default;virtual~MulTask()=default;std::shared_ptr<void>run(MTL::MTLTaskInterface*interface=nullptr){MTL::MTLOrderedTaskInterface*orderedTaskIf=dynamic_cast<MTL::MTLOrderedTaskInterface*>(interface);std::cout<<orderedTaskIf->getTaskName()<<": "<<"Start Task "<<std::endl<<std::flush;std::cout<<orderedTaskIf->getTaskName()<<": "<<"Simulating Working for 3 seconds"<<std::endl<<std::flush;for(inti=0;i<3;i++){std::cout<<"."<<std::flush;std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout<<std::endl<<std::flush;autopredecessorsResults=orderedTaskIf->getPredecessorsResults();std::shared_ptr<void>result(newint(0));if(predecessorsResults.empty()){std::shared_ptr<void>result(newint(0));returnresult;}else{autoit=predecessorsResults.begin();std::shared_ptr<void>result((int*)it->second.get());++it;for(it;it!=predecessorsResults.end();++it){int*previousResult=(int*)(it->second.get());int*currentResult=(int*)(result.get());*currentResult=(*currentResult)*(*previousResult);}std::cout<<orderedTaskIf->getTaskName()<<": "<<"Result = "<<*((int*)(result.get()))<<std::endl<<std::flush;returnresult;}}};
main.cpp
#include"DerivedTasks.hpp"// This Example simulate the execution of the following expression ((a+b)+c)*(d+e) where a,b,c,d,e are value 2intmain(){std::cout<<"Running Example 7 for MTL Version "<<MTL_VERSION_MAJOR<<"."<<MTL_VERSION_MINOR<<"."<<MTL_VERSION_PATCH<<std::endl;Var2Taskvar2Task;// Task that return 2SumTasksum2Task;// Task that return the sum of the predecessors MulTaskmul2Task;// Task that return the multiplication of the predecessorsautotaskA=std::make_shared<MTL::MTLOrderedTask>("a",var2Task);// Task that return 2autotaskB=std::make_shared<MTL::MTLOrderedTask>("b",var2Task);// Task that return 2autotaskC=std::make_shared<MTL::MTLOrderedTask>("c",var2Task);// Task that return 2autotaskD=std::make_shared<MTL::MTLOrderedTask>("d",var2Task);// Task that return 2autotaskE=std::make_shared<MTL::MTLOrderedTask>("e",var2Task);// Task that return 2autotaskF=std::make_shared<MTL::MTLOrderedTask>("f",sum2Task);// Task that return the sum of the predecessorsautotaskG=std::make_shared<MTL::MTLOrderedTask>("g",sum2Task);// Task that return the sum of the predecessorsautotaskH=std::make_shared<MTL::MTLOrderedTask>("h",sum2Task);// Task that return the sum of the predecessorsautotaskI=std::make_shared<MTL::MTLOrderedTask>("i",mul2Task);// Task that return the multiplication of the predecessorsMTL::MTLTaskFlowtaskFlow;// Task FlowtaskFlow.precede(taskA,taskF);// Task A precede Task FtaskFlow.precede(taskB,taskF);// Task B precede Task FtaskFlow.precede(taskC,taskG);// Task C precede Task GtaskFlow.precede(taskF,taskG);// Task F precede Task GtaskFlow.precede(taskD,taskH);// Task D precede Task HtaskFlow.precede(taskE,taskH);// Task E precede Task HtaskFlow.precede(taskG,taskI);// Task G precede Task ItaskFlow.precede(taskH,taskI);// Task H precede Task ItaskFlow.run();// Run the task flowstd::shared_ptr<void>result=taskFlow.getResult();// Get the result of the task flowstd::cout<<"Result: "<<*(static_cast<int*>(result.get()))<<std::endl;// Print the resultreturn0;}
git clone https://github.com/google/googletest.git # Dowload the Google Test repositorycd googletest # Main directory of the cloned repository.mkdir-p build # Create a directory to hold the build output.cd build # Move into the build directory.
cmake .. # Generate native build scripts for GoogleTest.
make # Compilesudo make install# Install in /usr/local/ by default
Installation
git clone https://github.com/ZigRazor/MTL.git # Dowload the MTL repositorycd MTL # Main directory of the cloned repository.mkdir-p build # Create a directory to hold the build output.cd build # Move into the build directory.
cmake .. # Generate native build scripts for MTL.
make # Compilesudo make install# Install in /usr/local/ by default
Usage
The only things you have to do to get library in you project are to:
- Include the header file MTL.h in your project.
- Link the library MTL.so to your project.
How to contribute
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Don't forget to give the project a star! Thanks again!
Before these steps please read the Contributing Guidelines and the Code of Conduct
Fork the Project
Create your Feature Branch (git checkout -b feature/AmazingFeature)
Commit your Changes (git commit -m 'Add some AmazingFeature')
Push to the Branch (git push origin feature/AmazingFeature)