Multithreading module crashes?

I've made a multithreading module in C (for the psp), but for some reason it keeps crashing when loading the second thread. Anyone knows what's going on?
(
- startThread() is called when starting threads,
- initThreads() is called when initialising the application,
- termThreads() is called as a garbage collector
- When a callback returns, the thread is to be cleaned up and removed from memory (item from pool is released and thread is terminated.)
)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include "headers/types.h" //Basic types!
#include "headers/emu/threads.h" //Basic threads!
#include "headers/support/zalloc.h" //Zero allocation!
#include "headers/support/log.h" //Logging!

#define MAX_THREAD 10
//Maximum ammount of threads:

#define THREADSTATUS_ALLOCATED 0
#define THREADSTATUS_CREATEN 1
#define THREADSTATUS_RUNNING 2


//To debug threads?
#define DEBUG_THREADS 0


//Stuff for easy thread functions.


typedef struct
{
Handler callback; //The callback to use!
SceUID threadID; //The ID of the thread!
byte status; //Used thread entries status: 0=Allocated, 1=Created, 2=Running! All else is invalid: regard as NULL record, only allocated!
char name[256]; //Names of the threads (just for debugging)
} ThreadParams, *ThreadParams_p; //The thread's params!

#define THREAD_STACKSIZE sizeof(ThreadParams_p)
//Originally 0x10000!

ThreadParams_p threadpool[MAX_THREAD]; //Thread pool!


//Thread allocation/deallocation!


ThreadParams_p *allocateThread() //Allocate a new thread to run (waits if none to allocate)!
{
	uint_32 curindex;
	newallocate: //Try (again)!
	for (curindex=0;curindex<NUMITEMS(threadpool);curindex++) //Find an unused entry!
	{
		if (!threadpool[curindex]) //Not used?
		{
			dolog("threads","Allocating thread entry...");
			threadpool[curindex] = zalloc(sizeof(*threadpool[curindex])); //Allocated?
			if (threadpool[curindex]) //Allocated?
			{
				return &threadpool[curindex]; //Give the newly allocated thread params!
			}
			//Failed to allocate, passthrough!
		}
	}
	delay(1); //Wait a bit for some space: allow other threads to!
	goto newallocate; //Try again till we work!
}

int is_pooledthread(ThreadParams_p *params) //Is a pooled thread?
{
	if (!params) return 0; //Invalid pointer!
	if (!memprotect(*params,sizeof(**params))) //Not allocated?
	{
		*params = NULL; //Not allocated anymore!
		return 0; //Abort: not allocated!
	}
	uint_32 i;
	if (!params) return 0; //Not a thread we allocated (NULL isn't a thread)!
	if (!*params) return 0; //See above!
	for (i=0;i<NUMITEMS(threadpool);i++)
	{
		if (threadpool[i]==*params) //Found?
		{
			return 1; //Found our thread, so we are one!
		}
	}
	return 0; //Not in pool!
}


void freeThread(ThreadParams_p *params) //Free a (pooled) thread!
{
	dolog("threads","Freethread..."); //Log freethread!
	static int counter = 0; //Counter!
	if (!params) return; //Valid pointer to param pointer?
	if (!*params) return; //Still allocated?
	if (!memprotect(*params,sizeof(**params))) //Not allocated?
	{
		dolog("threads","Removing invalid thread: %p",*params);
		dolog("threads",""); //Empty line!
		*params = NULL; //Not allocated anymore!
		dolog("threads","freeThread: RET.");
		dolog("threads",""); //Log empty row!
		return; //Abort: not allocated!
	}

	ThreadParams_p realparams = *params; //Back-up of the real params pointer!
	
	dolog("threads","FreeThread %p %i",realparams,counter++);
	freez((void **)params,sizeof(ThreadParams)); //Free it!
	
	dolog("threads","Removing thread %p from pool if it exists...",realparams);
	//Processing the thread pool if we're out there!
	uint_32 curindex;
	for (curindex=0;curindex<NUMITEMS(threadpool);curindex++)
	{
		if (threadpool[curindex]==realparams && realparams) //Found and not NULL?
		{
			threadpool[curindex] = NULL; //Deallocated!
			dolog("threads","Thread %p has been removed from the pool.",realparams);
		}
	}
	dolog("threads","freeThread: RET.");
	dolog("threads",""); //Log empty row!
}


