Is this how to do SDL threads with objective c?

I found the SDL tutorial on threads to be a bit confusing and it didn’t mention things like making an autorelease pool for every sdl thread. I made a test program to demonstrate the way I think you’re supposed to do things.

Here is the Xcode project:http://lackeyccg.com/code/ThreadTest.zip
or for just the 1 source file:http://lackeyccg.com/code/MainThreadTest.mmI documented my code to explain why I was doing things. If anyone wants to take a look at it, please let me know if you have any suggestions or if you think I did something wrong.

Code:
/*
The purpose of this test program is to show how to use threads with SDL in an objective c environment.

This program has one main process which is in an endless loop. If there is not currently a worker thread,
the main process creates one. The worker thread should perform its tasks without making the main process
hang, which is the main reason for using a thread in the first place. When the worker thread process has
completed, the next time the main process checks if the worker is done, it will then see that it is and
launch another worker thread, and so on.
*/

#include <CoreFoundation/CFURL.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <Cocoa/Cocoa.h>
#include <SDL.h>
#include

using namespace std;

static SDL_threadID gMainThreadId;
static NSAutoreleasePool * gAutoReleasePool=NULL;
static SDL_Thread *gWorkerThread=NULL;
static SDL_mutex *gMutex = NULL;
static bool gIsWorkerThreadFinished = true;

void SetUpPool()
{
if(gAutoReleasePool)
[gAutoReleasePool release];
gAutoReleasePool = [[NSAutoreleasePool alloc] init];
}

void FreePool()
{
if(gAutoReleasePool)
[gAutoReleasePool release];
gAutoReleasePool=NULL;
}

void Quit()
{
if(gMutex)
SDL_DestroyMutex(gMutex);
FreePool();
}

void SomeFunctionThatUsesAutoReleasePool()
{// This function just retrieves the contents of a URL.
/// This function is run from the worker thread and uses the autorelease pool to test that it’s freed from the thread.
NSString urlString=@“http://lackeyccg.com/code/test.txt”;
NSError
error = nil;
NSString* text = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSASCIIStringEncoding error:&error];
if( text )
NSLog(@“Text=%@”, text);
else
NSLog(@“Error = %@”, error);
}

int SDLCALL ThreadWorkerFunction(void *data)
{
// Gotta make a autorelease pool for each thread if you explicitly or implicitly use things that use the pool.
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
if(!autoreleasepool)
exit(1);

Uint32 StartTime=SDL_GetTicks();

SomeFunctionThatUsesAutoReleasePool();//Run a function that uses autorelease pool, to test that it's freed.
SDL_Delay(10000);// This 10 second delay is put in here to simulate a lot of work being done.

while(1)
	{
	if(gMutex)
		{
		if (SDL_LockMutex(gMutex) == 0)//Make this only readable while locked. If you can't lock it, try until you can.
			{
			gIsWorkerThreadFinished=true; //This is a flag that the main thread will look for.
			cout << "\tWorker thread " << SDL_ThreadID() << " is done after " << SDL_GetTicks()-StartTime << " milliseconds." << endl;
			int Result=SDL_UnlockMutex(gMutex);
			if(Result!=0)// if you just successfully locked it, you should be able to unlock it
				exit(1);
			break;
			}
		else
			SDL_Delay(100);
		}
	}
[autoreleasepool release];// Gotta free the pool at the end of your thread.
return 0;

}

int main(int argc, char *argv[])
{
SetUpPool();// Set up the autorelease pool for the main process.

if (SDL_Init(0) < 0)	// Load the SDL library
    exit(1);

gMainThreadId=SDL_ThreadID();
cout << "gMainThreadId:" << gMainThreadId << endl;

gMutex = SDL_CreateMutex();
if(!gMutex)
	exit(1);

gIsWorkerThreadFinished = true;//Set this true to start the first worker thread.
Uint32 ThreadStartTime=0;

int TotalNumThreadsEverMade=0;

int done = 0;
while(!done)
	{
	if(gMutex)
		{
		if (SDL_LockMutex(gMutex) == 0)//Make this only readable while locked. If you can't lock it, try next loop.
			{
			if(gIsWorkerThreadFinished)
				{
				int ThreadReturnValue=0;
				if(gWorkerThread)
					{
					SDL_WaitThread(gWorkerThread, &ThreadReturnValue);//If there is currently a worker thread, wait for it to finish.
					cout << "Main process notes the worker thread completed with return value of " << ThreadReturnValue << endl;
					}
				ThreadStartTime=SDL_GetTicks();
				gIsWorkerThreadFinished=false;
				gWorkerThread=SDL_CreateThread(ThreadWorkerFunction, NULL, NULL);// make a new worker thread if old one is finished.
				
				TotalNumThreadsEverMade++;
				cout << "Main process made a worker thread. Total threads ever made:" << TotalNumThreadsEverMade << endl;
				
				if(!gWorkerThread)
					exit(1);
				}
			else
				{
				cout << "Main process has been waiting for worker thread for " << SDL_GetTicks()-ThreadStartTime << " MS." << endl;
				}
				
			int Result=SDL_UnlockMutex(gMutex);
			if(Result!=0)// if you just successfully locked it, you should be able to unlock it
				exit(1);
			}
		else
			cout << "main thread can't unlock mutex" << endl;
		}
	
	SDL_Delay(1000);//This simulates the main process performing work.
	}
	
Quit();
return 0;

}

