Discussion:
Multimedia Timer Callback Function
(too old to reply)
Michael Bate
2003-10-10 21:02:43 UTC
Permalink
According to the Windows documentation (online at
/library/en-us/multimed/htm/_win32_multimedia_timers.asp) the callback
function invoked by a multimedia timer can only do VERY limited things,
mainly MIDI functions, a couple of other timing functions, and PostMessage.

The problem with PostMessage is that this places a message in the Windows
message queue, to be dealt with when Windows gets around to it. The whole
point of the multimedia timer, I thought, was to do things at a precisely
scheduled time.

I have seen code that calls SetEvent from the callback function, even though
this is technically illegal. I have written and tested such code, and it
seems to work OK. But I am nervous.

Does anyone have any information that I am missing?

Thanks in advance.

Michael Bate
Senior Software Developer
Radionics, Inc. a division of Tyco Healthcare
Stephen Kellett
2003-10-10 22:45:31 UTC
Permalink
Post by Michael Bate
According to the Windows documentation (online at
/library/en-us/multimed/htm/_win32_multimedia_timers.asp) the callback
function invoked by a multimedia timer can only do VERY limited things,
Huh? Can you supply the quoted text? No such limitation to my knowledge
(and certainly based on various product implementations I've been
involved withon W2K/NT/XP - I have no experience when using W95/98/Me).

I think what they are implying is that any actions you take inside these
functions should be of "short" duration, where "short" means preferably
less than the interval with which the multimedia callback will be
called.

One product implementation I wrote allowed the user to choose the
callback delay (right down to 1mS accuracy). For most applications down
to 3mS was OK, but dependent upon their app it may not be. When it
wasn't OK, ranges 1..3mS would sooner or later result in a very slow
machine - it wouldn't lockup, it would just run VERY slowly. We updated
the help to make sure people knew what they were getting into with such
optimistic timing assumptions.

I think calculating Prime numbers in the callback may be optimistic :-)

Stephen
--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk
RSI Information: http://www.objmedia.demon.co.uk/rsi.html
Michael Bate
2003-10-11 00:39:45 UTC
Permalink
Stephen,

Thanks for replying. What you say makes a lot of sense. That is certainly
how it ought to work. But the reference says otherwise:

Here is an exact quote:

Applications should not call any system-defined functions from inside a
callback function, except for PostMessage, timeGetSystemTime, timeGetTime,
timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and
OutputDebugString.

This is from the MSDN online reference, found at:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/ht
m/_win32_timeproc.asp

Note that "SetEvent" (which is what I wanted to use and am using with no
obvious ill effects) is not included in that list.

Doesn't make too much sense to me, either.

I gather that you have been violating this restriction with no problems.

Thanks again for responding.

Michael Bate
Post by Stephen Kellett
Post by Michael Bate
According to the Windows documentation (online at
/library/en-us/multimed/htm/_win32_multimedia_timers.asp) the callback
function invoked by a multimedia timer can only do VERY limited things,
Huh? Can you supply the quoted text? No such limitation to my knowledge
(and certainly based on various product implementations I've been
involved withon W2K/NT/XP - I have no experience when using W95/98/Me).
I think what they are implying is that any actions you take inside these
functions should be of "short" duration, where "short" means preferably
less than the interval with which the multimedia callback will be
called.
One product implementation I wrote allowed the user to choose the
callback delay (right down to 1mS accuracy). For most applications down
to 3mS was OK, but dependent upon their app it may not be. When it
wasn't OK, ranges 1..3mS would sooner or later result in a very slow
machine - it wouldn't lockup, it would just run VERY slowly. We updated
the help to make sure people knew what they were getting into with such
optimistic timing assumptions.
I think calculating Prime numbers in the callback may be optimistic :-)
Stephen
--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk
RSI Information: http://www.objmedia.demon.co.uk/rsi.html
Stephen Kellett
2003-10-11 09:23:27 UTC
Permalink
Post by Michael Bate
Note that "SetEvent" (which is what I wanted to use and am using with no
obvious ill effects) is not included in that list.
Doesn't make too much sense to me, either.
I gather that you have been violating this restriction with no problems.
"Violating" would be a total understatement. I've written a variety of
tools using multimedia timers. If you need sub 10mS resolution you must
use the multimedia timers (I've validated them down to 1mS which is the
lowest the API allows you to specify), where as normal thread scheduling
is around the 10mS ballpark.