void deleteThread(ThreadParams_p *params)
{
	if (!params) return; //Not a thread pointer!
	if (!*params) return; //Not a thread we allocated!
	ThreadParams_p threadinfo = *params; //The thread info!
	dolog("threads","Deletethread...");
	if (sceKernelDeleteThread(threadinfo->threadID)>=0) //Deleted?
	{
		dolog("threads","Deletethread: freethread...");
		freeThread(params); //Free it!
	}
}
















//Running/stop etc. function for the thread!


int ThreadsRunning() //Are there any threads running or ready to run?
{
	int i;
	int numthreads = 0; //Number of running threads?
	for (i=0;i<NUMITEMS(threadpool);i++) //Check all threads!
	{
		if (threadpool[i]) //Allocated?
		{
			if (threadpool[i]->status>=THREADSTATUS_CREATEN) //Createn or running?
			{
				++numthreads; //We have some createn or running threads!
			}
		}
	}
	return numthreads; //How many threads are running?
}

int minthreadsrunning() //Minimum ammount of threads running when nothing's there!
{
	return DEBUG_THREADS; //1 or 0 minimal!
}


void activeThread(ThreadParams_p *threadparams) //Mark the current thread as running!
{
	dolog("threads","ActiveThread...");
	if (threadparams) //Gotten valid params?
	{
		dolog("threads","ActiveThread, checkrealparams...");
		ThreadParams_p realparams = *threadparams; //Real params!
		dolog("threads","ActiveThread, realparams valid?...");
		if (realparams)
		{
			realparams->status = THREADSTATUS_RUNNING; //We're running!
		}
	}
	dolog("threads","ActiveThread_RET...");
}


void terminateThread(ThreadParams_p *threadparams) //Terminate the current thread!
{
	dolog("threads","TerminateThread...");
	SceUID thid = sceKernelGetThreadId(); //The thread ID!
	if (threadparams) //Gotten valid params?
	{
		dolog("threads","TerminateThread: CheckRealParams");
		ThreadParams_p realparams = *threadparams; //Real params!
		if (realparams)
		{
			dolog("threads","TerminateThread: freethread...");
			thid = realparams->threadID; //The thread ID!
			freeThread(threadparams); //Free the params, we're done with it!
		}
		else //Use current ID?
		{
			dolog("threads","TerminateThread: no realparams...");
		}
	}
	else //Use current ID?
	{
		dolog("threads","TerminateThread: current thread1...");
	}

	dolog("threads","Terminating thread: %x",thid);
	sceKernelTerminateDeleteThread(thid); //Exit and delete myself!
}
Last edited on
Second part:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
void runcallback(ThreadParams_p *threadparams)
{
	dolog("threads","Runcallback...");
	//Now run the requested thread!
	if (threadparams) //Gotten params?
	{
		ThreadParams_p realparams = *threadparams; //The real parameters!
		if (realparams) //Gotten real params?
		{
			if (realparams->callback) //Found the callback?
			{
				dolog("threads","RunRealCallback...");
				realparams->callback(); //Execute the real callback!
			}
		}
	}
	dolog("threads","Callback RET.");
}

int threadhandler(SceSize args, void *params)
{
	dolog("threads","threadhandler...");
	dolog("threads","threadhandler2...");
	ThreadParams_p *threadparams;
	threadparams = params; //Get our parameters!
	dolog("threads","threadhandler: Mark thread active...");
	activeThread(threadparams); //Mark us as running!
	dolog("threads","threadhandler: Run callback...");
	runcallback(threadparams); //Run the callback!
	dolog("threads","threadhandler: Terminate thread...");
	terminateThread(threadparams); //Terminate ourselves!
	dolog("threads","threadhandler: ThreadRET?...");
	return 0; //Disable threads?
}


void termThreads() //Terminate all threads but our own!
{
	dolog("threads","termThreads...");
	int i;
	SceUID my_thid = sceKernelGetThreadId(); //My own thread ID!
	for (i=0;i<NUMITEMS(threadpool);i++) //Process all of our threads!
	{
		if (is_pooledthread(&threadpool[i])) //Used?
		{
			if (threadpool[i]->threadID!=my_thid) //Not ourselves?
			{
				switch (threadpool[i]->status) //What status?
				{
					case THREADSTATUS_ALLOCATED: //Allocated?
						dolog("threads","termThreads: Allocated!");
						freeThread(&threadpool[i]); //Deallocate it!
						break; //Just allocated, nothing more!
					case THREADSTATUS_CREATEN: //Createn?
						dolog("threads","termThreads: Createn!");
						deleteThread(&threadpool[i]); //Delete it!
						break; //Removed!
					case THREADSTATUS_RUNNING: //Running?
						dolog("threads","termThreads: Running!");
						terminateThread(&threadpool[i]); //Terminate it!
						break;
				}
			}
		}
	}
}