/*
// SAMPLE OUTPUT:----------------------------------------

gMainThreadId:2897558056
Main process made a worker thread. Total threads ever made:1
2013-10-05 06:56:11.569 threadtest[11801:1203] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
Main process has been waiting for worker thread for 3002 MS.
Main process has been waiting for worker thread for 4003 MS.
Main process has been waiting for worker thread for 5004 MS.
Main process has been waiting for worker thread for 6004 MS.
Main process has been waiting for worker thread for 7005 MS.
Main process has been waiting for worker thread for 8006 MS.
Main process has been waiting for worker thread for 9007 MS.
Main process has been waiting for worker thread for 10009 MS.
Worker thread 2953318400 is done after 10108 milliseconds.
Main process notes the worker thread completed with return value of 0
Main process made a worker thread. Total threads ever made:2
2013-10-05 06:56:22.475 threadtest[11801:1207] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
Main process has been waiting for worker thread for 3003 MS.
Main process has been waiting for worker thread for 4004 MS.
Main process has been waiting for worker thread for 5005 MS.
Main process has been waiting for worker thread for 6007 MS.
Main process has been waiting for worker thread for 7008 MS.
Main process has been waiting for worker thread for 8009 MS.
Main process has been waiting for worker thread for 9010 MS.
Worker thread 2953318400 is done after 10002 milliseconds.
Main process notes the worker thread completed with return value of 0
Main process made a worker thread. Total threads ever made:3
2013-10-05 06:56:32.485 threadtest[11801:120b] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
*/

I’m unsure of when I should use SDL_Delay(n) for the purpose of making the algorithm function. (In the test program, I use SDL_Delay to simulate time it would take for a process to do things, but I’m not talking about that.)
In testlock.c, the author writes:
Code:
SDL_Delay(10);/* If this sleep isn’t done, then threads may starve */

I don’t know when I should be using a delay, and how much would be prudent. The number chosen seems to be a little hand-waivey and strikes me as the sort of thing that is either too long, or not guaranteed to be long enough.

? Isn’t the point of SDL to achieve portability?

If you’re already using Cocoa and ObjectiveC, why use SDL’s (C-based)
threading at all? You’ve already got things like Grand Central and
OpenCL in addition to NSThread and friends. I don’t know why you’d
be using SDL’s threading given that.

A portable app written in C or C++ would use SDL’s threads, in which
case there are lots of examples and guides. I just have no idea why
you’d be trying to use SDL’s threads in a Cocoa app.

JosephOn Sat, Oct 05, 2013 at 11:11:43AM +0000, Trev wrote:

I found the SDL tutorial on threads to be a bit confusing and it didn’t mention things like making an autorelease pool for every sdl thread. I made a test program to demonstrate the way I think you’re supposed to do things.

Here is the Xcode project:http://lackeyccg.com/code/ThreadTest.zip
or for just the 1 source file:http://lackeyccg.com/code/MainThreadTest.mmI documented my code to explain why I was doing things. If anyone wants to take a look at it, please let me know if you have any suggestions or if you think I did something wrong.

Code:
/*
The purpose of this test program is to show how to use threads with SDL in an objective c environment.

This program has one main process which is in an endless loop. If there is not currently a worker thread,
the main process creates one. The worker thread should perform its tasks without making the main process
hang, which is the main reason for using a thread in the first place. When the worker thread process has
completed, the next time the main process checks if the worker is done, it will then see that it is and
launch another worker thread, and so on.
*/

#include <CoreFoundation/CFURL.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFString.h>
#include <Cocoa/Cocoa.h>
#include <SDL.h>
#include

using namespace std;

static SDL_threadID gMainThreadId;
static NSAutoreleasePool * gAutoReleasePool=NULL;
static SDL_Thread *gWorkerThread=NULL;
static SDL_mutex *gMutex = NULL;
static bool gIsWorkerThreadFinished = true;