I approached the problems with a "lets see if we can make this work"
mentality. I don't remember reading the heading from which you quoted
the restrictions, so I didn't start with the "this isn't going to work"
mindset. I wanted to see if the idea would work and if the processing I
needed to do would fit in the narrow time window for the callback. It
did and it worked, and when it didn't, things got slow :-)

I think any CRT functions that use thread local storage to store
temporary buffers will leak the buffers when the thread dies - that is
the main problem I can see with using the multimedia timers, but as long
as you don't create too many threads that will not be an issue.

Regards

Stephen
--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk
RSI Information: http://www.objmedia.demon.co.uk/rsi.html
Lucian Wischik
2003-10-11 11:53:17 UTC
Permalink
Post by Stephen Kellett
I approached the problems with a "lets see if we can make this work"
mentality. I don't remember reading the heading from which you quoted
the restrictions, so I didn't start with the "this isn't going to work"
mindset. I wanted to see if the idea would work and if the processing I
needed to do would fit in the narrow time window for the callback. It
did and it worked, and when it didn't, things got slow :-)
I'd have thought the restriction was to do with deadlocks. The docs
say that the multimedia timer runs in its own thread. What if your
main app thread calls a windows function, and the multimedia timer
callback thread also calls a different windows function, and one of
them blocks waiting for the other? Let's suppose the multimedia thread
calls a system function which (internally) acquires lock A. Meanwhile,
the main app thread calls a system function which (internally)
acquires lock A and then blocks on the multimedia thread. Result:
deadlock.

Presumably, someone had to go through every single Windows API
function and figure out which locks it might potentially acquire (in
the current implementations of Windows as well as all future
implementations), and then say that the multimedia callbacks were
allowed to call only things that couldn't possibly lock. Except this
someone had only finished his safety evaluation on ten functions by
the time the API had to ship, or something.

--
Lucian
Stephen Kellett
2003-10-11 13:55:11 UTC
Permalink
Post by Lucian Wischik
I'd have thought the restriction was to do with deadlocks. The docs
say that the multimedia timer runs in its own thread. What if your
main app thread calls a windows function, and the multimedia timer
callback thread also calls a different windows function, and one of
them blocks waiting for the other? Let's suppose the multimedia thread
calls a system function which (internally) acquires lock A. Meanwhile,
the main app thread calls a system function which (internally)
deadlock.
Sounds plausible - this applies to threaded programming in general. I
never call SendMessage (or anything I think will call SendMessage) from
any thread other than the UI thread. PostMessage is OK though.
Post by Lucian Wischik
Presumably, someone had to go through every single Windows API
function and figure out which locks it might potentially acquire (in
the current implementations of Windows as well as all future
implementations), and then say that the multimedia callbacks were
allowed to call only things that couldn't possibly lock. Except this
someone had only finished his safety evaluation on ten functions by
the time the API had to ship, or something.
:-) Cynical?

The windows documentation is even more "you must not do" about calling
stuff in DllMain(), and yet I've written (with great care) some
deubgging code that worked just fine when called from DllMain. The code
is no longer in use, being made redundant by a more effective means of
achieving the same result.

I guess when writing documentation for various parts of the Win32 API
the authors write the documentation as disclaimers to avoid a lot of
work making the API work in situations they never expected or intended
(which given what happens behind the scenes for DllMain, is perfectly
acceptable).

Stephen
--
Stephen Kellett
Object Media Limited http://www.objmedia.demon.co.uk
RSI Information: http://www.objmedia.demon.co.uk/rsi.html
Bob Masta
2003-10-11 14:18:29 UTC
Permalink
On Sat, 11 Oct 2003 00:39:45 GMT, "Michael Bate"
Post by Michael Bate
Stephen,
Thanks for replying. What you say makes a lot of sense. That is certainly
Applications should not call any system-defined functions from inside a
callback function, except for PostMessage, timeGetSystemTime, timeGetTime,
timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and
OutputDebugString.
I think we are talking about two different things here. The callback
event is not a timer, it happens when a buffer you have sent for
output (for example) is finished, notifying you that you can refill
it. (Of course, since the sample rate is constant one might think
this is the same as a timer, but think that isn't true since Windows
and the sound card can accept buffers for internal queueing,
and tell you that it's ready for more data even before it sends the
old stuff out.)

