SimpleAudio/Plug-In/SA_Object.h

/*
     File: SA_Object.h 
 Abstract:  Part of SimpleAudioDriver Plug-In Example  
  Version: 1.0.1 
  
 Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
 Inc. ("Apple") in consideration of your agreement to the following 
 terms, and your use, installation, modification or redistribution of 
 this Apple software constitutes acceptance of these terms.  If you do 
 not agree with these terms, please do not use, install, modify or 
 redistribute this Apple software. 
  
 In consideration of your agreement to abide by the following terms, and 
 subject to these terms, Apple grants you a personal, non-exclusive 
 license, under Apple's copyrights in this original Apple software (the 
 "Apple Software"), to use, reproduce, modify and redistribute the Apple 
 Software, with or without modifications, in source and/or binary forms; 
 provided that if you redistribute the Apple Software in its entirety and 
 without modifications, you must retain this notice and the following 
 text and disclaimers in all such redistributions of the Apple Software. 
 Neither the name, trademarks, service marks or logos of Apple Inc. may 
 be used to endorse or promote products derived from the Apple Software 
 without specific prior written permission from Apple.  Except as 
 expressly stated in this notice, no other rights or licenses, express or 
 implied, are granted by Apple herein, including but not limited to any 
 patent rights that may be infringed by your derivative works or by other 
 works in which the Apple Software may be incorporated. 
  
 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
 MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
 THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
 FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
 OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
  
 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
 MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
 AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
 STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
 POSSIBILITY OF SUCH DAMAGE. 
  
 Copyright (C) 2013 Apple Inc. All Rights Reserved. 
  
*/
/*==================================================================================================
    SA_ObjectMap.h
==================================================================================================*/
#if !defined(__SA_ObjectMap_h__)
#define __SA_ObjectMap_h__
 
//==================================================================================================
//  Includes
//==================================================================================================
 
//  PublicUtility Includes
#include "CAMutex.h"
 
//  System Includes
#include <CoreAudio/AudioServerPlugIn.h>
 
//  Standard Library Includes
#include <vector>
 
//==================================================================================================
//  Types
//==================================================================================================
 
