Proposed Enhancements to PortAudio API

012 - Additional Pa_Terminate() Behavior

Enhancement Proposals Index, PortAudio Home Page

Updated: October 21, 2002

Status

This proposal is sufficiently well defined to be implemented and has been implemented in v19-devel.

Background

Some host APIs (eg ASIO, MME and DirectSound under Windows NT) can require a reboot to free devices when they are not closed properly due to a program not calling Pa_CloseStream() either in error, or due to a crash. As a quality of implementation issue PortAudio should seek to avoid such circumstances.

Proposal

To improve the chances that all streams are closed before an application using PortAudio exits, PortAudio will keep track of all open streams and automatically close any that are still open when Pa_Terminate() is called. If a process calls Pa_Initialise()/Pa_Terminate() in two or more separate subsystems, the first call to Pa_Terminate() must not close streams beloning to another subsystem. Therefore, PortAudio will require calls to Pa_Initialize() to be correctly matched with calls to Pa_Terminate(), and will only close open streams when the final call to Pa_Terminate() is made for which it has registered a matching Pa_Initialize().

The definition of Pa_Initialize() will be extended as follows:

/** Library initialization function - call this before using PortAudio.
 This function initialises internal data structures and prepares underlying
 host APIs for use. This function MUST be called before using any other
 PortAudio API functions.

 If Pa_Initialize() is called multiple times, each call must be matched with
 a corresponding call to Pa_Terminate(). Pairs of calls to
 Pa_Initialize()/Pa_Terminate() may overlap, and are not requireed to be fully
 nested.
*/
PaError Pa_Initialize( void );

The definition of Pa_Terminate() will be extended as follows:

/** Library termination function - call this when finished using PortAudio.
 This function deallocates all resources allocated by PortAudio since it was
 initializied by a call to Pa_Initialize(). In cases where Pa_Initialize() has
 been called multiple times, each call must be matched with a corresponding call
 to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
 close any PortAudio streams that are still open.

 Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
 Failure to do so may result in serious resource leaks, such as audio devices
 not being available until the next reboot.
*/
PaError Pa_Terminate( void );

Implementation Notes

One possible implementation strategy would be to add a "next" member to the internal stream data structure thus making it a linked list node, which could be linked into a list of all open streams. It has been noted that using a doubly linked list would make closing streams a constant-time operation.

Discussion

Some concerns have been raised about the overhead involved in PortAudio having to keep track of which streams are currently open.

The original (V18 and previous) behavior of calling Pa_Initialize() / Pa_Terminate() multiple times was that the first call to Pa_Initialize() would initialize the library, and the first call to Pa_Terminate() would uninitialize it. For some time it was considered desirable to retain this behavior. However, as noted above, this is not the best behavior for clients who initialize PortAudio multiple times from independent subsystems - especially when Pa_Terminate() can close all open streams. It was concluded that it was best to require matched calls to Pa_Initialize()/Pa_Terminate() as described in this proposal.

Impact Analysis

This proposal changes the termination behaviour of PortAudio to reduce the likelihood of resource leaks.

On Windows, the new Pa_Terminate() behaviour would allow users who want full protection against device leakage to install a global Win32 exception handler that calls Pa_Terminate() before exiting when a crash occurs. Similar techniques (using SIGNAL handlers perhaps?) may be possible on other platforms where necessary.