Thread Safety for DirectShow Filters That Output Samples from a Workflow
I'm working on a DirectShow filter that takes input samples and turns them into modified output samples, but where there is no one-to-one correspondence between input and output samples, so CTransformFilter doesn't seem to be the way to go.
The best way to write this file is to create a filter using CBaseFilter, CBaseInputPin and CBaseOutputPin, where samples are taken on an input pin and processed by a worker thread that creates and delivers new samples from the output output. The worker thread copies the data of the input samples before starting, so my filter doesn't need to maintain a reference to the input samples outside of the input call to CBaseInputPin :: Receive.
What's the best practice to ensure thread safety and avoid deadlocks in this case? Should the input and output pins share the same streaming lock, or should they have a stream lock for their streaming operations? Do I need to store buffers, sample deliveries, and other output operations to hold a stream block and / or a filter block? Any code example that does something like this? Any other issues requiring attention in this situation?
The DirectShow base classes contain scary comments for CBaseOutputPin :: Deliver and CBaseOutputPin :: GetDeliveryBuffer that I don't quite understand (pasted in below).
/* Deliver a filled-in sample to the connected input pin. NOTE the object must have locked itself before calling us otherwise we may get halfway through executing this method only to find the filter graph has got in and disconnected us from the input pin. If the filter has no worker threads then the lock is best applied on Receive(), otherwise it should be done when the worker thread is ready to deliver. There is a wee snag to worker threads that this shows up. The worker thread must lock the object when it is ready to deliver a sample, but it may have to wait until a state change has completed, but that may never complete because the state change is waiting for the worker thread to complete. The way to handle this is for the state change code to grab the critical section, then set an abort event for the worker thread, then release the critical section and wait for the worker thread to see the event we set and then signal that it has finished (with another event). At which point the state change code can complete */
source to share
You have some samples in the Windows SDK
, and the earlier SDK has even more. This is perhaps the best example of code you can check for locking practice.
Filter and contacts usually share critical sections for thread safety. For example CTransformFilter :: m_csFilter protects the filter state data, not only the section, but contacts also use this section. The optional section is also used to serialize streaming requests (pushing samples, sending EOS notifications).
Your filter may use a critical section of state, otherwise you can use an additional synchronization object (section, reader lock, or mutexes) to avoid blocking critical section locks by base classes.
Regular sentences apply: to avoid locks, you must make sure that your locking order is designed in such a way that if section A could be blocked in a thread that is already blocked by section B, you must block B [on other threads] when without an existing lock to A so that there is no deadlock.
Typically, you have two scripts that are most commonly used in the following cases:
- you are reusing the critical section of the filter state
- you are using a separate critical section that protects your privacy, and you are not blocking the section when making calls to methods and methods of the base class of other objects, such as peer filters.
source to share