Running multiple processes concurrently (also threads)

Discuss the development of software, tools, libraries and anything else that helps make ps2dev happen.

Moderators: cheriff, Herben

Post Reply
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Running multiple processes concurrently (also threads)

Post by whatisdot »

Hi. Me again.

I was wondering, how do we execute multiple processes and manage them? There isn't a "fork()" and "exec()" system call for us to use. The only way I can see to spawn a new process is to use the "SifLoadElf()" function call, and from what I can tell from the source code, that ends up using RPCs to execute the command. Isn't there something a little more close to hardware that we can use (like fork)?
The reason I'm asking is because I'm trying to set up a process management tool that allows a user to "kill" a process if it hangs. Does anyone have any experience or any ideas to share?

Thanks
Last edited by whatisdot on Mon May 26, 2008 2:36 pm, edited 1 time in total.
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

...nobody...?
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

None of the Playstations until the PS3 are designed for that. The PS1/PS2/PSP all assume only one app is running, and they all use cooperative multitasking.
User avatar
Lukasz
Posts: 248
Joined: Mon Jan 19, 2004 8:37 pm
Location: Denmark
Contact:

Post by Lukasz »

cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

Also check Lukasz's Libito graphic library. Although I haven't tested it yet, it seems to have support for creating threads (it calls the thread support calls from the sdk) so even if you will not use Libito, you can learn to manage threads by looking at the Libito's source, I think. But like JF said, multitasking is not that great...
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

Thanks for all the replies.

I have to point out that there is a difference between a "process" and a "thread." A process is active machine code that has been loaded and is being executed, where a thread of execution is similar to a process but doesn't have as high of precedence. I can achieve what I wanted with threads, but I didn't know if there was a separate library provided for interfacing specifically with processes. Each process can have multiple threads, after all...

Thanks again.
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

As the posts and the linked thread indicate, threads are supported with cooperative multitasking - processes are not. The most you see on the PS2 is one app launching another, like with ULE or similar apps.

Given your stated goal of having a "management tool" that kills a hung process, and given that the PS2 really only runs one thing at a time, the goal can be met already via the RESET button. :)
User avatar
Lukasz
Posts: 248
Joined: Mon Jan 19, 2004 8:37 pm
Location: Denmark
Contact:

Post by Lukasz »

ptek wrote:Also check Lukasz's Libito graphic library. Although I haven't tested it yet, it seems to have support for creating threads (it calls the thread support calls from the sdk) so even if you will not use Libito, you can learn to manage threads by looking at the Libito's source, I think. But like JF said, multitasking is not that great...
The thread support was included mostly for making it easier to create SIFRPC server threads, like on the IOP. Besides from threads which are activated by interrupts, I don't see much use for threads on the EE.
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

Okay, I think I understand now.
After taking a closer look at the kernel code, I see how the threads are handled by system calls in the PS2SDK. Now I understand J.F.s comment about "only one process" being able to execute, but as long as it can have multiple threads then that's fine.
I am hoping that I can get multiple threads working, so I can load multiple ELFs. For example, being able to load an ELF like PS2FTP, while still being able to browse using uLaunchELF. My reasoning is that if it is possible to launch an ELF from another ELF, then it should also be possible to catch a return from it. How else would they be able to get PS2Linux working?
How does this hypothesis sound?
In the meantime, I'll try to get threads to work...
Last edited by whatisdot on Sun May 25, 2008 8:11 am, edited 1 time in total.
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

As the other thread showed, that while() should be doing iRotateThreadReadyQueue or you'll NEVER run another thread. Remember - COOPERATIVE multitasking. If one thread busy-loops, NOTHING else runs.
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

Okay, I got threads working now. Thanks to J.F. for pointing out the importance of iRotateThreadReadyQueue. At first I had some trouble understanding what was going on in the discussion that he pointed out, but after some experimenting I got it working. Here's a demo of what I got so far. (The references to the "thread" are system calls and can be found in kernel.h) If anyone has some tips about thread status, priorities, or keeping track of stack size, feel free to chime in. Any advise would be great. =)

Makefile

Code: Select all

EE_BIN = threads.elf
EE_OBJS = threads.o
EE_LIBS = -ldebug -lkernel -lstdc++