//==================================================================================================
//  SA_Object
//
//  This is the base class for objects managed by SA_ObjectMap. It's only job is to ensure that
//  objects of this type have the proper external semantics for a reference counted object. This
//  means that the desctructor is protected so that these objects cannot be deleted directly. Also,
//  these objects many not make a copy of another object or be assigned from another object. Note
//  that the reference count of the object is tracked and owned by the SA_ObjectMap.
//
//  These objects provide RTTI information tied to the constants describing the HAL's API class
//  hierarchy as described in the headers. The class ID and base class IDs passed in to the
//  constructor must operate with the semantics described in AudioObjectBase.h where the base class
//  has to always be one of the standard classes. The class ID can be a custom value or a standard
//  value however. If it is a standard value, the base class should be the proper standard base
//  class. So for example, a standard volume control object will say that it's class is
//  kAudioVolumeControlClassID and that its base class is kAudioLevelControlClassID. In the case of
//  a custom boolean control, it would say that it's class is a custom value like 'MYBL' and that
//  its base class is kAudioBooleanControlClassID.
//
//  Subclasses of this class must implement Activate(). This method is called after an object has
//  been constructed and inserted into the object map. Until Activate() is called, a constructed
//  object may not do anything active such as sending/receiving notifications or creating other
//  objects. Active operations may be performed in the Activate() method proper however. Note that
//  Activate() is called prior to any references to the object being handed out. As such, it does
//  not need to worry about being thread safe while Activate() is in progress.
//
//  Subclasses of this class must also implement Deactivate(). This method is called when the object
//  is at the end of it's lifecycle. Once Deactivate() has been called, the object may no longer
//  perform active opertions, including Deactivating other objects. This is based on the notion that
//  all the objects have a definite point at which they are considered dead to the outside world.
//  For example, an AudioDevice object is dead if it's hardware is unplugged. The point of death is
//  the notification the owner of the device gets to signal that it has been unplugged. Note that it
//  is both normal and expected that a dead object might still have outstanding references. Thus, an
//  object has to put in some care to do the right thing when these zombie references are used. The
//  best thing to do is to just have those queries return appropriate errors.
//
//  Deactivate() itself needs to be thread safe with respect to other opertions taking place on the
//  object. This also means taking care to handle the Deactivation of owned objects. For example, an
//  AudioDevice object will almost always own one or more AudioStream objects. If the stream is in a
//  separate lock domain from it's owning device, then the device has to be very careful about how
//  it deactivates the stream such that it doesn't try to lock the stream's lock while holding the
//  device's lock which will inevitably lead to a deadlock situation. There are two reasonable
//  approaches to dealing with this kind of situation. The first is to just not get into it by
//  making the device share a lock domain with all it's owned objects like streams and controls. The
//  other approach is to use dispatch queues to make the work of Deactivating owned objects take
//  place outside of the device's lock domain. For example, if the device needs to deactivate a
//  stream, it can remove the stream from any tracking in the device object and then dispatch
//  asynchronously the Deactivate() call on the stream and the release of the reference the device
//  has on the stream.
//
//  Note that both Activate() and Deactiveate() are called by objects at large. Typically,
//  Activate() is called by the creator of the object, usually right after the object has been
//  allocated. Deactivate() will usually be called by the owner of the object upon recognizing that
//  the object is dead to the outside world. Going back to the example of an AudioDevice getting
//  unplugged, the Deactivate() method will be called by whomever receives the notification about
//  the hardware going away, which is often the owner of the object.
//
//  This class also defines methods to implement the portion of the
//  AudioServerPlugInDriverInterface that deals with properties. The five methods all have the same
//  basic arguments and semantics. The class also provides the implementation for
//  the minimum required properties for all AudioObjects. There is a detailed commentary about each
//  specific property in the GetPropertyData() method.
//
//  It is important that a thread retain and hold a reference while it is using an SA_Object and 
//  that the reference be released promptly when the thread is finished using the object. By
//  assuming this, an SA_Objects can minimize the amount of locking it needs to do. In particular,
//  purely static or invariant data can be handled without any locking at all.
//==================================================================================================
 
class SA_Object
{
 
#pragma mark Construction/Destruction
public:
                        SA_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID);
                    
    virtual void        Activate();
    virtual void        Deactivate();
 
protected:
    virtual             ~SA_Object();
 
private:
                        SA_Object(const SA_Object&);
    SA_Object&          operator=(const SA_Object&);
 
#pragma mark Attributes
public:
    AudioObjectID       GetObjectID() const         { return mObjectID; }
    void*               GetObjectIDAsPtr() const    { uintptr_t thePtr = mObjectID; return reinterpret_cast<void*>(thePtr); }
    AudioClassID        GetClassID() const          { return mClassID; }
    AudioClassID        GetBaseClassID() const      { return mBaseClassID; }
    AudioObjectID       GetOwnerObjectID() const    { return mOwnerObjectID; }
    bool                IsActive() const            { return mIsActive; }
 
#pragma mark Property Operations
public:
    virtual bool        HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
    virtual bool        IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const;
    virtual UInt32      GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const;
    virtual void        GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const;
    virtual void        SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
 
#pragma mark Implementation
protected:
    friend class        SA_ObjectMap;
    
    AudioObjectID       mObjectID;
    AudioClassID        mClassID;
    AudioClassID        mBaseClassID;
    AudioObjectID       mOwnerObjectID;
    bool                mIsActive;
 
};
 
//==================================================================================================
//  SA_ObjectMap
//
//  SA_ObjectMap is a singleton object that maintains the mapping between AudioObjectIDs and
//  SA_Objects. In the process, it also manages the reference counting mechanism as well. The same
//  object can be mapped to multiple IDs.
//
//  The creator of an SA_Object must register the object with this map prior to calling Activate()
//  on the new object. The typical sequence of events for creating an object go like this:
//      - Get an AudioObjectID for the new object (via GetNextObjectID())
//      - Create the new object
//      - Register the object with the map (via MapObject())
//      - Activate the new object
//==================================================================================================
 
