http://bugs.winehq.org/show_bug.cgi?id=29531
--- Comment #7 from Jörg Höhle hoehle@users.sourceforge.net 2012-01-27 10:46:17 CST --- The sketch of the lock-less design is as follows. Strive for a particularly simple variant of RCU. List and partition every resource that the callback (CB) accesses, i.e. what's shared among CB and API functions like ReleaseBuffer or Stop. Make it such that there is one duplex resource with everything else either Read-Only or Write-Only.
Here's an excerpt of the objects off This->
held_frames RW => InterlockedAdd
pcm_handle RO constant means valid for the extent of the cb lifetime fmt->* RO constant
session->mute is the session pointer constant?
local_buffer RO constant bufsize_frames RO constant event RO made constant by upcoming "SetEventHandle once only" patch
started R variable
lcl_offs_frames WO = CB write pointer
Mmdevapi's "initialize once then never touch" design works towards our goal.
Cannot mix types. In GetBuffer (API side) or alsa_read_data(CB), (This->lcl_offs_frames + This->held_frames) is inacceptable. Instead, each side must use its own entity. Split the read from the write pointer, adding This->xy_offs_frames. CB updates one, API the other.
In CB, + if(!in_alsa && This->held_frames < This->hidden_frames){ + UINT32 s_frames = This->hidden_frames - This->held_frames; can see s_frames < 0. C in RCU means "Copy" which is trivial here as we use a single RW object. Well, RCU is just a guide, what I describe is not RCU. Our usage of non-constant objects (held_frames and started) is basic, otherwise we'd need a "U"(pdate)-like restart transaction loop.
Add extra W variables reflecting error state as needed. I'm thinking about using This->initted to hold a steady error state, such as AUDCLNT_DEVICE_INVALIDATED.
"This->" itself must exist for the life time of CB. That's why the waitable event in DeleteTimerQueueTimer is crucial.
Quite straightforward, isn't it?