<commit_before>/*************************************************************************
 *
 *  $RCSfile: cacheoptions.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: ka $ $Date: 2001-04-19 12:15:57 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#pragma hdrstop

//_________________________________________________________________________________________________________________
//  includes
//_________________________________________________________________________________________________________________

#include "cacheoptions.hxx"

#ifndef _UTL_CONFIGMGR_HXX_
#include <unotools/configmgr.hxx>
#endif
#ifndef _UTL_CONFIGITEM_HXX_
#include <unotools/configitem.hxx>
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_ANY_HXX_
#include <com/sun/star/uno/Any.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif

//_________________________________________________________________________________________________________________
//  namespaces
//_________________________________________________________________________________________________________________

using namespace ::utl;
using namespace ::rtl;
using namespace ::osl;
using namespace ::com::sun::star::uno;

//_________________________________________________________________________________________________________________
//  const
//_________________________________________________________________________________________________________________

#define ROOTNODE_START                      OUString(RTL_CONSTASCII_USTRINGPARAM("Office.Common/Cache"  ))
#define DEFAULT_WRITEROLE                   20
#define DEFAULT_DRAWINGOLE                  20
#define DEFAULT_GRFMGR_TOTALSIZE            10000000
#define DEFAULT_GRFMGR_OBJECTSIZE           2400000

#define PROPERTYNAME_WRITEROLE              OUString(RTL_CONSTASCII_USTRINGPARAM("Writer/OLE_Objects"))
#define PROPERTYNAME_DRAWINGOLE             OUString(RTL_CONSTASCII_USTRINGPARAM("DrawingEngine/OLE_Objects"))
#define PROPERTYNAME_GRFMGR_TOTALSIZE       OUString(RTL_CONSTASCII_USTRINGPARAM("GraphicManager/TotalCacheSize"))
#define PROPERTYNAME_GRFMGR_OBJECTSIZE      OUString(RTL_CONSTASCII_USTRINGPARAM("GraphicManager/ObjectCacheSize"))

#define PROPERTYHANDLE_WRITEROLE            0
#define PROPERTYHANDLE_DRAWINGOLE           1
#define PROPERTYHANDLE_GRFMGR_TOTALSIZE     2
#define PROPERTYHANDLE_GRFMGR_OBJECTSIZE    3

#define PROPERTYCOUNT                       4

class SvtCacheOptions_Impl : public ConfigItem
{
public:

//---------------------------------------------------------------------------------------------------------
//  constructor / destructor
//---------------------------------------------------------------------------------------------------------

     SvtCacheOptions_Impl();
    ~SvtCacheOptions_Impl();

//---------------------------------------------------------------------------------------------------------
//  overloaded methods of baseclass
//---------------------------------------------------------------------------------------------------------

    virtual void    Commit();

//---------------------------------------------------------------------------------------------------------
//  public interface
//---------------------------------------------------------------------------------------------------------

    sal_Int32       GetWriterOLE_Objects() const;
    sal_Int32       GetDrawingEngineOLE_Objects() const;
    sal_Int32       GetGraphicManagerTotalCacheSize() const;
    sal_Int32       GetGraphicManagerObjectCacheSize() const;

    void            SetWriterOLE_Objects( sal_Int32 nObjects );
    void            SetDrawingEngineOLE_Objects( sal_Int32 nObjects );
    void            SetGraphicManagerTotalCacheSize( sal_Int32 nTotalCacheSize );
    void            SetGraphicManagerObjectCacheSize( sal_Int32 nObjectCacheSize );

//-------------------------------------------------------------------------------------------------------------
//  private methods
//-------------------------------------------------------------------------------------------------------------

private:

    static Sequence< OUString > impl_GetPropertyNames();

//-------------------------------------------------------------------------------------------------------------
//  private member
//-------------------------------------------------------------------------------------------------------------

private:

        sal_Int32   mnWriterOLE;
        sal_Int32   mnDrawingOLE;
        sal_Int32   mnGrfMgrTotalSize;
        sal_Int32   mnGrfMgrObjectSize;
};

//_________________________________________________________________________________________________________________
//  definitions
//_________________________________________________________________________________________________________________

//*****************************************************************************************************************
//  constructor
//*****************************************************************************************************************
SvtCacheOptions_Impl::SvtCacheOptions_Impl() :
    ConfigItem( ROOTNODE_START  ),
    mnWriterOLE( DEFAULT_WRITEROLE ),
    mnDrawingOLE( DEFAULT_DRAWINGOLE ),
    mnGrfMgrTotalSize( DEFAULT_GRFMGR_TOTALSIZE ),
    mnGrfMgrObjectSize( DEFAULT_GRFMGR_OBJECTSIZE )
{
    Sequence< OUString >    seqNames( impl_GetPropertyNames() );
    Sequence< Any >         seqValues   = GetProperties( seqNames ) ;

    DBG_ASSERT( !(seqNames.getLength()!=seqValues.getLength()), "SvtCacheOptions_Impl::SvtCacheOptions_Impl()\nI miss some values of configuration keys!\n" );

    // Copy values from list in right order to ouer internal member.
    sal_Int32 nPropertyCount = seqValues.getLength();
    sal_Int32 nProperty = 0;

    for( nProperty=0; nProperty<nPropertyCount; ++nProperty )
    {
        DBG_ASSERT( !(seqValues[nProperty].hasValue()==sal_False), "SvtCacheOptions_Impl::SvtCacheOptions_Impl()\nInvalid property value for property detected!\n" );

        switch( nProperty )
        {
            case PROPERTYHANDLE_WRITEROLE:
            {
                if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                    seqValues[nProperty] >>= mnWriterOLE;
            }
            break;

            case PROPERTYHANDLE_DRAWINGOLE:
            {
                if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                    seqValues[nProperty] >>= mnDrawingOLE;
            }
            break;

            case PROPERTYHANDLE_GRFMGR_TOTALSIZE:
            {
                if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                    seqValues[nProperty] >>= mnGrfMgrTotalSize;
            }
            break;

            case PROPERTYHANDLE_GRFMGR_OBJECTSIZE:
            {
                if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                    seqValues[nProperty] >>= mnGrfMgrObjectSize;
            }
            break;
        }
    }
}

//*****************************************************************************************************************
//  destructor
//*****************************************************************************************************************
SvtCacheOptions_Impl::~SvtCacheOptions_Impl()
{
    if( IsModified() )
        Commit();
}

//*****************************************************************************************************************
//  Commit
//*****************************************************************************************************************
void SvtCacheOptions_Impl::Commit()
{
    Sequence< OUString >    aSeqNames( impl_GetPropertyNames() );
    Sequence< Any >         aSeqValues( aSeqNames.getLength() );

    for( sal_Int32 nProperty = 0, nCount = aSeqNames.getLength(); nProperty < nCount; ++nProperty )
    {
        switch( nProperty )
        {
            case PROPERTYHANDLE_WRITEROLE:
                aSeqValues[nProperty] <<= mnWriterOLE;
            break;

            case PROPERTYHANDLE_DRAWINGOLE:
                aSeqValues[nProperty] <<= mnDrawingOLE;
            break;

            case PROPERTYHANDLE_GRFMGR_TOTALSIZE:
                aSeqValues[nProperty] <<= mnGrfMgrTotalSize;
            break;

            case PROPERTYHANDLE_GRFMGR_OBJECTSIZE:
                aSeqValues[nProperty] <<= mnGrfMgrObjectSize;
            break;
        }
    }

    PutProperties( aSeqNames, aSeqValues );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetWriterOLE_Objects() const
{
    return mnWriterOLE;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetDrawingEngineOLE_Objects() const
{
    return mnDrawingOLE;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetGraphicManagerTotalCacheSize() const
{
    return mnGrfMgrTotalSize;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetGraphicManagerObjectCacheSize() const
{
    return mnGrfMgrObjectSize;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetWriterOLE_Objects( sal_Int32 nWriterOLE )
{
    mnWriterOLE = nWriterOLE;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetDrawingEngineOLE_Objects( sal_Int32 nDrawingOLE )
{
    mnDrawingOLE = nDrawingOLE;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetGraphicManagerTotalCacheSize( sal_Int32 nGrfMgrTotalSize )
{
    mnGrfMgrTotalSize = nGrfMgrTotalSize;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetGraphicManagerObjectCacheSize( sal_Int32 nGrfMgrObjectSize )
{
    mnGrfMgrObjectSize = nGrfMgrObjectSize;
    SetModified();
}

//*****************************************************************************************************************
//  private method
//*****************************************************************************************************************
Sequence< OUString > SvtCacheOptions_Impl::impl_GetPropertyNames()
{
    // Build static list of configuration key names.
    static const OUString pProperties[] =
    {
        PROPERTYNAME_WRITEROLE,
        PROPERTYNAME_DRAWINGOLE,
        PROPERTYNAME_GRFMGR_TOTALSIZE,
        PROPERTYNAME_GRFMGR_OBJECTSIZE
    };
    // Initialize return sequence with these list ...
    static const Sequence< OUString > seqPropertyNames( pProperties, PROPERTYCOUNT );
    // ... and return it.
    return seqPropertyNames;
}

//*****************************************************************************************************************
//  initialize static member
//  DON'T DO IT IN YOUR HEADER!
//  see definition for further informations
//*****************************************************************************************************************
SvtCacheOptions_Impl*   SvtCacheOptions::m_pDataContainer = NULL;
sal_Int32               SvtCacheOptions::m_nRefCount = 0;

//*****************************************************************************************************************
//  constructor
//*****************************************************************************************************************
SvtCacheOptions::SvtCacheOptions()
{
    // Global access, must be guarded (multithreading!).
    MutexGuard aGuard( GetOwnStaticMutex() );
    // Increase ouer refcount ...
    ++m_nRefCount;
    // ... and initialize ouer data container only if it not already!
    if( m_pDataContainer == NULL )
    {
        m_pDataContainer = new SvtCacheOptions_Impl();
    }
}

//*****************************************************************************************************************
//  destructor
//*****************************************************************************************************************
SvtCacheOptions::~SvtCacheOptions()
{
    // Global access, must be guarded (multithreading!)
    MutexGuard aGuard( GetOwnStaticMutex() );
    // Decrease ouer refcount.
    --m_nRefCount;
    // If last instance was deleted ...
    // we must destroy ouer static data container!
    if( m_nRefCount <= 0 )
    {
        delete m_pDataContainer;
        m_pDataContainer = NULL;
    }
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetWriterOLE_Objects() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetWriterOLE_Objects();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetDrawingEngineOLE_Objects() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetDrawingEngineOLE_Objects();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetGraphicManagerTotalCacheSize() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetGraphicManagerTotalCacheSize();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetGraphicManagerObjectCacheSize() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetGraphicManagerObjectCacheSize();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetWriterOLE_Objects( sal_Int32 nWriterOLE )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetWriterOLE_Objects( nWriterOLE );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetDrawingEngineOLE_Objects( sal_Int32 nDrawingOLE )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetDrawingEngineOLE_Objects( nDrawingOLE );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetGraphicManagerTotalCacheSize( sal_Int32 nGrfMgrTotalSize )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetGraphicManagerTotalCacheSize( nGrfMgrTotalSize );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetGraphicManagerObjectCacheSize( sal_Int32 nGrfMgrObjectSize )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetGraphicManagerObjectCacheSize( nGrfMgrObjectSize );
}

//*****************************************************************************************************************
//  private method
//*****************************************************************************************************************
Mutex& SvtCacheOptions::GetOwnStaticMutex()
{
    // Initialize static mutex only for one time!
    static Mutex* pMutex = NULL;
    // If these method first called (Mutex not already exist!) ...
    if( pMutex == NULL )
    {
        // ... we must create a new one. Protect follow code with the global mutex -
        // It must be - we create a static variable!
        MutexGuard aGuard( Mutex::getGlobalMutex() );
        // We must check our pointer again - because it can be that another instance of ouer class will be fastr then these!
        if( pMutex == NULL )
        {
            // Create the new mutex and set it for return on static variable.
            static Mutex aMutex;
            pMutex = &aMutex;
        }
    }
    // Return new created or already existing mutex object.
    return *pMutex;
}
<commit_msg>#86165#: assertions removed<commit_after>/*************************************************************************
 *
 *  $RCSfile: cacheoptions.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: ka $ $Date: 2001-04-19 12:24:50 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#pragma hdrstop

//_________________________________________________________________________________________________________________
//  includes
//_________________________________________________________________________________________________________________

#include "cacheoptions.hxx"

#ifndef _UTL_CONFIGMGR_HXX_
#include <unotools/configmgr.hxx>
#endif
#ifndef _UTL_CONFIGITEM_HXX_
#include <unotools/configitem.hxx>
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_ANY_HXX_
#include <com/sun/star/uno/Any.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_HXX_
#include <com/sun/star/uno/Sequence.hxx>
#endif

//_________________________________________________________________________________________________________________
//  namespaces
//_________________________________________________________________________________________________________________

using namespace ::utl;
using namespace ::rtl;
using namespace ::osl;
using namespace ::com::sun::star::uno;

//_________________________________________________________________________________________________________________
//  const
//_________________________________________________________________________________________________________________

#define ROOTNODE_START                      OUString(RTL_CONSTASCII_USTRINGPARAM("Office.Common/Cache"  ))
#define DEFAULT_WRITEROLE                   20
#define DEFAULT_DRAWINGOLE                  20
#define DEFAULT_GRFMGR_TOTALSIZE            10000000
#define DEFAULT_GRFMGR_OBJECTSIZE           2400000

#define PROPERTYNAME_WRITEROLE              OUString(RTL_CONSTASCII_USTRINGPARAM("Writer/OLE_Objects"))
#define PROPERTYNAME_DRAWINGOLE             OUString(RTL_CONSTASCII_USTRINGPARAM("DrawingEngine/OLE_Objects"))
#define PROPERTYNAME_GRFMGR_TOTALSIZE       OUString(RTL_CONSTASCII_USTRINGPARAM("GraphicManager/TotalCacheSize"))
#define PROPERTYNAME_GRFMGR_OBJECTSIZE      OUString(RTL_CONSTASCII_USTRINGPARAM("GraphicManager/ObjectCacheSize"))

#define PROPERTYHANDLE_WRITEROLE            0
#define PROPERTYHANDLE_DRAWINGOLE           1
#define PROPERTYHANDLE_GRFMGR_TOTALSIZE     2
#define PROPERTYHANDLE_GRFMGR_OBJECTSIZE    3

#define PROPERTYCOUNT                       4

class SvtCacheOptions_Impl : public ConfigItem
{
public:

//---------------------------------------------------------------------------------------------------------
//  constructor / destructor
//---------------------------------------------------------------------------------------------------------

     SvtCacheOptions_Impl();
    ~SvtCacheOptions_Impl();

//---------------------------------------------------------------------------------------------------------
//  overloaded methods of baseclass
//---------------------------------------------------------------------------------------------------------

    virtual void    Commit();

//---------------------------------------------------------------------------------------------------------
//  public interface
//---------------------------------------------------------------------------------------------------------

    sal_Int32       GetWriterOLE_Objects() const;
    sal_Int32       GetDrawingEngineOLE_Objects() const;
    sal_Int32       GetGraphicManagerTotalCacheSize() const;
    sal_Int32       GetGraphicManagerObjectCacheSize() const;

    void            SetWriterOLE_Objects( sal_Int32 nObjects );
    void            SetDrawingEngineOLE_Objects( sal_Int32 nObjects );
    void            SetGraphicManagerTotalCacheSize( sal_Int32 nTotalCacheSize );
    void            SetGraphicManagerObjectCacheSize( sal_Int32 nObjectCacheSize );

//-------------------------------------------------------------------------------------------------------------
//  private methods
//-------------------------------------------------------------------------------------------------------------

private:

    static Sequence< OUString > impl_GetPropertyNames();

//-------------------------------------------------------------------------------------------------------------
//  private member
//-------------------------------------------------------------------------------------------------------------

private:

        sal_Int32   mnWriterOLE;
        sal_Int32   mnDrawingOLE;
        sal_Int32   mnGrfMgrTotalSize;
        sal_Int32   mnGrfMgrObjectSize;
};

//_________________________________________________________________________________________________________________
//  definitions
//_________________________________________________________________________________________________________________

//*****************************************************************************************************************
//  constructor
//*****************************************************************************************************************
SvtCacheOptions_Impl::SvtCacheOptions_Impl() :
    ConfigItem( ROOTNODE_START  ),
    mnWriterOLE( DEFAULT_WRITEROLE ),
    mnDrawingOLE( DEFAULT_DRAWINGOLE ),
    mnGrfMgrTotalSize( DEFAULT_GRFMGR_TOTALSIZE ),
    mnGrfMgrObjectSize( DEFAULT_GRFMGR_OBJECTSIZE )
{
    Sequence< OUString >    seqNames( impl_GetPropertyNames() );
    Sequence< Any >         seqValues   = GetProperties( seqNames ) ;

    DBG_ASSERT( !(seqNames.getLength()!=seqValues.getLength()), "SvtCacheOptions_Impl::SvtCacheOptions_Impl()\nI miss some values of configuration keys!\n" );

    // Copy values from list in right order to ouer internal member.
    sal_Int32 nPropertyCount = seqValues.getLength();
    sal_Int32 nProperty = 0;

    for( nProperty=0; nProperty<nPropertyCount; ++nProperty )
    {
        if( seqValues[ nProperty ].hasValue() )
        {
            switch( nProperty )
            {
                case PROPERTYHANDLE_WRITEROLE:
                {
                    if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                        seqValues[nProperty] >>= mnWriterOLE;
                }
                break;

                case PROPERTYHANDLE_DRAWINGOLE:
                {
                    if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                        seqValues[nProperty] >>= mnDrawingOLE;
                }
                break;

                case PROPERTYHANDLE_GRFMGR_TOTALSIZE:
                {
                    if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                        seqValues[nProperty] >>= mnGrfMgrTotalSize;
                }
                break;

                case PROPERTYHANDLE_GRFMGR_OBJECTSIZE:
                {
                    if( seqValues[ nProperty ].getValueTypeClass() == TypeClass_LONG )
                        seqValues[nProperty] >>= mnGrfMgrObjectSize;
                }
                break;
            }
        }
    }
}

//*****************************************************************************************************************
//  destructor
//*****************************************************************************************************************
SvtCacheOptions_Impl::~SvtCacheOptions_Impl()
{
    if( IsModified() )
        Commit();
}

//*****************************************************************************************************************
//  Commit
//*****************************************************************************************************************
void SvtCacheOptions_Impl::Commit()
{
    Sequence< OUString >    aSeqNames( impl_GetPropertyNames() );
    Sequence< Any >         aSeqValues( aSeqNames.getLength() );

    for( sal_Int32 nProperty = 0, nCount = aSeqNames.getLength(); nProperty < nCount; ++nProperty )
    {
        switch( nProperty )
        {
            case PROPERTYHANDLE_WRITEROLE:
                aSeqValues[nProperty] <<= mnWriterOLE;
            break;

            case PROPERTYHANDLE_DRAWINGOLE:
                aSeqValues[nProperty] <<= mnDrawingOLE;
            break;

            case PROPERTYHANDLE_GRFMGR_TOTALSIZE:
                aSeqValues[nProperty] <<= mnGrfMgrTotalSize;
            break;

            case PROPERTYHANDLE_GRFMGR_OBJECTSIZE:
                aSeqValues[nProperty] <<= mnGrfMgrObjectSize;
            break;
        }
    }

    PutProperties( aSeqNames, aSeqValues );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetWriterOLE_Objects() const
{
    return mnWriterOLE;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetDrawingEngineOLE_Objects() const
{
    return mnDrawingOLE;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetGraphicManagerTotalCacheSize() const
{
    return mnGrfMgrTotalSize;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions_Impl::GetGraphicManagerObjectCacheSize() const
{
    return mnGrfMgrObjectSize;
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetWriterOLE_Objects( sal_Int32 nWriterOLE )
{
    mnWriterOLE = nWriterOLE;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetDrawingEngineOLE_Objects( sal_Int32 nDrawingOLE )
{
    mnDrawingOLE = nDrawingOLE;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetGraphicManagerTotalCacheSize( sal_Int32 nGrfMgrTotalSize )
{
    mnGrfMgrTotalSize = nGrfMgrTotalSize;
    SetModified();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions_Impl::SetGraphicManagerObjectCacheSize( sal_Int32 nGrfMgrObjectSize )
{
    mnGrfMgrObjectSize = nGrfMgrObjectSize;
    SetModified();
}

//*****************************************************************************************************************
//  private method
//*****************************************************************************************************************
Sequence< OUString > SvtCacheOptions_Impl::impl_GetPropertyNames()
{
    // Build static list of configuration key names.
    static const OUString pProperties[] =
    {
        PROPERTYNAME_WRITEROLE,
        PROPERTYNAME_DRAWINGOLE,
        PROPERTYNAME_GRFMGR_TOTALSIZE,
        PROPERTYNAME_GRFMGR_OBJECTSIZE
    };
    // Initialize return sequence with these list ...
    static const Sequence< OUString > seqPropertyNames( pProperties, PROPERTYCOUNT );
    // ... and return it.
    return seqPropertyNames;
}

//*****************************************************************************************************************
//  initialize static member
//  DON'T DO IT IN YOUR HEADER!
//  see definition for further informations
//*****************************************************************************************************************
SvtCacheOptions_Impl*   SvtCacheOptions::m_pDataContainer = NULL;
sal_Int32               SvtCacheOptions::m_nRefCount = 0;

//*****************************************************************************************************************
//  constructor
//*****************************************************************************************************************
SvtCacheOptions::SvtCacheOptions()
{
    // Global access, must be guarded (multithreading!).
    MutexGuard aGuard( GetOwnStaticMutex() );
    // Increase ouer refcount ...
    ++m_nRefCount;
    // ... and initialize ouer data container only if it not already!
    if( m_pDataContainer == NULL )
    {
        m_pDataContainer = new SvtCacheOptions_Impl();
    }
}

//*****************************************************************************************************************
//  destructor
//*****************************************************************************************************************
SvtCacheOptions::~SvtCacheOptions()
{
    // Global access, must be guarded (multithreading!)
    MutexGuard aGuard( GetOwnStaticMutex() );
    // Decrease ouer refcount.
    --m_nRefCount;
    // If last instance was deleted ...
    // we must destroy ouer static data container!
    if( m_nRefCount <= 0 )
    {
        delete m_pDataContainer;
        m_pDataContainer = NULL;
    }
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetWriterOLE_Objects() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetWriterOLE_Objects();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetDrawingEngineOLE_Objects() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetDrawingEngineOLE_Objects();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetGraphicManagerTotalCacheSize() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetGraphicManagerTotalCacheSize();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
sal_Int32 SvtCacheOptions::GetGraphicManagerObjectCacheSize() const
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    return m_pDataContainer->GetGraphicManagerObjectCacheSize();
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetWriterOLE_Objects( sal_Int32 nWriterOLE )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetWriterOLE_Objects( nWriterOLE );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetDrawingEngineOLE_Objects( sal_Int32 nDrawingOLE )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetDrawingEngineOLE_Objects( nDrawingOLE );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetGraphicManagerTotalCacheSize( sal_Int32 nGrfMgrTotalSize )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetGraphicManagerTotalCacheSize( nGrfMgrTotalSize );
}

//*****************************************************************************************************************
//  public method
//*****************************************************************************************************************
void SvtCacheOptions::SetGraphicManagerObjectCacheSize( sal_Int32 nGrfMgrObjectSize )
{
    MutexGuard aGuard( GetOwnStaticMutex() );
    m_pDataContainer->SetGraphicManagerObjectCacheSize( nGrfMgrObjectSize );
}

//*****************************************************************************************************************
//  private method
//*****************************************************************************************************************
Mutex& SvtCacheOptions::GetOwnStaticMutex()
{
    // Initialize static mutex only for one time!
    static Mutex* pMutex = NULL;
    // If these method first called (Mutex not already exist!) ...
    if( pMutex == NULL )
    {
        // ... we must create a new one. Protect follow code with the global mutex -
        // It must be - we create a static variable!
        MutexGuard aGuard( Mutex::getGlobalMutex() );
        // We must check our pointer again - because it can be that another instance of ouer class will be fastr then these!
        if( pMutex == NULL )
        {
            // Create the new mutex and set it for return on static variable.
            static Mutex aMutex;
            pMutex = &aMutex;
        }
    }
    // Return new created or already existing mutex object.
    return *pMutex;
}
<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct MemoryDffWorker
{
	Module *module;
	SigMap sigmap;

	vector<Cell*> dff_cells;
	dict<SigBit, SigBit> invbits;
	dict<SigBit, int> sigbit_users_count;
	dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
	pool<Cell*> forward_merged_dffs, candidate_dffs;
	pool<SigBit> init_bits;

	MemoryDffWorker(Module *module) : module(module), sigmap(module)
	{
		for (auto wire : module->wires()) {
			if (wire->attributes.count("\\init") == 0)
				continue;
			SigSpec sig = sigmap(wire);
			Const initval = wire->attributes.count("\\init");
			for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
				if (initval[i] == State::S0 || initval[i] == State::S1)
					init_bits.insert(sig[i]);
		}
	}

	bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
	{
		sigmap.apply(sig);

		for (auto &bit : sig)
		{
			if (bit.wire == NULL)
				continue;

			if (!after && init_bits.count(sigmap(bit)))
				return false;

			for (auto cell : dff_cells)
			{
				if (after && forward_merged_dffs.count(cell))
					continue;

				SigSpec this_clk = cell->getPort("\\CLK");
				bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
				if (d.size() != 1)
					continue;

				if (after && init_bits.count(d))
					return false;

				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				candidate_dffs.insert(cell);
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	void handle_wr_cell(RTLIL::Cell *cell)
	{
		log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());

		RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
		bool clk_polarity = 0;
		candidate_dffs.clear();

		RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
		if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
			log("no (compatible) $dff for address input found.\n");
			return;
		}

		RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
		if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
			log("no (compatible) $dff for data input found.\n");
			return;
		}

		RTLIL::SigSpec sig_en = cell->getPort("\\EN");
		if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
			log("no (compatible) $dff for enable input found.\n");
			return;
		}

		if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			for (auto cell : candidate_dffs)
				forward_merged_dffs.insert(cell);

			cell->setPort("\\CLK", clk);
			cell->setPort("\\ADDR", sig_addr);
			cell->setPort("\\DATA", sig_data);
			cell->setPort("\\EN", sig_en);
			cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
			cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);

			log("merged $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void disconnect_dff(RTLIL::SigSpec sig)
	{
		sigmap.apply(sig);
		sig.sort_and_unify();

		std::stringstream sstr;
		sstr << "$memory_dff_disconnected$" << (autoidx++);

		RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());

		for (auto cell : module->cells())
			if (cell->type == "$dff") {
				RTLIL::SigSpec new_q = cell->getPort("\\Q");
				new_q.replace(sig, new_sig);
				cell->setPort("\\Q", new_q);
			}
	}

	void handle_rd_cell(RTLIL::Cell *cell)
	{
		log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());

		bool clk_polarity = 0;

		RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_data = cell->getPort("\\DATA");

		for (auto bit : sigmap(sig_data))
			if (sigbit_users_count[bit] > 1)
				goto skip_ff_after_read_merging;

		if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
		{
			bool enable_invert = mux_cells_a.count(sig_data) != 0;
			Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
			SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));

			sig_data = sigmap(mux->getPort("\\Y"));
			for (auto bit : sig_data)
				if (sigbit_users_count[bit] > 1)
					goto skip_ff_after_read_merging;

			if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
			{
				disconnect_dff(sig_data);
				cell->setPort("\\CLK", clk_data);
				cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
				cell->setPort("\\DATA", sig_data);
				cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
				cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
				cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
				log("merged data $dff with rd enable to cell.\n");
				return;
			}
		}
		else
		{
			if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
			{
				disconnect_dff(sig_data);
				cell->setPort("\\CLK", clk_data);
				cell->setPort("\\EN", State::S1);
				cell->setPort("\\DATA", sig_data);
				cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
				cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
				cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
				log("merged data $dff to cell.\n");
				return;
			}
		}

	skip_ff_after_read_merging:;
		RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
		if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
				clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			cell->setPort("\\CLK", clk_addr);
			cell->setPort("\\EN", State::S1);
			cell->setPort("\\ADDR", sig_addr);
			cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
			cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
			cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
			log("merged address $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void run(bool flag_wr_only)
	{
		for (auto wire : module->wires()) {
			if (wire->port_output)
				for (auto bit : sigmap(wire))
					sigbit_users_count[bit]++;
		}

		for (auto cell : module->cells()) {
			if (cell->type == "$dff")
				dff_cells.push_back(cell);
			if (cell->type == "$mux") {
				mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
				mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
			}
			if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
				SigSpec sig_a = cell->getPort("\\A");
				SigSpec sig_y = cell->getPort("\\Y");
				if (cell->type == "$not")
					sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
				if (cell->type == "$logic_not")
					sig_y.extend_u0(1);
				for (int i = 0; i < GetSize(sig_y); i++)
					invbits[sig_y[i]] = sig_a[i];
			}
			for (auto &conn : cell->connections())
				if (!cell->known() || cell->input(conn.first))
					for (auto bit : sigmap(conn.second))
						sigbit_users_count[bit]++;
		}

		for (auto cell : module->selected_cells())
			if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
				handle_wr_cell(cell);

		if (!flag_wr_only)
			for (auto cell : module->selected_cells())
				if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
					handle_rd_cell(cell);
	}
};

struct MemoryDffPass : public Pass {
	MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    memory_dff [options] [selection]\n");
		log("\n");
		log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
		log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
		log("interface and yields a synchronous memory port.\n");
		log("\n");
		log("    -nordfff\n");
		log("        do not merge registers on read ports\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		bool flag_wr_only = false;

		log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
				flag_wr_only = true;
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules()) {
			MemoryDffWorker worker(mod);
			worker.run(flag_wr_only);
		}
	}
} MemoryDffPass;

PRIVATE_NAMESPACE_END
<commit_msg>memory_dff: Fix typo when checking init value<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct MemoryDffWorker
{
	Module *module;
	SigMap sigmap;

	vector<Cell*> dff_cells;
	dict<SigBit, SigBit> invbits;
	dict<SigBit, int> sigbit_users_count;
	dict<SigSpec, Cell*> mux_cells_a, mux_cells_b;
	pool<Cell*> forward_merged_dffs, candidate_dffs;
	pool<SigBit> init_bits;

	MemoryDffWorker(Module *module) : module(module), sigmap(module)
	{
		for (auto wire : module->wires()) {
			if (wire->attributes.count("\\init") == 0)
				continue;
			SigSpec sig = sigmap(wire);
			Const initval = wire->attributes.at("\\init");
			for (int i = 0; i < GetSize(sig) && i < GetSize(initval); i++)
				if (initval[i] == State::S0 || initval[i] == State::S1)
					init_bits.insert(sig[i]);
		}
	}

	bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, bool after = false)
	{
		sigmap.apply(sig);

		for (auto &bit : sig)
		{
			if (bit.wire == NULL)
				continue;

			if (!after && init_bits.count(sigmap(bit)))
				return false;

			for (auto cell : dff_cells)
			{
				if (after && forward_merged_dffs.count(cell))
					continue;

				SigSpec this_clk = cell->getPort("\\CLK");
				bool this_clk_polarity = cell->parameters["\\CLK_POLARITY"].as_bool();

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(after ? "\\D" : "\\Q");
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(after ? "\\Q" : "\\D"));
				if (d.size() != 1)
					continue;

				if (after && init_bits.count(d))
					return false;

				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				candidate_dffs.insert(cell);
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	void handle_wr_cell(RTLIL::Cell *cell)
	{
		log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());

		RTLIL::SigSpec clk = RTLIL::SigSpec(RTLIL::State::Sx);
		bool clk_polarity = 0;
		candidate_dffs.clear();

		RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
		if (!find_sig_before_dff(sig_addr, clk, clk_polarity)) {
			log("no (compatible) $dff for address input found.\n");
			return;
		}

		RTLIL::SigSpec sig_data = cell->getPort("\\DATA");
		if (!find_sig_before_dff(sig_data, clk, clk_polarity)) {
			log("no (compatible) $dff for data input found.\n");
			return;
		}

		RTLIL::SigSpec sig_en = cell->getPort("\\EN");
		if (!find_sig_before_dff(sig_en, clk, clk_polarity)) {
			log("no (compatible) $dff for enable input found.\n");
			return;
		}

		if (clk != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			for (auto cell : candidate_dffs)
				forward_merged_dffs.insert(cell);

			cell->setPort("\\CLK", clk);
			cell->setPort("\\ADDR", sig_addr);
			cell->setPort("\\DATA", sig_data);
			cell->setPort("\\EN", sig_en);
			cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
			cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);

			log("merged $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void disconnect_dff(RTLIL::SigSpec sig)
	{
		sigmap.apply(sig);
		sig.sort_and_unify();

		std::stringstream sstr;
		sstr << "$memory_dff_disconnected$" << (autoidx++);

		RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());

		for (auto cell : module->cells())
			if (cell->type == "$dff") {
				RTLIL::SigSpec new_q = cell->getPort("\\Q");
				new_q.replace(sig, new_sig);
				cell->setPort("\\Q", new_q);
			}
	}

	void handle_rd_cell(RTLIL::Cell *cell)
	{
		log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());

		bool clk_polarity = 0;

		RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_data = cell->getPort("\\DATA");

		for (auto bit : sigmap(sig_data))
			if (sigbit_users_count[bit] > 1)
				goto skip_ff_after_read_merging;

		if (mux_cells_a.count(sig_data) || mux_cells_b.count(sig_data))
		{
			bool enable_invert = mux_cells_a.count(sig_data) != 0;
			Cell *mux = enable_invert ? mux_cells_a.at(sig_data) : mux_cells_b.at(sig_data);
			SigSpec check_q = sigmap(mux->getPort(enable_invert ? "\\B" : "\\A"));

			sig_data = sigmap(mux->getPort("\\Y"));
			for (auto bit : sig_data)
				if (sigbit_users_count[bit] > 1)
					goto skip_ff_after_read_merging;

			if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx) && sig_data == check_q)
			{
				disconnect_dff(sig_data);
				cell->setPort("\\CLK", clk_data);
				cell->setPort("\\EN", enable_invert ? module->LogicNot(NEW_ID, mux->getPort("\\S")) : mux->getPort("\\S"));
				cell->setPort("\\DATA", sig_data);
				cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
				cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
				cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
				log("merged data $dff with rd enable to cell.\n");
				return;
			}
		}
		else
		{
			if (find_sig_before_dff(sig_data, clk_data, clk_polarity, true) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
			{
				disconnect_dff(sig_data);
				cell->setPort("\\CLK", clk_data);
				cell->setPort("\\EN", State::S1);
				cell->setPort("\\DATA", sig_data);
				cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
				cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
				cell->parameters["\\TRANSPARENT"] = RTLIL::Const(0);
				log("merged data $dff to cell.\n");
				return;
			}
		}

	skip_ff_after_read_merging:;
		RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_addr = cell->getPort("\\ADDR");
		if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
				clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			cell->setPort("\\CLK", clk_addr);
			cell->setPort("\\EN", State::S1);
			cell->setPort("\\ADDR", sig_addr);
			cell->parameters["\\CLK_ENABLE"] = RTLIL::Const(1);
			cell->parameters["\\CLK_POLARITY"] = RTLIL::Const(clk_polarity);
			cell->parameters["\\TRANSPARENT"] = RTLIL::Const(1);
			log("merged address $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void run(bool flag_wr_only)
	{
		for (auto wire : module->wires()) {
			if (wire->port_output)
				for (auto bit : sigmap(wire))
					sigbit_users_count[bit]++;
		}

		for (auto cell : module->cells()) {
			if (cell->type == "$dff")
				dff_cells.push_back(cell);
			if (cell->type == "$mux") {
				mux_cells_a[sigmap(cell->getPort("\\A"))] = cell;
				mux_cells_b[sigmap(cell->getPort("\\B"))] = cell;
			}
			if (cell->type == "$not" || cell->type == "$_NOT_" || (cell->type == "$logic_not" && GetSize(cell->getPort("\\A")) == 1)) {
				SigSpec sig_a = cell->getPort("\\A");
				SigSpec sig_y = cell->getPort("\\Y");
				if (cell->type == "$not")
					sig_a.extend_u0(GetSize(sig_y), cell->getParam("\\A_SIGNED").as_bool());
				if (cell->type == "$logic_not")
					sig_y.extend_u0(1);
				for (int i = 0; i < GetSize(sig_y); i++)
					invbits[sig_y[i]] = sig_a[i];
			}
			for (auto &conn : cell->connections())
				if (!cell->known() || cell->input(conn.first))
					for (auto bit : sigmap(conn.second))
						sigbit_users_count[bit]++;
		}

		for (auto cell : module->selected_cells())
			if (cell->type == "$memwr" && !cell->parameters["\\CLK_ENABLE"].as_bool())
				handle_wr_cell(cell);

		if (!flag_wr_only)
			for (auto cell : module->selected_cells())
				if (cell->type == "$memrd" && !cell->parameters["\\CLK_ENABLE"].as_bool())
					handle_rd_cell(cell);
	}
};

struct MemoryDffPass : public Pass {
	MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memories") { }
	void help() YS_OVERRIDE
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    memory_dff [options] [selection]\n");
		log("\n");
		log("This pass detects DFFs at memory ports and merges them into the memory port.\n");
		log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
		log("interface and yields a synchronous memory port.\n");
		log("\n");
		log("    -nordfff\n");
		log("        do not merge registers on read ports\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
	{
		bool flag_wr_only = false;

		log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd and $memwr).\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			if (args[argidx] == "-nordff" || args[argidx] == "-wr_only") {
				flag_wr_only = true;
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules()) {
			MemoryDffWorker worker(mod);
			worker.run(flag_wr_only);
		}
	}
} MemoryDffPass;

PRIVATE_NAMESPACE_END
<|endoftext|>


<commit_before>AliAnalysisTaskQASym * AddTaskQAsym(Int_t runNumber)

{
  // Creates a QA task exploiting simple symmetries phi, eta +/-, charge ...

  // Get the pointer to the existing analysis manager via the static access method.
  //==============================================================================
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    ::Error("AddTaskQAsym", "No analysis manager to connect to.");
    return NULL;
  }

  // Check the analysis type using the event handlers connected to the analysis manager.
  //==============================================================================
  if (!mgr->GetInputEventHandler()) {
    ::Error("AddTasQAsym", "This task requires an input event handler");
    return NULL;
  }
   TString inputDataType = mgr->GetInputEventHandler()->GetDataType(); // can be "ESD" or "AOD"

   // Configure analysis
   //===========================================================================

   //Task for global tracks
   AliAnalysisTaskQASym *task0 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_Global");
   task0->SetTrackType(0);
   task0->SelectCollisionCandidates(); // default setting: kMB = min bias trigger
   task0->SetNChargedRange(30,50); // comparison of different trigger settings
   //Task for global tracks (high multiplicity)
   AliAnalysisTaskQASym *task0HM = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_Global_HighMult");
   task0HM->SetTrackType(0);
   task0HM->SelectCollisionCandidates(AliVEvent::kHighMult);
   task0HM->SetNChargedRange(30,50);
   //Task for ITS tracks
   AliAnalysisTaskQASym *task1 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_ITS");
   task1->SetTrackType(1);
   task1->SetStandAloneTrack(kFALSE);
   task1->SelectCollisionCandidates();
   task1->SetNChargedRange(30,50);
   //Task for ITS tracks SA
   AliAnalysisTaskQASym *task1sa = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_ITS_SA");
   task1sa->SetTrackType(1);
   task1sa->SetStandAloneTrack(kTRUE);
   task1sa->SelectCollisionCandidates();
   task1sa->SetNChargedRange(30,50);
   //Task for TPC tracks
   AliAnalysisTaskQASym *task2 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_TPC");
   task2->SetTrackType(2);
   task2->SelectCollisionCandidates();
   task2->SetNChargedRange(30,50);

   //cuts for global tracks
   AliESDtrackCuts* esdTrackCutsL0 = new AliESDtrackCuts("AliESDtrackCuts0","Global");
   esdTrackCutsL0->SetMinNClustersTPC(70);
   esdTrackCutsL0->SetRequireTPCRefit(kTRUE);
   esdTrackCutsL0->SetRequireITSRefit(kTRUE);
   esdTrackCutsL0->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL0->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL0->SetAcceptKinkDaughters(kFALSE);

   //cuts for ITS tracks
   AliESDtrackCuts* esdTrackCutsL1 = new AliESDtrackCuts("AliESDtrackCuts1","ITS");
   esdTrackCutsL1->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL1->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL1->SetAcceptKinkDaughters(kFALSE);
   esdTrackCutsL1->SetRequireITSRefit(kTRUE);
   esdTrackCutsL1->SetRequireITSStandAlone(kTRUE);

   //cuts for ITS tracks SA
   AliESDtrackCuts* esdTrackCutsL1sa = new AliESDtrackCuts("AliESDtrackCuts1","ITS_SA");
   esdTrackCutsL1sa->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL1sa->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL1sa->SetAcceptKinkDaughters(kFALSE);
   esdTrackCutsL1sa->SetRequireITSRefit(kTRUE);
   esdTrackCutsL1sa->SetRequireITSPureStandAlone(kTRUE);

   //cuts for TPC tracks
   AliESDtrackCuts* esdTrackCutsL2 = new AliESDtrackCuts("AliESDtrackCuts2","TPC");
   esdTrackCutsL2->SetRequireTPCRefit(kFALSE);
   esdTrackCutsL2->SetAcceptKinkDaughters(kFALSE);
   //jacek's cuts:
   esdTrackCutsL2->SetMinNClustersTPC(70);
   // cut on max ncl=160 in Task
   esdTrackCutsL2->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL2->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL2->SetMaxChi2PerClusterTPC(3.999);
   //cut minChi=0 in task
   //esdTrackCutsL2->SetPRange(0.15,16); // not needed for QA
   //esdTrackCutsL2->SetEtaRange(-0.8, 0.7999); // not needed for QA

   task0->SetCuts(esdTrackCutsL0);
   task0HM->SetCuts(esdTrackCutsL0);
   task1->SetCuts(esdTrackCutsL1);
   task1sa->SetCuts(esdTrackCutsL1sa);
   task2->SetCuts(esdTrackCutsL2);

   mgr->AddTask(task0);
   mgr->AddTask(task0HM);
   mgr->AddTask(task1);
   mgr->AddTask(task1sa);
   mgr->AddTask(task2);

   AliAnalysisDataContainer *cout0    = 0;
   AliAnalysisDataContainer *cout0HM  = 0;
   AliAnalysisDataContainer *cout1    = 0;
   AliAnalysisDataContainer *cout1sa  = 0;
   AliAnalysisDataContainer *cout2    = 0;

   if(runNumber>0){
    cout0 =  mgr->CreateContainer("QAsymHists_Global",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout0HM =  mgr->CreateContainer("QAsymHists_Global_HighMult",TList::Class(),
				    AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout1 =  mgr->CreateContainer("QAsymHists_ITS",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout1sa =  mgr->CreateContainer("QAsymHists_ITS_SA",TList::Class(),
				    AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout2 =  mgr->CreateContainer("QAsymHists_TPC",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
   }

   else{
      cout0 = mgr->CreateContainer("QAsymHists_Global",TList::Class(),
				 AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout0HM = mgr->CreateContainer("QAsymHists_Global_HighMult",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				   Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout1 = mgr->CreateContainer("QAsymHists_ITS",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout1sa = mgr->CreateContainer("QAsymHists_ITS_SA",TList::Class(),
				     AliAnalysisManager::kOutputContainer,
				     Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout2 = mgr->CreateContainer("QAsymHists_TPC",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
   }

   mgr->ConnectInput  (task0,   0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task0HM, 0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task1,   0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task1sa, 0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task2,   0, mgr->GetCommonInputContainer());

   mgr->ConnectOutput (task0,   1, cout0);
   mgr->ConnectOutput (task0HM, 1, cout0HM);
   mgr->ConnectOutput (task1,   1, cout1);
   mgr->ConnectOutput (task1sa, 1, cout1sa);
   mgr->ConnectOutput (task2,   1, cout2);

   return task0;

}

<commit_msg>Allow to pass trigger mask value as parameter to AddTaskQAsym<commit_after>AliAnalysisTaskQASym * AddTaskQAsym(Int_t runNumber, UInt_t maskMB, UInt_t maskHM)

{
  // Creates a QA task exploiting simple symmetries phi, eta +/-, charge ...

  // Get the pointer to the existing analysis manager via the static access method.
  //==============================================================================
  AliAnalysisManager *mgr = AliAnalysisManager::GetAnalysisManager();
  if (!mgr) {
    ::Error("AddTaskQAsym", "No analysis manager to connect to.");
    return NULL;
  }

  // Check the analysis type using the event handlers connected to the analysis manager.
  //==============================================================================
  if (!mgr->GetInputEventHandler()) {
    ::Error("AddTasQAsym", "This task requires an input event handler");
    return NULL;
  }
   TString inputDataType = mgr->GetInputEventHandler()->GetDataType(); // can be "ESD" or "AOD"

   // Configure analysis
   //===========================================================================

   //Task for global tracks
   AliAnalysisTaskQASym *task0 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_Global");
   task0->SetTrackType(0);
   task0->SelectCollisionCandidates(maskMB); // default setting: kMB = min bias trigger
   task0->SetNChargedRange(30,50); // comparison of different trigger settings
   //Task for global tracks (high multiplicity)
   AliAnalysisTaskQASym *task0HM = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_Global_HighMult");
   task0HM->SetTrackType(0);
   task0HM->SelectCollisionCandidates(maskHM);
   task0HM->SetNChargedRange(30,50);
   //Task for ITS tracks
   AliAnalysisTaskQASym *task1 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_ITS");
   task1->SetTrackType(1);
   task1->SetStandAloneTrack(kFALSE);
   task1->SelectCollisionCandidates(maskMB);
   task1->SetNChargedRange(30,50);
   //Task for ITS tracks SA
   AliAnalysisTaskQASym *task1sa = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_ITS_SA");
   task1sa->SetTrackType(1);
   task1sa->SetStandAloneTrack(kTRUE);
   task1sa->SelectCollisionCandidates(maskMB);
   task1sa->SetNChargedRange(30,50);
   //Task for TPC tracks
   AliAnalysisTaskQASym *task2 = new AliAnalysisTaskQASym("AliAnalysisTaskQASym_TPC");
   task2->SetTrackType(2);
   task2->SelectCollisionCandidates(maskMB);
   task2->SetNChargedRange(30,50);

   //cuts for global tracks
   AliESDtrackCuts* esdTrackCutsL0 = new AliESDtrackCuts("AliESDtrackCuts0","Global");
   esdTrackCutsL0->SetMinNClustersTPC(70);
   esdTrackCutsL0->SetRequireTPCRefit(kTRUE);
   esdTrackCutsL0->SetRequireITSRefit(kTRUE);
   esdTrackCutsL0->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL0->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL0->SetAcceptKinkDaughters(kFALSE);

   //cuts for ITS tracks
   AliESDtrackCuts* esdTrackCutsL1 = new AliESDtrackCuts("AliESDtrackCuts1","ITS");
   esdTrackCutsL1->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL1->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL1->SetAcceptKinkDaughters(kFALSE);
   esdTrackCutsL1->SetRequireITSRefit(kTRUE);
   esdTrackCutsL1->SetRequireITSStandAlone(kTRUE);

   //cuts for ITS tracks SA
   AliESDtrackCuts* esdTrackCutsL1sa = new AliESDtrackCuts("AliESDtrackCuts1sa","ITS_SA");
   esdTrackCutsL1sa->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL1sa->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL1sa->SetAcceptKinkDaughters(kFALSE);
   esdTrackCutsL1sa->SetRequireITSRefit(kTRUE);
   esdTrackCutsL1sa->SetRequireITSPureStandAlone(kTRUE);

   //cuts for TPC tracks
   AliESDtrackCuts* esdTrackCutsL2 = new AliESDtrackCuts("AliESDtrackCuts2","TPC");
   esdTrackCutsL2->SetRequireTPCRefit(kFALSE);
   esdTrackCutsL2->SetAcceptKinkDaughters(kFALSE);
   //jacek's cuts:
   esdTrackCutsL2->SetMinNClustersTPC(70);
   // cut on max ncl=160 in Task
   esdTrackCutsL2->SetMaxDCAToVertexXY(3.);
   esdTrackCutsL2->SetMaxDCAToVertexZ(3.);
   esdTrackCutsL2->SetMaxChi2PerClusterTPC(3.999);
   //cut minChi=0 in task
   //esdTrackCutsL2->SetPRange(0.15,16); // not needed for QA
   //esdTrackCutsL2->SetEtaRange(-0.8, 0.7999); // not needed for QA

   task0->SetCuts(esdTrackCutsL0);
   task0HM->SetCuts(esdTrackCutsL0);
   task1->SetCuts(esdTrackCutsL1);
   task1sa->SetCuts(esdTrackCutsL1sa);
   task2->SetCuts(esdTrackCutsL2);

   mgr->AddTask(task0);
   mgr->AddTask(task0HM);
   mgr->AddTask(task1);
   mgr->AddTask(task1sa);
   mgr->AddTask(task2);

   AliAnalysisDataContainer *cout0    = 0;
   AliAnalysisDataContainer *cout0HM  = 0;
   AliAnalysisDataContainer *cout1    = 0;
   AliAnalysisDataContainer *cout1sa  = 0;
   AliAnalysisDataContainer *cout2    = 0;

   if(runNumber>0){
    cout0 =  mgr->CreateContainer("QAsymHists_Global",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout0HM =  mgr->CreateContainer("QAsymHists_Global_HighMult",TList::Class(),
				    AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout1 =  mgr->CreateContainer("QAsymHists_ITS",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout1sa =  mgr->CreateContainer("QAsymHists_ITS_SA",TList::Class(),
				    AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
    cout2 =  mgr->CreateContainer("QAsymHists_TPC",TList::Class(),
				  AliAnalysisManager::kOutputContainer, Form("run%d.root",runNumber));
   }

   else{
      cout0 = mgr->CreateContainer("QAsymHists_Global",TList::Class(),
				 AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout0HM = mgr->CreateContainer("QAsymHists_Global_HighMult",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				   Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout1 = mgr->CreateContainer("QAsymHists_ITS",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout1sa = mgr->CreateContainer("QAsymHists_ITS_SA",TList::Class(),
				     AliAnalysisManager::kOutputContainer,
				     Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
      cout2 = mgr->CreateContainer("QAsymHists_TPC",TList::Class(),
				   AliAnalysisManager::kOutputContainer,
				 Form("%s:PWG1_QAsymHists",AliAnalysisManager::GetCommonFileName()));
   }

   mgr->ConnectInput  (task0,   0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task0HM, 0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task1,   0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task1sa, 0, mgr->GetCommonInputContainer());
   mgr->ConnectInput  (task2,   0, mgr->GetCommonInputContainer());

   mgr->ConnectOutput (task0,   1, cout0);
   mgr->ConnectOutput (task0HM, 1, cout0HM);
   mgr->ConnectOutput (task1,   1, cout1);
   mgr->ConnectOutput (task1sa, 1, cout1sa);
   mgr->ConnectOutput (task2,   1, cout2);

   return task0;

}

<|endoftext|>


<commit_before>/** @file

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

////////////////////////////////////////////////////////////////////////////////
// collapsed_forwarding::
//
// ATS plugin to allow collapsed forwarding of concurrent requests for the same
// object. This plugin is based on open_write_fail_action feature, which detects
// cache open write failure on a cache miss and returns a 502 error along with a
// special @-header indicating the reason for 502 error. The plugin acts on the
// error by using an internal redirect follow back to itself, essentially blocking
// the request until a response arrives, at which point, relies on read-while-writer
// feature to start downloading the object to all waiting clients. The following
// config parameters are assumed to be set for this plugin to work:
////////////////////////////////////////////////////////////////////////////////////
// proxy.config.http.cache.open_write_fail_action        1 /////////////////////////
// proxy.config.cache.enable_read_while_writer           1 /////////////////////////
// proxy.config.http.redirection_enabled                 1 /////////////////////////
// proxy.config.http.number_of_redirections             10 /////////////////////////
// proxy.config.http.redirect_use_orig_cache_key         1 /////////////////////////
// proxy.config.http.background_fill_active_timeout      0 /////////////////////////
// proxy.config.http.background_fill_completed_threshold 0 /////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Additionally, given that collapsed forwarding works based on cache write
// lock failure detection, the plugin requires cache to be enabled and ready.
// On a restart, Traffic Server typically takes a few seconds to initialize
// the cache depending on the cache size and number of dirents. While the
// cache is not ready yet, collapsed forwarding can not detect the write lock
// contention and so can not work. The setting proxy.config.http.wait_for_cache
// may be enabled which allows blocking incoming connections from being
// accepted until cache is ready.
////////////////////////////////////////////////////////////////////////////////////
// This plugin currently supports only per-remap mode activation.
////////////////////////////////////////////////////////////////////////////////////

#include <sys/time.h>
#include <ts/ts.h>
#include <ts/remap.h>
#include <set>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <getopt.h>
#include <netdb.h>
#include <map>

static const char *DEBUG_TAG = (char *)"collapsed_forwarding";

static const char *LOCATION_HEADER      = "Location";
static const char *REDIRECT_REASON      = "See Other";
static const char *ATS_INTERNAL_MESSAGE = "@Ats-Internal";

static int OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = 5;
static int OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT     = 500;

typedef struct _RequestData {
  TSHttpTxn txnp;
  int wl_retry; // write lock failure retry count
  std::string req_url;
} RequestData;

static int
add_redirect_header(TSMBuffer &bufp, TSMLoc &hdr_loc, const std::string &location)
{
  // This is needed in case the response already contains a Location header
  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER));

  if (field_loc == TS_NULL_MLOC) {
    TSMimeHdrFieldCreateNamed(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER), &field_loc);
  }

  if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, location.c_str(), location.size())) {
    TSDebug(DEBUG_TAG, "Adding Location header %s", LOCATION_HEADER);
    TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
  }

  TSHandleMLocRelease(bufp, hdr_loc, field_loc);

  TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_SEE_OTHER);
  TSHttpHdrReasonSet(bufp, hdr_loc, REDIRECT_REASON, strlen(REDIRECT_REASON));
  return TS_SUCCESS;
}

static bool
check_internal_message_hdr(TSHttpTxn &txnp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  bool found = false;

  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("check_internal_message_hdr: couldn't retrieve client response header");
    return false;
  }

  TSMLoc header_loc = TSMimeHdrFieldFind(bufp, hdr_loc, ATS_INTERNAL_MESSAGE, strlen(ATS_INTERNAL_MESSAGE));
  if (header_loc) {
    found = true;
    // found the header, remove it now..
    TSMimeHdrFieldDestroy(bufp, hdr_loc, header_loc);
    TSHandleMLocRelease(bufp, hdr_loc, header_loc);
  }

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);

  return found;
}

static int
on_OS_DNS(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "OS_DNS request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_request_header(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "Send_Req request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_read_response_header(TSHttpTxn &txnp)
{
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_immediate(RequestData *req, TSCont &contp)
{
  if (!req) {
    TSError("%s: invalid req_data", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSDebug(DEBUG_TAG, "continuation delayed, scheduling now..for url: %s", req->req_url.c_str());

  // add retry_done header to prevent looping
  std::string value;
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(req->txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header for url %s", DEBUG_TAG,
            req->req_url.c_str());
    TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  add_redirect_header(bufp, hdr_loc, req->req_url);
  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_response_header(RequestData *req, TSHttpTxn &txnp, TSCont &contp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSHttpStatus status = TSHttpHdrStatusGet(bufp, hdr_loc);
  TSDebug(DEBUG_TAG, "Response code: %d", status);

  if ((status == TS_HTTP_STATUS_BAD_GATEWAY) || (status == TS_HTTP_STATUS_SEE_OTHER) ||
      status == TS_HTTP_STATUS_INTERNAL_SERVER_ERROR) {
    bool is_internal_message_hdr = check_internal_message_hdr(txnp);
    bool delay_request =
      is_internal_message_hdr || ((req->wl_retry > 0) && (req->wl_retry < OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES));

    if (delay_request) {
      req->wl_retry++;
      TSDebug(DEBUG_TAG, "delaying request, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(), req->wl_retry);
      TSContSchedule(contp, OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT, TS_THREAD_POOL_TASK);
      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
      return TS_SUCCESS;
    }
  }

  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "request delayed, but unsuccessful, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(),
            req->wl_retry);
    req->wl_retry = 0;
  }

  // done..cleanup
  delete req;
  TSContDestroy(contp);

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
collapsed_cont(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp      = static_cast<TSHttpTxn>(edata);
  RequestData *my_req = static_cast<RequestData *>(TSContDataGet(contp));

  switch (event) {
  case TS_EVENT_HTTP_OS_DNS: {
    return on_OS_DNS(my_req, txnp);
  }

  case TS_EVENT_HTTP_SEND_REQUEST_HDR: {
    return on_send_request_header(my_req, txnp);
  }

  case TS_EVENT_HTTP_READ_RESPONSE_HDR: {
    return on_read_response_header(txnp);
  }
  case TS_EVENT_IMMEDIATE:
  case TS_EVENT_TIMEOUT: {
    return on_immediate(my_req, contp);
  }
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR: {
    return on_send_response_header(my_req, txnp, contp);
  }
  default: {
    TSDebug(DEBUG_TAG, "Unexpected event: %d", event);
    break;
  }
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

TSReturnCode
TSRemapInit(TSRemapInterface * /* api_info */, char * /* errbuf */, int /* errbuf_size */)
{
  TSDebug(DEBUG_TAG, "plugin is succesfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void ** /* ih */, char * /* errbuf */, int /* errbuf_size */)
{
  // basic argv processing..
  for (int i = 2; i < argc; ++i) {
    if (strncmp(argv[i], "--delay=", 8) == 0) {
      OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = atoi((char *)(argv[i] + 8));
    } else if (strncmp(argv[i], "--retries=", 10) == 0) {
      OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = atoi((char *)(argv[i] + 10));
    }
  }

  return TS_SUCCESS;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
  TSCont cont = TSContCreate(collapsed_cont, TSMutexCreate());

  RequestData *req_data = new RequestData();

  req_data->txnp     = rh;
  req_data->wl_retry = 0;

  int url_len = 0;
  char *url   = TSHttpTxnEffectiveUrlStringGet(rh, &url_len);
  req_data->req_url.assign(url, url_len);

  TSfree(url);
  TSContDataSet(cont, req_data);

  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_REQUEST_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_OS_DNS_HOOK, cont);

  return TSREMAP_DID_REMAP;
}
<commit_msg>TS-4888: Modified collapsed_forwarding plugin to return TSREMAP_NO_REMAP.<commit_after>/** @file

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

////////////////////////////////////////////////////////////////////////////////
// collapsed_forwarding::
//
// ATS plugin to allow collapsed forwarding of concurrent requests for the same
// object. This plugin is based on open_write_fail_action feature, which detects
// cache open write failure on a cache miss and returns a 502 error along with a
// special @-header indicating the reason for 502 error. The plugin acts on the
// error by using an internal redirect follow back to itself, essentially blocking
// the request until a response arrives, at which point, relies on read-while-writer
// feature to start downloading the object to all waiting clients. The following
// config parameters are assumed to be set for this plugin to work:
////////////////////////////////////////////////////////////////////////////////////
// proxy.config.http.cache.open_write_fail_action        1 /////////////////////////
// proxy.config.cache.enable_read_while_writer           1 /////////////////////////
// proxy.config.http.redirection_enabled                 1 /////////////////////////
// proxy.config.http.number_of_redirections             10 /////////////////////////
// proxy.config.http.redirect_use_orig_cache_key         1 /////////////////////////
// proxy.config.http.background_fill_active_timeout      0 /////////////////////////
// proxy.config.http.background_fill_completed_threshold 0 /////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// Additionally, given that collapsed forwarding works based on cache write
// lock failure detection, the plugin requires cache to be enabled and ready.
// On a restart, Traffic Server typically takes a few seconds to initialize
// the cache depending on the cache size and number of dirents. While the
// cache is not ready yet, collapsed forwarding can not detect the write lock
// contention and so can not work. The setting proxy.config.http.wait_for_cache
// may be enabled which allows blocking incoming connections from being
// accepted until cache is ready.
////////////////////////////////////////////////////////////////////////////////////
// This plugin currently supports only per-remap mode activation.
////////////////////////////////////////////////////////////////////////////////////

#include <sys/time.h>
#include <ts/ts.h>
#include <ts/remap.h>
#include <set>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <getopt.h>
#include <netdb.h>
#include <map>

static const char *DEBUG_TAG = (char *)"collapsed_forwarding";

static const char *LOCATION_HEADER      = "Location";
static const char *REDIRECT_REASON      = "See Other";
static const char *ATS_INTERNAL_MESSAGE = "@Ats-Internal";

static int OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = 5;
static int OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT     = 500;

typedef struct _RequestData {
  TSHttpTxn txnp;
  int wl_retry; // write lock failure retry count
  std::string req_url;
} RequestData;

static int
add_redirect_header(TSMBuffer &bufp, TSMLoc &hdr_loc, const std::string &location)
{
  // This is needed in case the response already contains a Location header
  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER));

  if (field_loc == TS_NULL_MLOC) {
    TSMimeHdrFieldCreateNamed(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER), &field_loc);
  }

  if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, location.c_str(), location.size())) {
    TSDebug(DEBUG_TAG, "Adding Location header %s", LOCATION_HEADER);
    TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
  }

  TSHandleMLocRelease(bufp, hdr_loc, field_loc);

  TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_SEE_OTHER);
  TSHttpHdrReasonSet(bufp, hdr_loc, REDIRECT_REASON, strlen(REDIRECT_REASON));
  return TS_SUCCESS;
}

static bool
check_internal_message_hdr(TSHttpTxn &txnp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  bool found = false;

  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("check_internal_message_hdr: couldn't retrieve client response header");
    return false;
  }

  TSMLoc header_loc = TSMimeHdrFieldFind(bufp, hdr_loc, ATS_INTERNAL_MESSAGE, strlen(ATS_INTERNAL_MESSAGE));
  if (header_loc) {
    found = true;
    // found the header, remove it now..
    TSMimeHdrFieldDestroy(bufp, hdr_loc, header_loc);
    TSHandleMLocRelease(bufp, hdr_loc, header_loc);
  }

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);

  return found;
}

static int
on_OS_DNS(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "OS_DNS request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_request_header(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "Send_Req request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_read_response_header(TSHttpTxn &txnp)
{
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_immediate(RequestData *req, TSCont &contp)
{
  if (!req) {
    TSError("%s: invalid req_data", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSDebug(DEBUG_TAG, "continuation delayed, scheduling now..for url: %s", req->req_url.c_str());

  // add retry_done header to prevent looping
  std::string value;
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(req->txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header for url %s", DEBUG_TAG,
            req->req_url.c_str());
    TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  add_redirect_header(bufp, hdr_loc, req->req_url);
  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_response_header(RequestData *req, TSHttpTxn &txnp, TSCont &contp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSHttpStatus status = TSHttpHdrStatusGet(bufp, hdr_loc);
  TSDebug(DEBUG_TAG, "Response code: %d", status);

  if ((status == TS_HTTP_STATUS_BAD_GATEWAY) || (status == TS_HTTP_STATUS_SEE_OTHER) ||
      status == TS_HTTP_STATUS_INTERNAL_SERVER_ERROR) {
    bool is_internal_message_hdr = check_internal_message_hdr(txnp);
    bool delay_request =
      is_internal_message_hdr || ((req->wl_retry > 0) && (req->wl_retry < OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES));

    if (delay_request) {
      req->wl_retry++;
      TSDebug(DEBUG_TAG, "delaying request, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(), req->wl_retry);
      TSContSchedule(contp, OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT, TS_THREAD_POOL_TASK);
      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
      return TS_SUCCESS;
    }
  }

  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "request delayed, but unsuccessful, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(),
            req->wl_retry);
    req->wl_retry = 0;
  }

  // done..cleanup
  delete req;
  TSContDestroy(contp);

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
collapsed_cont(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp      = static_cast<TSHttpTxn>(edata);
  RequestData *my_req = static_cast<RequestData *>(TSContDataGet(contp));

  switch (event) {
  case TS_EVENT_HTTP_OS_DNS: {
    return on_OS_DNS(my_req, txnp);
  }

  case TS_EVENT_HTTP_SEND_REQUEST_HDR: {
    return on_send_request_header(my_req, txnp);
  }

  case TS_EVENT_HTTP_READ_RESPONSE_HDR: {
    return on_read_response_header(txnp);
  }
  case TS_EVENT_IMMEDIATE:
  case TS_EVENT_TIMEOUT: {
    return on_immediate(my_req, contp);
  }
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR: {
    return on_send_response_header(my_req, txnp, contp);
  }
  default: {
    TSDebug(DEBUG_TAG, "Unexpected event: %d", event);
    break;
  }
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

TSReturnCode
TSRemapInit(TSRemapInterface * /* api_info */, char * /* errbuf */, int /* errbuf_size */)
{
  TSDebug(DEBUG_TAG, "plugin is succesfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void ** /* ih */, char * /* errbuf */, int /* errbuf_size */)
{
  // basic argv processing..
  for (int i = 2; i < argc; ++i) {
    if (strncmp(argv[i], "--delay=", 8) == 0) {
      OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = atoi((char *)(argv[i] + 8));
    } else if (strncmp(argv[i], "--retries=", 10) == 0) {
      OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = atoi((char *)(argv[i] + 10));
    }
  }

  return TS_SUCCESS;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
  TSCont cont = TSContCreate(collapsed_cont, TSMutexCreate());

  RequestData *req_data = new RequestData();

  req_data->txnp     = rh;
  req_data->wl_retry = 0;

  int url_len = 0;
  char *url   = TSHttpTxnEffectiveUrlStringGet(rh, &url_len);
  req_data->req_url.assign(url, url_len);

  TSfree(url);
  TSContDataSet(cont, req_data);

  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_REQUEST_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_OS_DNS_HOOK, cont);

  return TSREMAP_NO_REMAP;
}

void
TSRemapDeleteInstance(void *ih)
{
  // To resolve run time error
}
<|endoftext|>