Multi-thread in PSP failed . Need help

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
jesil
Posts: 10
Joined: Tue Feb 17, 2009 2:15 pm

Multi-thread in PSP failed . Need help

Post by jesil »

Hi ,I am writing an game engine on psp. I need to play music in my game . So i study the mp3 sample in the PSPSDK wrote by raphael.
Then i want to create a thread to deal with playing music while i can do something else in the game. But when i start the thread , it freeze . I don't know why. Please if you counld help me.Here is my part of code .

Code: Select all



PSP_MODULE_INFO("LevolAVG", 0, 1, 6);
PSP_MAIN_THREAD_ATTR(0);
PSP_HEAP_SIZE_KB(18*1024);
/* Exit callback */
int exit_callback(int arg1, int arg2, void *common)
{
	sceKernelExitGame();

	return 0;
}
int CallbackThread(SceSize args, void *argp)
{
	int cbid;
	cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
	sceKernelRegisterExitCallback(cbid);

	sceKernelSleepThreadCB();

	return 0;
}

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks(void)
{
	int thid = 0;

	thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0x3F40, 0, 0);
	if(thid >= 0)
	{
		sceKernelStartThread(thid, 0, 0);
	}

	return thid;
}

//Functions Definition and Variables Delaration

void show_menu(void);

void show_menu(void)
{
	  
	printf("Press circle to load a imge\n");
	
   
}

int main()
{      
	int errorInteger=0;

	
	pspDebugScreenInit();
	SetupCallbacks();
	sceDisplaySetMode(0, SCREEN_WIDTH, SCREEN_HEIGHT);
	EnableGU();

	errorInteger=FreeTypeInitial();
	if ( errorInteger == 0 )
	{
		ERRORMSG("Error Error in FreeTypeInitial\n");
	}
	
	errorInteger =	MovieInitial();
	if (errorInteger == 0)
	{
		ERRORMSG("Error in Movie Initial\n");
	}

	ScriptFlow("my.txt");
	sceKernelSleepThread();
	return 0;
}


The code above is my main thread. while in the ScriptFlow functions ,i start the thread as follows.

Code: Select all

   myAvgThread->MusicPlayThread=sceKernelCreateThread("playmusic",PlayMusicThread,0x8,0x10000,0,0);
	
	if&#40;myAvgThread->MusicPlayThread < 0&#41;
	&#123;
		ERRORMSG&#40;"ERROR&#58; creat startPlay thread failed returned 0x%08X\n", myAvgThread->MusicPlayThread&#41;;
	&#125;

      sceKernelStartThread&#40;myAvgThread->MusicPlayThread,4,&myAvgThread&#41;;
	
	sceKerneSleepThread&#40;&#41;; 


It can play music but my program freeze.I can not do anything else while listening to the music i Played. But indeed i want do some display actions while playing music.


So counld somebody tell me my fault in this program.
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

Priority of 0x8 is very high. Try reducing it less than the main thread. Is your music thread delaying to allow other threads to execute?
jesil
Posts: 10
Joined: Tue Feb 17, 2009 2:15 pm

Post by jesil »

THX for Torch reply.
So it means in the PSP,it cannot run two threads at the same time .should i add scekerneldelaythread or other functions to delay music thread and let main thread run.
i ajust my code . and the new code are as follows :

Code: Select all


