Hi,

OK well here are the results from my research on Windows messages:

1. If thread 1 has spawned thread 2, then thread 1 is the thread that
receives and processes messages for thread 2. So, using this example,
suppose a thread 3 sent thread 2 a message which took a while to process --
thread 1 is the thread that would be blocked while thread 2 processed it. I
imagine this could be got around by spawning a new thread whenever an
time-intensive message is received to handle it.

A sidenote - another thing we should look into is how messaging is currently
occurring in FreeSCI on this platform. SDL (and I have checked the source)
is currently what handles messages to the main window. As the main app
indirectly spawns an SDL thread, the way SDL processes its messages may be
taking time away from the main thread without us knowing.

2. When a message is sent or posted, it disappears somewhere into Windows,
which then makes the thread creator call the message handler of the spawned
thread to process the message. As for when this is done, I have little idea,
as while there I did declare and define a 'check for messages' message loop
in the main process of my test app, I did not do it in the threads. The
message loop in the main process was not handling the messages to the
threads I created either. So the threads must have relied on Windows which
provided a built-in message loop which jumped in to action whenever there
was a message waiting. However, if we wish to control this by always
defining a message loop, we can.

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

Sleeping unfortunately blocks the thread and the message handler of any
spawn threads. Sleeping on Windows, without doing fancy DirectX things, can
only be done to millisecond resolution anyway which from what you say isn't
enough (Matt would know for sure though, he looked into all that of course).
I would again mention SendMessageCallback() for the purpose of releasing a
mutex, but I think a different approach would be better - read my
implementation suggestion below.

As I see it the best way to implement a Win32 event-driven sound server
would be as follows:
 - still have the main process spawning a sound server as it currently does
 - the server is a Windows 'check for messages' message loop, which only
kicks into action when it receives a message (this removes the needs for
sleeps or something that would cause 100% CPU usage)
 - these messages would be sent using SendNotifyMessage() from wherever
FreeSCI currently puts sound-related stuff into the queues currently used
 - when the sound server receives a message, it spawns a thread to handle
that message so it can go back to checking for more messages
 - the spawned thread uses a mutex appropriately to ensure no deadlocks and
releases it when it's done. Since a new thread is spawned for (probably
almost) every message received, if one or more threads are blocked they just
wait for the holder of the mutex to finish before the next starts. This
won't hold up the main server at all. This gets around the problems you
mentioned with blocked threads. Any idea on the maximum number of threads we
might have running at a time and what impact that could be on system
performance?

If this is all good, i'm very happy with this idea. Does this sound OK and
can this work or fake-work under UNIX?

I neglected to mention something when sending messages for the purpose of
passing pointers. This would be dangerous in our case because there is a
possibility (again, dependent on how this is all implemented), that because
all this can be made to behave (at least, we hope) asynchronously, the
pointer may be destroyed by the time the message receiver tries to access
it.

I hope to put the app I wrote for testing this on my Web site tonight at
http://www.users.on.net/wgd/freesci/ .

Regarding going to release with this new code, I personally would prefer to
stick with what we have and concentrate on getting FSCI as stable and
bug-free as we can within a reasonable time frame. I'm a strong supporter of
only allowing good quality code with decent commenting in to a project of
this size. However, I have seen code in this project which IMHO should have
been rejected by CJR (no offense intended!). The problem is, if the code
gets in and usually works, it's a lot easier to just leave it without
cleaning it up, commenting it well, and going over it to check 'that one
more time' for mistakes. A lack of comments (even for simple code) also
makes parts of the project seem more daunting than they really are because
all you can see is block after block of code with no explanations breaking
it up.

My two cents,

Alex.


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