all: $(EE_BIN)

clean:
	rm -f threads.elf *.o *.a

include $(PS2SDK)/samples/Makefile.pref
include $(PS2SDK)/samples/Makefile.eeglobal
threads.cpp

Code: Select all

#include <tamtypes.h>
#include <fileio.h>
#include <stdlib.h>
#include <stdio.h>
#include <kernel.h>
#include <debug.h>

static int POTATO_DONE = false; 
static int RADISH_DONE = false; 

void countPotatoes&#40;&#41; &#123;
	for&#40;u32 i=0; i<100000000; i++&#41; &#123;
		if&#40; i%10000000 == 0 &#41;
			printf&#40;"%d potato...\n", i/10000000&#41;;
	&#125;
	POTATO_DONE = true;
&#125;

void countRadishes&#40;&#41; &#123;
	for&#40;u32 i=0; i<100000000; i++&#41; &#123;
		if&#40; i%10000000 == 0 &#41;
			printf&#40;"%d radish...\n", i/10000000&#41;;
	&#125;
	RADISH_DONE = true;
&#125;

int main&#40; int argc, char **argv&#41; &#123;
	struct t_ee_thread potatoThread, radishThread;
	int potatoThreadId, radishThreadId;

	printf&#40;"\nBeginning thread demo&#58;\n"&#41;;

	potatoThread.status = 0;
	potatoThread.func = &#40;void*&#41;countPotatoes;
	potatoThread.stack = &#40;void*&#41;&#40;&#40;int*&#41;malloc&#40;0x800&#41; + 0x800 - 4&#41;;
	potatoThread.initial_priority = 0x1e;

	radishThread.status = 0;
	radishThread.func = &#40;void*&#41;countRadishes;
	radishThread.stack = &#40;void*&#41;&#40;&#40;int*&#41;malloc&#40;0x800&#41; + 0x800 - 4&#41;;
	radishThread.initial_priority = 0x1e;

	potatoThreadId = CreateThread&#40; &potatoThread &#41;;
	radishThreadId = CreateThread&#40; &radishThread &#41;;

	if&#40; potatoThreadId <= 0 &#41; &#123;
		printf&#40; "Server thread failed to start. %i\n", potatoThreadId &#41;;
		return -1;
	&#125;
	
	if&#40; radishThreadId <= 0 &#41; &#123;
		printf&#40; "Server thread failed to start. %i\n", radishThreadId &#41;;
		return -1;
	&#125;

	StartThread&#40; potatoThreadId, NULL &#41;;
	StartThread&#40; radishThreadId, NULL &#41;;

	while&#40; !POTATO_DONE && !RADISH_DONE &#41; &#123;
		iRotateThreadReadyQueue&#40; 0x1e &#41;;
	&#125;

	TerminateThread&#40; potatoThreadId &#41;;
	printf&#40;"Terminated potato thread.\n"&#41;;
	TerminateThread&#40; radishThreadId &#41;;
	printf&#40;"Terminated radish thread.\n"&#41;;

	printf&#40;"Goodbye\n"&#41;;

	return 0;
&#125;
Last edited by whatisdot on Mon May 26, 2008 7:27 am, edited 1 time in total.
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

Interesting stuff. Thanks.

BTW : Is the -lstdc++ at EE_LIBS really needed? As far I remember it is related to C++.

---
Edited

Hmm I didn't notice your source filename is .cpp, since you're coding in C style.
Last edited by cosmito on Sun May 25, 2008 9:40 pm, edited 1 time in total.
cheriff
Regular
Posts: 258
Joined: Wed Jun 23, 2004 5:35 pm
Location: Sydney.au

Post by cheriff »

whatisdot wrote:threads.cpp

Code: Select all

	potatoThread.stack = &#40;void*&#41;malloc&#40;0x800&#41;;
Should the stack not instead be:

Code: Select all

         potatoThread.stack = &#40;void*&#41;malloc&#40;0x800&#41; + 0x800 - 4;
??
To account for the whole growing down thing? Once these threads start making function calls you might start seeing random stuff as it drops down and starts overwriting other stuff on the heap...
Damn, I need a decent signature!
EEUG
Posts: 136
Joined: Fri May 13, 2005 4:49 am
Location: The Netherlands

