<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regcomplazy.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 09:37:00 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 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
 *
 ************************************************************************/

#include <stdio.h>

#include "sal/main.h"
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <osl/file.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>

#include <vector>
#include <hash_map>

#include <registry/registry.hxx>

#define OUSTR(x) ::rtl::OUString::createFromAscii( x )
#define OSToOUS(x) ::rtl::OStringToOUString(x, osl_getThreadTextEncoding())
#define OUSToOS(x) ::rtl::OUStringToOString(x, osl_getThreadTextEncoding())
using namespace ::rtl;

typedef ::std::vector< ::rtl::OString > OSVector;

typedef ::std::pair< ::rtl::OString, OSVector > DataPair;

typedef ::std::vector< DataPair > DataVector;

struct CompDescriptor {
    OString sImplementationName;
    OString sComponentName;
    OString sLoaderName;
    OSVector vSupportedServices;
    DataVector vData;
};

typedef ::std::vector< CompDescriptor > CDescrVector;

static void print_options() SAL_THROW( () )
{
    printf(
        "\nusage: regcomplazy [-v]registry_file cmp_descr_file ...\n\n"
        "Register a cmponent using a comp description file.\n"
        "Option -v prints verbose output on stdout.\n" );
}

static bool checkImplValue(RegistryValueList<sal_Char*>* pValueList, OString sImplName) {
    int i = 0;
    for (i=0; i < pValueList->getLength(); i++) {
        if (sImplName.equals(pValueList->getElement(i)))
            return true;
    }

    return false;
}

//==================================================================================================
SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
    if (argc < 3)
    {
        print_options();
        return 1;
    }

    bool bVerbose = false;
    int nPos = 1;
    if ('-' == argv[ nPos ][ 0 ] && 'v' == argv[ nPos ][ 1 ])
    {
        if ('\0' != argv[ nPos ][ 2 ])
        {
            print_options();
            return 1;
        }
        bVerbose = true;
        ++nPos;
    }

    OUString sys_path( OUSTR( argv[ nPos ] ) );
    OUString reg_url;
    oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &reg_url.pData );
    if (osl_File_E_None != rc)
    {
        if (bVerbose)
            fprintf( stderr, "\nERROR: cannot make file url out of %s\n", argv[nPos]);
        return 1;
    }

    FILE* fDescr = fopen(argv[ ++nPos ], "r");
    OStringBuffer sBuffer(512);

    if ( fDescr) {
        size_t totalSize = 0;
        size_t readSize  = 0;
        char   pBuffer[513];

        while ( !feof(fDescr) )
        {
            if ( (readSize = fread(pBuffer, 1, 512, fDescr)) > 0
                 && !ferror(fDescr) ) {
                totalSize += readSize;
                if (totalSize >= 512)
                    sBuffer.ensureCapacity(totalSize * 2);

                sBuffer.append(pBuffer, readSize);
            }
        }
    }

    OString sDescr = sBuffer.makeStringAndClear();
    sal_Int32 nTokenIndex = 0;
    sal_Int32 nIndex = 0;

    CDescrVector vDescr;
    CompDescriptor descr;
    bool bFirst = true;

    do {
        OString sTmp = sDescr.getToken(0, '\x0A', nTokenIndex);
        OString sToken(sTmp);
        if (sTmp.pData->buffer[sTmp.getLength()-1] == '\x0D')
            sToken = sTmp.copy(0, sTmp.getLength()-1);

        if (nIndex = sToken.indexOf("[Data]") >= 0) {
            do {
                OString sTmp = sDescr.getToken(0, '\x0A', nTokenIndex);
                OString sToken(sTmp);
                if (sTmp.pData->buffer[sTmp.getLength()-1] == '\x0D')
                    sToken = sTmp.copy(0, sTmp.getLength()-1);

                if ((sToken.getLength() > 0) && (sToken.pData->buffer[0] != '[')) {
                    OString dataKey(sToken.copy(0, sToken.indexOf('=')));
                    OString sValues(sToken.copy(sToken.indexOf('=')+1));
                    sal_Int32 nVIndex = 0;
                    OSVector vValues;
                    do {
                        OString sValue = sValues.getToken(0, ';', nVIndex);
                        vValues.push_back(sValue);
                    } while (nVIndex >= 0 );
                    descr.vData.push_back(DataPair(dataKey, vValues));
                } else
                    break;
            } while (nTokenIndex >= 0 );
        }
        if ( nIndex = sToken.indexOf("[ComponentDescriptor]") >= 0) {
            if (bFirst)
                bFirst = false;
            else
                vDescr.push_back(descr);

            descr = CompDescriptor();
        }
        else if ( nIndex = sToken.indexOf("ImplementationName=") >= 0) {
            descr.sImplementationName = sToken.copy(19);
        }
        else if ( nIndex = sToken.indexOf("ComponentName=") >= 0) {
            descr.sComponentName = sToken.copy(14);
        }
        else if ( nIndex = sToken.indexOf("LoaderName=") >= 0) {
            descr.sLoaderName = sToken.copy(11);
        }
        else if ( (nIndex = sToken.indexOf("[SupportedServices]") < 0) &&
                  (sToken.getLength() > 0) &&
                  (sToken.pData->buffer[0] != '[') ) {
            descr.vSupportedServices.push_back(sToken);
        }
    } while (nTokenIndex >= 0 );
    // insert the last descriptor
    vDescr.push_back(descr);

    RegistryLoader* pLoader = new RegistryLoader();
    if (!pLoader->isLoaded())
    {
        delete pLoader;
        return 1;
    }

    Registry *pReg = new Registry(*pLoader);
    delete pLoader;

    RegistryKey rootKey, key, subKey, serviceKey;

    if (pReg->open(reg_url, REG_READWRITE))
    {
        if (pReg->create(reg_url))
        {
            if (bVerbose)
                fprintf(stderr, "ERROR: open registry \"%s\" failed\n", argv[1]);
            return 1;
        }
    }
    if (pReg->openRootKey(rootKey)) {
        if (bVerbose)
            fprintf(stderr, "ERROR: open root key failed\n");
        return 1;
    }

    CDescrVector::const_iterator comp_iter = vDescr.begin();
    do {
        OString sImplName = (*comp_iter).sImplementationName;
        OUStringBuffer sbImpl;
        sbImpl.appendAscii("/IMPLEMENTATIONS/");
        sbImpl.append(OSToOUS(sImplName));
        OUString sImplKeyName = sbImpl.makeStringAndClear();

        if (rootKey.openKey(sImplKeyName, key) == REG_NO_ERROR) {
            if (bVerbose) {
               fprintf(stderr, "WARNING: implementation entry for \"%s\" already exists, existing entries are overwritten\n", sImplName.getStr());
            }
        } else {
            if (rootKey.createKey(sImplKeyName, key)) {
                if (bVerbose) {
                    fprintf(stderr, "ERROR: can't create new implementation entry \"%s\".\n", sImplName.getStr());
                }
                return 1;
            }
        }

        OString sLoaderName = (*comp_iter).sLoaderName;
        OUString usKeyName(OUSTR("UNO/ACTIVATOR"));
        key.createKey(usKeyName, subKey);
        subKey.setValue(OUString(), RG_VALUETYPE_STRING,
                        (sal_Char*)sLoaderName.getStr(), sLoaderName.getLength()+1);

        OString sCompName = (*comp_iter).sComponentName;
        usKeyName = OUSTR("UNO/LOCATION");
        key.createKey(usKeyName, subKey);
        subKey.setValue(OUString(), RG_VALUETYPE_STRING,
                        (sal_Char*)sCompName.getStr(), sCompName.getLength()+1);

        if ((*comp_iter).vData.size() > 0) {
            usKeyName = OUSTR("DATA");
            RegistryKey dataKey, valueKey;
            key.createKey(usKeyName, dataKey);

            DataVector::const_iterator data_iter = ((*comp_iter).vData).begin();
            do {
                OUString sDataKey(OSToOUS((*data_iter).first));
                dataKey.createKey(sDataKey, valueKey);

                OSVector::const_iterator value_iter = ((*data_iter).second).begin();
                int vlen = (*data_iter).second.size();
                sal_Char** pValueList = (sal_Char**)rtl_allocateZeroMemory(
                    vlen * sizeof(sal_Char*));
                int i = 0;
                do {
                    pValueList[i] = (sal_Char*)rtl_allocateZeroMemory(
                        (*value_iter).getLength()+1 * sizeof(sal_Char));
                    rtl_copyMemory(pValueList[i], (sal_Char*)(*value_iter).getStr(),
                               (*value_iter).getLength()+1);
                    i++;
                    value_iter++;
                } while (value_iter != (*data_iter).second.end());

                valueKey.setStringListValue(OUString(), pValueList, vlen);

                // free memory
                for (i=0; i<vlen; i++)
                    rtl_freeMemory(pValueList[i]);
                rtl_freeMemory(pValueList);

                data_iter++;
            } while (data_iter != (*comp_iter).vData.end());
        }

        usKeyName = OUSTR("UNO/SERVICES");
        key.createKey(usKeyName, subKey);

        rootKey.createKey(OUSTR("/SERVICES"), serviceKey);

        OSVector::const_iterator serv_iter = ((*comp_iter).vSupportedServices).begin();
        OUString usServiceKeyName;
        do {
            usServiceKeyName = OSToOUS(*serv_iter);
            // write service key in impl section
            subKey.createKey(usServiceKeyName, key);

            if (serviceKey.openKey(usServiceKeyName, key) == REG_NO_ERROR) {
                RegistryValueList<sal_Char*> valueList;
                serviceKey.getStringListValue(usServiceKeyName, valueList);
                if ( checkImplValue(&valueList, sImplName) ) {
                    serv_iter++;
                    continue;
                }

                sal_Int32 nServices = valueList.getLength()+1;
                sal_Char** pImplList = (sal_Char**)rtl_allocateZeroMemory(
                    nServices * sizeof(sal_Char*));
                pImplList[0] = (sal_Char*)rtl_allocateZeroMemory(
                    sImplName.getLength()+1 * sizeof(sal_Char));
                rtl_copyMemory(pImplList[0], (sal_Char*)sImplName.getStr(),
                               sImplName.getLength()+1);
                int i = 0;
                for (i=0; i < valueList.getLength(); i++) {
                    pImplList[i+1]=valueList.getElement(i);
                }
                key.setStringListValue(OUString(), pImplList, nServices);

                // free memory
                rtl_freeMemory(pImplList[0]);
                rtl_freeMemory(pImplList);

            } else {
                serviceKey.createKey(usServiceKeyName, key);

                sal_Char* pImplList[1];
                pImplList[0] = (sal_Char*)rtl_allocateZeroMemory(
                    sImplName.getLength()+1 * sizeof(sal_Char));
                rtl_copyMemory(pImplList[0], (sal_Char*)sImplName.getStr(),
                               sImplName.getLength()+1);
                key.setStringListValue(OUString(), pImplList, 1);

                // free memory
                rtl_freeMemory(pImplList[0]);
            }
            serv_iter++;
        } while (serv_iter != (*comp_iter).vSupportedServices.end());

        comp_iter++;
    } while (comp_iter != vDescr.end());

    key.closeKey();
    subKey.closeKey();
    serviceKey.closeKey();
    rootKey.closeKey();
    pReg->close();

    return 0;
}
<commit_msg>INTEGRATION: CWS warnings01 (1.2.12); FILE MERGED 2005/11/29 09:30:05 sb 1.2.12.4: #i53898# Made the code warning-free. 2005/09/23 02:54:54 sb 1.2.12.3: RESYNC: (1.2-1.3); FILE MERGED 2005/09/07 11:52:48 sb 1.2.12.2: #i53898# Made code warning-free. 2005/09/01 07:35:51 sb 1.2.12.1: #i53898# Made code warning-free.<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regcomplazy.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 21:55:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 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
 *
 ************************************************************************/