void SetUpPool()
{
if(gAutoReleasePool)
[gAutoReleasePool release];
gAutoReleasePool = [[NSAutoreleasePool alloc] init];
}

void FreePool()
{
if(gAutoReleasePool)
[gAutoReleasePool release];
gAutoReleasePool=NULL;
}

void Quit()
{
if(gMutex)
SDL_DestroyMutex(gMutex);
FreePool();
}

void SomeFunctionThatUsesAutoReleasePool()
{// This function just retrieves the contents of a URL.
/// This function is run from the worker thread and uses the autorelease pool to test that it’s freed from the thread.
NSString urlString=@“http://lackeyccg.com/code/test.txt”;
NSError
error = nil;
NSString* text = [NSString stringWithContentsOfURL:[NSURL URLWithString:urlString] encoding:NSASCIIStringEncoding error:&error];
if( text )
NSLog(@“Text=%@”, text);
else
NSLog(@“Error = %@”, error);
}

int SDLCALL ThreadWorkerFunction(void *data)
{
// Gotta make a autorelease pool for each thread if you explicitly or implicitly use things that use the pool.
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
if(!autoreleasepool)
exit(1);

Uint32 StartTime=SDL_GetTicks();

SomeFunctionThatUsesAutoReleasePool();//Run a function that uses autorelease pool, to test that it’s freed.
SDL_Delay(10000);// This 10 second delay is put in here to simulate a lot of work being done.

while(1)
{
if(gMutex)
{
if (SDL_LockMutex(gMutex) == 0)//Make this only readable while locked. If you can’t lock it, try until you can.
{
gIsWorkerThreadFinished=true; //This is a flag that the main thread will look for.
cout << “\tWorker thread " << SDL_ThreadID() << " is done after " << SDL_GetTicks()-StartTime << " milliseconds.” << endl;
int Result=SDL_UnlockMutex(gMutex);
if(Result!=0)// if you just successfully locked it, you should be able to unlock it
exit(1);
break;
}
else
SDL_Delay(100);
}
}
[autoreleasepool release];// Gotta free the pool at the end of your thread.
return 0;
}

int main(int argc, char *argv[])
{
SetUpPool();// Set up the autorelease pool for the main process.

if (SDL_Init(0) < 0) // Load the SDL library
exit(1);

gMainThreadId=SDL_ThreadID();
cout << “gMainThreadId:” << gMainThreadId << endl;

gMutex = SDL_CreateMutex();
if(!gMutex)
exit(1);

gIsWorkerThreadFinished = true;//Set this true to start the first worker thread.
Uint32 ThreadStartTime=0;

int TotalNumThreadsEverMade=0;

int done = 0;
while(!done)
{
if(gMutex)
{
if (SDL_LockMutex(gMutex) == 0)//Make this only readable while locked. If you can’t lock it, try next loop.
{
if(gIsWorkerThreadFinished)
{
int ThreadReturnValue=0;
if(gWorkerThread)
{
SDL_WaitThread(gWorkerThread, &ThreadReturnValue);//If there is currently a worker thread, wait for it to finish.
cout << "Main process notes the worker thread completed with return value of " << ThreadReturnValue << endl;
}
ThreadStartTime=SDL_GetTicks();
gIsWorkerThreadFinished=false;
gWorkerThread=SDL_CreateThread(ThreadWorkerFunction, NULL, NULL);// make a new worker thread if old one is finished.

  			TotalNumThreadsEverMade++;
  			cout << "Main process made a worker thread. Total threads ever made:" << TotalNumThreadsEverMade << endl;
  			
  			if(!gWorkerThread)
  				exit(1);
  			}
  		else
  			{
  			cout << "Main process has been waiting for worker thread for " << SDL_GetTicks()-ThreadStartTime << " MS." << endl;
  			}
  			
  		int Result=SDL_UnlockMutex(gMutex);
  		if(Result!=0)// if you just successfully locked it, you should be able to unlock it
  			exit(1);
  		}
  	else
  		cout << "main thread can't unlock mutex" << endl;
  	}
  
  SDL_Delay(1000);//This simulates the main process performing work.
  }

Quit();
return 0;
}