Post by EEUG »

...actually the sample is not really correct as 'iRotateThreadReadyQueue' is intended to be called from the interrupt handler, so it would be more correct to setup alarm handler ('SetAlarm' kernel API) (or timer interrupt handler) and call 'iRotateThreadReadyQueue' from it. It will not matter if threads are doing busy-loops or not as alarm handler will be called anyway so each thread will get its time interval for execution (=preemptive multithreading). Be careful with 'printf' (and any RPC calls (=file I/O etc.)), DMA transfers and other hardware related things as there can be be a chaos when multiple threads will try to use them simultaneously...
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

... 'iRotateThreadReadyQueue' is intended to be called from the interrupt handler...
Yes, I feel bad for writing code like this, but so far I have just been trying to create and execute a thread successfully. I am currently lurking through the kernel source to find more info on signals and interrupts.
...account for the whole growing down thing?...
You're right. Thanks! *fixed*
BTW : Is the -lstdc++ at EE_LIBS really needed? As far I remember it is related to C++
You are correct. I use C++ as my default programming language, but I don't take advantage of it here because it's just a demo.
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

Umm...
I'm a little confused and I hope someone could help me out with regards to signal handling and interrupts. Each thread on the EE has the following attributes:

Code: Select all

typedef struct t_ee_thread  &#123;
	int   status;
	void* func;
	void* stack;
	int   stack_size;
	void* gp_reg;
	int   initial_priority;
	int   current_priority;
	u32   attr;
	u32   option;
&#125; ee_thread_t;
status - the code for the thread status
func - is a pointer to the function that gets created for that thread
stack - is the memory block that is preallocated and ready for the thread to use
initial_priority - is something we set before creating the thread
current_priority - can be changed with ChangeThreadPriority()

as for the others, I am at a loss...
gp_reg - ???
attr - ???
option - ???

But what integer values for 'status' correspond to the actual status of the thread?
0 = ready???
-1 = suspended???

Also, what is the difference between all of the thread functions with names like:
ResumeThread();
iResumeThread();
What does the "i" refer to?

(thanks again to those who are sticking with me on this) =)
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
User avatar
Lukasz
Posts: 248
Joined: Mon Jan 19, 2004 8:37 pm
Location: Denmark
Contact:

Post by Lukasz »

whatisdot wrote:Umm...
I'm a little confused and I hope someone could help me out with regards to signal handling and interrupts. Each thread on the EE has the following attributes:

Code: Select all

typedef struct t_ee_thread  &#123;
	int   status;
	void* func;
extern void * _end;
	void* stack;
	int   stack_size;
	void* gp_reg;
	int   initial_priority;
	int   current_priority;
	u32   attr;
	u32   option;
&#125; ee_thread_t;
status - the code for the thread status
func - is a pointer to the function that gets created for that thread
stack - is the memory block that is preallocated and ready for the thread to use
initial_priority - is something we set before creating the thread
current_priority - can be changed with ChangeThreadPriority()

as for the others, I am at a loss...
gp_reg - ???
attr - ???
option - ???
gp_reg is the MIPS global pointer register, which points to the global variables for a program, like the stack pointer (sp) points to the stack. (For more on MIPS registers, see here: http://www.cs.umd.edu/class/sum2003/cms ... ltReg.html). Both _gp and _end are setup by crt0.s (C Runtime 0, which is located in the startup folder of the PS2SDK source), which is the "init" code for your C/C++ program.

You can get the value for _gp and _end by declaring

Code: Select all

extern void * _gp;
extern void * _end;
I believe that attr and option are optional regs, one is used by the kernel (attr I believe) and the other you can set, if you want point at some data, assign an ID or something else for the thread :-)
whatisdot wrote: But what integer values for 'status' correspond to the actual status of the thread?
0 = ready???
-1 = suspended???
I remember playing around with this a one point, you can just put the thread into different states and what the values are with ReferThreadStatus(..). I seem to recall the values always are positive, just different for ready, running, waiting, etc.
whatisdot wrote: Also, what is the difference between all of the thread functions with names like:
ResumeThread();
iResumeThread();
What does the "i" refer to?