#include <stdio.h>

#include "sal/main.h"
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <osl/file.hxx>
#include <rtl/strbuf.hxx>
#include <rtl/ustrbuf.hxx>

#include <vector>
#include <hash_map>

#include <registry/registry.hxx>

#define OUSTR(x) ::rtl::OUString::createFromAscii( x )
#define OSToOUS(x) ::rtl::OStringToOUString(x, osl_getThreadTextEncoding())
#define OUSToOS(x) ::rtl::OUStringToOString(x, osl_getThreadTextEncoding())
using namespace ::rtl;

typedef ::std::vector< ::rtl::OString > OSVector;

typedef ::std::pair< ::rtl::OString, OSVector > DataPair;

typedef ::std::vector< DataPair > DataVector;

struct CompDescriptor {
    OString sImplementationName;
    OString sComponentName;
    OString sLoaderName;
    OSVector vSupportedServices;
    DataVector vData;
};

typedef ::std::vector< CompDescriptor > CDescrVector;

static void print_options() SAL_THROW( () )
{
    printf(
        "\nusage: regcomplazy [-v]registry_file cmp_descr_file ...\n\n"
        "Register a cmponent using a comp description file.\n"
        "Option -v prints verbose output on stdout.\n" );
}

static bool checkImplValue(RegistryValueList<sal_Char*>* pValueList, OString sImplName) {
    for (sal_uInt32 i=0; i < pValueList->getLength(); i++) {
        if (sImplName.equals(pValueList->getElement(i)))
            return true;
    }

    return false;
}

//==================================================================================================
SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv)
{
    if (argc < 3)
    {
        print_options();
        return 1;
    }

    bool bVerbose = false;
    int nPos = 1;
    if ('-' == argv[ nPos ][ 0 ] && 'v' == argv[ nPos ][ 1 ])
    {
        if ('\0' != argv[ nPos ][ 2 ])
        {
            print_options();
            return 1;
        }
        bVerbose = true;
        ++nPos;
    }

    OUString sys_path( OUSTR( argv[ nPos ] ) );
    OUString reg_url;
    oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &reg_url.pData );
    if (osl_File_E_None != rc)
    {
        if (bVerbose)
            fprintf( stderr, "\nERROR: cannot make file url out of %s\n", argv[nPos]);
        return 1;
    }

    FILE* fDescr = fopen(argv[ ++nPos ], "r");
    OStringBuffer sBuffer(512);

    if ( fDescr) {
        size_t totalSize = 0;
        size_t readSize  = 0;
        char   pBuffer[513];

        while ( !feof(fDescr) )
        {
            if ( (readSize = fread(pBuffer, 1, 512, fDescr)) > 0
                 && !ferror(fDescr) ) {
                totalSize += readSize;
                if (totalSize >= 512)
                    sBuffer.ensureCapacity(totalSize * 2);

                sBuffer.append(pBuffer, readSize);
            }
        }
    }

    OString sDescr = sBuffer.makeStringAndClear();
    sal_Int32 nTokenIndex = 0;

    CDescrVector vDescr;
    CompDescriptor descr;
    bool bFirst = true;

    do {
        OString sTmp = sDescr.getToken(0, '\x0A', nTokenIndex);
        OString sToken(sTmp);
        if (sTmp.pData->buffer[sTmp.getLength()-1] == '\x0D')
            sToken = sTmp.copy(0, sTmp.getLength()-1);

        if (sToken.indexOf("[Data]") >= 0) {
            do {
                OString sTmp2 = sDescr.getToken(0, '\x0A', nTokenIndex);
                OString sToken2(sTmp2);
                if (sTmp2.pData->buffer[sTmp2.getLength()-1] == '\x0D')
                    sToken2 = sTmp2.copy(0, sTmp2.getLength()-1);

                if ((sToken2.getLength() > 0) && (sToken2.pData->buffer[0] != '[')) {
                    OString dataKey(sToken2.copy(0, sToken2.indexOf('=')));
                    OString sValues(sToken2.copy(sToken2.indexOf('=')+1));
                    sal_Int32 nVIndex = 0;
                    OSVector vValues;
                    do {
                        OString sValue = sValues.getToken(0, ';', nVIndex);
                        vValues.push_back(sValue);
                    } while (nVIndex >= 0 );
                    descr.vData.push_back(DataPair(dataKey, vValues));
                } else
                    break;
            } while (nTokenIndex >= 0 );
        }
        if ( sToken.indexOf("[ComponentDescriptor]") >= 0) {
            if (bFirst)
                bFirst = false;
            else
                vDescr.push_back(descr);

            descr = CompDescriptor();
        }
        else if ( sToken.indexOf("ImplementationName=") >= 0) {
            descr.sImplementationName = sToken.copy(19);
        }
        else if ( sToken.indexOf("ComponentName=") >= 0) {
            descr.sComponentName = sToken.copy(14);
        }
        else if ( sToken.indexOf("LoaderName=") >= 0) {
            descr.sLoaderName = sToken.copy(11);
        }
        else if ( (sToken.indexOf("[SupportedServices]") < 0) &&
                  (sToken.getLength() > 0) &&
                  (sToken.pData->buffer[0] != '[') ) {
            descr.vSupportedServices.push_back(sToken);
        }
    } while (nTokenIndex >= 0 );
    // insert the last descriptor
    vDescr.push_back(descr);

    RegistryLoader* pLoader = new RegistryLoader();
    if (!pLoader->isLoaded())
    {
        delete pLoader;
        return 1;
    }

    Registry *pReg = new Registry(*pLoader);
    delete pLoader;

    RegistryKey rootKey, key, subKey, serviceKey;

    if (pReg->open(reg_url, REG_READWRITE))
    {
        if (pReg->create(reg_url))
        {
            if (bVerbose)
                fprintf(stderr, "ERROR: open registry \"%s\" failed\n", argv[1]);
            return 1;
        }
    }
    if (pReg->openRootKey(rootKey)) {
        if (bVerbose)
            fprintf(stderr, "ERROR: open root key failed\n");
        return 1;
    }

    CDescrVector::const_iterator comp_iter = vDescr.begin();
    do {
        OString sImplName = (*comp_iter).sImplementationName;
        OUStringBuffer sbImpl;
        sbImpl.appendAscii("/IMPLEMENTATIONS/");
        sbImpl.append(OSToOUS(sImplName));
        OUString sImplKeyName = sbImpl.makeStringAndClear();

        if (rootKey.openKey(sImplKeyName, key) == REG_NO_ERROR) {
            if (bVerbose) {
               fprintf(stderr, "WARNING: implementation entry for \"%s\" already exists, existing entries are overwritten\n", sImplName.getStr());
            }
        } else {
            if (rootKey.createKey(sImplKeyName, key)) {
                if (bVerbose) {
                    fprintf(stderr, "ERROR: can't create new implementation entry \"%s\".\n", sImplName.getStr());
                }
                return 1;
            }
        }

        OString sLoaderName = (*comp_iter).sLoaderName;
        OUString usKeyName(OUSTR("UNO/ACTIVATOR"));
        key.createKey(usKeyName, subKey);
        subKey.setValue(OUString(), RG_VALUETYPE_STRING,
                        (sal_Char*)sLoaderName.getStr(), sLoaderName.getLength()+1);

        OString sCompName = (*comp_iter).sComponentName;
        usKeyName = OUSTR("UNO/LOCATION");
        key.createKey(usKeyName, subKey);
        subKey.setValue(OUString(), RG_VALUETYPE_STRING,
                        (sal_Char*)sCompName.getStr(), sCompName.getLength()+1);

        if ((*comp_iter).vData.size() > 0) {
            usKeyName = OUSTR("DATA");
            RegistryKey dataKey, valueKey;
            key.createKey(usKeyName, dataKey);

            DataVector::const_iterator data_iter = ((*comp_iter).vData).begin();
            do {
                OUString sDataKey(OSToOUS((*data_iter).first));
                dataKey.createKey(sDataKey, valueKey);

                OSVector::const_iterator value_iter = ((*data_iter).second).begin();
                int vlen = (*data_iter).second.size();
                sal_Char** pValueList = (sal_Char**)rtl_allocateZeroMemory(
                    vlen * sizeof(sal_Char*));
                int i = 0;
                do {
                    pValueList[i] = (sal_Char*)rtl_allocateZeroMemory(
                        (*value_iter).getLength()+1 * sizeof(sal_Char));
                    rtl_copyMemory(pValueList[i], (sal_Char*)(*value_iter).getStr(),
                               (*value_iter).getLength()+1);
                    i++;
                    value_iter++;
                } while (value_iter != (*data_iter).second.end());

                valueKey.setStringListValue(OUString(), pValueList, vlen);

                // free memory
                for (i=0; i<vlen; i++)
                    rtl_freeMemory(pValueList[i]);
                rtl_freeMemory(pValueList);

                data_iter++;
            } while (data_iter != (*comp_iter).vData.end());
        }

        usKeyName = OUSTR("UNO/SERVICES");
        key.createKey(usKeyName, subKey);

        rootKey.createKey(OUSTR("/SERVICES"), serviceKey);

        OSVector::const_iterator serv_iter = ((*comp_iter).vSupportedServices).begin();
        OUString usServiceKeyName;
        do {
            usServiceKeyName = OSToOUS(*serv_iter);
            // write service key in impl section
            subKey.createKey(usServiceKeyName, key);

            if (serviceKey.openKey(usServiceKeyName, key) == REG_NO_ERROR) {
                RegistryValueList<sal_Char*> valueList;
                serviceKey.getStringListValue(usServiceKeyName, valueList);
                if ( checkImplValue(&valueList, sImplName) ) {
                    serv_iter++;
                    continue;
                }

                sal_uInt32 nServices = valueList.getLength()+1;
                sal_Char** pImplList = (sal_Char**)rtl_allocateZeroMemory(
                    nServices * sizeof(sal_Char*));
                pImplList[0] = (sal_Char*)rtl_allocateZeroMemory(
                    sImplName.getLength()+1 * sizeof(sal_Char));
                rtl_copyMemory(pImplList[0], (sal_Char*)sImplName.getStr(),
                               sImplName.getLength()+1);
                for (sal_uInt32 i=0; i < valueList.getLength(); i++) {
                    pImplList[i+1]=valueList.getElement(i);
                }
                key.setStringListValue(OUString(), pImplList, nServices);

                // free memory
                rtl_freeMemory(pImplList[0]);
                rtl_freeMemory(pImplList);

            } else {
                serviceKey.createKey(usServiceKeyName, key);

                sal_Char* pImplList[1];
                pImplList[0] = (sal_Char*)rtl_allocateZeroMemory(
                    sImplName.getLength()+1 * sizeof(sal_Char));
                rtl_copyMemory(pImplList[0], (sal_Char*)sImplName.getStr(),
                               sImplName.getLength()+1);
                key.setStringListValue(OUString(), pImplList, 1);

                // free memory
                rtl_freeMemory(pImplList[0]);
            }
            serv_iter++;
        } while (serv_iter != (*comp_iter).vSupportedServices.end());

        comp_iter++;
    } while (comp_iter != vDescr.end());

    key.closeKey();
    subKey.closeKey();
    serviceKey.closeKey();
    rootKey.closeKey();
    pReg->close();

    return 0;
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_OPENCL_KERNEL_GENERATOR_HPP
#define STAN_MATH_OPENCL_KERNEL_GENERATOR_HPP
#ifdef STAN_OPENCL

