xboxscene.org forums

Author Topic: Posix Threads For Xbox  (Read 309 times)

Hyper_Eye

  • Recovered User
  • Sr. Member
  • *
  • Posts: 366
Posix Threads For Xbox
« on: August 21, 2010, 05:55:00 PM »

Ahhh pthreads, I love them and you should too. I have been using them for a long time and, while I am very familiar with the CreateThread API and related functions, pthreads equal portable code and I love portable code. The API is also consistent and the behavior is well defined.

In my effort to produce a good port of libAgar as well as a fast server query in the Odamex launcher I decided to tackle support for Agar threads which is simply an interface into pthreads. So, with pthreads-win32 in hand I started my work and the result is pthreads-xbox. The library has been mildly tested. I do not guarantee that any particular function works. I will be able to get a better idea of what does and does not work as I test it more extensively. I can tell you what my tests show to work: pthread_create, pthread_mutex_lock, pthread_mutex_unlock, and pthread_join. My testing has shown, as I expected, that pthread_kill does not work. I don't believe it will ever work so you will not be able to force kill a thread from outside the thread. A good implementation should never require you to do that anyway! For the rest of the API it is simply a matter of trying them out. There are a bunch of test applications in a folder that you could try out. I simply haven't gotten around to building any of them.

You will find the repo browser here: http://svn.huntsvega...n/pthreads-xbox?
You can check out the code with the url: http://www.huntsvega...n/pthreads-xbox

It is important to read the README that is provided. Here are the contents of it:

QUOTE
!!!IMPORTANT!!!
---------------
Please remember to initialize the pthread library
by calling ptw32_processInitialize() when your
application starts. Only call it once.  This is
required. Should you forget to do it your Xbox
will become frozen and your debugger/IDE will crash
if it is attached when you attempt to use any pthread
library function.
---------------
!!!IMPORTANT!!!

pthreads-win32 ported by Michael "Hyper_Eye" Wood
in support of the libAgar port and Odamex on behalf
of the Odamex development team.
Please visit:
http://www.odamex.net
and
http://www.xbox-scene.com

Thanks to the pthreads-win32 developers who have
made cross-platform thread code development a
thousand times easier in the face of Microsofts
refusal to support good POSIX API on their OS.

I hope current and future Xbox homebrew developers
will find this work useful and that I might
benefit from its release by playing great new
games and using great new software on my Xbox.


Let me know what you use this library for! Also, I welcome any patches that you would like committed to subversion. Should anyone be interested in commit access to any of the repos on the Huntsvegas svn server request it with a little info about what you would like to work on and I will consider it! Enjoy!

Just a note: For anyone that decides to use Agar when the new port is available, you will not need to initialize the pthread library or directly access pthread API. It will all be taken care of for you by Agar threads and you will be able to use them as you would on any other platform.

In working on this I created two examples you are welcome to try. Here are their descriptions and the code.

