PDA

View Full Version : integrating threads with OpenGL



wikaman1
04-17-2012, 05:26 PM
I'm trying to make a very simple openGL animation of the classic dining philosophers problem. (http://en.wikipedia.org/wiki/Dining_philosophers_problem)

I have the basic program working with just C++ and cout's etc. but when I try and represent this using simple primitives things go wrong. As you can see when you run it, 3 (sometimes 4) of the philosophers eat at the same time which is impossible and sometimes one or more doesn't finish at all. Red means thinking, blue means eating and green means finished eating. here is my code:

main:



#include <windows.h>
#include <stdio.h>
#include <mmsystem.h>
#include <math.h>
#include "philosopher.h"
#include <gl/gl.h>
#include <gl/glu.h>


#define COLOUR_DEPTH 16 //Colour depth
#define AMOUNT 5

typedef struct Mouse
{
int x,y;
}Mouse;




HWND ghwnd;
HDC ghdc;
HGLRC ghrc; //hardware RENDERING CONTEXT
HINSTANCE ghInstance;
RECT gRect;
RECT screenRect;


int S_WIDTH = 800; //client area resolution
int S_HEIGHT = 600;

HANDLE * chops = new HANDLE[AMOUNT];
HANDLE * philo_guys = new HANDLE[AMOUNT];
//chops

Philosopher one("1", &amp;chops[0], &amp;chops[1]);
Philosopher two("2", &amp;chops[1], &amp;chops[2]);
Philosopher three("3", &amp;chops[2], &amp;chops[3]);
Philosopher four("4", &amp;chops[3], &amp;chops[4]);
Philosopher five("5", &amp;chops[4], &amp;chops[0]);

bool keys[256];


Mouse MousePos;


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
void DrawScene();
void DrawTable();



HWND CreateOurWindow(LPSTR strWindowName, int width, int height, DWORD dwStyle, bool bFullScreen, HINSTANCE hInstance)
{
HWND hwnd;

WNDCLASS wcex;

memset(&amp;wcex, 0, sizeof(WNDCLASS));
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "FirstWindowClass";


RegisterClass(&amp;wcex);// Register the class

dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

ghInstance = hInstance;// Assign our global hInstance to the window's hInstance

//Set the Client area of the window to be our resolution.
RECT glwindow;
glwindow.left = 0;
glwindow.right = width;
glwindow.top = 0;
glwindow.bottom = height;

AdjustWindowRect( &amp;glwindow, dwStyle, false);

//Create the window
hwnd = CreateWindow( "FirstWindowClass",
strWindowName,
dwStyle,
0,
0,
glwindow.right - glwindow.left,
glwindow.bottom - glwindow.top,
NULL,
NULL,
hInstance,
NULL
);

if(!hwnd) return NULL;// If we could get a handle, return NULL

ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
SetFocus(hwnd);

return hwnd;
}

bool SetPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd = {0};
int pixelformat;

pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); // Set the size of the structure
pfd.nVersion = 1; // Always set this to 1
// Pass in the appropriate OpenGL flags
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.dwLayerMask = PFD_MAIN_PLANE; // standard mask (this is ignored anyway)
pfd.iPixelType = PFD_TYPE_RGBA; // RGB and Alpha pixel type
pfd.cColorBits = COLOUR_DEPTH; // Here we use our #define for the color bits
pfd.cDepthBits = COLOUR_DEPTH; // Ignored for RBA
pfd.cAccumBits = 0; // nothing for accumulation
pfd.cStencilBits = 0; // nothing for stencil

//Gets a best match on the pixel format as passed in from device
if ( (pixelformat = ChoosePixelFormat(hdc, &amp;pfd)) == false )
{
MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK);
return false;
}

//sets the pixel format if its ok.
if (SetPixelFormat(hdc, pixelformat, &amp;pfd) == false)
{
MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
return false;
}

return true;
}

void ResizeGLWindow(int width, int height)// Initialize The GL Window
{
if (height==0)// Prevent A Divide By Zero error
{
height=1;// Make the Height Equal One
}

glViewport(0,0,width,height);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

//calculate aspect ratio
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height, 1 ,150.0f);

glMatrixMode(GL_MODELVIEW);// Select The Modelview Matrix
glLoadIdentity();// Reset The Modelview Matrix
}