/**
 * \ingroup opencl
 * \defgroup opencl_kernel_generator OpenCL Kernel Generator
 *
 * The OpenCL kernel generator is used to combine multiple matrix operations
 * into a single OpenCL kernel. This is much simpler than writing
 * multi-operation kernels by hand.
 *
 * Because global GPU memory loads and stores are relativly slow compared to
 * calculations in a kernel, using one kernel for multiple operations is faster
 * than using one kernel per operation.
 *
 * The kernel generator uses lazy evaluation. Each operation is represented by
 * an object derived from `operation_cl`. Such an object holds arguments of the
 * operations as well as meta information needed to generate calculations on the
 * arguments. Arguments to operations can be other operations, scalars
 * or `matrix_cl` objects. An operation is evaluated when either an operation is
 * assigned to a `matrix_cl` or a left-hand-side operation or `.eval()` is
 * called.
 *
 * ## Defining a new kernel generator operation
 *
 * Unary functions can be added using one of the macros in
 * `unary_functions.hpp`.
 *
 * New kernel generator classes must satsify the conditions below:
 *
 * 1. The class must be derived from a class inheriting from `operation_cl`.
 * Optionally, if the operation should support being assigned to, it can be
 * derived from a class inheriting `operation_cl_lhs` instead.
 * 2. It's parent template arguments should be set to derived type, type of
 *  scalar and types of any expression arguements.
 * 3. Member type `Scalar` should be defined as scalar type of the result of
 * the operation.
 * 4. Member function `generate` has the signature
 * ```cpp
 * inline kernel_parts generate(const std::string& i, const std::string& j,
 *                            const std::string& var_name_arg)
 * ```
 * 5. Member function `deep_copy` should make a copy of the expression.
 * Arguments that are operations should be copied by calling their `deep_copy`.
 *
 * The following functions can optionally be defined. Defaults are implemented
 * in `operation_cl`:
 * - `void modify_argument_indices(std::string& i, std::string& j)`:
 *     - Modifies what indices are passed to argument's `generate()`.
 *     - Default: No-op
 * - `void set_args(std::set<const operation_cl_base*>& generated,
 *        cl::Kernel& kernel, int& arg_num)`:
 *     - Sets additional kernel arguments.
 *     - Default: Calls `set_args()` on arguments.
 * - `int rows()`:
 *     - Returns Number of rows of the result.
 *     - Default: Returns maximum of the arguments' rows.
 * - `int cols()`:
 *     - Returns number of columns of the result.
 *     - Default: Returns maximum of the arguments' columns.
 * - `int thread_rows()`:
 *     - Returns number of threads required for this operation in rows
 * direction.
 *     - Default: returns `rows()`.
 * - `int thread_cols()`:
 *     - Returns number of threads required for this operation in cols direction.
 *     - Default: `cols()`.
 * - `std::pair<int,int> extreme_diagonals()`:
 *     - Returns indices of the extreme bottom and top nonzero diagonals of the result of
 * the operation. Diagonal has index 0, first superdiagonal 1, first subdiagonal
 * -1 and so on. For instance `load_` of a matrix with lower triangular view
 * would return bottom diagonal of `1-rows()` and top diagonal `0`. Returning a
 * more extreme value is also allowed and especially useful for operations that
 * are broadcast so their exact size is not known.
 *     - Default: Bottom diagonal equals to min of bottom diagonals of
 * arguments. Top diagonal equals to max of top diagonals of arguments.
 *
 * If an operation should support being assigned to it should also define the
 * following:
 *
 * 1. Member function `generate_lhs` with same signature as `generate`
 * that returns generated code when the operation is assigned to.
 *
 * The below functions can be optionally defined for operations that support
 * being assigned to. Defaults are in `operation_cl_lhs`.
 * - `void set_view(int bottom_diagonal, int top_diagonal, int
 * bottom_zero_diagonal, int top_zero_diagonal)`:
 *    - Sets view of the underlying `matrix_cl` depending on where the extreme
 * sub-/super-diagonals  are written.
 *    - Default: Calls `set_view` on expression arguments with the same
 * arguments.
 * - `void check_assign_dimensions(int rows, int cols)`:
 *    - If the operation size can be modified, it should be set to the given
 * size. Otherwise it should check that this operation's size matches given
 * size.
 *    - Default: By default calls `check_assign_dimensions` on expression
 * arguments with the same arguments.
 *
 * A new operation should also have a user-facing function that accepts
 * arguments to the operation and returns the operation object. Arguments should
 * be passed trough function `as_operation_cl` so that they are wrapped in
 * operations if they are not operations themselves. If the operation defines
 * `modify_argument_indices` this function should make copies of arguments by
 * calling `deep_copy()` on them internally.
 */

#include <stan/math/opencl/kernel_generator/operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/operation_cl_lhs.hpp>
#include <stan/math/opencl/kernel_generator/as_operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/is_valid_expression.hpp>
#include <stan/math/opencl/kernel_generator/name_generator.hpp>
#include <stan/math/opencl/kernel_generator/type_str.hpp>

#include <stan/math/opencl/kernel_generator/load.hpp>
#include <stan/math/opencl/kernel_generator/scalar.hpp>
#include <stan/math/opencl/kernel_generator/binary_operation.hpp>
#include <stan/math/opencl/kernel_generator/unary_function_cl.hpp>
#include <stan/math/opencl/kernel_generator/unary_operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/block.hpp>
#include <stan/math/opencl/kernel_generator/select.hpp>
#include <stan/math/opencl/kernel_generator/rowwise_reduction.hpp>
#include <stan/math/opencl/kernel_generator/colwise_reduction.hpp>
#include <stan/math/opencl/kernel_generator/transpose.hpp>
#include <stan/math/opencl/kernel_generator/broadcast.hpp>
#include <stan/math/opencl/kernel_generator/optional_broadcast.hpp>

#include <stan/math/opencl/kernel_generator/multi_result_kernel.hpp>
#include <stan/math/opencl/kernel_generator/get_kernel_source_for_evaluating_into.hpp>
#include <stan/math/opencl/kernel_generator/evaluate_into.hpp>

#include <stan/math/opencl/kernel_generator/matrix_cl_conversion.hpp>

#include <stan/math/opencl/kernel_generator/matrix_vector_multiply.hpp>

#endif
#endif
<commit_msg>[Jenkins] auto-formatting by clang-format version 6.0.0<commit_after>#ifndef STAN_MATH_OPENCL_KERNEL_GENERATOR_HPP
#define STAN_MATH_OPENCL_KERNEL_GENERATOR_HPP
#ifdef STAN_OPENCL

