Hi,

> I'll attempt explain the Win32 method. If someone can please detail how the 
> UNIX/cross-platform method would/could work or fit in with this, that would be 
> wonderful.

Sure, I'll try to do that.

[SendMessage]
> The second way sounds better since it's 'immediate', but there are more risks 
> with deadlocks.

This is an issue here, since the sound server usually responds to
commands it receives. If the main thread is blocked because of the
SendMessage call (is it?), we'll deadlock at this point. We
could use PostMessage() for server->main communication, or queue up
outgoing events in the server (IIRC we're already doing this) and only
send them when process_sound() (rather than process_command()) is called,
which we don't have to do from the message handler anyway. However, this
could easily give us delayed responses in the range of deciseconds, which
isn't acceptable (since the sound server thread may be sleeping for quite
a while until it wakes up for the next sound event). This could be fixed
if your 'sleep()' can be interrupted by some means.

> message is being sent to a different thread. So the best option here assuming 
> sound events are being sent and received by the same thread (which could be 
> wrong - it depends on how the cross-platform approach is implemented), is by 
> using SendMessageCallback.

Actually, I was thinking of a two-thread approach here (which is probably
the main cause for the confusion...?)

> So that's sending and posting messages. Receiving them is a little 'hand-
> wavey'.
[...]

> LRESULT CALLBACK
> MainWndProc (HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam)

So we can effectively send 64 bits as payload (wParam and lParam), and
have to share nMsg with some global message namespace?
Our current sound events transport 3 values, so this would match nicely.

(Note that this particular callback design, and the dependancy on window
objects in general doesn't strike me as particularly well-designed for a
generic OS-level message passing mechanism, but that's a different issue.)

[...]

> And that's about it. My question is, can the UNIX/cross-platform event-driven 
> sound server be based around this or work in this way?

With two threads, it should be possible to do that. Let's first summarize
the outer edges the sound server needs to touch:

Main process/thread:
         Sound implementation must provide:
                init()/exit()/configure()/...
                send_message_to_server()
                send_song()
                poll_message()

Server process/thread:
        Sound implementation must provide:
                send_message_to_main()
        Sound implementation must INVOKE:
                snd_process_message() - each time a message is received
                snd_process_song() - each time a song is received
                snd_process_output() - each tick, or after a period determined
                                        by the function's return value, etc.


Note that the implementation of the function calling snd_process_output()
still needs to be determined in your case- you could be looping in that
separate thread, too, sleeping appropriate amounts of time and relying on
your OS' time-sharing capabilities to wake you up at an appropriate time,
or you could instruct it to send a delayed message, if that's possible. Or
you could loop and send a message to yourself. Or whatever.

Leaving out exit()/etc and song management (which should be
analogous to message handling, except that, as suggested, it should avoid
actually duplicating the data and just handle stuff in shared memory (the
resource manager code would have to decompress directly into shm to take
maximum advantage of this) where possible), this leaves to implement:

init()
send_message_to_server()
poll_message()
send_message_to_main()


UNIX approach:
--------------
queue_t queue;
int pipes[2];

init()
{
        init(queue);
        pipe(pipes);
        if (!fork()) sound_server_loop();
}

sound_server_loop()
{
        time_t next_time = get_now();

        while (1) {
                calculate(time, next_time);
                int msg_p = select(pipes, &time);
                if (msg_p)
                        snd_process_message(get_message_from_pipe(pipes));

                if (time < 0)
                        next_time = snd_process_sound();

                while (!queue.empty())
                        put_message_in_pipe(pipes, queue.pop());
        }
}

send_message_to_server(msg)
{
        put_message_in_pipe(pipes, msg);
}

poll_message()
{
        while (select(pipes, 0))
                queue.push(get_message_from_pipe(pipes));

        return queue.pop();
}

send_message_to_main(msg)
{
        queue.push(msg); /* checked and sent by the main loop */
}


Win32 message approach
----------------------
/* if POST_MESSAGE is defined, we'll be using this method, otherwise,
** we'll rely on messages interrupting 'xsleep()' in the same way
** signals interrupt sleep()/usleep() in UNIX  */

thread_t sound_thread;
thread_t main_thread;
queue_t queue_main, queue_server;

init()
{
        init(queue_main);
        init(queue_server);

        main_thread = get_current_thread();
        set_callback(main_callback);
        sound_thread = new_thread(sound_loop);
}

sound_loop()
{
        set_callback(server_callback);
        while (1) {
                xsleep(snd_process_sound());
#ifndef POST_MESSAGE
                while (!queue_server.empty())
                        Win32_SendMessage(queue_server.pop());
#endif
        }
}

main_callback(msg)
{
        queue_main.push(msg);
}

server_callback(msg)
{
        snd_process_message(msg);
}

send_message_to_server(msg)
{
        Win32_SendMessage(msg);
}

poll_message()
{
        return queue_main.pop();
}

send_message_to_main(msg)
{
#ifdef POST_MESSAGE
        Win32_PostMessage(msg);
#else
        queue_server.push(msg);
#endif
}



llap,
 Christoph


Reply via email to