Hi,

> > 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?

Yes, exactly.

> Is the main process what is doing the timing and sending of messages? 

Only the sending of messages. I thought of the sound server process as
doing its own timing, although an implementation with three threads is
conceiveable (we can't do reasonable timing in the main thread).

> If this is the main game thread, where/why would sleep() be used?

Sub-second sleeps are used in a few kernel functions, but this isn't
relevant to the matters at hand.

> 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?

No. In the concept I outlined, the sound server thread would be running
all the time, although it'd spend most of that time in a sub-second sleep.
Unless Win32 has deferred events (delivered after a specified amount of
milli- or even microseconds has passed), we need a loop with a delay
mechanism to keep the sound server running.

I'm not certain how this should be implemented, though.

> This is a summary of all send message calls:
[...]
> 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 are the messages in the queue processed? Is there some sort of
"process the next incoming message" call, or is the thread interrupted at
whatever point it may be in at an undetermined time later on?

What I'd like to see would be a
"sleep_n_microseconds_and_process_messages_in_the_meantime(long n)" call,
or an emulation of this call- the SendNotifyMessage()-like functions
appear to interrupt the thread at whatever point it currently is at, so
that manipulating message queues etc. isn't possible without risking
undetermined behaviour. We also can't use mutexes here, since the
callback function must return in order for the mutex to be released.

(BTW, on an unrelated note- in case this isn't clear, I don't think
there's a point in having a thread send messages to itself in our current
scenario.)

> With all this said, this may change the UNIX and Win32 approaches you have 
> detailed below so they may need updating.

They might need some functions updated, but first I need to know a few
more things about message delivery:

> > 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
> >     }
> > }

This is what the sound server thread is supposed to do all the time,
unless it has received a command (and therefore has its server_callback()
function invoked).
The critical point here is: When can this server_callback() function be
called? If we can get it to be called only while xsleep() is running, then
Bob's your uncle. Otherwise, we risk data corruption-
snd_process_message() may not be called while snd_process_sound() is being
run.
If this cannot be guaranteed, we need a third (command) thread:

TMain   TCmd    TServer
 .       .       .
 .       .       M
 .       .       .
 .       .       M
 s_______.       .
 .       C============
 ._______._______M (*)
 c       .       .

where:
        [.]: Doing boring stuff
        [_]: Message transmission
        [=]: Mutex
        [s]: Sending command
        [C]: Executing command
        [M]: Making music
        [c]: Receiving message in the main thread

The reason for this is that we cannot use mutexes otherwise. If the sound
server thread has locked mutex M while running snd_process_sound() and
suddenly is interrupted to call snd_process_message(),
snd_process_message() can't do anything about mutex M- it can't ignore it
without risking data corruption, and it can't wait for it to be freed
since it is running in the same thread that locked M in the first place.

A similar problem occurs when sending back- if both TCmd and TServer want
to reply at the same time, we risk a deadlock- this could be circumvented
by queueing all results in the same queue and letting it be processed by
TServer- however, in this case we risk latencies, unless we can shake
TServer out of its sleep when we want something to be sent.


OK, to summarize, I currently see two acceptable solutions. I'll try to
phrase the requirements reasonably precise:

Solution 1:
-----------
[where we use two threads and force message execution at a specified point
in an infinite loop running in the second (sound server) thread]

For this we need:
- a Win32 function foosleep()
- a Win32 function fooPostMessage()
for which the following holds:
- fooPostMessage(MSG, THR) can send a message MSG to a thread THR, where:
        for any MSG:
        - MSG is processed in THR iff THR is running foosleep()
                OR
        - receiving MSG in THR causes any running foosleep() to be
          aborted, and there exists a function fooprocess() which
          processes MSG explicitly
- foosleep() can suspend thread execution for a time interval specified in
  microsecond resolution, unless it is interrupted

Solution 2:
-----------
[where we use three threads and coordinate responses in the server thread]

For this we need:
- a Win32 function barsleep()
- a Win32 function barwakeup()
for which the following holds:
- barwakeup(THR) on a thread THR will cause any barsleep() running in THR
  to stop waiting and return control to the next instruction after
  barsleep() (again, in THR).
- barsleep() can suspend thread execution for a time interval specified in
  microsecond resolution, unless it is interrupted


Is either of this possible?

llap,
 Christoph


Reply via email to