/**
 * \ingroup opencl
 * \defgroup opencl_kernel_generator OpenCL Kernel Generator
 *
 * The OpenCL kernel generator is used to combine multiple matrix operations
 * into a single OpenCL kernel. This is much simpler than writing
 * multi-operation kernels by hand.
 *
 * Because global GPU memory loads and stores are relativly slow compared to
 * calculations in a kernel, using one kernel for multiple operations is faster
 * than using one kernel per operation.
 *
 * The kernel generator uses lazy evaluation. Each operation is represented by
 * an object derived from `operation_cl`. Such an object holds arguments of the
 * operations as well as meta information needed to generate calculations on the
 * arguments. Arguments to operations can be other operations, scalars
 * or `matrix_cl` objects. An operation is evaluated when either an operation is
 * assigned to a `matrix_cl` or a left-hand-side operation or `.eval()` is
 * called.
 *
 * ## Defining a new kernel generator operation
 *
 * Unary functions can be added using one of the macros in
 * `unary_functions.hpp`.
 *
 * New kernel generator classes must satsify the conditions below:
 *
 * 1. The class must be derived from a class inheriting from `operation_cl`.
 * Optionally, if the operation should support being assigned to, it can be
 * derived from a class inheriting `operation_cl_lhs` instead.
 * 2. It's parent template arguments should be set to derived type, type of
 *  scalar and types of any expression arguements.
 * 3. Member type `Scalar` should be defined as scalar type of the result of
 * the operation.
 * 4. Member function `generate` has the signature
 * ```cpp
 * inline kernel_parts generate(const std::string& i, const std::string& j,
 *                            const std::string& var_name_arg)
 * ```
 * 5. Member function `deep_copy` should make a copy of the expression.
 * Arguments that are operations should be copied by calling their `deep_copy`.
 *
 * The following functions can optionally be defined. Defaults are implemented
 * in `operation_cl`:
 * - `void modify_argument_indices(std::string& i, std::string& j)`:
 *     - Modifies what indices are passed to argument's `generate()`.
 *     - Default: No-op
 * - `void set_args(std::set<const operation_cl_base*>& generated,
 *        cl::Kernel& kernel, int& arg_num)`:
 *     - Sets additional kernel arguments.
 *     - Default: Calls `set_args()` on arguments.
 * - `int rows()`:
 *     - Returns Number of rows of the result.
 *     - Default: Returns maximum of the arguments' rows.
 * - `int cols()`:
 *     - Returns number of columns of the result.
 *     - Default: Returns maximum of the arguments' columns.
 * - `int thread_rows()`:
 *     - Returns number of threads required for this operation in rows
 * direction.
 *     - Default: returns `rows()`.
 * - `int thread_cols()`:
 *     - Returns number of threads required for this operation in cols
 * direction.
 *     - Default: `cols()`.
 * - `std::pair<int,int> extreme_diagonals()`:
 *     - Returns indices of the extreme bottom and top nonzero diagonals of the
 * result of the operation. Diagonal has index 0, first superdiagonal 1, first
 * subdiagonal -1 and so on. For instance `load_` of a matrix with lower
 * triangular view would return bottom diagonal of `1-rows()` and top diagonal
 * `0`. Returning a more extreme value is also allowed and especially useful for
 * operations that are broadcast so their exact size is not known.
 *     - Default: Bottom diagonal equals to min of bottom diagonals of
 * arguments. Top diagonal equals to max of top diagonals of arguments.
 *
 * If an operation should support being assigned to it should also define the
 * following:
 *
 * 1. Member function `generate_lhs` with same signature as `generate`
 * that returns generated code when the operation is assigned to.
 *
 * The below functions can be optionally defined for operations that support
 * being assigned to. Defaults are in `operation_cl_lhs`.
 * - `void set_view(int bottom_diagonal, int top_diagonal, int
 * bottom_zero_diagonal, int top_zero_diagonal)`:
 *    - Sets view of the underlying `matrix_cl` depending on where the extreme
 * sub-/super-diagonals  are written.
 *    - Default: Calls `set_view` on expression arguments with the same
 * arguments.
 * - `void check_assign_dimensions(int rows, int cols)`:
 *    - If the operation size can be modified, it should be set to the given
 * size. Otherwise it should check that this operation's size matches given
 * size.
 *    - Default: By default calls `check_assign_dimensions` on expression
 * arguments with the same arguments.
 *
 * A new operation should also have a user-facing function that accepts
 * arguments to the operation and returns the operation object. Arguments should
 * be passed trough function `as_operation_cl` so that they are wrapped in
 * operations if they are not operations themselves. If the operation defines
 * `modify_argument_indices` this function should make copies of arguments by
 * calling `deep_copy()` on them internally.
 */

#include <stan/math/opencl/kernel_generator/operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/operation_cl_lhs.hpp>
#include <stan/math/opencl/kernel_generator/as_operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/is_valid_expression.hpp>
#include <stan/math/opencl/kernel_generator/name_generator.hpp>
#include <stan/math/opencl/kernel_generator/type_str.hpp>

#include <stan/math/opencl/kernel_generator/load.hpp>
#include <stan/math/opencl/kernel_generator/scalar.hpp>
#include <stan/math/opencl/kernel_generator/binary_operation.hpp>
#include <stan/math/opencl/kernel_generator/unary_function_cl.hpp>
#include <stan/math/opencl/kernel_generator/unary_operation_cl.hpp>
#include <stan/math/opencl/kernel_generator/block.hpp>
#include <stan/math/opencl/kernel_generator/select.hpp>
#include <stan/math/opencl/kernel_generator/rowwise_reduction.hpp>
#include <stan/math/opencl/kernel_generator/colwise_reduction.hpp>
#include <stan/math/opencl/kernel_generator/transpose.hpp>
#include <stan/math/opencl/kernel_generator/broadcast.hpp>
#include <stan/math/opencl/kernel_generator/optional_broadcast.hpp>

#include <stan/math/opencl/kernel_generator/multi_result_kernel.hpp>
#include <stan/math/opencl/kernel_generator/get_kernel_source_for_evaluating_into.hpp>
#include <stan/math/opencl/kernel_generator/evaluate_into.hpp>

#include <stan/math/opencl/kernel_generator/matrix_cl_conversion.hpp>

#include <stan/math/opencl/kernel_generator/matrix_vector_multiply.hpp>

#endif
#endif
<|endoftext|>


<commit_before>#ifndef STAN_MATH_REV_FUNCTOR_ODE_ADJOINT_HPP
#define STAN_MATH_REV_FUNCTOR_ODE_ADJOINT_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/prim/fun/eval.hpp>
#include <stan/math/rev/functor/cvodes_integrator_adjoint.hpp>
#include <ostream>
#include <vector>