Anyway, this has come up repeatedly in the Multimedia NG,
since the "obvious" thing to do in a callback is to call waveOutWrite
to update the buffer. In fact, I did that for a long time and never
found a problem on any system I tested it on. But others in that
NG convinced me that _somewhere_ there might be a system
or situation that can't tolerate that. So the way I do it now is
use waveOutOpen with CALLBACK_EVENT, and create a thread
that invokes WaitForSingleObject and then does the waveOutWrite.
(I also unprepare and re-prepare the header there... another thing
that seems totally redundant but which the docs are unclear about and
which others insisted is needed to avoid problems in certain
peculiar situations that I've never found.)

Hope this helps!


Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Michael Bate
2003-10-13 14:52:32 UTC
Permalink
Bob,

Thanks for the reply.

The lines that I quoted come from the multimedia timer callback function
description.

Can I assume that your multimedia timer calls SetEvent (signalling the
CALLBACK_EVENT)?

Thanks again,
Michael Bate
Post by Bob Masta
On Sat, 11 Oct 2003 00:39:45 GMT, "Michael Bate"
Post by Michael Bate
Stephen,
Thanks for replying. What you say makes a lot of sense. That is certainly
Applications should not call any system-defined functions from inside a
callback function, except for PostMessage, timeGetSystemTime,
timeGetTime,
Post by Bob Masta
Post by Michael Bate
timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, and
OutputDebugString.
I think we are talking about two different things here. The callback
event is not a timer, it happens when a buffer you have sent for
output (for example) is finished, notifying you that you can refill
it. (Of course, since the sample rate is constant one might think
this is the same as a timer, but think that isn't true since Windows
and the sound card can accept buffers for internal queueing,
and tell you that it's ready for more data even before it sends the
old stuff out.)
Anyway, this has come up repeatedly in the Multimedia NG,
since the "obvious" thing to do in a callback is to call waveOutWrite
to update the buffer. In fact, I did that for a long time and never
found a problem on any system I tested it on. But others in that
NG convinced me that _somewhere_ there might be a system
or situation that can't tolerate that. So the way I do it now is
use waveOutOpen with CALLBACK_EVENT, and create a thread
that invokes WaitForSingleObject and then does the waveOutWrite.
(I also unprepare and re-prepare the header there... another thing
that seems totally redundant but which the docs are unclear about and
which others insisted is needed to avoid problems in certain
peculiar situations that I've never found.)
Hope this helps!
Bob Masta
dqatechATdaqartaDOTcom
D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com
Bob Masta
2003-10-14 11:32:01 UTC
Permalink
On Mon, 13 Oct 2003 10:52:32 -0400, "Michael Bate"
Post by Michael Bate
Bob,
Thanks for the reply.
The lines that I quoted come from the multimedia timer callback function
description.
Can I assume that your multimedia timer calls SetEvent (signalling the
CALLBACK_EVENT)?
Thanks again,
Michael Bate
Michael:

First, let me apologize: It was me who was talking about
something different. I had skimmed your initial post too
quickly, and then saw you had quoted the section in the
API about callbacks... which is the same text that waveOut
implementations are concerned with. But with waveOut
(and waveIn) you don't use an actual timer to pace the
operations; the sound card has its own internal pacer
timer that runs at the sample rate, and all you need to
do is feed it a buffer of data whenever it needs one
(for waveOut). The CALLBACK_EVENT is signalled
by the card/driver whenever a buffer runs dry, if you
specify that during waveOutOpen. I don't use
an explicit SetEvent. I use CreateEvent to get an
event handle that I specify in waveOutOpen,
and CreateThread to create a handler for the callback
events. The handler invokes WaitForSingleObject
and when it gets a hit it updates the next buffer and
writes it to the card. This works for the wave functions,
and I assume something similar would work for MIDI
though I haven't done that.





Bob Masta
dqatechATdaqartaDOTcom

D A Q A R T A
Data AcQuisition And Real-Time Analysis
www.daqarta.com

Loading...