Hi,
> [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.
It depends on what variant of SendMessage you use. I'll explain all the
variants below, but whichever thread makes the SendMessage call is blocked
until the message has been processed by the receiver. This is not the case if
using SendMessageCallback, and if we will usually be giving a response on
completion of message processing, we may wish to use this function.
> 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.
Hmmm... I don't exactly follow this. But I'll ask questions below.
> > 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?
Yes - two lots of 32 bits, and each message is uniquely defined in a header
file, along the lines of:
#define UWM_MESSAGE_X "UWM_MESSAGE_X-{580C1C32-1AE3-4013-9EC4-47D72DC508F0}"
#define UWM_MESSAGE_Y "UWM_MESSAGE_Y-{580C1C32-1AE3-4013-9EC4-47D72DC508F0}"
etc...
> 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.
So when you say 'main process' here, do you mean the main process/thread we
currently have, with what you call 'server process' being the sound server
thread? Is the main process what is doing the timing and sending of messages?
If this is the main game thread, where/why would sleep() be used? Particularly,
how is timing done - is there another thread completely whose only job is to
send a non-queued message every second to keep things synchronised and 'wake
up' the sound thread?
> 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.
I don't exactly follow this, but it might be of some help to us if I detail
some of the points you raise relating to Win32 messages a little more.
This is a summary of all send message calls:
* SendMessage - immediate message delivery
- message sender and receiver the same -> blocked until message processed
- message sender and receiver different -> blocked until message processed
* SendMessageCallback - immediate message delivery with call to callback
function when message has been processed
- message sender and receiver the same -> not blocked / returns immediately
- message sender and receiver different -> not blocked / returns immediately
* SendNotifyMessage - immediate message delivery
- message sender and receiver the same -> blocked until message processed
- message sender and receiver different -> not blocked / returns immediately
* SendMessageTimeout - too unreliable and unusable in this time-critical app
* PostMessage - queued message delivery to current thread
- never blocked / returns immediately
* PostThreadMessage - queued message delivery to specified thread
- never blocked / returns immediately
SendMessage and its variants strictly send non-queued messages that are sent
immediately to the receiving thread. PostMessage strictly send queued messages
that are put in the message queue for the receiving thread.
Once a message has been sent from a thread, if it is a SendMessage variant,
Windows immediately calls the callback function in the receiving thread ('wakes
it up'), and leaves the callback to handle the message by calling other
functions or doing whatever. If it is a PostMessage variant, Windows puts the
message in the queue for the receiving thread. The built-in message checking
loop for the receiving thread effectively calls its own callback with the next
message as parameter if it finds one waiting in the queue. Messages in a queue
are processed in a FIFO order unless you override the loop and implement your
own priorities. It should also be noted that there is a limit to the the number
of messages that can be stored in the message queue (10,000 on Windows 2000/XP).
When using SendMessage variants, if both the message sender and receiver are
the same thread, the callback function is still called as a subroutine, and if
you do use something like SendMessageCallback which returns immediately, things
could get a little unpredictable if using the same variables and memory. The
Platform SDK docs don't really explain this. Hmmm... yuck yuck yuck. To
summarise, if using a SendMessage variant where the message sender and receiver
and the same thread, life is a lot harder - you either have deadlock problems
or lose time-criticality. I think I'd use PostMessage in these cases, or else
it's just too unpredictable.
With all this said, this may change the UNIX and Win32 approaches you have
detailed below so they may need updating.
> 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
> }
So there we are. Can you please explain a little more with the extra info I've
given above?
Cheers,
Alex.