namespace stan {
namespace math {

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of
 * times, { t1, t2, t3, ... } using the stiff backward differentiation formula
 * BDF solver or the non-stiff Adams solver from CVODES. The ODE system is
 * integrated using the adjoint sensitivity approach of CVODES.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param function_name Calling function name (for printing debugging messages)
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
          typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
          require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd,
                                         T_abs_tol_bwd>* = nullptr,
          require_any_not_st_arithmetic<T_y0, T_t0, T_ts, T_Args...>* = nullptr>
auto ode_adjoint_impl(
    const char* function_name, F&& f, const T_y0& y0, const T_t0& t0,
    const std::vector<T_ts>& ts, double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  using integrator_vari
      = cvodes_integrator_adjoint_vari<F, plain_type_t<T_y0>, T_t0, T_ts,
                                       plain_type_t<T_Args>...>;
  auto integrator = new integrator_vari(
      function_name, std::forward<F>(f), y0, t0, ts, relative_tolerance_forward,
      absolute_tolerance_forward, relative_tolerance_backward,
      absolute_tolerance_backward, relative_tolerance_quadrature,
      absolute_tolerance_quadrature, max_num_steps,
      num_steps_between_checkpoints, interpolation_polynomial, solver_forward,
      solver_backward, msgs, args...);
  return integrator->solution();
}

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of times, { t1, t2, t3, ... } using the stiff backward differentiation formula BDF solver or the non-stiff Adams solver from CVODES. The ODE system is integrated using the adjoint sensitivity approach of CVODES. This implementation handles the case of a double return type which ensures that no resources are left on the AD stack.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param function_name Calling function name (for printing debugging messages)
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
  typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
  require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd, T_abs_tol_bwd>* = nullptr,
  require_all_st_arithmetic<T_y0, T_t0, T_ts, T_Args...>* = nullptr>
std::vector<Eigen::VectorXd> ode_adjoint_impl(
    const char* function_name, F&& f, const T_y0& y0, const T_t0& t0,
    const std::vector<T_ts>& ts, double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  std::vector<Eigen::VectorXd> ode_solution;
  {
    nested_rev_autodiff nested;

    using integrator_vari
        = cvodes_integrator_adjoint_vari<F, plain_type_t<T_y0>, T_t0, T_ts,
                                       plain_type_t<T_Args>...>;

    auto integrator = new integrator_vari(
        function_name, std::forward<F>(f), y0, t0, ts, relative_tolerance_forward,
        absolute_tolerance_forward, relative_tolerance_backward,
        absolute_tolerance_backward, relative_tolerance_quadrature,
        absolute_tolerance_quadrature, max_num_steps,
        num_steps_between_checkpoints, interpolation_polynomial, solver_forward,
        solver_backward, msgs, args...);

    ode_solution = integrator->solution();
  }
  return ode_solution;
}

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of
 * times, { t1, t2, t3, ... } using the stiff backward differentiation formula
 * BDF solver or the non-stiff Adams solver from CVODES. The ODE system is
 * integrated using the adjoint sensitivity approach of CVODES.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
          typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
          require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd,
                                         T_abs_tol_bwd>* = nullptr>
auto ode_adjoint_tol_ctl(
    F&& f, const T_y0& y0, const T_t0& t0, const std::vector<T_ts>& ts,
    double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  return ode_adjoint_impl(
      "ode_adjoint_tol_ctl", std::forward<F>(f), y0, t0, ts,
      relative_tolerance_forward, absolute_tolerance_forward,
      relative_tolerance_backward, absolute_tolerance_backward,
      relative_tolerance_quadrature, absolute_tolerance_quadrature,
      max_num_steps, num_steps_between_checkpoints, interpolation_polynomial,
      solver_forward, solver_backward, msgs, args...);
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final)<commit_after>#ifndef STAN_MATH_REV_FUNCTOR_ODE_ADJOINT_HPP
#define STAN_MATH_REV_FUNCTOR_ODE_ADJOINT_HPP

#include <stan/math/rev/meta.hpp>
#include <stan/math/prim/fun/eval.hpp>
#include <stan/math/rev/functor/cvodes_integrator_adjoint.hpp>
#include <ostream>
#include <vector>

namespace stan {
namespace math {

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of
 * times, { t1, t2, t3, ... } using the stiff backward differentiation formula
 * BDF solver or the non-stiff Adams solver from CVODES. The ODE system is
 * integrated using the adjoint sensitivity approach of CVODES.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param function_name Calling function name (for printing debugging messages)
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
          typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
          require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd,
                                         T_abs_tol_bwd>* = nullptr,
          require_any_not_st_arithmetic<T_y0, T_t0, T_ts, T_Args...>* = nullptr>
auto ode_adjoint_impl(
    const char* function_name, F&& f, const T_y0& y0, const T_t0& t0,
    const std::vector<T_ts>& ts, double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  using integrator_vari
      = cvodes_integrator_adjoint_vari<F, plain_type_t<T_y0>, T_t0, T_ts,
                                       plain_type_t<T_Args>...>;
  auto integrator = new integrator_vari(
      function_name, std::forward<F>(f), y0, t0, ts, relative_tolerance_forward,
      absolute_tolerance_forward, relative_tolerance_backward,
      absolute_tolerance_backward, relative_tolerance_quadrature,
      absolute_tolerance_quadrature, max_num_steps,
      num_steps_between_checkpoints, interpolation_polynomial, solver_forward,
      solver_backward, msgs, args...);
  return integrator->solution();
}

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of
 * times, { t1, t2, t3, ... } using the stiff backward differentiation formula
 * BDF solver or the non-stiff Adams solver from CVODES. The ODE system is
 * integrated using the adjoint sensitivity approach of CVODES. This
 * implementation handles the case of a double return type which ensures that no
 * resources are left on the AD stack.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param function_name Calling function name (for printing debugging messages)
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
          typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
          require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd,
                                         T_abs_tol_bwd>* = nullptr,
          require_all_st_arithmetic<T_y0, T_t0, T_ts, T_Args...>* = nullptr>
std::vector<Eigen::VectorXd> ode_adjoint_impl(
    const char* function_name, F&& f, const T_y0& y0, const T_t0& t0,
    const std::vector<T_ts>& ts, double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  std::vector<Eigen::VectorXd> ode_solution;
  {
    nested_rev_autodiff nested;

    using integrator_vari
        = cvodes_integrator_adjoint_vari<F, plain_type_t<T_y0>, T_t0, T_ts,
                                         plain_type_t<T_Args>...>;

    auto integrator = new integrator_vari(
        function_name, std::forward<F>(f), y0, t0, ts,
        relative_tolerance_forward, absolute_tolerance_forward,
        relative_tolerance_backward, absolute_tolerance_backward,
        relative_tolerance_quadrature, absolute_tolerance_quadrature,
        max_num_steps, num_steps_between_checkpoints, interpolation_polynomial,
        solver_forward, solver_backward, msgs, args...);

    ode_solution = integrator->solution();
  }
  return ode_solution;
}

/**
 * Solve the ODE initial value problem y' = f(t, y), y(t0) = y0 at a set of
 * times, { t1, t2, t3, ... } using the stiff backward differentiation formula
 * BDF solver or the non-stiff Adams solver from CVODES. The ODE system is
 * integrated using the adjoint sensitivity approach of CVODES.
 *
 * \p f must define an operator() with the signature as:
 *   template<typename T_t, typename T_y, typename... T_Args>
 *   Eigen::Matrix<stan::return_type_t<T_t, T_y, T_Args...>, Eigen::Dynamic, 1>
 *     operator()(const T_t& t, const Eigen::Matrix<T_y, Eigen::Dynamic, 1>& y,
 *     std::ostream* msgs, const T_Args&... args);
 *
 * t is the time, y is the state, msgs is a stream for error messages, and args
 * are optional arguments passed to the ODE solve function (which are passed
 * through to \p f without modification).
 *
 * @tparam F Type of ODE right hand side
 * @tparam T_y0 Type of initial state
 * @tparam T_t0 Type of initial time
 * @tparam T_ts Type of output times
 * @tparam T_Args Types of pass-through parameters
 *
 * @param f Right hand side of the ODE
 * @param y0 Initial state
 * @param t0 Initial time
 * @param ts Times at which to solve the ODE at. All values must be sorted and
 *   not less than t0.
 * @param relative_tolerance_forward Relative tolerance for forward problem
 * passed to CVODES
 * @param absolute_tolerance_forward Absolute tolerance per ODE state for
 * forward problem passed to CVODES
 * @param relative_tolerance_backward Relative tolerance for backward problem
 * passed to CVODES
 * @param absolute_tolerance_backward Absolute tolerance per ODE state for
 * backward problem passed to CVODES
 * @param relative_tolerance_quadrature Relative tolerance for quadrature
 * problem passed to CVODES
 * @param absolute_tolerance_quadrature Absolute tolerance for quadrature
 * problem passed to CVODES
 * @param max_num_steps Upper limit on the number of integration steps to
 *   take between each output (error if exceeded)
 * @param num_steps_between_checkpoints Number of integrator steps after which a
 * checkpoint is stored for the backward pass
 * @param interpolation_polynomial type of polynomial used for interpolation
 * @param solver_forward solver used for forward pass
 * @param solver_backward solver used for backward pass
 * @param[in, out] msgs the print stream for warning messages
 * @param args Extra arguments passed unmodified through to ODE right hand side
 * @return An `std::vector` of Eigen column vectors with scalars equal to
 *  the least upper bound of `T_y0`, `T_t0`, `T_ts`, and the lambda's arguments.
 *  This represents the solution to ODE at times \p ts
 */
template <typename F, typename T_y0, typename T_t0, typename T_ts,
          typename T_abs_tol_fwd, typename T_abs_tol_bwd, typename... T_Args,
          require_all_eigen_col_vector_t<T_y0, T_abs_tol_fwd,
                                         T_abs_tol_bwd>* = nullptr>
auto ode_adjoint_tol_ctl(
    F&& f, const T_y0& y0, const T_t0& t0, const std::vector<T_ts>& ts,
    double relative_tolerance_forward,
    const T_abs_tol_fwd& absolute_tolerance_forward,
    double relative_tolerance_backward,
    const T_abs_tol_bwd& absolute_tolerance_backward,
    double relative_tolerance_quadrature, double absolute_tolerance_quadrature,
    long int max_num_steps,                  // NOLINT(runtime/int)
    long int num_steps_between_checkpoints,  // NOLINT(runtime/int)
    int interpolation_polynomial, int solver_forward, int solver_backward,
    std::ostream* msgs, const T_Args&... args) {
  return ode_adjoint_impl(
      "ode_adjoint_tol_ctl", std::forward<F>(f), y0, t0, ts,
      relative_tolerance_forward, absolute_tolerance_forward,
      relative_tolerance_backward, absolute_tolerance_backward,
      relative_tolerance_quadrature, absolute_tolerance_quadrature,
      max_num_steps, num_steps_between_checkpoints, interpolation_polynomial,
      solver_forward, solver_backward, msgs, args...);
}

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/algorithm/string.hpp>

#include "testdriverconfig.h" // SRC and BIN dir definitions
#include "specification.h" // parsing spec files
#include "zorba/zorba_api.h"

namespace fs = boost::filesystem;

void
printFile(std::ostream& os, std::string aInFile)
{
  std::ifstream lInFileStream(aInFile.c_str());
  assert(lInFileStream);

  os << lInFileStream.rdbuf() << std::endl;
}

// print parts of a file
// starting at aStartPos with the length of aLen
void
printPart(std::ostream& os, std::string aInFile,
          int aStartPos, int aLen)
{
  char* buffer = new char [aLen];
  try {
    std::ifstream lIn(aInFile.c_str());
    lIn.seekg(aStartPos);

    int lCharsRead = lIn.readsome (buffer, aLen);
    os.write (buffer, lCharsRead);
    os.flush();
    delete buffer;
  } catch (...)
  {
    delete buffer;
  }
  return;
}

// check of an error that was repored was expected
// by the given specification object
bool
isErrorExpected(xqp::ZorbaAlertsManager* aManager, Specification* aSpec)
{
  for (xqp::ZorbaAlertsManager::const_iterator lIter = aManager->begin();
       lIter != aManager->end(); ++lIter)
  {
    xqp::ZorbaAlert* lAlert = *lIter;
    switch (lAlert->theKind)
    {
      case xqp::ZorbaAlert::ERROR_ALERT:
        {
          xqp::ZorbaError* lErrorAlert = dynamic_cast<xqp::ZorbaError*>(lAlert);
          std::string lErrorCode = lErrorAlert->toString(lErrorAlert->theCode);
          for (std::vector<std::string>::const_iterator lErrorIter = aSpec->errorsBegin();
               lErrorIter != aSpec->errorsEnd(); ++lErrorIter)
          {
            // error is expected
            if ((*lErrorIter).compare(lErrorCode) == 0)
              return true;
          }
          break;
        }
      default:
        { }
    }
  }
  return false;
}

// print all errors that were raised
void
printErrors(xqp::ZorbaAlertsManager* aManager)
{
  if (aManager->size() == 0) return;

  std::cout<< "Errors:" << std::endl;

  for (xqp::ZorbaAlertsManager::const_iterator lIter = aManager->begin();
       lIter != aManager->end(); ++lIter)
  {
    xqp::ZorbaAlert* lAlert = *lIter;
    switch (lAlert->theKind)
    {
    case xqp::ZorbaAlert::ERROR_ALERT:
    {
      xqp::ZorbaError* lErrorAlert = dynamic_cast<xqp::ZorbaError*>(lAlert);
      assert(lErrorAlert);
      std::string lErrorCode = lErrorAlert->toString(lErrorAlert->theCode);
      std::cout << lErrorCode << " " << lErrorAlert->theDescription << std::endl;
      break;
    }
    default:
    {
    }
    }
  }
  return;
}

// set a variable in the dynamic context
// inlineFile specifies whether the given parameter is a file and it's value should
// be inlined or not
void
set_var (bool inlineFile, std::string name, std::string val,
         xqp::DynamicQueryContext_t dctx, xqp::XQueryExecution_t exec)
{
  boost::replace_all(val, "$RBKT_SRC_DIR", xqp::RBKT_SRC_DIR);
  if (!inlineFile && dctx != NULL) {
    dctx->SetVariableAsString (name, xqp::xqp_string (val));
  } else if (inlineFile && exec != NULL) {
    std::ifstream is (val.c_str ());
    assert (is);
    exec->SetVariable (name, val.c_str(), is);
  }
}

void
set_vars (Specification* aSpec, xqp::DynamicQueryContext_t dctx, xqp::XQueryExecution_t exec)
{
  for (std::vector<Specification::Variable>::const_iterator lIter = aSpec->variablesBegin();
       lIter != aSpec->variablesEnd(); ++lIter)
  {
    set_var ((*lIter).theInline, (*lIter).theVarName, (*lIter).theVarValue, dctx, exec);
  }
}

void
trim(std::string& str) {

  std::string::size_type  notwhite = str.find_first_not_of(" \t\n");
  str.erase(0,notwhite);

  notwhite = str.find_last_not_of(" \t\n");
  str.erase(notwhite+1);
}

// return false if the files are not equal
// aLine contains the line number in which the first difference occurs
// aCol contains the column number in which the first difference occurs
// aPos is the character number off the first difference in the file
// -1 is returned for aLine, aCol, and aPos if the files are equal
bool
isEqual(fs::path aRefFile, fs::path aResFile, int& aLine, int& aCol, int& aPos)
{
  std::ifstream li(aRefFile.native_file_string().c_str());
  std::ifstream ri(aResFile.native_file_string().c_str());

  std::streambuf * lb = li.rdbuf();
  std::streambuf * rb = ri.rdbuf();

  long ls = lb->pubseekoff (0, std::ios::end, std::ios::in);
  long rs = rb->pubseekoff (0, std::ios::end, std::ios::in);
  lb->pubseekpos (0, std::ios::in);
  rb->pubseekpos (0, std::ios::in);

  char* lBuf = new char[ls];
  char* rBuf = new char[rs];

  try {
    lb->sgetn (lBuf, ls);
    rb->sgetn (rBuf, rs);
  } catch (...)
  {
    li.close(); ri.close();
    delete[] lBuf; delete[] rBuf;
    return false;
  }

  li.close();
  ri.close();

  std::string lString(lBuf, ls);
  std::string rString(rBuf, rs);

  delete[] lBuf; delete[] rBuf;

  trim(lString);
  trim(rString);

  aLine = 1; aCol = 0; aPos = -1;

  size_t aLPos = 0, aRPos = 0;
  char lc, rc;
  while (aLPos != lString.length() && aRPos != rString.length())
  {
    lc = lString.at(aLPos);
    rc = rString.at(aRPos);
    ++aPos; ++aCol;
    if (lc == '\n') { ++aLine; aCol = 0; }
    if ( lc != rc ) return false;
    ++aLPos; ++aRPos;
  }
  aLine = aCol = aPos = -1;
  return true;
}

class ZorbaEngineWrapper {
public:
  xqp::ZorbaEngine &factory;