void InitializeOpenGL(int width, int height)
{
ghdc = GetDC(ghwnd);// sets global HDC

if (!SetPixelFormat(ghdc))// sets pixel format
PostQuitMessage (0);


ghrc = wglCreateContext(ghdc); // creates rendering context from hdc
wglMakeCurrent(ghdc, ghrc); // Use this HRC.

ResizeGLWindow(width, height); // Setup the Screen
}



void Init(HWND hwnd)
{
ghwnd = hwnd;
GetClientRect(ghwnd, &amp;gRect); //get rect into our handy global rect
InitializeOpenGL(gRect.right, gRect.bottom);// initialise openGL

//OpenGL settings
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations

//Also, do any other setting ov variables here for your app if you wish.
}

void DrawScene()
{
int i=0;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Clear The Screen And The Depth Buffer
glLoadIdentity();// load Identity Matrix

//set camera looking down the -z axis, 6 units away from the center
gluLookAt(0, 0, 6, 0, 0, 0, 0, 1, 0); //Where we are, What we look at, and which way is up


glColor3f(1.0,1.0,1.0);
DrawTable();
glClearColor(0,0,0,0);
glPushMatrix();
one.Draw(-2,-2,0,45,0,0,-1);
glPopMatrix();

glPushMatrix();
two.Draw(2,-2,0,45,0,0,1);
glPopMatrix();

glPushMatrix();
three.Draw(2,2,0,135,0,0,1);
glPopMatrix();

glPushMatrix();
four.Draw(-2,2,0,135,0,0,-1);
glPopMatrix();

glPushMatrix();
five.Draw(-2,0,0,90,0,0,-1);
glPopMatrix();



SwapBuffers(ghdc);// Swap the frame buffers.

}

void DrawTable()
{
glScalef(0.5,0.5,0.5);
glBegin (GL_QUADS);//Begin drawing state
glVertex3f(-1.0f, -1.0f, 0.0f); // The bottom left corner
glVertex3f(-1.0f, 1.0f, 0.0f); // The top left corner
glVertex3f(1.0f, 1.0f, 0.0f); // The top right corner
glVertex3f(1.0f, -1.0f, 0.0f); // The bottom right corner
glEnd();//end drawing
}


void Cleanup()
{
if (ghrc)
{
wglMakeCurrent(NULL, NULL); // free rendering memory
wglDeleteContext(ghrc); // Delete our OpenGL Rendering Context
}

if (ghdc)
ReleaseDC(ghwnd, ghdc); // Release our HDC from memory

UnregisterClass("FirstWindowClass", ghInstance);// Free the window class

PostQuitMessage (0); // Post a QUIT message to the window
}



LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{


switch (message)
{
case WM_CREATE:
break;

case WM_SIZE:
//resize the open gl window when the window is resized
ResizeGLWindow(LOWORD(lParam),HIWORD(lParam));
GetClientRect(hwnd, &amp;gRect);
break;

case WM_KEYDOWN:
keys[wParam]=true;
break;

case WM_KEYUP:
keys[wParam]=false;
break;

case WM_MOUSEMOVE:
MousePos.x = LOWORD (lParam);
MousePos.y = HIWORD (lParam);
break;

case WM_PAINT:


break;

case WM_DESTROY:

PostQuitMessage(0);

break;
}

return DefWindowProc (hwnd, message, wParam, lParam);

}