void debug_threads()
{
	char thread_name[256];
	bzero(thread_name,sizeof(thread_name)); //Init!
	while (1)
	{
		byte numthreads = 0; //Number of installed threads running!
		int i,i2;
		for (i=0;i<NUMITEMS(threadpool);i++)
		{
			if (threadpool[i]) //Allocated?
			{
				if (threadpool[i]->status>=THREADSTATUS_CREATEN) //Created or running?
				{
					++numthreads; //Count ammount of threads!
					gotoxy(0,30-NUMITEMS(threadpool)+numthreads); //Goto the debug row start!
					fontcolor(RGB(0xFF,0x00,0x00));
					backcolor(RGB(0x00,0xFF,0x00)); //Red on green!
					sprintf(thread_name,"Active thread: %s",threadpool[i]->name); //Get data string!
					printf(thread_name);
					for (i2=strlen(thread_name);i2<=50;i2++)
					{
						printf(" "); //Filler to 50 chars!
					} 
				}
			}
		}
		gotoxy(0,30);
		printf("Number of threads: %i",numthreads); //Debug the ammount of threads used!
		delay(100000); //Wait 100ms!
	}
}


void initThreads() //Initialise&reset thread subsystem!
{
	dolog("threads","initThreads...");
	dolog("threads","initThreads: termThreads...");
	termThreads(); //Make sure all running threads are stopped!
	dolog("threads","debugThreads?...");
	if (DEBUG_THREADS) startThread(&debug_threads,"Thread Debugger",DEFAULT_PRIORITY); //Plain debug threads!
}


void threadCreaten(ThreadParams_p *params, SceUID threadID, char *name)
{
	dolog("threads","threadCreaten...");
	if (params) //Gotten params pointer?
	{
		dolog("threads","threadCreaten1...");
		if (*params) //Gotten params?
		{
			dolog("threads","threadCreaten2...");
			ThreadParams_p realparams = *params; //Real params?
			realparams->status = THREADSTATUS_CREATEN; //Createn!
			realparams->threadID = threadID; //The thread ID!
			bzero(realparams->name,sizeof(realparams->name));
			strcpy(realparams->name,name); //Save the name for usage!
		}
	}
}


void startThread(Handler thefunc, char *name, int priority) //Start a thread!
{
	dolog("threads","startThread...");
	if (!thefunc || thefunc==NULL) //No func?
	{
		dolog("threads","startThread: NULLfunc...");
		raiseError("thread manager","NULL thread: %s",name); //Show the thread as being NULL!
		return; //Don't start: can't start no function!
	}


	//We create our handler in dynamic memory, because we need to keep it in the threadhandler!
	
	dolog("threads","startThread: allocThread...");
	//First, allocate a thread position!
	ThreadParams_p *params = allocateThread(); //Allocate a thread for us, wait for any to come free in the meanwhile!
//Next, start the timer function!
	SceUID thid; //The thread ID to allocate!
	
	dolog("threads","startThread: createThread...");
	docreatethread: //Try to create a thread!
	thid = sceKernelCreateThread(name, threadhandler, priority, THREAD_STACKSIZE, PSP_THREAD_ATTR_USER|PSP_THREAD_ATTR_NO_FILLSTACK|PSP_THREAD_ATTR_CLEAR_STACK, NULL); //Try to create the thread!
	switch ((thid>>16)&0xFFFF) //Failed to create?
	{
		case 0x0000: //Might be null?
			dolog("threads","startThread: failcreateNULL?...");
			if (!thid) //NULL?
			{
				dolog("threads","startThread: failcreateNULL!...");
					goto docreatethread; //Retry until a thread comes free to use!
			}
			break; //Just continue!
		case 0x8002: //Kernel error?
		case 0x8001: //LibC error?
			dolog("threads","startThread: delay(1)!...");
			delay(1); //Wait for any threads to come free!
			dolog("threads","startThread: checkUnpooled!...");
			if (!is_pooledthread(params)) //We've been terminated?
			{
				dolog("threads","startThread: Unpooled!...");
				freeThread(params); //Free the thread!
				return; //Abort: we've been terminated!
			}
			dolog("threads","startThread: Retry800X!...");
			goto docreatethread; //Retry until a thread comes free to use!
			break; //Just here to be sure!
		default: //No error?
			dolog("threads","startThread: ThreadCreaten!...");
			break; //Just continue!
	}


	dolog("threads","startThread: threadCreaten...");
	threadCreaten(params,thid,name); //We've been createn!
	
	dolog("threads","startThread: checking params...");
	if (params) //Valid params?
	{
		ThreadParams_p realparams = *params;
		dolog("threads","startThread: Ready to go?");
		if (realparams->status) //We're ready to go?
		{
			dolog("threads","startThread: Ready! Setting callback...");
			//Set our parameters!
			realparams->callback = thefunc; //The function to run!
			
			dolog("threads","startThread: Ready! StartThread...");
			//We have a thread we can use now!
			sceKernelStartThread(thid, sizeof(params), params); //Start thread, if possible! Give it the handler we need!
		}
		else //Remove the thread: we're done with it!
		{
			dolog("threads","startThread: Status wrong. Calling deleteThread...");
			deleteThread(params); //Cleanup the parameters!
		}
	}
}
zalloc is simply a preregistered malloc (which keeps all allocations stored in a buffer for easy cleanup afterwards).