int PlayMusicThread&#40;SceSize input_lenth, void *input&#41;
&#123;

       volatile AvgThread *myAvgThread = *&#40;&#40;void**&#41; input&#41;;

       SceCtrlData pad;
       SceUID  fd;

       int status;

       sceCtrlSetSamplingCycle&#40;0&#41;;
       sceCtrlSetSamplingMode&#40;0&#41;;
  
	
	LoadMp3Module&#40;&#41;;

	
	// Open the input file
	fd = sceIoOpen&#40; myAvgThread->filename, PSP_O_RDONLY, 0777 &#41;;


	status = sceMp3InitResource&#40;&#41;;
	if &#40;status<0&#41;
	&#123;
		ERRORMSG&#40;"ERROR&#58; sceMp3InitResource returned 0x%08X\n", status&#41;;
	&#125;
	
	// Reserve a mp3 handle for our playback
	SceMp3InitArg mp3Init;
	mp3Init.mp3StreamStart = 0;
	mp3Init.mp3StreamEnd = sceIoLseek32&#40; fd, 0, SEEK_END &#41;;
	mp3Init.unk1 = 0;
	mp3Init.unk2 = 0;
	mp3Init.mp3Buf = mp3Buf;
	mp3Init.mp3BufSize = sizeof&#40;mp3Buf&#41;;
	mp3Init.pcmBuf = pcmBuf;
	mp3Init.pcmBufSize = sizeof&#40;pcmBuf&#41;;
	
	SceInt32 handle = sceMp3ReserveMp3Handle&#40; &mp3Init &#41;;
	
	
	// Fill the stream buffer with some data so that sceMp3Init has something to work with
	FillMp3StreamBuffer&#40; fd, handle &#41;;
	
	status = sceMp3Init&#40; handle &#41;;

	
	int channel = -1;
	int samplingRate = sceMp3GetSamplingRate&#40; handle &#41;;
	int numChannels = sceMp3GetMp3ChannelNum&#40; handle &#41;;
	int lastDecoded = 0;
	int volume = PSP_AUDIO_VOLUME_MAX;
	int numPlayed = 0;
	int paused = 0;
	
	while &#40;1&#41;
	&#123;
		if &#40;!paused&#41;
		&#123;
			sceKernelDelayThread&#40;100000&#41;;
			// Check if we need to fill our stream buffer
			if &#40;sceMp3CheckStreamDataNeeded&#40; handle &#41;>0&#41;
			&#123;
				FillMp3StreamBuffer&#40; fd, handle &#41;;
			&#125;

			short* buf;
			int bytesDecoded;
			int retries = 0;
			// We retry in case it's just that we reached the end of the stream and need to loop		
			
			for &#40;retries = 0;retries<1;retries++&#41;
			&#123;
				bytesDecoded = sceMp3Decode&#40; handle, &buf &#41;;
				if &#40;bytesDecoded>0&#41;
					break;
				
				if &#40;sceMp3CheckStreamDataNeeded&#40; handle &#41;<=0&#41;
					break;
				
				if &#40;!FillMp3StreamBuffer&#40; fd, handle &#41;&#41;
				&#123;
					numPlayed = 0;
				&#125;
			&#125;
		
			
			// Nothing more to decode? Must have reached end of input buffer
			if &#40;bytesDecoded==0 || bytesDecoded==0x80671402&#41;
			&#123;
				paused = 1;
				sceMp3ResetPlayPosition&#40; handle &#41;;
				//numPlayed = 0;
			&#125;
			else
			&#123;
				// Reserve the Audio channel for our output if not yet done
				if &#40;channel<0 || lastDecoded!=bytesDecoded&#41;
				&#123;
					if &#40;channel>=0&#41;
						sceAudioSRCChRelease&#40;&#41;;
					
					channel = sceAudioSRCChReserve&#40; bytesDecoded/&#40;2*numChannels&#41;, samplingRate, numChannels &#41;;
				&#125;
				// Output the decoded samples and accumulate the number of played samples to get the playtime
				sceAudioSRCOutputBlocking&#40; volume, buf &#41;;
			&#125;
		&#125;
		
		
    &#125;
 
   	if &#40;channel>=0&#41;
		sceAudioSRCChRelease&#40;&#41;;
	
	status = sceMp3ReleaseMp3Handle&#40; handle &#41;;

	
	status = sceMp3TermResource&#40;&#41;;

	
	status = sceIoClose&#40; fd &#41;;

    return 0;
&#125;
I delete some debug information. I call scekerneldelaythread () in the music playing thread . and i change the music thread prioty to 45.
Now the program turns out to blink several times (which i think the delay of the music playing causes) and then freeze.
And could anybody help me for that problem.And is there any examples to explain how two threads run in the PSP.Thank you very much.
jesil
Posts: 10
Joined: Tue Feb 17, 2009 2:15 pm

Post by jesil »

Ok it's me .Finally I figure it out .
We should call sceKernelDelayThread() to delay the current thread and let the thread which you want to start run. And in my Music playing thread . I should add delay time to a certain amount , and thus the thread can run perfectly. Maybe different threads demand different delay time ,depending on initialization of the functions .

So it is my experience .Wish it helps somebody .
jesil
Posts: 10
Joined: Tue Feb 17, 2009 2:15 pm

Post by jesil »

And in this mp3 playing thread ,it needs about 100000us delay
User avatar
Torch
Posts: 825
Joined: Wed May 28, 2008 2:50 am

Post by Torch »

jesil wrote:And in this mp3 playing thread ,it needs about 100000us delay
That depends on how much audio is buffered.
Devun_06
Posts: 15
Joined: Sat Jun 17, 2006 7:01 pm

Post by Devun_06 »

It is quite old, but this thread was my better option to gathering whether or not I may have had a thread issue, instead of opening an entire new one, since this may actually already have a solution to my problem.

Code: Select all