(thanks again to those who are sticking with me on this) =)
The "i" functions are safe to call from within a interrupt handler. You have to remember that when you are inside an interrupt handler, interrupts are disabled, which means that the code inside the interrupt handler cannot be interrupted by another interrupt (because then all hell would break loose :). So you need variants of system functions which take this into account, as the code might be a little different for the syscall if called from within an interrupt. For instance the regular system call might disable interrupts, check interrupt state and what not, while the interrupt safe variant might not need to.
Last edited by Lukasz on Fri May 30, 2008 4:50 am, edited 1 time in total.
User avatar
whatisdot
Posts: 43
Joined: Mon Apr 07, 2008 8:43 am
Location: Purdue University, USA

Post by whatisdot »

Lukasz to the rescue again. Thanks a bunch. =)
Right now I'm trying to improve the example in the way that everyone has suggested, by using callbacks for interrupts and semaphores to keep the threads from interfering with each other. I'm having some problems here, but I don't know if it is because I have a deadlock or because I don't know semaphores like I'm supposed to...
When I run this code in PS2Link I think that one thread generates an error, while the others continue to execute. How can I handle these errors? I figure there must be a system call for setting a interrupt handler that handles thread read/write errors and things of that nature, so we could kill a thread when it starts behaving badly. The function names for the system calls in "kernel.h" aren't that intuitive...any ideas...?

Code: Select all

#include <tamtypes.h>
#include <fileio.h>
#include <stdlib.h>
#include <stdio.h>
#include <kernel.h>
#include <debug.h>

//Flags
static int POTATO_DONE = false; 
static int RADISH_DONE = false; 

//Semaphores, Threads, and IDs to be used across functions
static struct t_ee_thread potatoThread, radishThread;
static int potatoThreadId, radishThreadId, counterSemaId;
static struct t_ee_sema counterSema;

//busywork thread function
void countPotatoes&#40;void *args&#41; &#123;
	for&#40;u32 i=0; i<100000; i++&#41; &#123;
		if&#40; i%10000 == 0 &#41; &#123;
			//if the semaphore is triggered, wait until it's released
			//and then trigger it &#40;decrease value by 1&#41;
			WaitSema&#40; counterSemaId &#41;;

			scr_printf&#40;"This is a sentance that is long\n"&#41;;
			printf&#40;"This is a sentance that is long\n"&#41;;
			printf&#40;"%d potato...\n", i/10000&#41;;
			scr_printf&#40;"%d potato...\n", i/10000&#41;;

			//release the semaphore &#40;increase value by 1&#41;
			SignalSema&#40; counterSemaId &#41;;
		&#125;
	&#125;
	POTATO_DONE = true;
&#125;

//busywork thread function
void countRadishes&#40;void *args&#41; &#123;
	for&#40;u32 i=0; i<100000; i++&#41; &#123;
		if&#40; i%10000 == 0 &#41; &#123;
			//if the semaphore is triggered, wait until it's released
			//and then trigger it &#40;decrease value by 1&#41;
			WaitSema&#40; counterSemaId &#41;;

			scr_printf&#40;"fast dogs make good friends\n"&#41;;
			printf&#40;"fast dogs make good friends\n"&#41;;
			printf&#40;"%d radish...\n", i/10000&#41;;
			scr_printf&#40;"%d radish...\n", i/10000&#41;;

			//release the semaphore &#40;increase value by 1&#41;
			SignalSema&#40; counterSemaId &#41;;
		&#125;
	&#125;
	RADISH_DONE = true;
&#125;

//interrupt handler to rotate the threads
void RotateThreads&#40;s32 id, u16 time, void *arg&#41; &#123;
	iRotateThreadReadyQueue&#40; 0x1e &#41;;
&#125;