class SA_ObjectMap
{
 
#pragma mark Construction/Destruction
private:
                                        SA_ObjectMap();
    virtual                             ~SA_ObjectMap();
    
    static void                         StaticInitializer();
                                        SA_ObjectMap(const SA_ObjectMap&);
    SA_ObjectMap&                       operator=(const SA_ObjectMap&);
 
#pragma mark External Methods
public:
    static AudioObjectID                GetNextObjectID();
    static bool                         MapObject(AudioObjectID inObjectID, SA_Object* inObject);
    static void                         UnmapObject(AudioObjectID inObjectID, SA_Object* inObject);
    static SA_Object*                   CopyObjectByObjectID(AudioObjectID inObjectID);
    template <class T> static T*        CopyObjectOfClassByObjectID(AudioObjectID inObjectID)           { return reinterpret_cast<T*>(CopyObjectByObjectID(inObjectID)); }
    static UInt64                       RetainObject(SA_Object* inObject);
    static UInt64                       ReleaseObject(SA_Object* inObject);
    static void                         Dump();
 
private:
    void                                DestroyObject(SA_Object* inObject);
 
#pragma mark Internal Methods
private:
    AudioObjectID                       _GetNextObjectID();
    bool                                _MapObject(AudioObjectID inObjectID, SA_Object* inObject);
    void                                _UnmapObject(AudioObjectID inObjectID, SA_Object* inObject);
    SA_Object*                          _CopyObjectByObjectID(AudioObjectID inObjectID);
    UInt64                              _RetainObject(SA_Object* inObject);
    UInt64                              _ReleaseObject(SA_Object* inObject);
    void                                _Dump();    
 
#pragma mark Implemenatation
private:
    typedef std::vector<AudioObjectID>  ObjectIDList;
    struct ObjectInfo
    {
        SA_Object*                      mObject;
        UInt64                          mReferenceCount;
        ObjectIDList                    mObjectIDList;
 
                                        ObjectInfo(AudioObjectID inObjectID, SA_Object* inObject)   : mObject(inObject), mReferenceCount(1), mObjectIDList(1, inObjectID) {}
        bool                            operator==(AudioObjectID inObjectID) const                  { return std::find(mObjectIDList.begin(), mObjectIDList.end(), inObjectID) != mObjectIDList.end(); }
        bool                            operator==(const SA_Object* inObject) const                 { return mObject == inObject; }
    };
    typedef std::vector<ObjectInfo>     ObjectInfoList;
    
    CAMutex                             mMutex;
    AudioObjectID                       mNextObjectID;
    ObjectInfoList                      mObjectInfoList;
    
    static pthread_once_t               sStaticInitializer;
    static SA_ObjectMap*                sInstance;
    
};
 
//==================================================================================================
//  SA_ObjectReleaser
//==================================================================================================
 
template <typename T>
class SA_ObjectReleaser
{
 
#pragma mark Construction/Destruction
public:
    explicit            SA_ObjectReleaser(T* inObject)                          : mObject(inObject) {}
                        SA_ObjectReleaser(const SA_ObjectReleaser& inObject)    : mObject(inObject.mObject) { SA_ObjectMap::RetainObject(mObject); }
                        ~SA_ObjectReleaser()                                    { SA_ObjectMap::ReleaseObject(mObject); }
    SA_ObjectReleaser&  operator=(const SA_ObjectReleaser& inObject)            { if(mObject != inObject.mObject) { SA_ObjectMap::ReleaseObject(mObject); } mObject = inObject.mObject; SA_ObjectMap::RetainObject(mObject); return *this; }
    
 
#pragma mark Operations
public:
                    operator T*() const         { return mObject; }
    T*              operator ->() const         { return mObject; }
    bool            IsValid() const             { return mObject != NULL; }
    T*              GetObject() const           { return mObject; }
 
#pragma mark Implementation
private:
    T*              mObject;
    
};
 
#endif  //  __SA_ObjectMap_h__