PSP_MODULE_INFO&#40;"LevolAVG", 0, 1, 6&#41;;
PSP_MAIN_THREAD_ATTR&#40;0&#41;;
PSP_HEAP_SIZE_KB&#40;18*1024&#41;;
/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
   sceKernelExitGame&#40;&#41;;

   return 0;
&#125;
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
   int cbid;
   cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
   sceKernelRegisterExitCallback&#40;cbid&#41;;

   sceKernelSleepThreadCB&#40;&#41;;

   return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
   int thid = 0;

   thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0x3F40, 0, 0&#41;;
   if&#40;thid >= 0&#41;
   &#123;
      sceKernelStartThread&#40;thid, 0, 0&#41;;
   &#125;

   return thid;
&#125;

//Functions Definition and Variables Delaration

void show_menu&#40;void&#41;;

void show_menu&#40;void&#41;
&#123;
    
   printf&#40;"Press circle to load a imge\n"&#41;;
   
   
&#125;

int main&#40;&#41;
&#123;     
   int errorInteger=0;

   
   pspDebugScreenInit&#40;&#41;;
   SetupCallbacks&#40;&#41;;
   sceDisplaySetMode&#40;0, SCREEN_WIDTH, SCREEN_HEIGHT&#41;;
   EnableGU&#40;&#41;;

   errorInteger=FreeTypeInitial&#40;&#41;;
   if &#40; errorInteger == 0 &#41;
   &#123;
      ERRORMSG&#40;"Error Error in FreeTypeInitial\n"&#41;;
   &#125;
   
   errorInteger =   MovieInitial&#40;&#41;;
   if &#40;errorInteger == 0&#41;
   &#123;
      ERRORMSG&#40;"Error in Movie Initial\n"&#41;;
   &#125;

   ScriptFlow&#40;"my.txt"&#41;;
   sceKernelSleepThread&#40;&#41;;
   return 0;
&#125;
I added a phony identifier to jesil's ScriptFlow function below. Only for easing the comprehension strain.

Code: Select all

void * ScriptFlow&#40;char *&#41; 
&#123;
myAvgThread->MusicPlayThread=sceKernelCreateThread&#40;"playmusic",PlayMusicThread,0x8,0x10000,0,0&#41;;
   
   if&#40;myAvgThread->MusicPlayThread < 0&#41;
   &#123;
      ERRORMSG&#40;"ERROR&#58; creat startPlay thread failed returned 0x%08X\n", myAvgThread->MusicPlayThread&#41;;
   &#125;

      sceKernelStartThread&#40;myAvgThread->MusicPlayThread,4,&myAvgThread&#41;;
   
   sceKerneSleepThread&#40;&#41;; 
&#125;
jesil wrote:Ok it's me .Finally I figure it out .
We should call sceKernelDelayThread() to delay the current thread and let the thread which you want to start run. And in my Music playing thread . I should add delay time to a certain amount , and thus the thread can run perfectly. Maybe different threads demand different delay time ,depending on initialization of the functions .

So it is my experience .Wish it helps somebody .
1.) Okay, so you effectively start the script flow, and then, immediately after it, you put the main thread into sleep mode.
2.) Next, you start another thread, the music thread, and launch it at a decently high priority to avoid anything near real-time. Then, you sleep the main thread again.
3.) As you continue, you then place a delay at a 10th less than a second, at every instance the separate music thread is NOT paused in the dead lock.

I also noticed your ScriptFlow() captures the error, and handles it. But, from how it seems, it quickly forgets the thread wasn't able to be started, and follows up on attempting to call a thread that wasn't created successfully.

So, are you saying that this is the proper threading technique?

Code: Select all

int  function thread&#40;&#41;;
void make thread&#40;&#41;;

void make thread&#40;&#41;
&#123;
create thread&#40; function thread&#41;;
launch thread&#40; function thread&#41;;
sleep main thread&#40;&#41;;
&#125;

int  function thread&#40;&#41;
&#123;
          don't sleep function thread&#40;&#41;;

          while&#40;1&#41;
          &#123;
               delaythread&#40;10000&#41;;
          &#125;
 &#125;

int main&#40;&#41;
&#123;

make thread&#40;&#41;;
sleep main thread again&#40;&#41;;

while&#40;1&#41;
&#123;

delaythread&#40;100000&#41;;
&#125;
end program&#40;&#41;;
&#125;
I noticed you said delay to let the new thread run, so does that mean the main threads delay should come immediately after the launch thread(); where the main thread's sleep call is?
Now, I'm confused, why sleep the thread twice? By trial and error, you can adjust the delay to threads, but when you said you have to delay the primary thread, why is that if it's already placed in a subtle state such as sleep, or anything similar? If sleep isn't sufficient for that, what is the use of it? When would you wake the sleep thread? I think this is valuable information that may actually help me quite a bit, since your error seems a lot like my own at the moment.http://forums.ps2dev.org/viewtopic.php?p=87015#87015
User avatar
Jim
Posts: 476
Joined: Sat Jul 02, 2005 10:06 pm
Location: Sydney
Contact:

Post by Jim »

Look up Cooperative Multitasking.
When a thread is running, nothing else[1] can run
When a thread sleeps, it's put on a queue for its priority level with a restart time of now+sleep time, and the next queued highest priority thread that is ready to run is restarted.
Every thread must play this game of running for a short time then sleeping for it to work.

So there is no 'sleep a thread twice in a row'. It just means it skips being scheduled and the next thread runs.

Jim
[1] there are exceptions, like interrupt handlers.
Devun_06
Posts: 15
Joined: Sat Jun 17, 2006 7:01 pm

Post by Devun_06 »

Jim wrote:Look up Cooperative Multitasking.
When a thread is running, nothing else[1] can run
When a thread sleeps, it's put on a queue for its priority level with a restart time of now+sleep time, and the next queued highest priority thread that is ready to run is restarted.
Every thread must play this game of running for a short time then sleeping for it to work.

So there is no 'sleep a thread twice in a row'. It just means it skips being scheduled and the next thread runs.

Jim
[1] there are exceptions, like interrupt handlers.
Thanks Jim, I took your advice, I reviewed plenty of documents, but of them all, the most useful I could find were these:

Code: Select all

http&#58;//www.netrino.com/Embedded-Systems/How-To/RTOS-Preemption-Multitasking
http&#58;//en.wikipedia.org/wiki/Computer_multitasking
http&#58;//www.softvelocity.com/Clarion/pdf/Cooperative%20threading%20versus%20Preemptive%20threading.pdf
http&#58;//en.wikipedia.org/wiki/Thread_%28computer_science%29
http&#58;//c2.com/cgi/wiki$?CooperativeThreading
And the best, was :
http://wiki.tcl.tk/20153 It sucked on explaining the code itself, and doesn't look like C or or any language I've come across. But, at the least, it kindof gave a simple frame work to the design.

Code: Select all

load ./tcor.so

 set cmd &#123;
  while 1 &#123;
   puts HELLO
   tcor-yield
  &#125;
 &#125;

 tcor-spawn $&#58;&#58;cmd

 while 1 &#123;
  puts MAIN
  tcor-yield
  after 1000
 &#125;
Still though, none of them fully explained sleep, suspend, or any of the other terminology used int the pspthreadman.h library. Which, essentially, is getting me no where, all of the material I found basically reworded "FlatMush" at http://www.psp-programming.com/forums/i ... 820.0.html, but in poorer methods using way more words. Except http://www.netrino.com/Embedded-Systems ... ltitasking gave a very good visual illustration, to explain another threading method including Interrupts.

It's proving quite hard to find, could someone point me to the best material they've found for this topic. I'd definitely love to read it, and follow up on my work.

Thanks a lot though Jim, I appreciate your assistance. I completely understand how the concept works, my only problem now though, is how do I implement it. Doing so, still is difficult to do since I haven't completely wrapped my brain around how you explained sleep. What do you mean by NOW TIME when you said NOW + SLEEP time, and by sleep time, do you mean the delay time? I don't recall sleep having a time-stamp, I'm assuming by your explanation that it only yields the CPU over to the next thread, right? Does sleep record, or set a time or anything?

EDIT:
I found a FANTASTIC source to follow, even better than a framework. I wouldn't have assumed the pspthreadman.h would be built to resemble Java like code. Thanks Jim, that was very helpful. I couldn't have found it without you telling me what the name of it was!

So, basically, the concept still flows completely, there is no actual sleep, just a delay(0), and now that the CPU has moved on, it won't go back till it's completed it's job with other threads next in line, threads with higher priority, or other threads yield with a delay or sleep themselves.

I'm going to assume that Sleep, and Delay(0) are the same.

Go Here: http://www.geom.uiuc.edu/~daeron/docs/j ... tates.html
jesil
Posts: 10
Joined: Tue Feb 17, 2009 2:15 pm

Post by jesil »

Ok&#65292; i've read your problem,But i can just explain the few things you mentioned above.
Althougn psp support multithread working ,it doesn't mean that it can run two thread at the same time.When a thread is runing ,other threads can not make any function.The Other threads wait until the running thread delay. So you should always add "scekerneldelay(xxx)" in your thread to make other thread run. In my opinion,i call it suspend. SleepThread is not reqaired in my Project.
Post Reply