int main&#40; int argc, char **argv&#41; &#123;

	init_scr&#40;&#41;;

	printf&#40;"\nBeginning thread demo&#58;\n"&#41;;

	//Set initial values for potato thread
	potatoThread.status = 0;
	potatoThread.func = &#40;void*&#41;countPotatoes;
	potatoThread.stack = &#40;void*&#41;&#40;&#40;int*&#41;malloc&#40;0x800&#41; + 0x800 - 4&#41;;
	potatoThread.initial_priority = 0x1e;

	//Set initial values for radish thread
	radishThread.status = 0;
	radishThread.func = &#40;void*&#41;countRadishes;
	radishThread.stack = &#40;void*&#41;&#40;&#40;int*&#41;malloc&#40;0x800&#41; + 0x800 - 4&#41;;
	radishThread.initial_priority = 0x1e;

	//set initial values for semaphore
	counterSema.init_count = 1;
	counterSemaId = CreateSema&#40; &counterSema &#41;;

	//create threads
	potatoThreadId = CreateThread&#40; &potatoThread &#41;;
	radishThreadId = CreateThread&#40; &radishThread &#41;;

	//check to make sure the semaphore and the threads were created
	if&#40; counterSemaId <= 0 &#41; &#123;
		printf&#40; "Server sema failed to start. %i\n", counterSemaId &#41;;
		return -1;
	&#125;
	if&#40; potatoThreadId <= 0 &#41; &#123;
		printf&#40; "Server thread failed to start. %i\n", potatoThreadId &#41;;
		return -1;
	&#125;
	if&#40; radishThreadId <= 0 &#41; &#123;
		printf&#40; "Server thread failed to start. %i\n", radishThreadId &#41;;
		return -1;
	&#125;

	//Begin thread execution
	StartThread&#40; potatoThreadId, NULL &#41;;
	StartThread&#40; radishThreadId, NULL &#41;;

	//Set the time in miliseconds to call the function RotateThreads
	SetAlarm&#40;16, RotateThreads, 0&#41;;

	//Don't do anything until both flags are set
	while&#40; !POTATO_DONE && !RADISH_DONE &#41;
		SleepThread&#40;&#41;;

	//clean up threads
	TerminateThread&#40; potatoThreadId &#41;;
	TerminateThread&#40; radishThreadId &#41;;

	//delete threads
	DeleteThread&#40; potatoThreadId &#41;;
	DeleteThread&#40; radishThreadId &#41;;

	//delete the semaphore
	DeleteSema&#40; counterSemaId &#41;;

	printf&#40;"Goodbye\n"&#41;;

	return 0;
&#125;
--------------------------------------------------------------
"A witty saying proves nothing."
--------------------------------------------------------------
User avatar
Lukasz
Posts: 248
Joined: Mon Jan 19, 2004 8:37 pm
Location: Denmark
Contact:

Post by Lukasz »

Things are getting bit complicated now and not very PS2 specific. :-)

I suggest that you make/debug your multithreaded application on your PC with pthreads/semaphores or similar and then port it over.

This way you can validate the correctness of the PS2 port on PC and also compare output for PC and PS2 version.
EEUG
Posts: 136
Joined: Fri May 13, 2005 4:49 am
Location: The Netherlands

Post by EEUG »

...some remarks:
- handler set by 'SetAlarm' is executed only once. If you want to execute it continuously then you can use 'iSetAlarm' kernel API from your handler to schedule its execution again;
- time interval is expressed in H-Sync's, not in milliseconds;
- pay attention at the priority of your 'main' thread. Make sure that it is higher than threads you create (i.e. priority value is lower than 0x1E). I think ps2link sets it to 64, so as soon as you start 'countPotatoes' 'main' thread blocks until 'countPotatoes' will terminate. Then 'countRadishes' starts and 'main' thread 'freezes' again. Then alarm handler will be set up, but vegetables counters are already terminated at that moment. You may try to do following:
- set the priority of your main thread to '0x1D':

Code: Select all

ChangeThreadPriority &#40;  GetThreadId &#40;&#41;, 0x1D  &#41;;
- create/start your "vegetables counters", set alarm handlers etc.;
- lower priority of your 'main' thread:

Code: Select all

ChangeThreadPriority &#40;  GetThreadId &#40;&#41;, 0x1F  &#41;;
At this point main thread will "freeze" and "vegetables counters" will be executed;
cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

I've been trying the code listed here and applying EEUG suggestions to do a modified small example where an alarm timer increments a counter besides taking care of thread switching and a thread checks it's value to tell the main thread about its value. It seems there's something wrong with it, and I'm hoping you can spot it.