  ZorbaEngineWrapper ()
    : factory (xqp::ZorbaEngine::getInstance())
  {
    factory.initThread();
  }
  ~ZorbaEngineWrapper () {

    factory.uninitThread();
    factory.shutdown();
  }
};

void
slurp_file (const char *fname, std::string &result) {
  std::ifstream qfile(fname); assert (qfile);

  qfile.seekg (0, std::ios::end);
  size_t len = qfile.tellg ();
  qfile.seekg (0, std::ios::beg);
  char *str = new char [len];
  qfile.read (str, len);

  std::string sstr (str, len);
  result.swap (sstr);
  delete [] str;
}

int
#ifdef _WIN32_WCE
_tmain(int argc, _TCHAR* argv[])
#else
main(int argc, char** argv)
#endif
{
  fs::path lQueryFile, lResultFile, lErrorFile, lRefFile, lSpecFile;
  Specification lSpec;

  // do initial stuff
  if ( argc == 2 )
  {
    std::string lQueryFileString  = xqp::RBKT_SRC_DIR +"/Queries/" + argv[1];
    lQueryFile = fs::system_complete( fs::path( lQueryFileString, fs::native ) );

    std::string lQueryWithoutSuffix = std::string(argv[1]).substr( 0, std::string(argv[1]).size()-3 );
    std::cout << "test " << lQueryWithoutSuffix << std::endl;

    lResultFile = fs::system_complete(fs::path( xqp::RBKT_BINARY_DIR +"/QueryResults/"
                                      +lQueryWithoutSuffix + ".res", fs::native) );
    lErrorFile = fs::system_complete(fs::path(xqp::RBKT_BINARY_DIR +"/"
                                     +lQueryWithoutSuffix + ".err", fs::native) );
    lRefFile   = fs::system_complete(fs::path( xqp::RBKT_SRC_DIR +"/ExpQueryResults/"
                                     +lQueryWithoutSuffix +".xml.res", fs::native) );
    lSpecFile  = fs::system_complete(fs::path(xqp::RBKT_SRC_DIR+ "/Queries/"
                                     +lQueryWithoutSuffix +".spec", fs::native) );
  }
  else
  {
    std::cerr << "\nusage:   testdriver [testfile]" << std::endl;
    return 1;
  }

  // does the query file exists
  if ( (! fs::exists( lQueryFile )) || fs::is_directory( lQueryFile) )
  {
    std::cerr << "\n query file " << lQueryFile.native_file_string()
              << " does not exist or is not a file" << std::endl;
    return 2;
  }

  // delete previous files if they exists
  if ( fs::exists ( lResultFile ) ) { fs::remove (lResultFile); }
  if ( fs::exists ( lErrorFile ) )  { fs::remove (lErrorFile);  }

  // create the result directory
  fs::path lBucket = fs::system_complete(fs::path( lResultFile.branch_path().string(), fs::native ));
  if ( ! fs::exists( lBucket ) )
    fs::create_directories(lBucket); // create deep directories

  // read the xargs and errors if the spec file exists
  if ( fs::exists( lSpecFile ))
  {
    lSpec.parseFile(lSpecFile.native_file_string());
  }

  // we must either have a reference file or an expected error code
  if ( (lSpec.errorsSize() == 0) && ((! fs::exists( lRefFile )) || fs::is_directory( lRefFile)))
  {
    std::cerr << "No reference result and no expected errors." << std::endl;
    return 3;
  }

  // print the query
  std::cout << "Query:" << std::endl;
  printFile(std::cout, lQueryFile.native_file_string());
  std::cout << std::endl;

  // initialize the zorba engine
  ZorbaEngineWrapper lEngine; // the engine is up as long as this object lives

  // create and compile the query
  std::string lQueryString;
  slurp_file(lQueryFile.native_file_string().c_str(), lQueryString);
  xqp::XQuery_t lQuery = lEngine.factory.createQuery(lQueryString.c_str());

  xqp::ZorbaAlertsManager& lAlertsManager = lEngine.factory.getAlertsManagerForCurrentThread();

  if (lQuery.isNull())
  {
    if (isErrorExpected(&lAlertsManager, &lSpec))
    {
      // done, we expected an error during compile
      return 0;
    }
    else
    {
      std::cerr << "Error compiling query" << std::endl;
      printErrors(&lAlertsManager); return 4;
    }
  }

  // set the variables in the dynamic context
	xqp::DynamicQueryContext_t lDynCtxt = lEngine.factory.createDynamicContext();
  set_vars(&lSpec, lDynCtxt, NULL);

  // execute the query
  xqp::XQueryExecution_t lQueryResult = lQuery->createExecution(lDynCtxt);
  set_vars(&lSpec, NULL, lQueryResult);

  if (lQueryResult.isNull()) // how can this happen?
  {
    if (isErrorExpected(&lAlertsManager, &lSpec)) { return 0; } // done, we expected this error
    else
    {
      std::cerr << "Error executing query" << std::endl;
      printErrors(&lAlertsManager);
      return 5;
    }
  }

  {
    // serialize xml
    std::ofstream lResFileStream(lResultFile.native_file_string().c_str());
    assert (lResFileStream.good());

    lQueryResult->serializeXML(lResFileStream);

    if (lQueryResult->isError())
    {
      if (isErrorExpected(&lAlertsManager, &lSpec)) { return 0; } // again done, we expected this error
      else
      {
        std::cerr << "Error executing query" << std::endl;
        printErrors(&lAlertsManager);
        return 6;
      }
    }
    else if ( lSpec.errorsSize() > 0 )
    {
      std::cerr << "Expected error(s)";
      for (std::vector<std::string>::const_iterator lIter = lSpec.errorsBegin();
           lIter != lSpec.errorsEnd(); ++lIter)
      {
        std::cerr << " " << *lIter;
      }
      if ( fs::exists(lResultFile) && fs::file_size(lResultFile) == 0)
        std::cerr << " but got empty result" << std::endl;
      else
      {
        std::cerr << " but got result:" << std::endl;
        printFile(std::cerr, lResultFile.native_file_string());
        std::cerr<< std::endl;
      }
      return 7;
    }
  }
  std::cout << "Result:" << std::endl;
  printFile(std::cout, lResultFile.native_file_string());
  std::cout.flush();
  std::cout << std::endl;

  // last, we have to diff the result
  int lLine, lCol, lPos; // where do the files differ
  bool lRes = isEqual(lRefFile, lResultFile, lLine, lCol, lPos);
  if ( !lRes )  // results differ
  {
    std::cerr << std::endl << "Result does not match expected result" << std::endl;
    printFile(std::cerr, lRefFile.native_file_string());
    std::cerr << std::endl;

    std::cerr << "See line " << lLine << ", col " << lCol << " of expected result. " << std::endl;
    std::cerr << "Got ";
    printPart(std::cerr, lResultFile.native_file_string(), lPos, 15);
    std::cerr << std::endl << "Expected ";
    printPart(std::cerr, lRefFile.native_file_string(), lPos, 15);
    std::cerr <<  std::endl;

    return 8;
  }

  return 0;
}
<commit_msg>fixed: mismatched delete<commit_after>#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/algorithm/string.hpp>

#include "testdriverconfig.h" // SRC and BIN dir definitions
#include "specification.h" // parsing spec files
#include "zorba/zorba_api.h"

namespace fs = boost::filesystem;

void
printFile(std::ostream& os, std::string aInFile)
{
  std::ifstream lInFileStream(aInFile.c_str());
  assert(lInFileStream);

  os << lInFileStream.rdbuf() << std::endl;
}

// print parts of a file
// starting at aStartPos with the length of aLen
void
printPart(std::ostream& os, std::string aInFile,
          int aStartPos, int aLen)
{
  char* buffer = new char [aLen];
  try {
    std::ifstream lIn(aInFile.c_str());
    lIn.seekg(aStartPos);

    int lCharsRead = lIn.readsome (buffer, aLen);
    os.write (buffer, lCharsRead);
    os.flush();
    delete[] buffer;
  } catch (...)
  {
    delete[] buffer;
  }
  return;
}

// check of an error that was repored was expected
// by the given specification object
bool
isErrorExpected(xqp::ZorbaAlertsManager* aManager, Specification* aSpec)
{
  for (xqp::ZorbaAlertsManager::const_iterator lIter = aManager->begin();
       lIter != aManager->end(); ++lIter)
  {
    xqp::ZorbaAlert* lAlert = *lIter;
    switch (lAlert->theKind)
    {
      case xqp::ZorbaAlert::ERROR_ALERT:
        {
          xqp::ZorbaError* lErrorAlert = dynamic_cast<xqp::ZorbaError*>(lAlert);
          std::string lErrorCode = lErrorAlert->toString(lErrorAlert->theCode);
          for (std::vector<std::string>::const_iterator lErrorIter = aSpec->errorsBegin();
               lErrorIter != aSpec->errorsEnd(); ++lErrorIter)
          {
            // error is expected
            if ((*lErrorIter).compare(lErrorCode) == 0)
              return true;
          }
          break;
        }
      default:
        { }
    }
  }
  return false;
}

// print all errors that were raised
void
printErrors(xqp::ZorbaAlertsManager* aManager)
{
  if (aManager->size() == 0) return;

  std::cout<< "Errors:" << std::endl;

  for (xqp::ZorbaAlertsManager::const_iterator lIter = aManager->begin();
       lIter != aManager->end(); ++lIter)
  {
    xqp::ZorbaAlert* lAlert = *lIter;
    switch (lAlert->theKind)
    {
    case xqp::ZorbaAlert::ERROR_ALERT:
    {
      xqp::ZorbaError* lErrorAlert = dynamic_cast<xqp::ZorbaError*>(lAlert);
      assert(lErrorAlert);
      std::string lErrorCode = lErrorAlert->toString(lErrorAlert->theCode);
      std::cout << lErrorCode << " " << lErrorAlert->theDescription << std::endl;
      break;
    }
    default:
    {
    }
    }
  }
  return;
}

// set a variable in the dynamic context
// inlineFile specifies whether the given parameter is a file and it's value should
// be inlined or not
void
set_var (bool inlineFile, std::string name, std::string val,
         xqp::DynamicQueryContext_t dctx, xqp::XQueryExecution_t exec)
{
  boost::replace_all(val, "$RBKT_SRC_DIR", xqp::RBKT_SRC_DIR);
  if (!inlineFile && dctx != NULL) {
    dctx->SetVariableAsString (name, xqp::xqp_string (val));
  } else if (inlineFile && exec != NULL) {
    std::ifstream is (val.c_str ());
    assert (is);
    exec->SetVariable (name, val.c_str(), is);
  }
}

void
set_vars (Specification* aSpec, xqp::DynamicQueryContext_t dctx, xqp::XQueryExecution_t exec)
{
  for (std::vector<Specification::Variable>::const_iterator lIter = aSpec->variablesBegin();
       lIter != aSpec->variablesEnd(); ++lIter)
  {
    set_var ((*lIter).theInline, (*lIter).theVarName, (*lIter).theVarValue, dctx, exec);
  }
}

void
trim(std::string& str) {

  std::string::size_type  notwhite = str.find_first_not_of(" \t\n");
  str.erase(0,notwhite);

  notwhite = str.find_last_not_of(" \t\n");
  str.erase(notwhite+1);
}

// return false if the files are not equal
// aLine contains the line number in which the first difference occurs
// aCol contains the column number in which the first difference occurs
// aPos is the character number off the first difference in the file
// -1 is returned for aLine, aCol, and aPos if the files are equal
bool
isEqual(fs::path aRefFile, fs::path aResFile, int& aLine, int& aCol, int& aPos)
{
  std::ifstream li(aRefFile.native_file_string().c_str());
  std::ifstream ri(aResFile.native_file_string().c_str());

  std::streambuf * lb = li.rdbuf();
  std::streambuf * rb = ri.rdbuf();

  long ls = lb->pubseekoff (0, std::ios::end, std::ios::in);
  long rs = rb->pubseekoff (0, std::ios::end, std::ios::in);
  lb->pubseekpos (0, std::ios::in);
  rb->pubseekpos (0, std::ios::in);

  char* lBuf = new char[ls];
  char* rBuf = new char[rs];

  try {
    lb->sgetn (lBuf, ls);
    rb->sgetn (rBuf, rs);
  } catch (...)
  {
    li.close(); ri.close();
    delete[] lBuf; delete[] rBuf;
    return false;
  }

  li.close();
  ri.close();

  std::string lString(lBuf, ls);
  std::string rString(rBuf, rs);

  delete[] lBuf; delete[] rBuf;

  trim(lString);
  trim(rString);

  aLine = 1; aCol = 0; aPos = -1;

  size_t aLPos = 0, aRPos = 0;
  char lc, rc;
  while (aLPos != lString.length() && aRPos != rString.length())
  {
    lc = lString.at(aLPos);
    rc = rString.at(aRPos);
    ++aPos; ++aCol;
    if (lc == '\n') { ++aLine; aCol = 0; }
    if ( lc != rc ) return false;
    ++aLPos; ++aRPos;
  }
  aLine = aCol = aPos = -1;
  return true;
}

class ZorbaEngineWrapper {
public:
  xqp::ZorbaEngine &factory;

