Busy waits?

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

Moderators: cheriff, Herben

Post Reply
mharris
Posts: 155
Joined: Sun Jan 25, 2004 2:26 pm
Location: Annapolis, MD, USA

Busy waits?

Post by mharris »

A lot of the code I've seen has a vblank routine that looks something like this:

Code: Select all

    while(!((*CSR) & GS_VSINT))
        /* do nothing */ ;
This is obviously going to eat up all of the CPU cycles that it can while it's waiting for the GS to set the VSync flag. Not a big deal if this is the only thread, but I'd like to have a render thread and one or more "worker" threads, and not have the workers starve while the render thread's doing nothing.

Is there a more elegant way of doing this? Most of the "delay" routines I've seen have been NOP loops as well.

Here's what I'm thinking: I suppose there's a way to hook an interrupt handler on the VSync signal from the GS; the render thread could call SleepThread() when it's done working. The interrupt handler would then call iWakeupThread() on the render thread when it gets its interrupt, and the render thread would pick up where it left off. Meanwhile, the worker threads could be chugging along, doing their own things.

Is this the correct way of doing it? How hard is it to add an interrupt handler?

A somewhat-related question regarding threads on the PS2 -- does the kernel automatically handle the context switches? Or does each thread need to call a suspend() routine (i.e., non-preemptive multitasking).

thx
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

We use something similiar to what you describe - a thread woken up by a vblank interrupt handler.. You can use AddIntcHandler() to add a vblank handler. IIRC there are some ps2dev examples that show how to do this.

Every thread must yield unless an interrupt or syscall causes a context switch, i.e. if your render thread had a higher priority than your main thread, and you put it to sleep, the main thread will lose control once you wake the render thread.
"He was warned..."
mharris
Posts: 155
Joined: Sun Jan 25, 2004 2:26 pm
Location: Annapolis, MD, USA

Post by mharris »

mrbrown wrote:We use something similiar to what you describe - a thread woken up by a vblank interrupt handler.. You can use AddIntcHandler() to add a vblank handler. IIRC there are some ps2dev examples that show how to do this.
Cool, thanks. Also, I see this in kernel.h:

Code: Select all

void SetVInterruptHandler(s32 handler_num, void* handler_func);
I haven't tried it yet, but from its name. it looks like it's a VSync-specific interrupt handler. Has anyone experimented with this one? If so, what's the first argument used for, and what's the signature for the callback?
mrbrown wrote:Every thread must yield unless an interrupt or syscall causes a context switch, i.e. if your render thread had a higher priority than your main thread, and you put it to sleep, the main thread will lose control once you wake the render thread.
That's also useful information that I haven't seen elsewhere. Thanks.

I'm guessing that waiting on a semaphore will also suspend the thread -- it should context-switch (because WaitSema() is a syscall), but I assume the kernel doesn't schedule the waiting thread(s) until the semaphore has been signaled. Right?
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

mharris wrote:Cool, thanks. Also, I see this in kernel.h:

Code: Select all

void SetVInterruptHandler(s32 handler_num, void* handler_func);
I haven't tried it yet, but from its name. it looks like it's a VSync-specific interrupt handler. Has anyone experimented with this one? If so, what's the first argument used for, and what's the signature for the callback?
SetVInterruptHandler() is used to set MIPS hardware interrupt handlers, i.e. INT0 and INT1. I don't remember how all of that works, but you can only attach a vblank handler by using AddIntcHandler(), as AIH() builds on top of SetVInterruptHandler(). To make a long story short, you would only ever use SetVInterruptHandler() if you were writing kernel-level code.
mharris wrote: I'm guessing that waiting on a semaphore will also suspend the thread -- it should context-switch (because WaitSema() is a syscall), but I assume the kernel doesn't schedule the waiting thread(s) until the semaphore has been signaled. Right?
WaitSema() will suspend the calling thread if the semaphore can't be acquired. So if you create a semaphore with it's initial count at 0 and max count at 1, then the first time you call WaitSema() on that semaphore your thread will yield. And you're right, the kernel maintains a list of threads waiting on that semaphore, and their priorities are checked once the semaphore becomes available. The thread with the highest priority is woken up.

Just remember that the kernel always schedules the thread with the highest priority, and that threads with a lower priority can only be scheduled if the higher priority threads are suspended (via SleepThread, WaitSema, or whatever). Typically you give your main thread a low priority (like 100) and your render, rpc, or file I/O threads a higher priority than the main thread.
"He was warned..."
mrbrown
Site Admin
Posts: 1537
Joined: Sat Jan 17, 2004 11:24 am

Post by mrbrown »

Oh, another thing, I've seen games like Xenosaga implement a ghetto round-robin scheme using the RotateThreadReadyQueue() syscall. IIRC, they started all threads (and changed the main thread's priority) at a single priority, and when one thread was done with it's work called a function that did RotateThreadReadyQueue(). I don't know how well that worked out for them.
"He was warned..."
Post Reply