integrating threads with OpenGL

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", &chops[0], &chops[1]); 
Philosopher two("2", &chops[1], &chops[2]); 
Philosopher three("3", &chops[2], &chops[3]);
Philosopher four("4", &chops[3], &chops[4]);
 Philosopher five("5", &chops[4], &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(&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(&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( &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, &pfd)) == false ) 
    { 
        MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); 
        return false; 
    } 
 
	//sets the pixel format if its ok. 
    if (SetPixelFormat(hdc, pixelformat, &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, &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, &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, &PhiloInit, (void *)&one, 0, 0);
    philo_guys[1] = (HANDLE)_beginthreadex(0, 0, &PhiloInit, (void *)&two, 0, 0);
    philo_guys[2] = (HANDLE)_beginthreadex(0, 0, &PhiloInit, (void *)&three, 0, 0);
    philo_guys[3] = (HANDLE)_beginthreadex(0, 0, &PhiloInit, (void *)&four, 0, 0);
    philo_guys[4] = (HANDLE)_beginthreadex(0, 0, &PhiloInit, (void *)&five, 0, 0);

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

	while (true)					
    {							
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
		    if (msg.message==WM_QUIT)
				break;
			TranslateMessage (&msg);							
			DispatchMessage (&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
";
        Sleep(2000);
        Eat();
    
}

void Philosopher::Eat()
{


        if (!has_eaten){
			state = 1;

            cout << name << " is attempting to eat
";
            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
";
				
                Think();
            }
            else
            {
                cout << name << " is now eating
";
				state = 2;
                Sleep(5000); //eating time 
                ReleaseMutex(*left_chop); ReleaseMutex(*right_chop); //let go of chopstick
                cout << name << " has finished eating <<<<<<----
";
				state = 3;
                has_eaten = true;
            }
        }
        else
            cout << name << " has already eaten
";

		
		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

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.

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