  ZorbaEngineWrapper ()
    : factory (xqp::ZorbaEngine::getInstance())
  {
    factory.initThread();
  }
  ~ZorbaEngineWrapper () {

    factory.uninitThread();
    factory.shutdown();
  }
};

void
slurp_file (const char *fname, std::string &result) {
  std::ifstream qfile(fname); assert (qfile);

  qfile.seekg (0, std::ios::end);
  size_t len = qfile.tellg ();
  qfile.seekg (0, std::ios::beg);
  char *str = new char [len];
  qfile.read (str, len);

  std::string sstr (str, len);
  result.swap (sstr);
  delete [] str;
}

int
#ifdef _WIN32_WCE
_tmain(int argc, _TCHAR* argv[])
#else
main(int argc, char** argv)
#endif
{
  fs::path lQueryFile, lResultFile, lErrorFile, lRefFile, lSpecFile;
  Specification lSpec;

  // do initial stuff
  if ( argc == 2 )
  {
    std::string lQueryFileString  = xqp::RBKT_SRC_DIR +"/Queries/" + argv[1];
    lQueryFile = fs::system_complete( fs::path( lQueryFileString, fs::native ) );

    std::string lQueryWithoutSuffix = std::string(argv[1]).substr( 0, std::string(argv[1]).size()-3 );
    std::cout << "test " << lQueryWithoutSuffix << std::endl;

    lResultFile = fs::system_complete(fs::path( xqp::RBKT_BINARY_DIR +"/QueryResults/"
                                      +lQueryWithoutSuffix + ".res", fs::native) );
    lErrorFile = fs::system_complete(fs::path(xqp::RBKT_BINARY_DIR +"/"
                                     +lQueryWithoutSuffix + ".err", fs::native) );
    lRefFile   = fs::system_complete(fs::path( xqp::RBKT_SRC_DIR +"/ExpQueryResults/"
                                     +lQueryWithoutSuffix +".xml.res", fs::native) );
    lSpecFile  = fs::system_complete(fs::path(xqp::RBKT_SRC_DIR+ "/Queries/"
                                     +lQueryWithoutSuffix +".spec", fs::native) );
  }
  else
  {
    std::cerr << "\nusage:   testdriver [testfile]" << std::endl;
    return 1;
  }

  // does the query file exists
  if ( (! fs::exists( lQueryFile )) || fs::is_directory( lQueryFile) )
  {
    std::cerr << "\n query file " << lQueryFile.native_file_string()
              << " does not exist or is not a file" << std::endl;
    return 2;
  }

  // delete previous files if they exists
  if ( fs::exists ( lResultFile ) ) { fs::remove (lResultFile); }
  if ( fs::exists ( lErrorFile ) )  { fs::remove (lErrorFile);  }

  // create the result directory
  fs::path lBucket = fs::system_complete(fs::path( lResultFile.branch_path().string(), fs::native ));
  if ( ! fs::exists( lBucket ) )
    fs::create_directories(lBucket); // create deep directories

  // read the xargs and errors if the spec file exists
  if ( fs::exists( lSpecFile ))
  {
    lSpec.parseFile(lSpecFile.native_file_string());
  }

  // we must either have a reference file or an expected error code
  if ( (lSpec.errorsSize() == 0) && ((! fs::exists( lRefFile )) || fs::is_directory( lRefFile)))
  {
    std::cerr << "No reference result and no expected errors." << std::endl;
    return 3;
  }

  // print the query
  std::cout << "Query:" << std::endl;
  printFile(std::cout, lQueryFile.native_file_string());
  std::cout << std::endl;

  // initialize the zorba engine
  ZorbaEngineWrapper lEngine; // the engine is up as long as this object lives

  // create and compile the query
  std::string lQueryString;
  slurp_file(lQueryFile.native_file_string().c_str(), lQueryString);
  xqp::XQuery_t lQuery = lEngine.factory.createQuery(lQueryString.c_str());

  xqp::ZorbaAlertsManager& lAlertsManager = lEngine.factory.getAlertsManagerForCurrentThread();

  if (lQuery.isNull())
  {
    if (isErrorExpected(&lAlertsManager, &lSpec))
    {
      // done, we expected an error during compile
      return 0;
    }
    else
    {
      std::cerr << "Error compiling query" << std::endl;
      printErrors(&lAlertsManager); return 4;
    }
  }

  // set the variables in the dynamic context
	xqp::DynamicQueryContext_t lDynCtxt = lEngine.factory.createDynamicContext();
  set_vars(&lSpec, lDynCtxt, NULL);

  // execute the query
  xqp::XQueryExecution_t lQueryResult = lQuery->createExecution(lDynCtxt);
  set_vars(&lSpec, NULL, lQueryResult);

  if (lQueryResult.isNull()) // how can this happen?
  {
    if (isErrorExpected(&lAlertsManager, &lSpec)) { return 0; } // done, we expected this error
    else
    {
      std::cerr << "Error executing query" << std::endl;
      printErrors(&lAlertsManager);
      return 5;
    }
  }

  {
    // serialize xml
    std::ofstream lResFileStream(lResultFile.native_file_string().c_str());
    assert (lResFileStream.good());

    lQueryResult->serializeXML(lResFileStream);

    if (lQueryResult->isError())
    {
      if (isErrorExpected(&lAlertsManager, &lSpec)) { return 0; } // again done, we expected this error
      else
      {
        std::cerr << "Error executing query" << std::endl;
        printErrors(&lAlertsManager);
        return 6;
      }
    }
    else if ( lSpec.errorsSize() > 0 )
    {
      std::cerr << "Expected error(s)";
      for (std::vector<std::string>::const_iterator lIter = lSpec.errorsBegin();
           lIter != lSpec.errorsEnd(); ++lIter)
      {
        std::cerr << " " << *lIter;
      }
      if ( fs::exists(lResultFile) && fs::file_size(lResultFile) == 0)
        std::cerr << " but got empty result" << std::endl;
      else
      {
        std::cerr << " but got result:" << std::endl;
        printFile(std::cerr, lResultFile.native_file_string());
        std::cerr<< std::endl;
      }
      return 7;
    }
  }
  std::cout << "Result:" << std::endl;
  printFile(std::cout, lResultFile.native_file_string());
  std::cout.flush();
  std::cout << std::endl;

  // last, we have to diff the result
  int lLine, lCol, lPos; // where do the files differ
  bool lRes = isEqual(lRefFile, lResultFile, lLine, lCol, lPos);
  if ( !lRes )  // results differ
  {
    std::cerr << std::endl << "Result does not match expected result" << std::endl;
    printFile(std::cerr, lRefFile.native_file_string());
    std::cerr << std::endl;

    std::cerr << "See line " << lLine << ", col " << lCol << " of expected result. " << std::endl;
    std::cerr << "Got ";
    printPart(std::cerr, lResultFile.native_file_string(), lPos, 15);
    std::cerr << std::endl << "Expected ";
    printPart(std::cerr, lRefFile.native_file_string(), lPos, 15);
    std::cerr <<  std::endl;

    return 8;
  }

  return 0;
}
<|endoftext|>


<commit_before>#include "testsettings.hpp"
#ifdef TEST_FILE_LOCKS

#include <cassert>
#include <map>
#include <iostream>
#include <functional>

#include <realm/util/thread.hpp>
#include <realm/util/file.hpp>
#include <realm/util/features.h>

#include "test.hpp"
#include "util/thread_wrapper.hpp"

using namespace realm::util;
using namespace realm::test_util;

// Test independence and thread-safety
// -----------------------------------
//
// All tests must be thread safe and independent of each other. This
// is required because it allows for both shuffling of the execution
// order and for parallelized testing.
//
// In particular, avoid using std::rand() since it is not guaranteed
// to be thread safe. Instead use the API offered in
// `test/util/random.hpp`.
//
// All files created in tests must use the TEST_PATH macro (or one of
// its friends) to obtain a suitable file system path. See
// `test/util/test_path.hpp`.
//
//
// Debugging and the ONLY() macro
// ------------------------------
//
// A simple way of disabling all tests except one called `Foo`, is to
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
// test suite. Note that you can also use filtering by setting the
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
// this.
//
// Another way to debug a particular test, is to copy that test into
// `experiments/testcase.cpp` and then run `sh build.sh
// check-testcase` (or one of its friends) from the command line.

// The assumption is that if multiple processes try to place an
// exclusive lock on a file in a non-blocking fashion, then at least
// one will succeed (assuming that no one else interferes). This test
// trys to verify that this is the case by repeatedly letting two
// treads compete for the lock. This is by no means a "water tight"
// test, but it is probably the best we can do.
TEST(File_NoSpuriousTryLockFailures)
{
#if TEST_DURATION < 1
    const int num_rounds = 1000;
#elif TEST_DURATION < 2
    const int num_rounds = 10000;
#elif TEST_DURATION < 3
    const int num_rounds = 100000;
#else
    const int num_rounds = 1000000;
#endif

    const int num_slaves = 2;

    Mutex mutex;
    CondVar cond;
    int num_slaves_ready = 0;
    int num_good_locks = 0;
    bool slaves_run[num_slaves];
    std::map<int, int> results;
    bool terminate = false;

    auto kill_em_all = [&] {
        LockGuard l(mutex);
        terminate = true;
        cond.notify_all();
    };

    auto master = [&] {
        try {
            LockGuard l(mutex);
            for (int i = 0; i != num_rounds; ++i) {
                while (num_slaves_ready != num_slaves) {
                    if (terminate)
                        return;
                    cond.wait(l);
                }
                num_slaves_ready = 0;

                ++results[num_good_locks];
                num_good_locks = 0;

                for (int j = 0; j != num_slaves; ++j)
                    slaves_run[j] = true;
                cond.notify_all();
            }
        }
        catch (...) {
            kill_em_all();
            throw;
        }
    };

    auto slave = [&](int ndx, std::string path) {
        try {
            File file(path, File::mode_Write);
            for (int i = 0; i != num_rounds; ++i) {
                bool good_lock = file.try_lock_exclusive();
                if (good_lock)
                    file.unlock();
                {
                    LockGuard l(mutex);
                    if (good_lock)
                        ++num_good_locks;
                    ++num_slaves_ready;
                    cond.notify_all();
                    while (!slaves_run[ndx]) {
                        if (terminate)
                            return;
                        cond.wait(l);
                    }
                    slaves_run[ndx] = false;
                }
            }
        }
        catch (...) {
            kill_em_all();
            throw;
        }
    };

    TEST_PATH(path);
    ThreadWrapper slaves[num_slaves];
    for (int i = 0; i != num_slaves; ++i) {
        slaves_run[i] = false;
        slaves[i].start([=] { slave(i, std::string(path)); });
    }
    master();
    for (int i = 0; i != num_slaves; ++i)
        CHECK(!slaves[i].join());

/*
    typedef std::map<int, int>::const_iterator iter;
    iter end = results.end();
    for (iter i = results.begin(); i != end; ++i)
        std::cout << i->first << " -> " << i->second << "\n";
*/

    // Check that there are no cases where no one got the lock
    CHECK_EQUAL(0, results[0]);
}

#endif // TEST_FILE_LOCKS
<commit_msg>Fixed instance of (likely) unintentional copy of a TestPathGuard object<commit_after>#include "testsettings.hpp"
#ifdef TEST_FILE_LOCKS

#include <cassert>
#include <map>
#include <iostream>
#include <functional>

#include <realm/util/thread.hpp>
#include <realm/util/file.hpp>
#include <realm/util/features.h>

#include "test.hpp"
#include "util/thread_wrapper.hpp"

using namespace realm::util;
using namespace realm::test_util;

// Test independence and thread-safety
// -----------------------------------
//
// All tests must be thread safe and independent of each other. This
// is required because it allows for both shuffling of the execution
// order and for parallelized testing.
//
// In particular, avoid using std::rand() since it is not guaranteed
// to be thread safe. Instead use the API offered in
// `test/util/random.hpp`.
//
// All files created in tests must use the TEST_PATH macro (or one of
// its friends) to obtain a suitable file system path. See
// `test/util/test_path.hpp`.
//
//
// Debugging and the ONLY() macro
// ------------------------------
//
// A simple way of disabling all tests except one called `Foo`, is to
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
// test suite. Note that you can also use filtering by setting the
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
// this.
//
// Another way to debug a particular test, is to copy that test into
// `experiments/testcase.cpp` and then run `sh build.sh
// check-testcase` (or one of its friends) from the command line.

// The assumption is that if multiple processes try to place an
// exclusive lock on a file in a non-blocking fashion, then at least
// one will succeed (assuming that no one else interferes). This test
// trys to verify that this is the case by repeatedly letting two
// treads compete for the lock. This is by no means a "water tight"
// test, but it is probably the best we can do.
TEST(File_NoSpuriousTryLockFailures)
{
#if TEST_DURATION < 1
    const int num_rounds = 1000;
#elif TEST_DURATION < 2
    const int num_rounds = 10000;
#elif TEST_DURATION < 3
    const int num_rounds = 100000;
#else
    const int num_rounds = 1000000;
#endif

    const int num_slaves = 2;

    Mutex mutex;
    CondVar cond;
    int num_slaves_ready = 0;
    int num_good_locks = 0;
    bool slaves_run[num_slaves];
    std::map<int, int> results;
    bool terminate = false;

    auto kill_em_all = [&] {
        LockGuard l(mutex);
        terminate = true;
        cond.notify_all();
    };

    auto master = [&] {
        try {
            LockGuard l(mutex);
            for (int i = 0; i != num_rounds; ++i) {
                while (num_slaves_ready != num_slaves) {
                    if (terminate)
                        return;
                    cond.wait(l);
                }
                num_slaves_ready = 0;

                ++results[num_good_locks];
                num_good_locks = 0;

                for (int j = 0; j != num_slaves; ++j)
                    slaves_run[j] = true;
                cond.notify_all();
            }
        }
        catch (...) {
            kill_em_all();
            throw;
        }
    };

    auto slave = [&](int ndx, std::string path) {
        try {
            File file(path, File::mode_Write);
            for (int i = 0; i != num_rounds; ++i) {
                bool good_lock = file.try_lock_exclusive();
                if (good_lock)
                    file.unlock();
                {
                    LockGuard l(mutex);
                    if (good_lock)
                        ++num_good_locks;
                    ++num_slaves_ready;
                    cond.notify_all();
                    while (!slaves_run[ndx]) {
                        if (terminate)
                            return;
                        cond.wait(l);
                    }
                    slaves_run[ndx] = false;
                }
            }
        }
        catch (...) {
            kill_em_all();
            throw;
        }
    };

    TEST_PATH(path);
    std::string str_path = path;
    ThreadWrapper slaves[num_slaves];
    for (int i = 0; i != num_slaves; ++i) {
        slaves_run[i] = false;
        slaves[i].start([=] { slave(i, str_path); });
    }
    master();
    for (int i = 0; i != num_slaves; ++i)
        CHECK(!slaves[i].join());

/*
    typedef std::map<int, int>::const_iterator iter;
    iter end = results.end();
    for (iter i = results.begin(); i != end; ++i)
        std::cout << i->first << " -> " << i->second << "\n";
*/

    // Check that there are no cases where no one got the lock
    CHECK_EQUAL(0, results[0]);
}

#endif // TEST_FILE_LOCKS
<|endoftext|>