I load a dll with win32 LoadLibrary, and when I am done with it, I call FreeLibrary, destorying all the memory allocated in the dll ect... Actually, memory leak problem only occures with std containers. It seems they are not willing to release their memory on destroy. Here is the code that leaks.
namespace ToolKit
{
class Game : public GamePlugin
{
public:
void Init(ToolKit::Main* master);
void Destroy();
void Frame(float deltaTime, Viewport* viewport);
void Resize(int width, int height);
void Event(SDL_Event event);
std::vector<int> point; // If I remove this line, no leaks are reported.
};
}
extern "C" TK_GAME_API ToolKit::Game * __stdcall GetInstance()
{
return new ToolKit::Game(); // Instance is deleted in the caller process than FreeLibrary() is called.
}
All functions are no-op in GamePlugin, and the process is not reporting any memory issue if there is no std container. I trap the leak in here. For the completion, I am sharing my standart CRT memory dump code.
int main(int argc, char* argv[])
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
return ToolKit_Main(argc, argv);
}
Load and unload code for the dll
void PluginManager::Load(const String& name)
{
HINSTANCE hinstLib;
TKPROC ProcAdd;
BOOL fRunTimeLinkSuccess = FALSE;
String dllName = name;
hinstLib = LoadLibrary(dllName.c_str());
if (hinstLib != NULL)
{
m_moduleHandle = (void*)hinstLib;
ProcAdd = (TKPROC)GetProcAddress(hinstLib, "GetInstance");
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
m_plugin = (ProcAdd)();
m_plugin->Init(ToolKit::Main::GetInstance());
}
}
if (!fRunTimeLinkSuccess)
{
m_reporterFn("Can not load plugin module " + dllName);
}
}
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
SafeDel(m_plugin);
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
Inorder to add some more clarification to the question, I am describing the program flow here:
PluginManager::Load loads the dll
GetInstance function is fetched from the dll
GetInstance return a plugin instance and it is stored in m_plugin
PluginManager::Unload deletes m_plugin and free the dll.
Here is the minimalcase that reproduces the leak.
Process side:
#include <stdio.h>
#include <cstdlib>
#include <crtdbg.h>
#include <string>
#include <functional>
#include <Windows.h>
#include "Plugin.h"
using namespace std;
class PluginManager
{
public:
void Load(const string& plugin);
void Unload();
public:
GamePlugin* m_plugin = nullptr;
void* m_moduleHandle = nullptr;
};
typedef GamePlugin* (__cdecl* TKPROC)();
void PluginManager::Load(const string& name)
{
HINSTANCE hinstLib;
TKPROC ProcAdd;
hinstLib = LoadLibrary(name.c_str());
if (hinstLib != NULL)
{
m_moduleHandle = (void*)hinstLib;
ProcAdd = (TKPROC)GetProcAddress(hinstLib, "GetInstance");
if (NULL != ProcAdd)
{
m_plugin = (ProcAdd)();
m_plugin->Init();
}
}
}
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
delete m_plugin;
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
int main(int argc, char* argv[])
{
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
PluginManager* pm = new PluginManager();
pm->Load("plugin.dll");
pm->Unload();
delete pm;
return 0;
}
Plugin interface:
#pragma once
#ifdef _WIN32
# ifdef TK_EXPORTS
# define TK_GAME_API __declspec(dllexport)
# else
# define TK_GAME_API __declspec(dllimport)
# endif
#elif
# define TK_GAME_API
#endif
struct GamePlugin
{
virtual void Init() = 0;
virtual void Destroy() = 0;
virtual void Frame() = 0;
};
DLL side:
#define TK_EXPORTS
#include "Plugin.h"
#include <vector>
class Game : public GamePlugin
{
public:
void Init() {}
void Destroy() {}
void Frame() {}
std::vector<int> point;
};
extern "C" TK_GAME_API GamePlugin * __stdcall GetInstance()
{
return new Game();
}
Exactly the same, if we remove the std::vector<int> point, there is no leak.
As can be seen in the minimal case, I am creating the plugin instance in the dll however deleting it in the process / executable. Eventough the memory is being freed, it gets reported as leak because that memory allocated within the dll and not freed there. Wow what a hassle was that ....
There are better ways of doing this, but in case some one lives the same problem, here is a quick fix.
Process:
void PluginManager::Unload()
{
if (m_plugin)
{
m_plugin->Destroy();
m_plugin = nullptr; // Clear the memory in where it is allocated.
}
if (m_moduleHandle)
{
FreeLibrary((HINSTANCE)m_moduleHandle);
m_moduleHandle = nullptr;
}
}
DLL:
void Destroy() { delete this; } // Now CRT Debug is happy. No false leak repots.
Related
I use P/Invoke, not C++/CLI and am working on Visual Studio 2017.
My app is WPF.NET – 32 bit, written in C#. The app loads successfully its 32-bit DLL when I run it on my PCx64, but it crashes when I run it on a virtual machine (64 or 32). I think couldn't add the DLL...and don't know why.
My DLL is written on C++ and includes a class, with some functions written on Assembler. May be the problem comes from it?
MyClass.cpp:
int MyClass::Foo_1()
{
__asm
{
mov eax, 0
}
}
void MyClass::SetFoo_1()
{
resultEAX = new int;
int a = -1;
try
{
a = Foo_1();
}
catch (int e) { }
if (a == 0) {
*resultEAX = 0;
}
else {
*resultEAX = 1;
}
}
MyClass.h:
#ifndef MYCLASS_H
#define MYCLASS_H
class __declspec(dllexport) MyClass {
public:
int * resultEAX ;
int Foo1();
void SetFoo_1();
int GetEAX();
};
#endif
MyClassCaller.h:
extern "C" {
#endif
__declspec(dllexport) MyClass* Create();
__declspec(dllexport) void Dispose(MyClass* a_pObject);
__declspec(dllexport) void SetFoo_1(MyClass* a_pObject);
__eclspec(dllexport) int GetEAX(MyClass* a_pObject);
#ifdef __cplusplus
}
#endif
MyClassCaller.cpp:
Graphics* Create()
{
return new MyClass();
}
void Dispose(MyClass * a_pObject)
{
if (a_pObject != NULL)
{
delete a_pObject;
a_pObject = NULL;
}
}
void SetFoo_1(MyClass * a_pObject)
{
if (a_pObject != nullptr)
{
a_pObject->SetFoo_1();
}
}
int GetEAX(MyClass * a_pObject)
{
if (a_pObject != NULL)
{
return a_pObject->GetEAX();
}
return 0;
}
And I call the class from WPF.NET using managed C# code:
IntPtr pMyClass = MyClassHandling.Create();
Int32 a = 0;
Int64 b = 0;
long c = 0;
long rslt = 0;
try
{
MyClassHandling.SetFoo_1(pMyClass);
if (Environment.Is64BitOperatingSystem)
{
//"SysWOW64"
b = MyClassHandling.GetEAX(pMyClass);
//……
}
else
{
//"system32"
a = pMyClassHandling.GetEAX(pGraphics);
//…
}
MyClassHandling.Dispose (pGraphics);
pMyClass = IntPtr.Zero;
[DllImport("SomeAssemblerFunctions.dll")]
static public extern IntPtr Create();
[DllImport("SomeAssemblerFunctions.dll")]
static public extern void Dispose(IntPtr pGraphicsObject);
[DllImport("SomeAssemblerFunctions.dll", EntryPoint = "SetGraphicMode1")]
static public extern void SetFoo_1(IntPtr pGraphicsObject);
[DllImport("SomeAssemblerFunctions.dll", EntryPoint = "GetEAX")]
static public extern int GetEAX(IntPtr pGraphicsObject);
there will be a error after the application run a period of time at the line delete busMsg .I don't understand how the error came about.
#include "stdafx.h"
#include "queue.h"
#include <queue>
#include <iostream>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
CWinApp theApp;
using namespace std;
CRITICAL_SECTION m_csReceivedMsgQueue;
HANDLE m_hReceivedDataEvent;
HANDLE m_hEventStop;
queue<CBusMsg* > m_qReceivedMsgQueue;
class CBusMsg
{
public:
CBusMsg(WORD nAgentID, WORD nAgentExclue, BYTE* pBuf,int nLen)
{
m_nAgentID=nAgentID;
m_nAgentExclue=nAgentExclue;
m_nLen=nLen;
m_pBuf=new BYTE[m_nLen];
memcpy(m_pBuf,pBuf,nLen);
}
~CBusMsg()
{
if (m_pBuf!=NULL)
delete[] m_pBuf;
}
WORD m_nAgentID; //0:群发,其它:单播
WORD m_nAgentExclue;
BYTE* m_pBuf;
int m_nLen;
};
UINT SendhreadProc( LPVOID pParam)
{
HANDLE hWaitObjList[2]={m_hEventStop,m_hReceivedDataEvent};
bool haveData = false;
DWORD dwWaitRes;
for(;;)
{
if(haveData)//如果还有未处理的数据不等待内核对象直接返回
{
dwWaitRes=::WaitForMultipleObjects(2,hWaitObjList,FALSE,0);
}
else//如果没有未处理的数据不等待内核对象直接返回
{
dwWaitRes=::WaitForMultipleObjects(2,hWaitObjList,FALSE,200);
}
if((dwWaitRes-WAIT_OBJECT_0)==0)//,索引为0的内核对象被触发,也就是停止线程被触发
{
break;
}
haveData = false;
try
{
EnterCriticalSection(&m_csReceivedMsgQueue);
if(m_qReceivedMsgQueue.empty())
{
LeaveCriticalSection(&m_csReceivedMsgQueue);
continue;
}
CBusMsg*& busMsg = m_qReceivedMsgQueue.front();//取队首元素
m_qReceivedMsgQueue.pop();//弹出队首元素
if(NULL==busMsg)
{
LeaveCriticalSection(&m_csReceivedMsgQueue);
continue;
}
//ASSERT(busMsg->m_nLen<=0);
//pAgent->SetData(busMsg->m_pBuf,busMsg->m_nLen);
haveData = !m_qReceivedMsgQueue.empty();
LeaveCriticalSection(&m_csReceivedMsgQueue);
//**************There is the error********************/
delete busMsg;
//********************************************************/
//busMsg = NULL;
//ProcessData(pAgent);
}
catch(...)
{
LeaveCriticalSection(&m_csReceivedMsgQueue);
}
}
//delete pAgent;
return 0;
}
UINT PushData( LPVOID pParam)
{
BYTE pPacket[1000];
memset(pPacket,0,1000);
for (int i=0;i<100000;i++)
{
CBusMsg *pBusMsg;
pPacket[0]= i;
pBusMsg=new CBusMsg(i,0,pPacket,1000);
TRACE("请求向队列加消息\n");
EnterCriticalSection(&m_csReceivedMsgQueue);
TRACE("开始向队列加消息\n");
m_qReceivedMsgQueue.push(pBusMsg);
LeaveCriticalSection(&m_csReceivedMsgQueue);
SetEvent(m_hReceivedDataEvent);
}
return 0;
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
InitializeCriticalSection(&m_csReceivedMsgQueue);
HMODULE hModule = ::GetModuleHandle(NULL);
m_hReceivedDataEvent =::CreateEvent(NULL,FALSE,FALSE,NULL);
m_hEventStop=::CreateEvent(NULL,FALSE,FALSE,NULL);
//创建发送线程
CWinThread* m_pSendThread=AfxBeginThread(SendhreadProc,NULL,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);
m_pSendThread->m_bAutoDelete=FALSE;
m_pSendThread->ResumeThread();
CWinThread* processThread=AfxBeginThread(PushData,NULL,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);
processThread->m_bAutoDelete=FALSE;
processThread->ResumeThread();
char str[1000];
cin.getline(str,900);
return nRetCode;
}
The problem might be here
CBusMsg*& busMsg = m_qReceivedMsgQueue.front();//取队首元素
m_qReceivedMsgQueue.pop();//弹出队首元素
You take a reference to the pointer from the queue.
Then you pop the queue making the reference invalid.
Which will make the program crash some times.
This might make you happier
CBusMsg* busMsg = m_qReceivedMsgQueue.front();//取队首元素
m_qReceivedMsgQueue.pop();//弹出队首元素
Why does it crash, because the address that is referenced might be reused for something different before you get to
delete busMsg;
You release the lock just before the delete and in that time another can put something new in the queue using the same address for example if the queue is empty after the .pop. Then the delete doesn't delete the original referenced message but the new one it is referenced to.
i'm writing server with will handle client connection, but i have problem moving class with thread inside to vector, i know if i had only thread i can move it to vector with std::move(), but here i have thread inside a class and i'm getting a lot of errors because thread is non-movable object.
Core.cpp:
#define OS_LINUX
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <csignal>
#include <cstdlib>
#include <TCPServer/TCPServer.h>
#include <TCPServer/TCPServerConnection.h>
#include <ProcessManip/ProcessManip.h>
#include <Log/Log.h>
#include "ConnectionHandler.h"
//Global
bool TERMNINATE = false;//If true loops must end
const int PORT = 8822;
//Proto
void terminate(int sig);
void receive_message(ConnectionHandler *handler,string message);
using namespace std;
int main(int argc,char *argv[])
{
//Add Terminate handler
signal(SIGINT,terminate);
//Load configuration
Log::logDebug("Starting ...");
//Init
vector<ConnectionHandler> connections;
//Init modules
//Main
Log::logDebug("Running");
TCPServer server;
if(!server._bind(PORT))
return 1;
if(!server._listen())
return 2;
ProcessManip::cleanProcess(); /* Clean dead processes */
server.setBlocking(false);/* accept returns invalid socket if no connection */
Log::logDebug("Listening ...");
while(!TERMNINATE)
{
TCPServerConnection conn = server._accept();
if(!conn.isValid())
continue;
Log::logDebug((string)"Got connection from: "+conn.getAddress());/* Print IP address of client */
ConnectionHandler ch(&TERMNINATE,std::move(conn));
ch.setCallback(receive_message);
ch.start();
connections.push_back(std::move(ch)); //here is problem
/*connections.push_back(ConnectionHandler(&TERMNINATE,conn)); /* Add it to vector */
/*connections.back().setCallback(receive_message);
connections.back().start();*/
Log::logDebug("Connection added to vector");
}
server.setBlocking(true);
//Dispose
Log::logDebug("Stopping ...");
/*for(auto it = connections.begin();it!=connections.end();)
{
Log::logDebug((string)"Closed connection with: "+(*it).getConnection().getAddress());/* Print IP address of client */
//(*it).close(); /* Close connetion */
// it = connections.erase(it); /* Delete ConnectionHandler from vector */
// }
server._close();
Log::logDebug("Closed");
return 0;
}
void terminate(int sig)
{
//Change global value to true
TERMNINATE = true;
}
void receive_message(ConnectionHandler *handler,string message)
{
Log::logDebug((string)"Message ("+handler->getConnection().getAddress()+") : "+message);
}
ConnectionHandler.h
#ifndef EXT_CONNECTIONHANDLER
#define EXT_CONNECTIONHANDLER
#include <TCPServer/TCPServerConnection.h>
#include <thread>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
class ConnectionHandler
{
public:
ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection);
ConnectionHandler(TCPServerConnection pConnection);
~ConnectionHandler();
void start(); /* Start listening */
void stop(); /* Stop listening */
void close(); /* Stops listening + close connection */
void setConnection(TCPServerConnection pConnection);
TCPServerConnection getConnection();
void setCallback(function<void(ConnectionHandler*,string)> pFunction);
private:
bool *mainTerminate = NULL;
bool handler_terminate = false;
short status = 0;
TCPServerConnection connection;
bool needTerminate();
void run();
void waitForEnd();
function<void(ConnectionHandler*,string)> callback = NULL;
std::thread m_thread;
};
#endif
ConnectionHandler.cpp
#include "ConnectionHandler.h"
ConnectionHandler::ConnectionHandler(bool *pMainTerminate,TCPServerConnection pConnection)
{
this->mainTerminate = pMainTerminate;
this->connection = pConnection;
}
ConnectionHandler::ConnectionHandler(TCPServerConnection pConnection)
{
this->mainTerminate = NULL;
this->connection = pConnection;
}
ConnectionHandler::~ConnectionHandler()
{
this->close();
}
void ConnectionHandler::start()
{
m_thread = std::thread(&ConnectionHandler::run, this);
this->status = 1;
}
void ConnectionHandler::waitForEnd()
{
if(this->m_thread.joinable())
this->m_thread.join();
}
bool ConnectionHandler::needTerminate()
{
if(mainTerminate!=NULL)
return this->handler_terminate||*(this->mainTerminate);
else
return this->handler_terminate;
}
void ConnectionHandler::run()
{
string message = "";
string tmp = "";
this->connection.setBlocking(false); // So we can terminate any time
while(!this->needTerminate())
{
message = this->connection._receive();
if(message!="")
{
do
{
tmp = this->connection._receive();
message+=tmp;
}while(tmp!=""); /* If we get longer message than we can grab at one time */
this->connection._send(message); /* TODO Remove */
if(this->callback!=NULL)
this->callback(this,message);
message = "";
}
}
this->connection.setBlocking(true);
}
void ConnectionHandler::stop()
{
this->handler_terminate = true; /* Signals thread to stop */
this->waitForEnd();
this->status = 2;
}
void ConnectionHandler::close()
{
this->stop();
this->connection._close(); /* Close connection */
this->status = 3;
}
TCPServerConnection ConnectionHandler::getConnection()
{
return this->connection;
}
void ConnectionHandler::setConnection(TCPServerConnection pConnection)
{
this->connection = pConnection;
}
void ConnectionHandler::setCallback(function<void(ConnectionHandler*,string)> pFunction)
{
this->callback = pFunction;
}
Because this class violates the Rule Of Three, even if the std::thread issue gets addressed, other problems will likely appear; most likely taking the form of mysterious runtime bugs.
The compilation issue with std::thread is not the problem, it's merely a symptom of the real problem: this class should not be moved or copied. This class should only be new-constructed, then stuffed into a std::shared_ptr (or a reasonable facsimile) and stay there until it gets destroyed. Only the std::shared_ptr should be passed around, stuffed into a vector, etc...
I'm trying to write a C++ program that will communicate with different DB providers. Everything works fine now, however upon exiting I'm trying to close the connection and the disconnect command crashes. At the moment I'm testing with SQLite, so it probably belong to this specific one.
Do I have to close the connection to the SQLite upon program termination? I believe generally it is a good idea to release the resources the program is using, but what about this case? I mean in this case the SQLite DB file will be released back to the system when the program exits, so its not actually a loss.
Thank you.
[EDIT]
As requested I'm submitting the code that I use.
In DLL1 (database.h):
#ifndef DBMANAGER_DATABASE
#define DBMANAGER_DATABASE
class __declspec(dllexport) Database
{
public:
Database();
virtual ~Database();
static void *operator new(size_t size);
static void operator delete(void *ptr, size_t size);
};
#endif
In DLL1 (database.cpp)
#include <string>
#include "database.h"
Database::Database()
{
}
Database::~Database()
{
}
void *Database::operator new(std::size_t size)
{
return ::operator new( size );
}
void Database::operator delete(void *ptr, std::size_t size)
{
return ::operator delete( ptr );
}
In DLL2: (database_sqlite.h):
#ifndef DBMANAGER_SQLITE
#define DBMANAGER_SQLITE
class __declspec(dllexport) SQLiteDatabase : public Database
{
public:
SQLiteDatabase();
virtual ~SQLiteDatabase();
virtual int Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg);
protected:
void GetErrorMessage(int code, std::wstring &errorMsg);
private:
sqlite3 *m_db;
};
#endif
In DLL2: (database_sqlite.cpp)
#ifdef WIN32
#include <windows.h>
#pragma execution_character_set("utf-8")
#endif
#include <vector>
#include <string>
#include <sqlext.h>
#include "sqlite3.h"
#include "database.h"
#include "database_sqlite.h"
SQLiteDatabase::SQLiteDatabase() : Database()
{
}
SQLiteDatabase::~SQLiteDatabase()
{
sqlite3_close( m_db );
}
int SQLiteDatabase::Connect(const char *selectedDSN, std::vector<std::wstring> &errorMsg)
{
int result = 0;
std::wstring errorMessage;
int res = sqlite3_open( selectedDSN, &m_db );
if( res != SQLITE_OK )
{
// get error messages GetErrorMessage( res, errorMessage );
errorMsg.push_back( errorMessage );
result = 1;
}
else
{
res = sqlite3_exec( m_db, "PRAGMA foreign_keys = ON", NULL, NULL, NULL );
if( res != SQLITE_OK )
{
// get error message GetErrorMessage( res, errorMessage );
errorMsg.push_back( errorMessage );
result = 1;
}
}
return result;
}
In DLL3: (dialogs.cpp)
#ifdef __GNUC__
#pragma implementation "dialogs.h"
#endif
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#include <vector>
#include "sqlite3ext.h"
#include "database.h"
#include "database_sqlite.h"
#ifdef _WINDOWS
BOOL APIENTRY DllMain( HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
{
lpReserved = lpReserved;
hModule = hModule;
int argc = 0;
char **argv = NULL;
switch( fdwReason )
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#endif
extern "C" __declspec(dllexport) Database *DatabaseProfile(Database *db)
{
db = new SQLiteDatabase;
return db;
}
In main application (test_app.cpp):
#include "stdafx.h"
#include "database.h"
#include <windows.h>
#include <stdio.h>
typedef Database *(__cdecl *MYFUNC)(Database *);
class A
{
public:
A(Database *db)
{
m_db = NULL;
HINSTANCE hInst = LoadLibrary( TEXT( "dialogs.dll" ) );
MYFUNC func = (MYFUNC) GetProcAddress( hInst, "DatabaseProfile" );
m_db = func( db );
}
~A()
{
}
Database *GetDatabase()
{
return m_db;
}
private:
Database *m_db;
};
int _tmain(int argc, _TCHAR* argv[])
{
Database *db = NULL, *m_db;
A *a = new A( db );
m_db = a->GetDatabase();
delete m_db;
delete a;
return 0;
}
[EDIT]
Running this code I am getting the crash executing "delete "m_db" line. SQLite is compiled in from source code.
Any idea?
Thank you.
I am reading a message from a socket with C++ code and am trying to plot it interactively with matplotlib, but it seems Python code will block the main thread, no matter I use show() or ion() and draw(). ion() and draw() won't block in Python.
Any idea how to plot interactively with matplotlib in C++ code?
An example would be really good.
Thanks a lot.
You may also try creating a new thread that does the call to the
blocking function, so that it does not block IO in your main program
loop. Use an array of thread objects and loop through to find an unused
one, create a thread to do the blocking calls, and have another thread
that joins them when they are completed.
This code is a quick slap-together I did to demonstrate what I mean about
using threads to get pseudo asynchronous behavior for blocking functions...
I have not compiled it or combed over it very well, it is simply to show
you how to accomplish this.
#include <pthread.h>
#include <sys/types.h>
#include <string>
#include <memory.h>
#include <malloc.h>
#define MAX_THREADS 256 // Make this as low as possible!
using namespace std;
pthread_t PTHREAD_NULL;
typedef string someTypeOrStruct;
class MyClass
{
typedef struct
{
int id;
MyClass *obj;
someTypeOrStruct input;
} thread_data;
void draw(); //Undefined in this example
bool getInput(someTypeOrStruct *); //Undefined in this example
int AsyncDraw(MyClass * obj, someTypeOrStruct &input);
static void * Joiner(MyClass * obj);
static void * DoDraw(thread_data *arg);
pthread_t thread[MAX_THREADS], JoinThread;
bool threadRunning[MAX_THREADS], StopJoinThread;
bool exitRequested;
public:
void Main();
};
bool MyClass::getInput(someTypeOrStruct *input)
{
}
void MyClass::Main()
{
exitRequested = false;
pthread_create( &JoinThread, NULL, (void *(*)(void *))MyClass::Joiner, this);
while(!exitRequested)
{
someTypeOrStruct tmpinput;
if(getInput(&tmpinput))
AsyncDraw(this, tmpinput);
}
if(JoinThread != PTHREAD_NULL)
{
StopJoinThread = true;
pthread_join(JoinThread, NULL);
}
}
void *MyClass::DoDraw(thread_data *arg)
{
if(arg == NULL) return NULL;
thread_data *data = (thread_data *) arg;
data->obj->threadRunning[data->id] = true;
// -> Do your draw here <- //
free(arg);
data->obj->threadRunning[data->id] = false; // Let the joinThread know we are done with this handle...
}
int MyClass::AsyncDraw(MyClass *obj, someTypeOrStruct &input)
{
int timeout = 10; // Adjust higher to make it try harder...
while(timeout)
{
for(int i = 0; i < MAX_THREADS; i++)
{
if(thread[i] == PTHREAD_NULL)
{
thread_data *data = (thread_data *)malloc(sizeof(thread_data));
if(data)
{
data->id = i;
data->obj = this;
data->input = input;
pthread_create( &(thread[i]), NULL,(void* (*)(void*))MyClass::DoDraw, (void *)&data);
return 1;
}
return 0;
}
}
timeout--;
}
}
void *MyClass::Joiner(MyClass * obj)
{
obj->StopJoinThread = false;
while(!obj->StopJoinThread)
{
for(int i = 0; i < MAX_THREADS; i++)
if(!obj->threadRunning[i] && obj->thread[i] != PTHREAD_NULL)
{
pthread_join(obj->thread[i], NULL);
obj->thread[i] = PTHREAD_NULL;
}
}
}
int main(int argc, char **argv)
{
MyClass base;
base.Main();
return 0;
}
This way you can continue accepting input while the draw is occurring.
~~Fixed so the above code actually compiles, make sure to add -lpthread