My goal is to use an alarm timer to periodically do a iRotateThreadReadyQueue so all threads get a time execution slot.
This is better than do a RotateThreadReadyQueue inside every loop cycle at the source.

Code: Select all

#include <tamtypes.h>
#include <stdio.h>
#include <kernel.h>

static int counter = 0;
static int continueloop;

#define THREAD_STACK_SIZE	&#40;8 * 1024&#41;

static u8          thread_stack&#91;THREAD_STACK_SIZE&#93; ALIGNED&#40;16&#41;;
static ee_thread_t thread_thread;
static int         thread_threadid;

void TheThread&#40;void *arg&#41;;


void alarmfunction&#40;s32 id, u16 time, void *arg&#41;
&#123;
    counter++;
    iRotateThreadReadyQueue&#40;31&#41;;
    iSetAlarm &#40; 625, alarmfunction, NULL &#41;;
&#125;

int main&#40; int argc, char **argv&#41;
&#123;
    extern void *_gp;

    // EEUG &#58; pay attention at the priority of your 'main' thread.
    // Make sure that it is higher than threads you create
    //&#40;i.e. priority value is lower than 0x1E&#41;. I think ps2link sets it to 64
    // - set the priority of your main thread to '0x1D'&#58; 
    // - create/start your "vegetables counters", set alarm handlers etc.; 
    // - lower priority of your 'main' thread
    ChangeThreadPriority &#40;  GetThreadId &#40;&#41;, 29  &#41;;   // 29 is lower than 30 ;&#41;

    continueloop = 1;

    SetAlarm&#40; 625, alarmfunction, NULL&#41;;
    
    printf&#40;"\ncreating thread\n"&#41;;
	
    thread_thread.func = TheThread;
	thread_thread.stack = thread_stack;
	thread_thread.stack_size = THREAD_STACK_SIZE;
	thread_thread.gp_reg = _gp;
	thread_thread.initial_priority = 30;
	if &#40;&#40;thread_threadid = CreateThread&#40;&thread_thread&#41;&#41; < 0&#41; 
	&#123;
		printf&#40;"CREATE THREAD ERROR!!!\n"&#41;;
		return -1;
	&#125;
	StartThread&#40;thread_threadid, NULL&#41;;
    printf&#40;"thread started\n"&#41;;

	ChangeThreadPriority &#40; GetThreadId &#40;&#41;, 31 &#41;;

    while &#40;continueloop == 1&#41;
    &#123;
       printf&#40;"%d\n", counter&#41;;
    &#125;

    printf&#40;"terminating\n"&#41;;
    TerminateThread&#40;thread_threadid&#41;;
    DeleteThread&#40;thread_threadid&#41;;

    printf&#40;"\ndone\n"&#41;;
    return 0;
&#125;

///
void TheThread&#40;void *arg&#41;
&#123;
    while &#40;1&#41;
    &#123;
        if &#40;counter == 50&#41;
            continueloop = 0;

        //RotateThreadReadyQueue&#40;31&#41;;
    &#125;
&#125;
I kept printf at a minimum and not in critical zones of the code since it seems to induce thread switching.

So the above example doesn't work. I get only the "creating thread" and
"thread started" message and that's it.

I can only get it to work if I I change all priorities to same value (31 for example) and do a RotateThreadReadyQueue(31) at TheThread().

It seems the iRotateThreadReadyQueue() is not doing anything at the alarm function...

Any hints?
EEUG
Posts: 136
Joined: Fri May 13, 2005 4:49 am
Location: The Netherlands

Post by EEUG »

...well, as soon as you call 'ChangeThreadPriority ( GetThreadId (), 31 );' your 'main' thread will never get a chance to run beyond that code as 'TheThread' has a higher priority of 30 and at the moment of 'ChangeThreadPriority' call it will take control. Would you try 'ChangeThreadPriority ( GetThreadId (), 30 );' instead and modify your alarm handler (iRotateThreadReadyQueue(30);)?
Note that 'printf' will most probably switch the threads also as 'main' thread will go asleep for a while (while Rpc call will complete) and 'TheThread' will take control at that moment...