/*
// SAMPLE OUTPUT:----------------------------------------

gMainThreadId:2897558056
Main process made a worker thread. Total threads ever made:1
2013-10-05 06:56:11.569 threadtest[11801:1203] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
Main process has been waiting for worker thread for 3002 MS.
Main process has been waiting for worker thread for 4003 MS.
Main process has been waiting for worker thread for 5004 MS.
Main process has been waiting for worker thread for 6004 MS.
Main process has been waiting for worker thread for 7005 MS.
Main process has been waiting for worker thread for 8006 MS.
Main process has been waiting for worker thread for 9007 MS.
Main process has been waiting for worker thread for 10009 MS.
Worker thread 2953318400 is done after 10108 milliseconds.
Main process notes the worker thread completed with return value of 0
Main process made a worker thread. Total threads ever made:2
2013-10-05 06:56:22.475 threadtest[11801:1207] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
Main process has been waiting for worker thread for 3003 MS.
Main process has been waiting for worker thread for 4004 MS.
Main process has been waiting for worker thread for 5005 MS.
Main process has been waiting for worker thread for 6007 MS.
Main process has been waiting for worker thread for 7008 MS.
Main process has been waiting for worker thread for 8009 MS.
Main process has been waiting for worker thread for 9010 MS.
Worker thread 2953318400 is done after 10002 milliseconds.
Main process notes the worker thread completed with return value of 0
Main process made a worker thread. Total threads ever made:3
2013-10-05 06:56:32.485 threadtest[11801:120b] Text=This is a test.
Main process has been waiting for worker thread for 1001 MS.
Main process has been waiting for worker thread for 2002 MS.
*/


SDL mailing list
SDL at lists.libsdl.org
http://lists.libsdl.org/listinfo.cgi/sdl-libsdl.org

Joseph Carter wrote:

??? Isn’t the point of SDL to achieve portability?

If you’re already using Cocoa and ObjectiveC, why use SDL’s (C-based)
threading at all? You’ve already got things like Grand Central and
OpenCL in addition to NSThread and friends. I don’t know why you’d
be using SDL’s threading given that.

A portable app written in C or C++ would use SDL’s threads, in which
case there are lots of examples and guides. I just have no idea why
you’d be trying to use SDL’s threads in a Cocoa app.

Joseph
Well, I am trying to make things portable (as much as possible) and that’s why I want to use SDL’s threading as much as possible. But as I understand it, if I use any functions that require the autorelease pool within a thread, I also need the additional step of making and clearing an autorelease pool for the cocoa version of my app. Always, there is the intent to make things as system independent as possible, and that’s why I’m using SDL, but occasionally, some parts just need operating-specific code.

I grabbed the iOS documentation (mostly because the link was ranked
ever so slightly higher than the OS X version in my search results,
but what I found was this:

https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-1041876

It’s a good bet that OS X will do it exactly the same way.

IMO the proper way to mix Cocoa and SDL is:

If the app is primarily a Cocoa app and uses SDL as a relatively easy
way to get at video and typical game hardware, then your threading
should be done in Cocoa and SDL should run in one thread.

If your app is primarily SDL-based, but you need to provide some
Cocoa UI elements for a clean and integrated interface on the
platform, SDL should do all of the multithreading and Cocoa should
run in one of those threads.

I wouldn’t try to mix multithreaded Cocoa and multithreaded SDL if I
could find any reasonable alternative to doing it. SDL abstracts
every aspect of the system it supports into a portable form. The
only thing it cannot do is handle the system Cocoa toolkit (in any
meaningful way), beyond the lowest common denominator for pasteboard
and drop objects, integration with services, etc. These are all UI
issues. If your app is behaving itself in all other threads in a
portable fashion, one thread ought to be able to handle the
interfacing.

But to answer your question, yeah, you pretty much have to manage the
autorelease pool for each subthread that wants to use one or you’re
going to leak objects.

JosephOn Sat, Oct 05, 2013 at 06:34:30PM +0000, Trev wrote:

??? Isn’t the point of SDL to achieve portability?

If you’re already using Cocoa and ObjectiveC, why use SDL’s (C-based)
threading at all? You’ve already got things like Grand Central and
OpenCL in addition to NSThread and friends. I don’t know why you’d
be using SDL’s threading given that.

A portable app written in C or C++ would use SDL’s threads, in which
case there are lots of examples and guides. I just have no idea why
you’d be trying to use SDL’s threads in a Cocoa app.

Well, I am trying to make things portable (as much as possible) and that’s why I want to use SDL’s threading as much as possible. But as I understand it, if I use any functions that require the autorelease pool within a thread, I also need the additional step of making and clearing an autorelease pool for the cocoa version of my app. Always, there is the intent to make things as system independent as possible, and that’s why I’m using SDL, but occasionally, some parts just need operating-specific code.

I am using c++ and SDL (and openGL) for mostly everything (including my GUI), with a few exceptions like system clipboard and some file manager stuff which, unless I’m mistaken, aren’t handled via SDL for easy cross platform coding.

But something I just figured out is that I should be using SDL_TryLockMutex instead of SDL_LockMutex in my sample program above. I assumed SDL_LockMutex functioned like SDL_TryLockMutex does, and I found out that my program was hanging at times because of that false assumption.