Thread-safety in winmm?

Joerg-Cyril.Hoehle at t-systems.com Joerg-Cyril.Hoehle at t-systems.com
Wed Dec 15 11:16:22 CST 2010


Hi,

I'm thankful for any kind of information regarding thread-safety and
re-entrancy involving WINMM.  I found little on the net.

Basically, I'm wondering what kind of guarantees must code in winmm
(which includes the MCI) provide w.r.t. multiple application threads
using the winmm API.

Use cases for the MCI:
A) Thread 1: MCI_PLAY, thread 2: MCI_STOP
B) Thread 1: MCI_PLAY, 2: MCI_PAUSE, 3: MCI_STOP
C) Thread 1: MCI_PLAY, 2: MCI_STOP, 3: WINMM shutdown via ^C
D) Any 2 MCI commands from 2 threads, excl. MCI_CLOSE
E) Including MCI_CLOSE

For instance, the following construct that is used in some Wine MCI
drivers is not fully thread safe:
    WINE_MCI* wmw = (WINE_MCI*)mciGetDriverData(wDevID);
    /* oops */
    if (wmw) {
        /* oops */
    	EnterCriticalSection(wmw->cs);
The thread may be preempted at oops time, another one may close the
device meanwhile; crash follows.


Note that the MCI is easier to deal with than the rest of WINMM,
because it has no CALLBACK_FUNCTION.  Function callbacks create an
additional burden via re-entrant code.  Often enough, they are
invoked within a critical section (e.g. lookup xyzNotifiyClient() in
wine*.drv/).  However, a CS is not a MUTEX, and callback code will
happily enter any critical section already hold within the same
thread!

One could say that critical sections give a wrong feeling of security.

Presumably CS are used over mutexes all over the place because a) they
are said to be more efficient b) using mutexes to protect against
re-entrant code is no solution either, as dead-locks would occur.

The above example shows that it would be tough to make a driver
thread-safe without the help of the central WINMM component.


So I'm left wondering what are adequate levels of protection that I
should design into code while I'm rewriting some of the MCI.

- Should I care about robustness against driver shutdown
  as a consequence of ^C? (a particular case of close while playing)

- One may argue that re-entrancy is not an issue for WINMM. Bug #3930,
  comment #37 explains that function callbacks should be invoked from
  another thread...

- What is the worst that I should expect?
  (We already know that apps call winmm functions from within
  callbacks even though MSDN says it may deadlock...)

- What should WINMM guarantee?
  - Will DRV_CLOSE ever be called once only?
  - Should a closing driver kill player threads or patiently wait?


I've been thinking about supporting a graduated scenario similar to:
E: robustness against any sequence of commands from a single thread;
    Bug #22978 shows mciseq does not satisfy that condition.
D: asynchronous player + thread-safe stop/reset command;
C: ...
A: fully thread- and re-entrancy-safe

One thing I've learned already is that innocent looking code such as
PostThreadMessage() may cause the currently running thread to be
preempted, even though it did not yet set all state variables
correctly.  E.g., the app may process a notification earlier that
you'd believe it does!

Thanks for any pointers,
 Jörg Höhle


More information about the wine-devel mailing list