Hi Alex,


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

This is completely different from what I expected, and invalidates many
of the approaches I suggested.

So, just to make sure I understood you correctly:
If A < B means that thread A spawned thread B, then, if indeed A < B
holds, sending a message to B would interrupt both A and B, sending A to
sleep and making B process the message, causing it to implicitly wake up A
again when it has finished processing?
What about sending messages to thread A?

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

What were you doing in the threads? Just looping randomly, or doing system
calls of sorts?

> However, if we wish to control this by always
> defining a message loop, we can.

And this would guarantee us that all messages would be processed exactly
at the point we do the loop?

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

It's not great, but it'd be sufficient.  _
Currently, we sleep for multiples of  16.6 ms, meaning that only having
millisecond accuracy would give a timing precision loss in the order of
magnitude of about 4%. 

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

I don't see what's so bad about using sleep()- as you pointed out, it's
an elegant method not to cause 100% CPU usage. Is there some
platform-specific issue I'm missing here?

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

Hmm, where is the periodic processing of MIDI data in this model?

> 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 don't think I understand it yet- you intend to fork off threads to
process sound commands; so far, that's fine (note that this probably won't
even be neccessary in a shared address space). But you state that the
server only kicks into action when it receives a message, and that is
insufficient for playing music.

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

Pointers would only be used for songs, but you raise a valid issue here-
if the sound server thread would query the songs itself, we'd have to make
the resource manager asynchronously re-entrant (using an abstracted
sci_mutex API) and lock the resources (in order to prevent LRU
management). This would also require a minor functional change to resource
locking.
The alternative would be to transfer the pointer and lock/unlock in the
main thread, defining additional sound server messages to handle this (at
least unlocking).

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

Agreed.

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

None taken, and thanks for mentioning it.

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

Not if it just works 'usually'. Someone will take offense eventually and
give the code its deserved beating. However, there's always the risk of
old,  unsafe and ugly code being forgotten if it works fine.

Note that most parts of FreeSCI have seen a major overhaul or been
replaced at least once. There still are some relics left from the
'test & research' code that made up large parts of FreeSCI 0.2.x, though.

Particularly if the code can be replaced easily, I find little harm in
running with some 'bad' code for a few versions, and replacing it at an
appropriate time- this will allow other parts of the program that depend
on this part to be tested anyway.
Right now, the only examples of where this strategy has been applied are
the gfx subsystem and the resource manager, though.

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

Interface information is what I personally consider to be most important-
if you know what, globally speaking, a function does, it's usually easy to
understand the code, or to replace it if the function is broken beyond
hope.
(Yes, the sound subsystem still hasn't been documented, I know.)

I know that the idea of good code being as easy to read as a comment
fails to work from time to time, at least in the world of imperative
languages. Therefore, I'd suggest this: Send me a list of places where you
think comments would be in order in my code- it's always easier to
poinpoint those places if you aren't the one responsible for the code- and
I'll add those comments and try to get used to a more commented coding
style this way.
How about it?


llap,
 Christoph


Reply via email to