Example 1: This code simply starts two threads and waits for them to exit. Each thread receives an id. They loop 10 times during which they sleep (this is mostly so the first thread doesn't complete before the second one can get started), lock a mutex, print a message to the debug console, and unlock the mutex. In the meantime the main process uses pthread_join() to wait on the threads to exit. This example has to be started from the debugger (Debug->Start in your IDE) to see any output. This example only depends on the pthreads library.

CODE
#include
#include

#ifdef _XBOX
#include
#endif

pthread_mutex_t dbsMutex = PTHREAD_MUTEX_INITIALIZER;

void *pthread_func(void *arg)
{
    int id;

    if(!arg)
        return NULL;

    id = *(int*)arg;

    for(int i = 0; i < 10; i++)
    {
        char str[32];

        Sleep(500);

        pthread_mutex_lock(&dbsMutex);

        sprintf(str, "Thread %d running: Loop %d\n", id, i);
        OutputDebugString(str);

        pthread_mutex_unlock(&dbsMutex);
    }

    return NULL;
}

int main()
{
    pthread_t thread1, thread2;
    int       id1 = 1, id2 = 2;

#ifdef _XBOX
    // Initialize the pthreads library
    ptw32_processInitialize();
#endif

    OutputDebugString("Main process running! Starting threads...\n");

    // Create 2 threads
    pthread_create(&thread1, NULL, pthread_func, (void *)&id1);
    pthread_create(&thread2, NULL, pthread_func, (void *)&id2);

    // Wait for the first thread to exit
    pthread_join(thread1, NULL);
    OutputDebugString("First thread completed!\n");

    // Wait for the second thread to exit
    pthread_join(thread2, NULL);
    OutputDebugString("Second thread completed!\n");

    // Have some time to analyze your output!
    Sleep(5000);

#ifdef _XBOX
    XLaunchNewImage(NULL, NULL);
#endif

    return 0;
}


Example 2: This code utilizes SDL to draw some lines on the screen. The code initializes the pthread library and SDL. It then starts 3 threads.

Thread 1 will draw a line from the left of the screen to the right. It loops for the whole of the screen width during which it sleeps for 15ms (so that you see the line draw instead of an already drawn line), locks a mutex, gets a pointer to the next pixel it needs in the sdl surface, flips the pixel to white, and then unlocks the mutex.

Thread 2 will do the same thing thread 1 does but it will draw a line from the top of the screen to the bottom and it sleeps for 17ms instead of 15 since thread 1 has to swap more pixels and we want the lines to meet close to the center.

Thread 3 loops infinitely locking the mutex, updates the screen, and then unlocks the mutex.

The main process waits for the first 2 threads to complete, kills thread 3 (which has no affect on Xbox), gives you a moment to stare at your excellent creation which should be a quartered screen, and then quits. You may be asking why I said earlier that good code won't need pthread_kill and then I use it. Well this is an example. I would trigger the thread to quit with a variable or a condition. Problem solved.

CODE
#include
#include
#include

#include

#ifdef _XBOX
#include
#endif

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define SCREEN_DEPTH 8

// This mutex should be locked before accessing screen
pthread_mutex_t scrMutex = PTHREAD_MUTEX_INITIALIZER;

// The SDL surface
SDL_Surface *screen;

void *draw_x(void *arg)
{
    Uint8 *p;
    int    i;

    for(i = 0; i < SCREEN_WIDTH; i++)
    {
        // Sleep a bit between turning pixels on so we can see the line grow
        Sleep(15);

        pthread_mutex_lock(&scrMutex);

        // Turn on the next x pixel
        p = (Uint8 *)screen->pixels + SCREEN_HEIGHT/2 * screen->pitch + i * screen->format->BytesPerPixel;
        *p=0xff;

        pthread_mutex_unlock(&scrMutex);
    }

    return NULL;
}

void *draw_y(void *arg)
{
    Uint8 *p;
    int    i;

    for(i = 0; i < SCREEN_HEIGHT; i++)
    {
        // Sleep a tad bit longer than x so y completes at roughly the same time
        Sleep(17);

        pthread_mutex_lock(&scrMutex);

        // Turn on the next y pixel
        p = (Uint8 *)screen->pixels + i * screen->pitch + SCREEN_WIDTH/2 * screen->format->BytesPerPixel;
        *p=0xff;

        pthread_mutex_unlock(&scrMutex);
    }

    return NULL;
}

void *flip_screen(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&scrMutex);

        SDL_Flip(screen);

        pthread_mutex_unlock(&scrMutex);
    }

    return NULL;
}

int main()
{
    pthread_t thread1, thread2, thread3;

#ifdef _XBOX
    // Initialize the pthreads library
    ptw32_processInitialize();
#endif

    SDL_Init(SDL_INIT_VIDEO);

    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, SDL_SWSURFACE);

    SDL_ShowCursor(SDL_FALSE);

    OutputDebugString("Main process running! Starting threads...\n");

    // Create 3 threads
    pthread_create(&thread1, NULL, draw_x, NULL);
    pthread_create(&thread2, NULL, draw_y, NULL);
    pthread_create(&thread3, NULL, flip_screen, NULL);

    // Wait for the first thread to exit
    pthread_join(thread1, NULL);
    OutputDebugString("draw_x completed!\n");

    // Wait for the second thread to exit
    pthread_join(thread2, NULL);
    OutputDebugString("draw_y completed!\n");

    // On Xbox this function does not work.
    pthread_kill(thread3, SIGINT);
    OutputDebugString("screen_flip completed!\n");

    // Look at what we have done!... for a few seconds
    Sleep(5000);

#ifdef _XBOX
    XLaunchNewImage(NULL, NULL);
#endif

    return 0;
}
Logged

Hyper_Eye

  • Recovered User
  • Sr. Member
  • *
  • Posts: 366
Posix Threads For Xbox
« Reply #1 on: August 22, 2010, 02:18:00 PM »

I added an examples directory to the repository. It includes the two examples posted above with project files.
Logged