Finally the typedefs:

types.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#ifndef TYPESEMU_H
#define TYPESEMU_H

#include <psptypes.h> //For long long etc. (u64 etc).
#include <stdlib.h>
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdebugkb.h> //Keyboard!
#include <stdint.h>
#include <pspdisplay.h>
#include <stdarg.h>
#include <string.h>
#include <malloc.h>
#include <pspctrl.h>
#include <psppower.h>
#include <ctype.h> //C type!
#include <stdlib.h>
#include <dirent.h>
#include <pspthreadman.h> //For threads!

//For speaker!
#include <pspaudiolib.h>
#include <math.h>
#include <limits.h>

//For timing!
#include <psprtc.h> //Real Time Clock!
#include <stdio.h>

#define EXIT_PRIORITY 0x11
//Exit priority, higest of all!

typedef uint32_t uint_32; //Take over using new name!
typedef int32_t int_32; //Take over using new name!
typedef unsigned char byte;
typedef unsigned short word;
typedef signed char sbyte; //Signed!
typedef signed short sword; //Signed!

#define TRUE 1
#define FALSE 0

#define printf pspDebugScreenPrintf
#define wherex pspDebugScreenGetX
#define wherey pspDebugScreenGetY
#define gotoxy pspDebugScreenSetXY
#define fontcolor pspDebugScreenSetTextColor
#define backcolor pspDebugScreenSetBackColor
//Real clearscreen:
#define realclrscr pspDebugScreenClear
#define delay sceKernelDelayThread
#define sleep sceKernelSleepThread
#define halt sceKernelExitGame

//64-bit file support (or not)!
#ifdef __FILE_SUPPORT_64
//64-bits wide file support!
typedef s64 int64;
typedef u64 uint64;
typedef uint64 FILEPOS;
#define fseek64 fseeko64
#define ftell64 ftello64
#define fopen64 fopen64
#endif

#ifndef __FILE_SUPPORT_64
//32-bits wide file support!
typedef uint_32 FILEPOS;
#define fseek64 fseek
#define ftell64 ftell
#define fopen64 fopen
#endif

//RGB, with and without A (full)
#define RGB(r, g, b) ((r)|((g)<<8)|((b)<<16)|(0xFF<<24))
#define RGBA(r, g, b, a) ((r)|((g)<<8)|((b)<<16)|((a)<<24))
//Same, but reversed!
#define GETR(x) ((x)&0xFF)
#define GETG(x) (((x)>>8)&0xFF)
#define GETB(x) (((x)>>16)&0xFF)
#define GETA(x) (((x)>>24)&0xFF)

typedef int bool; //Booleans are ints!

typedef void (*Handler)(void);    /* A pointer to a handler function */

//Number of characters
#define DEBUGSCREEN_WIDTH 67
#define DEBUGSCREEN_HEIGHT 34

//Ammount of items in a buffer!
#define NUMITEMS(buffer) (sizeof(buffer)/sizeof(buffer[0]))
//Timeout for shutdown: force shutdown!
//When set to 0, shutdown immediately shuts down, ignoring the emulated machine!
#define SHUTDOWN_TIMEOUT 0


//Different kinds of pointers!

typedef struct
{
	union
	{
		struct
		{
			uint_32 offset; //Default: 0
			unsigned int segment; //Default: 0
		};
		unsigned char data[6]; //46-bits as bytes!
	};
} ptr48; //48-bit pointer Adress (32-bit mode)!

typedef struct
{
	union
	{
		struct
		{
			unsigned int offset; //Default: 0
			unsigned int segment; //Default: 0
		};
		unsigned char data[4]; //32-bits as bytes!
	};
} ptr32; //32-bit pointer Adress (16-bit mode)!