Edit: Actually iRotateThreadQueue does not perform context switch at all. It just rotates a queue (an internal data structure). Some extra "assistance" is necessary to get it work as desired. Like this working example:

Code: Select all

#include <tamtypes.h> 
#include <stdio.h> 
#include <kernel.h> 

static volatile int counter = 0; 
static volatile int continueloop; 

#define THREAD_STACK_SIZE   &#40;8 * 1024&#41; 

static u8          thread_stack&#91;THREAD_STACK_SIZE&#93; ALIGNED&#40;16&#41;; 
static ee_thread_t thread_thread; 
static int         thread_threadid; 

static u8          disp_stack&#91;THREAD_STACK_SIZE&#93; ALIGNED&#40;16&#41;; 
static int         disp_threadid; 

void TheThread&#40;void *arg&#41;; 

void dispatcher &#40; void* apParam &#41; &#123;

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

  SleepThread &#40;&#41;;

 &#125;  /* end while */

&#125;  /* end dispatcher */

void alarmfunction&#40;s32 id, u16 time, void *arg&#41; 
&#123; 
    counter++;
    iWakeupThread &#40; disp_threadid &#41;;
    iRotateThreadReadyQueue &#40; 30 &#41;;
    iSetAlarm &#40; 625, alarmfunction, NULL &#41;; 
&#125; 

int main&#40; int argc, char **argv&#41; 
&#123; 
    extern void *_gp; 

    // EEUG &#58; pay attention at the priority of your 'main' thread. 
    // Make sure that it is higher than threads you create 
    //&#40;i.e. priority value is lower than 0x1E&#41;. I think ps2link sets it to 64 
    // - set the priority of your main thread to '0x1D'&#58; 
    // - create/start your "vegetables counters", set alarm handlers etc.; 
    // - lower priority of your 'main' thread 
    ChangeThreadPriority &#40;  GetThreadId &#40;&#41;, 29  &#41;;   // 29 is lower than 30 ;&#41; 

    continueloop = 1; 

    SetAlarm&#40; 625, alarmfunction, NULL&#41;; 
    
    printf&#40;"\ncreating thread\n"&#41;; 
    
   thread_thread.func = dispatcher; 
   thread_thread.stack = disp_stack; 
   thread_thread.stack_size = THREAD_STACK_SIZE; 
   thread_thread.gp_reg = &_gp; 
   thread_thread.initial_priority = 0;
   StartThread &#40;   disp_threadid = CreateThread &#40; &thread_thread &#41;, NULL  &#41;;

   thread_thread.func = TheThread; 
   thread_thread.stack = thread_stack; 
   thread_thread.stack_size = THREAD_STACK_SIZE; 
   thread_thread.gp_reg = &_gp; 
   thread_thread.initial_priority = 30; 
   if &#40;&#40;thread_threadid = CreateThread&#40;&thread_thread&#41;&#41; < 0&#41; 
   &#123; 
      printf&#40;"CREATE THREAD ERROR!!!\n"&#41;; 
      return -1; 
   &#125; 
   StartThread&#40;thread_threadid, NULL&#41;; 
    printf&#40;"thread started\n"&#41;; 

   ChangeThreadPriority &#40; GetThreadId &#40;&#41;, 30 &#41;; 

    while &#40;continueloop == 1&#41; 
    &#123; 
    &#125; 

    printf&#40;"terminating\n"&#41;; 
    TerminateThread&#40;thread_threadid&#41;; 
    DeleteThread&#40;thread_threadid&#41;; 

    printf&#40;"\ndone\n"&#41;; 
    return 0; 
&#125; 

void TheThread&#40;void *arg&#41; 
&#123; 
    while &#40;1&#41; 
    &#123; 
        if &#40;counter > 50&#41; 
            continueloop = 0; 
    &#125; 
&#125; 
Alternatively iRotateThreadReadyQueue can be moved into the dispatcher and replaced by RotateThreadReadyQueue.
cosmito
Posts: 307
Joined: Sun Mar 04, 2007 4:26 am
Location: Portugal
Contact:

Post by cosmito »

Wow, many thanks EEUG!

Very clever the SleepThread() + highest priority trick :)
Post Reply