unsigned int __stdcall PhiloInit(void * phil) //initiliaze eating
{

Philosopher * whichever = static_cast<Philosopher *>(phil);
whichever->Eat(); //eat
return NULL;
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int nCmdShow)
{
HWND hwnd;
MSG msg;



//initialise and create window
hwnd = CreateOurWindow("Dining Philosophers", S_WIDTH, S_HEIGHT, 0, false, hInstance);
if(hwnd == NULL) return true;

//initialise opengl and other settings
Init(hwnd);

for (int i = 0; i < AMOUNT; i++)
{
chops[i] = CreateMutex(0, 0, 0);
}



philo_guys[0] = (HANDLE)_beginthreadex(0, 0, &amp;PhiloInit, (void *)&amp;one, 0, 0);
philo_guys[1] = (HANDLE)_beginthreadex(0, 0, &amp;PhiloInit, (void *)&amp;two, 0, 0);
philo_guys[2] = (HANDLE)_beginthreadex(0, 0, &amp;PhiloInit, (void *)&amp;three, 0, 0);
philo_guys[3] = (HANDLE)_beginthreadex(0, 0, &amp;PhiloInit, (void *)&amp;four, 0, 0);
philo_guys[4] = (HANDLE)_beginthreadex(0, 0, &amp;PhiloInit, (void *)&amp;five, 0, 0);

for (int i = 0; i < AMOUNT; i++)
{
CloseHandle(chops[i]);
}

while (true)
{
if (PeekMessage(&amp;msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
break;
TranslateMessage (&amp;msg);
DispatchMessage (&amp;msg);
}

else
{
//any intensive proccessing for the app, do it here.
DrawScene();


}
}

return msg.wParam ;
}


philosopher.cpp:



#include "philosopher.h"
#include <windows.h>
#include <stdio.h>
#include <mmsystem.h>
#include <math.h>
#include <gl/gl.h>
#include <gl/glu.h>
using namespace std;

Philosopher::Philosopher()
{
state=1;
}


Philosopher::~Philosopher()
{

}


void Philosopher::Think()
{

cout << name << " is now thinking\n";
Sleep(2000);
Eat();

}

void Philosopher::Eat()
{


if (!has_eaten){
state = 1;

cout << name << " is attempting to eat\n";
if (WaitForSingleObject(*left_chop, 2000) == WAIT_TIMEOUT || WaitForSingleObject(*right_chop, 2000) == WAIT_TIMEOUT)
{
cout << name << " can't eat at this point in time. " << name << " decides to think\n";

Think();
}
else
{
cout << name << " is now eating\n";
state = 2;
Sleep(5000); //eating time
ReleaseMutex(*left_chop); ReleaseMutex(*right_chop); //let go of chopstick
cout << name << " has finished eating <<<<<<----\n";
state = 3;
has_eaten = true;
}
}
else
cout << name << " has already eaten\n";


return;

}

void Philosopher::Draw(GLfloat x, GLfloat y, GLfloat z, GLfloat Angle, GLfloat Rx, GLfloat Ry, GLfloat Rz)
{

if (state == 1)
glColor3f(1.0,0.0,0.0);
if (state == 2)
glColor3f(0.0,0.0,1.0);
if (state == 3)
glColor3f(0.0,1.0,0.0);


glTranslatef(x,y,z);
glRotatef(Angle,Rx,Ry,Rz);
glScalef(0.3f,0.7f,0.7f);
glBegin (GL_TRIANGLES);//Begin drawing state
glVertex3f( 0.0, 1.0, 0.0);
glVertex3f(-1.0, 0.0, 0.0);
glVertex3f( 1.0, 0.0, 0.0);
glEnd();//end drawing

}


philosopher.h



#ifndef __H_PHILOSOPHER__
#define __H_PHILOSOPHER__

#include <iostream>
#include <process.h>
#include <windows.h>
#include <string>
#include <time.h>
#include <gl/gl.h>
#include <gl/glu.h>

using namespace std;

class Philosopher
{
private:
HANDLE * left_chop, * right_chop;
string name;


public:
Philosopher();
~ Philosopher();
Philosopher(string phil_name, HANDLE * left_chop_handle, HANDLE * right_chop_handle) :
name(phil_name), left_chop(left_chop_handle), right_chop(right_chop_handle){ has_eaten = false; }
void Eat();
bool has_eaten;
void Think();
void Draw(GLfloat x, GLfloat y, GLfloat z, GLfloat Angle, GLfloat Rx, GLfloat Ry, GLfloat Rz);
int state;
};

#endif



thanks for any help

carsten neumann
04-18-2012, 01:06 PM
The member variable Philosopher::state is modified concurrently by the threads and read by your DrawScene function, but there is no mutex or other thread synchronization to protect access to it.

Also, have you seen http://www.opengl.org/wiki/OpenGL_and_multithreading?

One last point: since Philosopher::Eat and Philosopher::Think call each other repeatedly there is a chance you run out of stack space (probably not in practice with only 5 philosophers and the long sleeps). You could avoid that if your thread function would use a loop that terminates once a philosopher has eaten.

wikaman1
04-19-2012, 07:48 PM
thanks for the reply, I've been trying to add a mutex to protect that member variable but I'm still getting unpredictable (and wrong) results.

I added a private handle "State_Mutex" to the class.

and put
State_Mutex = CreateMutex(NULL, false, NULL); in the constructor

and put
WaitForSingleObject(State_Mutex, INFINITE);
state = state + 1;
ReleaseMutex(State_Mutex); each time the state is changed.


I also tried using a mutex class but that didn't seem to work either...
what am I doing wrong? Thanks for any help