typedef struct
{
	union
	{
		struct
		{
			unsigned short limit; //Limit!
			unsigned int base; //Base!
		};
		unsigned char data[6]; //46-bit adress!
	};
} GDTR_PTR;

typedef struct
{
	union
	{
		struct
		{
			unsigned int limit;
			uint_32 base;
		};
		unsigned char data[6]; //46-bit adress!
	};
} IDTR_PTR;

//NULL pointer definition
#define NULLPTR(x) ((x.segment==0) && (x.offset==0))
//Same, but for pointer dereference
#define NULLPTR_PTR(x,location) (ANTINULL(x,location)?((x->segment==0) && (x->offset==0)):1)

#define NULLPROTECT(ptr) ANTINULL(ptr,constsprintf("%s at %i",__FILE__,__LINE__))

//MIN/MAX: East calculation of min/max data!
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)

//SAFEDIV/MOD: Safe divide/modulo function. Divide by 0 is caught into becoming 0!
#define SAFEDIV(x,divideby) ((!divideby)?0:(x/divideby))
#define SAFEMOD(x,divideby) ((!divideby)?0:(x%divideby))

//Bit manipulation!
//Turn multiple bits on!
#define BITON(x,bit) ((x)|(bit))
//Turn multiple bits off!
#define BITOFF(x,bit) ((x)&(~(bit)))
//Get a bit value (0 or 1))
#define GETBIT(x,bitnr) (((x)>>(bitnr))&1)
//Set a bit on!
#define SETBIT1(x,bitnr) BITON((x),(1<<(bitnr)))
//Set a bit off!
#define SETBIT0(x,bitnr) BITOFF((x),(1<<(bitnr)))

//Easy rotating!
#define ror(x,moves) ((x >> moves) | (x << (sizeof(x)*8 - moves)))
#define rol(x,moves) ((x << moves) | (x >> (sizeof(x)*8 - moves)))


void BREAKPOINT(); //Safe breakpoint function!

int convertrel(int src, int fromres, int tores); //Relative convert!
uint_32 safe_strlen(char *str); //Safe safe_strlen function!
char *constsprintf(char *str1, ...); //Concatinate strings (or constants)!
void *ANTINULL(void *ptr, char *location); //ANTI NULL Dereference!

void clrscr(); //Clearscreen!

void EMU_Shutdown(int doshutdown); //Shut down the emulator?
void addtimer(float frequency, Handler timer, char *name); //Add a timer!
void removetimer(char *name); //Removes a timer!
void startTimers(); //Start timers!
void stopTimers(); //Stop timers!
void raiseError(char *source, char *text, ...); //Raises an error!
void printmsg(byte attribute, char *text, ...); //Prints a message to the screen!

void initEMU(int full); //Init EMU!
void doneEMU(); //Finish EMU!
#endif 


threads.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef THREADS_H
#define THREADS_H

#include "headers/types.h" //Basic type support!

#define DEFAULT_PRIORITY 0x18
//Default priority for threads!

void initThreads(); //Initialise&reset thread subsystem!
void startThread(Handler thefunc, char *name, int priority); //Start a thread!
void termThreads(); //Terminate all threads but our own!
int ThreadsRunning(); //Are there any threads running or ready to run?
int minthreadsrunning(); //Minimum ammount of threads running when nothing's there!
#endif 
it's highly dangerous to access and modify a global variable like threadpool in more than one thread. you need to protect this variable during use
@coder777: As far as I know the PSP doesn't have any problems with that: it can only execute one thread at a time (and delaying a thread causes one of the others to wake up, suspending the current thread effectively).


So like this:

thread1
{
while (1)
{
something 1
delay
}
}

thread2
{
while (1)
{
something 2
delay
}
}

main
{
start thread1
start thread2
}
sleep

Execution:
start thread 1
start thread 2
main:suspended because of sleep
repeatloop:
thread1: wakeup
thread1: something 1
thread1: delay->suspended
thread2: wakeup
thread2: something 2
thread2: delay-> suspended
repeatloop again.
it can only execute one thread at a time
This is how threading works

delaying a thread causes one of the others to wake up
this is wrong. It might appears as if, but isn't. the reason that it appears as if is that the scheduler of the threads is often busy until the delay.

But don't rely on this behavior! Your crash happens due to the unprotected shared variable.

I don't know the PSP (which is based on linux, but well). Does it have functions to protect variable like mutexe etc. if so, use it. if not, I cannot even guess what's the source of the crash
Topic archived. No new replies allowed.