<commit_before>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#undef NDEBUG
#define CPP_UTILS_ASSERT_EXCEPTION

#include "etl/etl_light.hpp"
#include "catch.hpp"

TEST_CASE("assert/sizes/1", "[assert]") {
    etl::dyn_vector<double> a = {-1.0, 2.0, 5.0};
    etl::dyn_vector<double> b = {2.5, 3.0, 4.0, 1.0};

    REQUIRE_THROWS(a + b);
}

TEST_CASE("assert/dim/1", "[assert]") {
    etl::fast_matrix<double, 2, 3, 4> matrix;

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/2", "[assert]") {
    etl::fast_dyn_matrix<double, 2, 3, 4> matrix;

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/3", "[assert]") {
    etl::dyn_matrix<double, 3> matrix(2, 3, 4);

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/4", "[assert]") {
    etl::sparse_matrix<double> matrix(2, 3);

    REQUIRE_NOTHROW(matrix(1, 1));
    REQUIRE_THROWS(matrix(3, 2));
    REQUIRE_THROWS(matrix(2, 2));
    REQUIRE_THROWS(matrix(1, 5));
    REQUIRE_THROWS(matrix(3, 5));
}
<commit_msg>Review assert test case<commit_after>//=======================================================================
// Copyright (c) 2014-2016 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#ifndef NDEBUG

#include "etl/etl_light.hpp"
#include "catch.hpp"

TEST_CASE("assert/nothrow/1", "[assert]") {
#ifdef CPP_UTILS_ASSERT_EXCEPTION
    REQUIRE(!etl::assert_nothrow);
#else
    REQUIRE(etl::assert_nothrow);
#endif
}

#ifdef CPP_UTILS_ASSERT_EXCEPTION
TEST_CASE("assert/sizes/1", "[assert]") {
    etl::dyn_vector<double> a = {-1.0, 2.0, 5.0};
    etl::dyn_vector<double> b = {2.5, 3.0, 4.0, 1.0};

    REQUIRE_THROWS(a + b);
}

TEST_CASE("assert/dim/1", "[assert]") {
    etl::fast_matrix<double, 2, 3, 4> matrix;

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/2", "[assert]") {
    etl::fast_dyn_matrix<double, 2, 3, 4> matrix;

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/3", "[assert]") {
    etl::dyn_matrix<double, 3> matrix(2, 3, 4);

    REQUIRE_NOTHROW(matrix(1, 1, 1));
    REQUIRE_THROWS(matrix(3, 2, 1));
    REQUIRE_THROWS(matrix(2, 2, 1));
    REQUIRE_THROWS(matrix(1, 5, 1));
    REQUIRE_THROWS(matrix(1, 1, 5));
    REQUIRE_THROWS(matrix(1, 1, 4));
    REQUIRE_THROWS(matrix(3, 3, 4));
}

TEST_CASE("assert/dim/4", "[assert]") {
    etl::sparse_matrix<double> matrix(2, 3);

    REQUIRE_NOTHROW(matrix(1, 1));
    REQUIRE_THROWS(matrix(3, 2));
    REQUIRE_THROWS(matrix(2, 2));
    REQUIRE_THROWS(matrix(1, 5));
    REQUIRE_THROWS(matrix(3, 5));
}
#endif

#endif
<|endoftext|>


<commit_before>#include "module_pressuresensor.h"
#include "pressure_form.h"
#include <Module_UID/module_uid.h>

#define REGISTER_CALIB 00 // 8 bytes
#define REGISTER_PRESSURE_RAW 8
#define REGISTER_TEMP_RAW 10
#define REGISTER_PRESSURE 12
#define REGISTER_TEMP 14
#define REGISTER_STATUS 17
#define REGISTER_COUNTER 20

// indicates problem between i2c-spi bridge and pressure sensor
#define STATUS_MAGIC_VALUE 0x55
#define CALIB_MAGIC_VALUE 224

// pressure range. everything outside this range will be regarded as
// a meassurement error
#define PRESSURE_MIN 900
#define PRESSURE_MAX 3000

Module_PressureSensor::Module_PressureSensor(QString id, Module_UID *uid)
    : RobotModule(id)
{
    this->uid=uid;

    setDefaultValue("i2cAddress", 0x50);
    setDefaultValue("frequency", 1);

    connect(&timer,SIGNAL(timeout()), this, SLOT(refreshData()));

    reset();
}

Module_PressureSensor::~Module_PressureSensor()
{
}

void Module_PressureSensor::terminate()
{
    RobotModule::terminate();
    timer.stop();
}

void Module_PressureSensor::reset()
{
    RobotModule::reset();

    int freq = 1000/getSettings().value("frequency").toInt();
    if (freq>0)
        timer.start(freq);
    else
        timer.stop();

    if (!getSettings().value("enabled").toBool())
        return;

    readCalibWords();

}

void Module_PressureSensor::refreshData()
{
    if (!getSettings().value("enabled").toBool())
        return;

    readPressure();
    readTemperature();
    readCounter();
    readRawRegisters();
    calc();

    if (getHealthStatus().isHealthOk()) {
        emit dataChanged(this);
        emit newDepthData(getDepth());
    }
}

void Module_PressureSensor::calc()
{
    short D1 = data["pressureRaw"].toInt();
    short D2 = data["tempRaw"].toInt();
    short C1 = data["C1"].toInt();
    short C2 = data["C2"].toInt();
    short C3 = data["C3"].toInt();
    short C4 = data["C4"].toInt();
    short C5 = data["C5"].toInt();
    short C6 = data["C6"].toInt();

    int UT1, dT, OFF, SENS, P;
    short int dT2;

    // Calculate calibration temperature
    UT1 = 8*C5+10000;

    // Calculate actual temperature
    dT = D2 - UT1;

    // Second-order temperature compensation
    if (dT < 0)
            dT2 = dT - (dT/128*dT/128)/2;
    else
            dT2 = dT - (dT/128*dT/128)/8;
    data["tempSW"] = 200+dT2*(C6+100)/2048;

    // Calculate temperature compensated pressure
    OFF = C2+((C4-250)*dT)/4096+10000;

    SENS = C1/2 + ((C3+200)*dT)/8192 + 3000;

    // Temperature compensated pressure in mbar
    P = (SENS*(D1-OFF))/4096+1000;

    data["pressureSW"] = P;
}

void Module_PressureSensor::readPressure()
{
    unsigned char readBuffer[2];

    if (!readRegister(REGISTER_PRESSURE, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    // this is the pressure in mBar
    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["pressure"] =  pressure;

    // 100 mBar == ca. 1m wassersäule - druck an der luft
    data["depth"] =  ((float)pressure-getSettings().value("airPressure").toFloat())/100;

    if (pressure < PRESSURE_MIN || pressure > PRESSURE_MAX) {
        setHealthToSick("Pressure of "+QString::number(pressure) + " doesn't make sense.");
    }

}

void Module_PressureSensor::readCounter()
{
    unsigned char readBuffer[1];

    if (!readRegister(REGISTER_COUNTER, 1, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    data["counter"] =  readBuffer[0];
}

void Module_PressureSensor::readCalibWords()
{
    unsigned char address = getSettings().value("i2cAddress").toInt();
    unsigned char cmd[] = { 0x12 };
    if (!uid->I2C_Write(address, cmd, 1)) {
        setHealthToSick("could not reread calib  words.");
        sleep(100);
        return;
    }
    sleep(100);

    unsigned char readBuffer[8];
    if (!readRegister(0, 8, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t pressure_CalibrationWords[4];
    for(int i=0;i<4;i++) {

        // this is the temperature in 10/degree celsius
        uint16_t c = (int)readBuffer[2*i] << 8 | (int)readBuffer[2*i+1];

        data["calib "+QString::number(i)] = "0x"+QString::number(c,16);
        pressure_CalibrationWords[i]=c;
    }

    data["C1"] = ((pressure_CalibrationWords[0] & 0xFFF8) >> 3);
    data["C2"] = ((pressure_CalibrationWords[0] & 0x0007) << 10) + ((pressure_CalibrationWords[1] & 0xFFC0) >> 6);
    data["C3"] = ((pressure_CalibrationWords[2] & 0xFFC0) >> 6);
    data["C4"] = ((pressure_CalibrationWords[3] & 0xFF80) >> 7);
    data["C5"] = ((pressure_CalibrationWords[1] & 0x003F) << 6) + ((pressure_CalibrationWords[2] & 0x003F));
    data["C6"] = (pressure_CalibrationWords[3] & 0x007F);

}

void Module_PressureSensor::readRawRegisters()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP_RAW, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["tempRaw"] = temp;

    if (!readRegister(REGISTER_PRESSURE_RAW, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["pressureRaw"] = pressure;
}

void Module_PressureSensor::readTemperature()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    // this is the temperature in 10/degree celsius
    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["temperature"] = ((float)temp)/10;
}

float Module_PressureSensor::getDepth()
{
    float p = data["pressure"].toFloat();
    if (p > 900 && p <= 2000) {
        return data["depth"].toFloat();
    } else {
        setHealthToSick("Pressure of "+QString::number(p) + " doesn't make sense.");
        return 0;
    }

}

float Module_PressureSensor::getTemperature()
{
    return data["temperature"].toFloat();
}

QList<RobotModule*> Module_PressureSensor::getDependencies()
{
    QList<RobotModule*> ret;
    ret.append(uid);
    return ret;
}

QWidget* Module_PressureSensor::createView(QWidget* parent)
{
    return new Pressure_Form(this, parent);
}

void Module_PressureSensor::doHealthCheck()
{
    if (!getSettings().value("enabled").toBool())
        return;

    unsigned char readBuffer[1];

    if (!readRegister(REGISTER_STATUS, 1, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }
    if (readBuffer[0] != STATUS_MAGIC_VALUE) {
        setHealthToSick("Status register doesn't match magic value: is="+QString::number(readBuffer[0]));
        return;
    }

    setHealthToOk();
}

bool Module_PressureSensor::readRegister2(unsigned char reg, int size, unsigned char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_Write(address, &reg, 1)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    if (!uid->I2C_Read(address, size, ret_buf)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    return true;
}

bool Module_PressureSensor::readRegister(unsigned char reg, int size, unsigned char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_ReadRegisters(address, reg, size, ret_buf)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    return true;
}
<commit_msg>enforce new read out of calibration words on reset<commit_after>#include "module_pressuresensor.h"
#include "pressure_form.h"
#include <Module_UID/module_uid.h>

#define REGISTER_CALIB 00 // 8 bytes
#define REGISTER_PRESSURE_RAW 8
#define REGISTER_TEMP_RAW 10
#define REGISTER_PRESSURE 12
#define REGISTER_TEMP 14
#define REGISTER_STATUS 17
#define REGISTER_COUNTER 20

#define REQUEST_NEW_CALIB_VALUES 18

// indicates problem between i2c-spi bridge and pressure sensor
#define STATUS_MAGIC_VALUE 0x55
#define CALIB_MAGIC_VALUE 224

// pressure range. everything outside this range will be regarded as
// a meassurement error
#define PRESSURE_MIN 900
#define PRESSURE_MAX 3000

Module_PressureSensor::Module_PressureSensor(QString id, Module_UID *uid)
    : RobotModule(id)
{
    this->uid=uid;

    setDefaultValue("i2cAddress", 0x50);
    setDefaultValue("frequency", 1);

    connect(&timer,SIGNAL(timeout()), this, SLOT(refreshData()));

    reset();
}

Module_PressureSensor::~Module_PressureSensor()
{
}

void Module_PressureSensor::terminate()
{
    RobotModule::terminate();
    timer.stop();
}

void Module_PressureSensor::reset()
{
    RobotModule::reset();

    int freq = 1000/getSettings().value("frequency").toInt();
    if (freq>0)
        timer.start(freq);
    else
        timer.stop();

    if (!getSettings().value("enabled").toBool())
        return;

    unsigned char address = getSettings().value("i2cAddress").toInt();
    unsigned char reg = REQUEST_NEW_CALIB_VALUES;
    if (!uid->I2C_Write(address, &reg, 1)) {
        setHealthToSick("UID reported error while REQUEST_NEW_CALIB_VALUES.");
    }
    sleep(100);

    readCalibWords();

}

void Module_PressureSensor::refreshData()
{
    if (!getSettings().value("enabled").toBool())
        return;

    readPressure();
    readTemperature();
    readCounter();
    readRawRegisters();
    calc();

    if (getHealthStatus().isHealthOk()) {
        emit dataChanged(this);
        emit newDepthData(getDepth());
    }
}

void Module_PressureSensor::calc()
{
    short D1 = data["pressureRaw"].toInt();
    short D2 = data["tempRaw"].toInt();
    short C1 = data["C1"].toInt();
    short C2 = data["C2"].toInt();
    short C3 = data["C3"].toInt();
    short C4 = data["C4"].toInt();
    short C5 = data["C5"].toInt();
    short C6 = data["C6"].toInt();

    int UT1, dT, OFF, SENS, P;
    short int dT2;

    // Calculate calibration temperature
    UT1 = 8*C5+10000;

    // Calculate actual temperature
    dT = D2 - UT1;

    // Second-order temperature compensation
    if (dT < 0)
            dT2 = dT - (dT/128*dT/128)/2;
    else
            dT2 = dT - (dT/128*dT/128)/8;
    data["tempSW"] = 200+dT2*(C6+100)/2048;

    // Calculate temperature compensated pressure
    OFF = C2+((C4-250)*dT)/4096+10000;

    SENS = C1/2 + ((C3+200)*dT)/8192 + 3000;

    // Temperature compensated pressure in mbar
    P = (SENS*(D1-OFF))/4096+1000;

    data["pressureSW"] = P;
}

void Module_PressureSensor::readPressure()
{
    unsigned char readBuffer[2];

    if (!readRegister(REGISTER_PRESSURE, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    // this is the pressure in mBar
    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["pressure"] =  pressure;

    // 100 mBar == ca. 1m wassersäule - druck an der luft
    data["depth"] =  ((float)pressure-getSettings().value("airPressure").toFloat())/100;

    if (pressure < PRESSURE_MIN || pressure > PRESSURE_MAX) {
        setHealthToSick("Pressure of "+QString::number(pressure) + " doesn't make sense.");
    }

}

void Module_PressureSensor::readCounter()
{
    unsigned char readBuffer[1];

    if (!readRegister(REGISTER_COUNTER, 1, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    data["counter"] =  readBuffer[0];
}

void Module_PressureSensor::readCalibWords()
{
    unsigned char address = getSettings().value("i2cAddress").toInt();
    unsigned char cmd[] = { 0x12 };
    if (!uid->I2C_Write(address, cmd, 1)) {
        setHealthToSick("could not reread calib  words.");
        sleep(100);
        return;
    }
    sleep(100);

    unsigned char readBuffer[8];
    if (!readRegister(0, 8, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t pressure_CalibrationWords[4];
    for(int i=0;i<4;i++) {

        // this is the temperature in 10/degree celsius
        uint16_t c = (int)readBuffer[2*i] << 8 | (int)readBuffer[2*i+1];

        data["calib "+QString::number(i)] = "0x"+QString::number(c,16);
        pressure_CalibrationWords[i]=c;
    }

    data["C1"] = ((pressure_CalibrationWords[0] & 0xFFF8) >> 3);
    data["C2"] = ((pressure_CalibrationWords[0] & 0x0007) << 10) + ((pressure_CalibrationWords[1] & 0xFFC0) >> 6);
    data["C3"] = ((pressure_CalibrationWords[2] & 0xFFC0) >> 6);
    data["C4"] = ((pressure_CalibrationWords[3] & 0xFF80) >> 7);
    data["C5"] = ((pressure_CalibrationWords[1] & 0x003F) << 6) + ((pressure_CalibrationWords[2] & 0x003F));
    data["C6"] = (pressure_CalibrationWords[3] & 0x007F);

}

void Module_PressureSensor::readRawRegisters()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP_RAW, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["tempRaw"] = temp;

    if (!readRegister(REGISTER_PRESSURE_RAW, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    uint16_t pressure = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["pressureRaw"] = pressure;
}

void Module_PressureSensor::readTemperature()
{

    unsigned char readBuffer[2];
    if (!readRegister(REGISTER_TEMP, 2, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }

    // this is the temperature in 10/degree celsius
    uint16_t temp = (int)readBuffer[0] << 8 | (int)readBuffer[1];

    data["temperature"] = ((float)temp)/10;
}

float Module_PressureSensor::getDepth()
{
    float p = data["pressure"].toFloat();
    if (p > 900 && p <= 2000) {
        return data["depth"].toFloat();
    } else {
        setHealthToSick("Pressure of "+QString::number(p) + " doesn't make sense.");
        return 0;
    }

}

float Module_PressureSensor::getTemperature()
{
    return data["temperature"].toFloat();
}

QList<RobotModule*> Module_PressureSensor::getDependencies()
{
    QList<RobotModule*> ret;
    ret.append(uid);
    return ret;
}

QWidget* Module_PressureSensor::createView(QWidget* parent)
{
    return new Pressure_Form(this, parent);
}

void Module_PressureSensor::doHealthCheck()
{
    if (!getSettings().value("enabled").toBool())
        return;

    unsigned char readBuffer[1];

    if (!readRegister(REGISTER_STATUS, 1, readBuffer)) {
        setHealthToSick("UID reported error.");
        return;
    }
    if (readBuffer[0] != STATUS_MAGIC_VALUE) {
        setHealthToSick("Status register doesn't match magic value: is="+QString::number(readBuffer[0]));
        return;
    }

    setHealthToOk();
}

bool Module_PressureSensor::readRegister2(unsigned char reg, int size, unsigned char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_Write(address, &reg, 1)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    if (!uid->I2C_Read(address, size, ret_buf)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    return true;
}

bool Module_PressureSensor::readRegister(unsigned char reg, int size, unsigned char *ret_buf)
{
    unsigned char address = getSettings().value("i2cAddress").toInt();

    if (!uid->I2C_ReadRegisters(address, reg, size, ret_buf)) {
        setHealthToSick("UID reported error.");
        return false;
    }
    return true;
}
<|endoftext|>


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

#include <unotools/atom.hxx>

using namespace utl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
#define NMSP_UTIL ::com::sun::star::util

AtomProvider::AtomProvider()
{
    m_nAtoms = 1;
}

AtomProvider::~AtomProvider()
{
}

int AtomProvider::getAtom( const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::iterator it = m_aAtomMap.find( rString );
    if( it != m_aAtomMap.end() )
        return it->second;
    if( ! bCreate )
        return INVALID_ATOM;
    m_aAtomMap[ rString ] = m_nAtoms;
    m_aStringMap[ m_nAtoms ] = rString;
    m_nAtoms++;
    return m_nAtoms-1;
}

void AtomProvider::getAll( ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        aDesc.atom          = it->second;
        aDesc.description   = it->first;
        atoms.push_back( aDesc );
        ++it;
    }
}

void AtomProvider::getRecent( int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();

    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        if( it->second > atom )
        {
            aDesc.atom          = it->second;
            aDesc.description   = it->first;
            atoms.push_back( aDesc );
        }
        ++it;
    }
}

const ::rtl::OUString& AtomProvider::getString( int nAtom ) const
{
    static ::rtl::OUString aEmpty;
    ::std::hash_map< int, ::rtl::OUString, ::std::hash< int > >::const_iterator it = m_aStringMap.find( nAtom );

    return it == m_aStringMap.end() ? aEmpty : it->second;
}

void AtomProvider::overrideAtom( int atom, const ::rtl::OUString& description )
{
    m_aAtomMap[ description ] = atom;
    m_aStringMap[ atom ] = description;
    if( m_nAtoms <= atom )
        m_nAtoms=atom+1;
}

sal_Bool AtomProvider::hasAtom( int atom ) const
{
    return m_aStringMap.find( atom ) != m_aStringMap.end() ? sal_True : sal_False;
}

// -----------------------------------------------------------------------

MultiAtomProvider::MultiAtomProvider()
{
}

MultiAtomProvider::~MultiAtomProvider()
{
    for( ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it = m_aAtomLists.begin(); it != m_aAtomLists.end(); ++it )
        delete it->second;
}

sal_Bool MultiAtomProvider::insertAtomClass( int atomClass )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return sal_False;
    m_aAtomLists[ atomClass ] = new AtomProvider();
    return sal_True;
}

int MultiAtomProvider::getAtom( int atomClass, const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getAtom( rString, bCreate );

    if( bCreate )
    {
        AtomProvider* pNewClass;
        m_aAtomLists[ atomClass ] = pNewClass = new AtomProvider();
        return pNewClass->getAtom( rString, bCreate );
    }
    return INVALID_ATOM;
}

int MultiAtomProvider::getLastAtom( int atomClass ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );

    return it != m_aAtomLists.end() ? it->second->getLastAtom() : INVALID_ATOM;
}

void MultiAtomProvider::getRecent( int atomClass, int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        it->second->getRecent( atom, atoms );
    else
        atoms.clear();
}

const ::rtl::OUString& MultiAtomProvider::getString( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getString( atom );

    static ::rtl::OUString aEmpty;
    return aEmpty;
}

sal_Bool MultiAtomProvider::hasAtom( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    return it != m_aAtomLists.end() ? it->second->hasAtom( atom ) : sal_False;
}

void MultiAtomProvider::getClass( int atomClass, ::std::list< ::utl::AtomDescription >& atoms) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );

    if( it != m_aAtomLists.end() )
        it->second->getAll( atoms );
    else
        atoms.clear();
}

void MultiAtomProvider::overrideAtom( int atomClass, int atom, const ::rtl::OUString& description )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    if( it == m_aAtomLists.end() )
        m_aAtomLists[ atomClass ] = new AtomProvider();
    m_aAtomLists[ atomClass ]->overrideAtom( atom, description );
}

// -----------------------------------------------------------------------

AtomServer::AtomServer()
{
}

AtomServer::~AtomServer()
{
}

sal_Int32 AtomServer::getAtom( sal_Int32 atomClass, const ::rtl::OUString& description, sal_Bool create ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    return m_aProvider.getAtom( atomClass, description, create );
}

Sequence< Sequence< NMSP_UTIL::AtomDescription > > AtomServer::getClasses( const Sequence< sal_Int32 >& atomClasses ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aRet( atomClasses.getLength() );
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        aRet.getArray()[i] = getClass( atomClasses.getConstArray()[i] );
    }
    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getClass( sal_Int32 atomClass ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getClass( atomClass, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getRecentAtoms( sal_Int32 atomClass, sal_Int32 atom ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getRecent( atomClass, atom, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< ::rtl::OUString > AtomServer::getAtomDescriptions( const Sequence< AtomClassRequest >& atoms ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    int nStrings = 0, i;
    for( i = 0; i < atoms.getLength(); i++ )
        nStrings += atoms.getConstArray()[ i ].atoms.getLength();
    Sequence< ::rtl::OUString > aRet( nStrings );
    for( i = 0, nStrings = 0; i < atoms.getLength(); i++ )
    {
        const AtomClassRequest& rRequest = atoms.getConstArray()[i];
        for( int n = 0; n < rRequest.atoms.getLength(); n++ )
            aRet.getArray()[ nStrings++ ] = m_aProvider.getString( rRequest.atomClass, rRequest.atoms.getConstArray()[ n ] );
    }
    return aRet;
}

// -----------------------------------------------------------------------

AtomClient::AtomClient( const Reference< XAtomServer >& xServer ) :
        m_xServer( xServer )
{
}

AtomClient::~AtomClient()
{
}

int AtomClient::getAtom( int atomClass, const ::rtl::OUString& description, sal_Bool bCreate )
{
    int nAtom = m_aProvider.getAtom( atomClass, description, sal_False );
    if( nAtom == INVALID_ATOM && bCreate )
    {
        try
        {
            nAtom = m_xServer->getAtom( atomClass, description, bCreate );
        }
        catch( RuntimeException& )
        {
            return INVALID_ATOM;
        }
        if( nAtom != INVALID_ATOM )
            m_aProvider.overrideAtom( atomClass, nAtom, description );
    }
    return nAtom;
}

const ::rtl::OUString& AtomClient::getString( int atomClass, int atom )
{
    if( ! m_aProvider.hasAtom( atomClass, atom ) )
    {
        Sequence< NMSP_UTIL::AtomDescription > aSeq;
        try
        {
            aSeq = m_xServer->getRecentAtoms( atomClass, m_aProvider.getLastAtom( atomClass ) );
        }
        catch( RuntimeException& )
        {
            return ::rtl::OUString();
        }
        const NMSP_UTIL::AtomDescription* pDescriptions = aSeq.getConstArray();
        for( int i = 0; i < aSeq.getLength(); i++ )
            m_aProvider.overrideAtom( atomClass,
                                      pDescriptions[i].atom,
                                      pDescriptions[i].description
                                      );

        if( ! m_aProvider.hasAtom( atomClass, atom ) )
        {
            // holes may occur by the above procedure!
            Sequence< AtomClassRequest > aSeq( 1 );
            aSeq.getArray()[0].atomClass = atomClass;
            aSeq.getArray()[0].atoms.realloc( 1 );
            aSeq.getArray()[0].atoms.getArray()[0] = atom;
            Sequence< ::rtl::OUString > aRet;
            try
            {
                aRet = m_xServer->getAtomDescriptions( aSeq );
            }
            catch( RuntimeException& )
            {
                return ::rtl::OUString();
            }
            if( aRet.getLength() == 1 )
                m_aProvider.overrideAtom( atomClass, atom, aRet.getConstArray()[0] );
        }
    }
    return m_aProvider.getString( atomClass, atom );
}

void AtomClient::updateAtomClasses( const Sequence< sal_Int32 >& atomClasses )
{
    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aUpdate;
    try
    {
        aUpdate = m_xServer->getClasses( atomClasses );
    }
    catch( RuntimeException& )
    {
        return;
    }
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        int nClass = atomClasses.getConstArray()[i];
        const Sequence< NMSP_UTIL::AtomDescription >& rClass = aUpdate.getConstArray()[i];
        const NMSP_UTIL::AtomDescription* pDesc = rClass.getConstArray();
        for( int n = 0; n < rClass.getLength(); n++, pDesc++ )
            m_aProvider.overrideAtom( nClass, pDesc->atom, pDesc->description );
    }
}
<commit_msg>#94448# do not return references to temporaries<commit_after>/*************************************************************************
 *
 *  $RCSfile: atom.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: pl $ $Date: 2001-12-12 13:57:09 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <unotools/atom.hxx>

using namespace utl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
#define NMSP_UTIL ::com::sun::star::util

AtomProvider::AtomProvider()
{
    m_nAtoms = 1;
}

AtomProvider::~AtomProvider()
{
}

int AtomProvider::getAtom( const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::iterator it = m_aAtomMap.find( rString );
    if( it != m_aAtomMap.end() )
        return it->second;
    if( ! bCreate )
        return INVALID_ATOM;
    m_aAtomMap[ rString ] = m_nAtoms;
    m_aStringMap[ m_nAtoms ] = rString;
    m_nAtoms++;
    return m_nAtoms-1;
}

void AtomProvider::getAll( ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();
    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        aDesc.atom          = it->second;
        aDesc.description   = it->first;
        atoms.push_back( aDesc );
        ++it;
    }
}

void AtomProvider::getRecent( int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    atoms.clear();

    ::std::hash_map< ::rtl::OUString, int, ::rtl::OUStringHash >::const_iterator it = m_aAtomMap.begin();

    ::utl::AtomDescription aDesc;
    while( it != m_aAtomMap.end() )
    {
        if( it->second > atom )
        {
            aDesc.atom          = it->second;
            aDesc.description   = it->first;
            atoms.push_back( aDesc );
        }
        ++it;
    }
}

const ::rtl::OUString& AtomProvider::getString( int nAtom ) const
{
    static ::rtl::OUString aEmpty;
    ::std::hash_map< int, ::rtl::OUString, ::std::hash< int > >::const_iterator it = m_aStringMap.find( nAtom );

    return it == m_aStringMap.end() ? aEmpty : it->second;
}

void AtomProvider::overrideAtom( int atom, const ::rtl::OUString& description )
{
    m_aAtomMap[ description ] = atom;
    m_aStringMap[ atom ] = description;
    if( m_nAtoms <= atom )
        m_nAtoms=atom+1;
}

sal_Bool AtomProvider::hasAtom( int atom ) const
{
    return m_aStringMap.find( atom ) != m_aStringMap.end() ? sal_True : sal_False;
}

// -----------------------------------------------------------------------

MultiAtomProvider::MultiAtomProvider()
{
}

MultiAtomProvider::~MultiAtomProvider()
{
    for( ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it = m_aAtomLists.begin(); it != m_aAtomLists.end(); ++it )
        delete it->second;
}

sal_Bool MultiAtomProvider::insertAtomClass( int atomClass )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return sal_False;
    m_aAtomLists[ atomClass ] = new AtomProvider();
    return sal_True;
}

int MultiAtomProvider::getAtom( int atomClass, const ::rtl::OUString& rString, sal_Bool bCreate )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getAtom( rString, bCreate );

    if( bCreate )
    {
        AtomProvider* pNewClass;
        m_aAtomLists[ atomClass ] = pNewClass = new AtomProvider();
        return pNewClass->getAtom( rString, bCreate );
    }
    return INVALID_ATOM;
}

int MultiAtomProvider::getLastAtom( int atomClass ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );

    return it != m_aAtomLists.end() ? it->second->getLastAtom() : INVALID_ATOM;
}

void MultiAtomProvider::getRecent( int atomClass, int atom, ::std::list< ::utl::AtomDescription >& atoms )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        it->second->getRecent( atom, atoms );
    else
        atoms.clear();
}

const ::rtl::OUString& MultiAtomProvider::getString( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it =
          m_aAtomLists.find( atomClass );
    if( it != m_aAtomLists.end() )
        return it->second->getString( atom );

    static ::rtl::OUString aEmpty;
    return aEmpty;
}

sal_Bool MultiAtomProvider::hasAtom( int atomClass, int atom ) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    return it != m_aAtomLists.end() ? it->second->hasAtom( atom ) : sal_False;
}

void MultiAtomProvider::getClass( int atomClass, ::std::list< ::utl::AtomDescription >& atoms) const
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );

    if( it != m_aAtomLists.end() )
        it->second->getAll( atoms );
    else
        atoms.clear();
}

void MultiAtomProvider::overrideAtom( int atomClass, int atom, const ::rtl::OUString& description )
{
    ::std::hash_map< int, AtomProvider*, ::std::hash< int > >::const_iterator it = m_aAtomLists.find( atomClass );
    if( it == m_aAtomLists.end() )
        m_aAtomLists[ atomClass ] = new AtomProvider();
    m_aAtomLists[ atomClass ]->overrideAtom( atom, description );
}

// -----------------------------------------------------------------------

AtomServer::AtomServer()
{
}

AtomServer::~AtomServer()
{
}

sal_Int32 AtomServer::getAtom( sal_Int32 atomClass, const ::rtl::OUString& description, sal_Bool create ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    return m_aProvider.getAtom( atomClass, description, create );
}

Sequence< Sequence< NMSP_UTIL::AtomDescription > > AtomServer::getClasses( const Sequence< sal_Int32 >& atomClasses ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aRet( atomClasses.getLength() );
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        aRet.getArray()[i] = getClass( atomClasses.getConstArray()[i] );
    }
    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getClass( sal_Int32 atomClass ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getClass( atomClass, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< NMSP_UTIL::AtomDescription > AtomServer::getRecentAtoms( sal_Int32 atomClass, sal_Int32 atom ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    ::std::list< ::utl::AtomDescription > atoms;
    m_aProvider.getRecent( atomClass, atom, atoms );

    Sequence< NMSP_UTIL::AtomDescription > aRet( atoms.size() );
    for( int i = aRet.getLength()-1; i >= 0; i-- )
    {
        aRet.getArray()[i].atom         = atoms.back().atom;
        aRet.getArray()[i].description  = atoms.back().description;
        atoms.pop_back();
    }

    return aRet;
}

Sequence< ::rtl::OUString > AtomServer::getAtomDescriptions( const Sequence< AtomClassRequest >& atoms ) throw()
{
    ::osl::Guard< ::osl::Mutex > guard( m_aMutex );

    int nStrings = 0, i;
    for( i = 0; i < atoms.getLength(); i++ )
        nStrings += atoms.getConstArray()[ i ].atoms.getLength();
    Sequence< ::rtl::OUString > aRet( nStrings );
    for( i = 0, nStrings = 0; i < atoms.getLength(); i++ )
    {
        const AtomClassRequest& rRequest = atoms.getConstArray()[i];
        for( int n = 0; n < rRequest.atoms.getLength(); n++ )
            aRet.getArray()[ nStrings++ ] = m_aProvider.getString( rRequest.atomClass, rRequest.atoms.getConstArray()[ n ] );
    }
    return aRet;
}

// -----------------------------------------------------------------------

AtomClient::AtomClient( const Reference< XAtomServer >& xServer ) :
        m_xServer( xServer )
{
}

AtomClient::~AtomClient()
{
}

int AtomClient::getAtom( int atomClass, const ::rtl::OUString& description, sal_Bool bCreate )
{
    int nAtom = m_aProvider.getAtom( atomClass, description, sal_False );
    if( nAtom == INVALID_ATOM && bCreate )
    {
        try
        {
            nAtom = m_xServer->getAtom( atomClass, description, bCreate );
        }
        catch( RuntimeException& )
        {
            return INVALID_ATOM;
        }
        if( nAtom != INVALID_ATOM )
            m_aProvider.overrideAtom( atomClass, nAtom, description );
    }
    return nAtom;
}

const ::rtl::OUString& AtomClient::getString( int atomClass, int atom )
{
    static ::rtl::OUString aEmpty;

    if( ! m_aProvider.hasAtom( atomClass, atom ) )
    {
        Sequence< NMSP_UTIL::AtomDescription > aSeq;
        try
        {
            aSeq = m_xServer->getRecentAtoms( atomClass, m_aProvider.getLastAtom( atomClass ) );
        }
        catch( RuntimeException& )
        {
            return aEmpty;
        }
        const NMSP_UTIL::AtomDescription* pDescriptions = aSeq.getConstArray();
        for( int i = 0; i < aSeq.getLength(); i++ )
            m_aProvider.overrideAtom( atomClass,
                                      pDescriptions[i].atom,
                                      pDescriptions[i].description
                                      );

        if( ! m_aProvider.hasAtom( atomClass, atom ) )
        {
            // holes may occur by the above procedure!
            Sequence< AtomClassRequest > aSeq( 1 );
            aSeq.getArray()[0].atomClass = atomClass;
            aSeq.getArray()[0].atoms.realloc( 1 );
            aSeq.getArray()[0].atoms.getArray()[0] = atom;
            Sequence< ::rtl::OUString > aRet;
            try
            {
                aRet = m_xServer->getAtomDescriptions( aSeq );
            }
            catch( RuntimeException& )
            {
                return aEmpty;
            }
            if( aRet.getLength() == 1 )
                m_aProvider.overrideAtom( atomClass, atom, aRet.getConstArray()[0] );
        }
    }
    return m_aProvider.getString( atomClass, atom );
}

void AtomClient::updateAtomClasses( const Sequence< sal_Int32 >& atomClasses )
{
    Sequence< Sequence< NMSP_UTIL::AtomDescription > > aUpdate;
    try
    {
        aUpdate = m_xServer->getClasses( atomClasses );
    }
    catch( RuntimeException& )
    {
        return;
    }
    for( int i = 0; i < atomClasses.getLength(); i++ )
    {
        int nClass = atomClasses.getConstArray()[i];
        const Sequence< NMSP_UTIL::AtomDescription >& rClass = aUpdate.getConstArray()[i];
        const NMSP_UTIL::AtomDescription* pDesc = rClass.getConstArray();
        for( int n = 0; n < rClass.getLength(); n++, pDesc++ )
            m_aProvider.overrideAtom( nClass, pDesc->atom, pDesc->description );
    }
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <fcntl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "arch/linux/arch.hpp"
#include "config/args.hpp"
#include "utils2.hpp"
#include "logger.hpp"

// #define DEBUG_DUMP_WRITES 1

/* Disk file object */

perfmon_counter_t
    pm_io_reads_started("io_reads_started[ioreads]"),
    pm_io_reads_completed("io_reads_completed[ioreads]"),
    pm_io_writes_started("io_writes_started[iowrites]"),
    pm_io_writes_completed("io_writes_completed[iowrites]");

linux_direct_file_t::linux_direct_file_t(const char *path, int mode)
    : fd(INVALID_FD)
{
    int res;

    // Determine if it is a block device

    struct stat64 file_stat;
    bzero((void*)&file_stat, sizeof(file_stat)); // make valgrind happy
    res = stat64(path, &file_stat);
    guarantee_err(res == 0 || errno == ENOENT, "Could not stat file '%s'", path);

    if (res == -1 && errno == ENOENT) {
        if (!(mode & mode_create)) {
            file_exists = false;
            return;
        }
        is_block = false;
    } else {
        is_block = S_ISBLK(file_stat.st_mode);
    }

    // Construct file flags

    int flags = O_CREAT | O_DIRECT | O_LARGEFILE;

    if ((mode & mode_write) && (mode & mode_read)) flags |= O_RDWR;
    else if (mode & mode_write) flags |= O_WRONLY;
    else if (mode & mode_read) flags |= O_RDONLY;
    else crash("Bad file access mode.");

    // O_NOATIME requires owner or root privileges. This is a bit of a hack; we assume that
    // if we are opening a regular file, we are the owner, but if we are opening a block device,
    // we are not.
    if (!is_block) flags |= O_NOATIME;

    // Open the file

    fd = open(path, flags, 0644);
    if (fd == INVALID_FD)
        fail_due_to_user_error("Inaccessible database file: \"%s\": %s", path, strerror(errno));

    file_exists = true;

    // Determine the file size

    if (is_block) {
        res = ioctl(fd, BLKGETSIZE64, &file_size);
        guarantee_err(res != -1, "Could not determine block device size");
    } else {
        off64_t size = lseek64(fd, 0, SEEK_END);
        guarantee_err(size != -1, "Could not determine file size");
        res = lseek64(fd, 0, SEEK_SET);
        guarantee_err(res != -1, "Could not reset file position");

        file_size = size;
    }
}

bool linux_direct_file_t::exists() {
    return file_exists;
}

bool linux_direct_file_t::is_block_device() {
    return is_block;
}

size_t linux_direct_file_t::get_size() {
    return file_size;
}

void linux_direct_file_t::set_size(size_t size) {
    assert(!is_block);
    int res = ftruncate(fd, size);
    guarantee_err(res == 0, "Could not ftruncate()");
    file_size = size;
}

void linux_direct_file_t::set_size_at_least(size_t size) {
    if (is_block) {
        assert(file_size >= size);
    } else {
        /* Grow in large chunks at a time */
        if (file_size < size) {
            // TODO: we should make the growth rate of a db file
            // configurable.
            set_size(ceil_aligned(size, DEVICE_BLOCK_SIZE * 128));
        }
    }
}

bool linux_direct_file_t::read_async(size_t offset, size_t length, void *buf, linux_iocallback_t *callback) {
    verify(offset, length, buf);

    linux_io_calls_t *iosys = &linux_thread_pool_t::thread->iosys;

    // Prepare the request
    iocb *request = new iocb;
    io_prep_pread(request, fd, buf, length, offset);
    io_set_eventfd(request, iosys->aio_notify_fd);
    request->data = callback;

    // Add it to a list of outstanding read requests
    iosys->io_requests.queue.push_back(request);

    pm_io_reads_started++;

    // Process whatever is left
    iosys->process_requests();

    return false;
}

bool linux_direct_file_t::write_async(size_t offset, size_t length, void *buf, linux_iocallback_t *callback) {
#ifdef DEBUG_DUMP_WRITES
    printf("--- WRITE BEGIN ---\n");
    print_backtrace(stdout);
    printf("\n");
    print_hd(buf, offset, length);
    printf("---- WRITE END ----\n\n");
#endif

    verify(offset, length, buf);

    linux_io_calls_t *iosys = &linux_thread_pool_t::thread->iosys;

    // Prepare the request
    iocb *request = new iocb;
    io_prep_pwrite(request, fd, buf, length, offset);
    io_set_eventfd(request, iosys->aio_notify_fd);
    request->data = callback;

    // Add it to a list of outstanding write requests
    iosys->io_requests.queue.push_back(request);

    pm_io_writes_started++;

    // Process whatever is left
    iosys->process_requests();

    return false;
}

void linux_direct_file_t::read_blocking(size_t offset, size_t length, void *buf) {
    verify(offset, length, buf);
    size_t res = pread(fd, buf, length, offset);
    assert(res == length, "Blocking read failed");
    (void)res;
}

void linux_direct_file_t::write_blocking(size_t offset, size_t length, void *buf) {
    verify(offset, length, buf);
    size_t res = pwrite(fd, buf, length, offset);
    assert(res == length, "Blocking write failed");
    (void)res;
}

linux_direct_file_t::~linux_direct_file_t() {
    if (fd != INVALID_FD) close(fd);
}

void linux_direct_file_t::verify(size_t offset, size_t length, void *buf) {
    assert(buf);
    assert(offset + length <= file_size);
    assert((intptr_t)buf % DEVICE_BLOCK_SIZE == 0);
    assert(offset % DEVICE_BLOCK_SIZE == 0);
    assert(length % DEVICE_BLOCK_SIZE == 0);
}

/* Async IO scheduler */

/* TODO: Batch requests internally so that we can send multiple requests per
call to io_submit(). */

linux_io_calls_t::linux_io_calls_t(linux_event_queue_t *queue)
    : queue(queue), n_pending(0), io_requests(this)
{
    int res;

    // Create aio context

    aio_context = 0;
    res = io_setup(MAX_CONCURRENT_IO_REQUESTS, &aio_context);
    guarantee_xerr(res == 0, -res, "Could not setup aio context");

    // Create aio notify fd

    aio_notify_fd = eventfd(0, 0);
    guarantee_err(aio_notify_fd != -1, "Could not create aio notification fd");

    res = fcntl(aio_notify_fd, F_SETFL, O_NONBLOCK);
    guarantee_err(res == 0, "Could not make aio notify fd non-blocking");

    queue->watch_resource(aio_notify_fd, poll_event_in, this);
}

linux_io_calls_t::~linux_io_calls_t()
{
    int res;

    assert(n_pending == 0);

    res = close(aio_notify_fd);
    guarantee_err(res == 0, "Could not close aio_notify_fd");

    res = io_destroy(aio_context);
    guarantee_xerr(res == 0, -res, "Could not destroy aio context");
}

void linux_io_calls_t::on_event(int event_mask) {

    int res, nevents;
    eventfd_t nevents_total;

    if (event_mask != poll_event_in) {
        logERR("Unexpected event mask: %d\n", event_mask);
    }

    res = eventfd_read(aio_notify_fd, &nevents_total);
    guarantee_err(res == 0, "Could not read aio_notify_fd value");

    // Note: O(1) array allocators are hard. To avoid all the
    // complexity, we'll use a fixed sized array and call io_getevents
    // multiple times if we have to (which should be very unlikely,
    // anyway).
    io_event events[MAX_IO_EVENT_PROCESSING_BATCH_SIZE];

    do {
        // Grab the events. Note: we need to make sure we don't read
        // more than nevents_total, otherwise we risk reading an io
        // event and getting an eventfd for this read event later due
        // to the way the kernel is structured. Better avoid this
        // complexity (hence std::min below).
        nevents = io_getevents(aio_context, 0,
                               std::min((int)nevents_total, MAX_IO_EVENT_PROCESSING_BATCH_SIZE),
                               events, NULL);
        guarantee_xerr(nevents >= 1, -nevents, "Waiting for AIO event failed");

        // Process the events
        for(int i = 0; i < nevents; i++) {
            aio_notify((iocb*)events[i].obj, events[i].res);
        }
        nevents_total -= nevents;

    } while (nevents_total > 0);
}

void linux_io_calls_t::aio_notify(iocb *event, int result) {
    // Update stats
    if (event->aio_lio_opcode == IO_CMD_PREAD) pm_io_reads_completed++;
    else pm_io_writes_completed++;

    // Schedule the requests we couldn't finish last time
    n_pending--;
    process_requests();

    // Check for failure (because the higher-level code usually doesn't)
    if (result != (int)event->u.c.nbytes) {
        // Currently AIO is used only for disk files, not sockets.
        // Thus, if something fails, we have a good reason to crash
        // (note that that is not true for sockets: we should just
        // close the socket and cleanup then).
        guarantee_xerr(false, -result, "Read or write failed");
    }

    // Notify the interested party about the event
    linux_iocallback_t *callback = (linux_iocallback_t*)event->data;

    // Prepare event_t for the callback
    event_t qevent;
    bzero((void*)&qevent, sizeof(qevent));
    qevent.state = NULL;
    qevent.result = result;
    qevent.buf = event->u.c.buf;
    qevent.offset = event->u.c.offset;
    qevent.op = event->aio_lio_opcode == IO_CMD_PREAD ? eo_read : eo_write;

    callback->on_io_complete(&qevent);

    // Free the iocb structure
    delete event;
}

void linux_io_calls_t::process_requests() {
    if (n_pending > TARGET_IO_QUEUE_DEPTH)
        return;

    int res = 0;
    while(!io_requests.queue.empty()) {
        res = io_requests.process_request_batch();
        if(res < 0)
            break;
    }
    guarantee_xerr(res >= 0 || res == -EAGAIN, -res, "Could not submit IO request");
}

linux_io_calls_t::queue_t::queue_t(linux_io_calls_t *parent)
    : parent(parent)
{
    queue.reserve(MAX_CONCURRENT_IO_REQUESTS);
}

int linux_io_calls_t::queue_t::process_request_batch() {
    // Submit a batch
    int res = 0;
    if(queue.size() > 0) {
        res = io_submit(parent->aio_context,
                        std::min(queue.size(), size_t(TARGET_IO_QUEUE_DEPTH / 2)),
                        &queue[0]);
        if (res > 0) {
            // TODO: erase will cause the vector to shift elements in
            // the back. Perhaps we should optimize this somehow.
            queue.erase(queue.begin(), queue.begin() + res);
            parent->n_pending += res;
        } else if (res < 0 && (-res) != EAGAIN) {
            logERR("io_submit failed: (%d) %s\n", -res, strerror(-res));
        }
    }
    return res;
}

linux_io_calls_t::queue_t::~queue_t() {
    assert(queue.size() == 0);
}
<commit_msg>Added EINTR looping around linux_direct_file_t::read_blocking and write_blocking.<commit_after>#include <algorithm>
#include <fcntl.h>
#include <linux/fs.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "arch/linux/arch.hpp"
#include "config/args.hpp"
#include "utils2.hpp"
#include "logger.hpp"

// #define DEBUG_DUMP_WRITES 1

/* Disk file object */

perfmon_counter_t
    pm_io_reads_started("io_reads_started[ioreads]"),
    pm_io_reads_completed("io_reads_completed[ioreads]"),
    pm_io_writes_started("io_writes_started[iowrites]"),
    pm_io_writes_completed("io_writes_completed[iowrites]");

linux_direct_file_t::linux_direct_file_t(const char *path, int mode)
    : fd(INVALID_FD)
{
    int res;

    // Determine if it is a block device

    struct stat64 file_stat;
    bzero((void*)&file_stat, sizeof(file_stat)); // make valgrind happy
    res = stat64(path, &file_stat);
    guarantee_err(res == 0 || errno == ENOENT, "Could not stat file '%s'", path);

    if (res == -1 && errno == ENOENT) {
        if (!(mode & mode_create)) {
            file_exists = false;
            return;
        }
        is_block = false;
    } else {
        is_block = S_ISBLK(file_stat.st_mode);
    }

    // Construct file flags

    int flags = O_CREAT | O_DIRECT | O_LARGEFILE;

    if ((mode & mode_write) && (mode & mode_read)) flags |= O_RDWR;
    else if (mode & mode_write) flags |= O_WRONLY;
    else if (mode & mode_read) flags |= O_RDONLY;
    else crash("Bad file access mode.");

    // O_NOATIME requires owner or root privileges. This is a bit of a hack; we assume that
    // if we are opening a regular file, we are the owner, but if we are opening a block device,
    // we are not.
    if (!is_block) flags |= O_NOATIME;

    // Open the file

    fd = open(path, flags, 0644);
    if (fd == INVALID_FD)
        fail_due_to_user_error("Inaccessible database file: \"%s\": %s", path, strerror(errno));

    file_exists = true;

    // Determine the file size

    if (is_block) {
        res = ioctl(fd, BLKGETSIZE64, &file_size);
        guarantee_err(res != -1, "Could not determine block device size");
    } else {
        off64_t size = lseek64(fd, 0, SEEK_END);
        guarantee_err(size != -1, "Could not determine file size");
        res = lseek64(fd, 0, SEEK_SET);
        guarantee_err(res != -1, "Could not reset file position");

        file_size = size;
    }
}

bool linux_direct_file_t::exists() {
    return file_exists;
}

bool linux_direct_file_t::is_block_device() {
    return is_block;
}

size_t linux_direct_file_t::get_size() {
    return file_size;
}

void linux_direct_file_t::set_size(size_t size) {
    assert(!is_block);
    int res = ftruncate(fd, size);
    guarantee_err(res == 0, "Could not ftruncate()");
    file_size = size;
}

void linux_direct_file_t::set_size_at_least(size_t size) {
    if (is_block) {
        assert(file_size >= size);
    } else {
        /* Grow in large chunks at a time */
        if (file_size < size) {
            // TODO: we should make the growth rate of a db file
            // configurable.
            set_size(ceil_aligned(size, DEVICE_BLOCK_SIZE * 128));
        }
    }
}

bool linux_direct_file_t::read_async(size_t offset, size_t length, void *buf, linux_iocallback_t *callback) {
    verify(offset, length, buf);

    linux_io_calls_t *iosys = &linux_thread_pool_t::thread->iosys;

    // Prepare the request
    iocb *request = new iocb;
    io_prep_pread(request, fd, buf, length, offset);
    io_set_eventfd(request, iosys->aio_notify_fd);
    request->data = callback;

    // Add it to a list of outstanding read requests
    iosys->io_requests.queue.push_back(request);

    pm_io_reads_started++;

    // Process whatever is left
    iosys->process_requests();

    return false;
}

bool linux_direct_file_t::write_async(size_t offset, size_t length, void *buf, linux_iocallback_t *callback) {
#ifdef DEBUG_DUMP_WRITES
    printf("--- WRITE BEGIN ---\n");
    print_backtrace(stdout);
    printf("\n");
    print_hd(buf, offset, length);
    printf("---- WRITE END ----\n\n");
#endif

    verify(offset, length, buf);

    linux_io_calls_t *iosys = &linux_thread_pool_t::thread->iosys;

    // Prepare the request
    iocb *request = new iocb;
    io_prep_pwrite(request, fd, buf, length, offset);
    io_set_eventfd(request, iosys->aio_notify_fd);
    request->data = callback;

    // Add it to a list of outstanding write requests
    iosys->io_requests.queue.push_back(request);

    pm_io_writes_started++;

    // Process whatever is left
    iosys->process_requests();

    return false;
}

void linux_direct_file_t::read_blocking(size_t offset, size_t length, void *buf) {
    verify(offset, length, buf);
 tryagain:
    ssize_t res = pread(fd, buf, length, offset);
    if (res == -1 && errno == EINTR) {
        goto tryagain;
    }
    assert(res == length, "Blocking read failed");
    (void)res;
}

void linux_direct_file_t::write_blocking(size_t offset, size_t length, void *buf) {
    verify(offset, length, buf);
 tryagain:
    ssize_t res = pwrite(fd, buf, length, offset);
    if (res == -1 && errno == EINTR) {
        goto tryagain;
    }
    assert(res == length, "Blocking write failed");
    (void)res;
}

linux_direct_file_t::~linux_direct_file_t() {
    if (fd != INVALID_FD) close(fd);
}

void linux_direct_file_t::verify(size_t offset, size_t length, void *buf) {
    assert(buf);
    assert(offset + length <= file_size);
    assert((intptr_t)buf % DEVICE_BLOCK_SIZE == 0);
    assert(offset % DEVICE_BLOCK_SIZE == 0);
    assert(length % DEVICE_BLOCK_SIZE == 0);
}

/* Async IO scheduler */

/* TODO: Batch requests internally so that we can send multiple requests per
call to io_submit(). */

linux_io_calls_t::linux_io_calls_t(linux_event_queue_t *queue)
    : queue(queue), n_pending(0), io_requests(this)
{
    int res;

    // Create aio context

    aio_context = 0;
    res = io_setup(MAX_CONCURRENT_IO_REQUESTS, &aio_context);
    guarantee_xerr(res == 0, -res, "Could not setup aio context");

    // Create aio notify fd

    aio_notify_fd = eventfd(0, 0);
    guarantee_err(aio_notify_fd != -1, "Could not create aio notification fd");

    res = fcntl(aio_notify_fd, F_SETFL, O_NONBLOCK);
    guarantee_err(res == 0, "Could not make aio notify fd non-blocking");

    queue->watch_resource(aio_notify_fd, poll_event_in, this);
}

linux_io_calls_t::~linux_io_calls_t()
{
    int res;

    assert(n_pending == 0);

    res = close(aio_notify_fd);
    guarantee_err(res == 0, "Could not close aio_notify_fd");

    res = io_destroy(aio_context);
    guarantee_xerr(res == 0, -res, "Could not destroy aio context");
}

void linux_io_calls_t::on_event(int event_mask) {

    int res, nevents;
    eventfd_t nevents_total;

    if (event_mask != poll_event_in) {
        logERR("Unexpected event mask: %d\n", event_mask);
    }

    res = eventfd_read(aio_notify_fd, &nevents_total);
    guarantee_err(res == 0, "Could not read aio_notify_fd value");

    // Note: O(1) array allocators are hard. To avoid all the
    // complexity, we'll use a fixed sized array and call io_getevents
    // multiple times if we have to (which should be very unlikely,
    // anyway).
    io_event events[MAX_IO_EVENT_PROCESSING_BATCH_SIZE];

    do {
        // Grab the events. Note: we need to make sure we don't read
        // more than nevents_total, otherwise we risk reading an io
        // event and getting an eventfd for this read event later due
        // to the way the kernel is structured. Better avoid this
        // complexity (hence std::min below).
        nevents = io_getevents(aio_context, 0,
                               std::min((int)nevents_total, MAX_IO_EVENT_PROCESSING_BATCH_SIZE),
                               events, NULL);
        guarantee_xerr(nevents >= 1, -nevents, "Waiting for AIO event failed");

        // Process the events
        for(int i = 0; i < nevents; i++) {
            aio_notify((iocb*)events[i].obj, events[i].res);
        }
        nevents_total -= nevents;

    } while (nevents_total > 0);
}

void linux_io_calls_t::aio_notify(iocb *event, int result) {
    // Update stats
    if (event->aio_lio_opcode == IO_CMD_PREAD) pm_io_reads_completed++;
    else pm_io_writes_completed++;

    // Schedule the requests we couldn't finish last time
    n_pending--;
    process_requests();

    // Check for failure (because the higher-level code usually doesn't)
    if (result != (int)event->u.c.nbytes) {
        // Currently AIO is used only for disk files, not sockets.
        // Thus, if something fails, we have a good reason to crash
        // (note that that is not true for sockets: we should just
        // close the socket and cleanup then).
        guarantee_xerr(false, -result, "Read or write failed");
    }

    // Notify the interested party about the event
    linux_iocallback_t *callback = (linux_iocallback_t*)event->data;

    // Prepare event_t for the callback
    event_t qevent;
    bzero((void*)&qevent, sizeof(qevent));
    qevent.state = NULL;
    qevent.result = result;
    qevent.buf = event->u.c.buf;
    qevent.offset = event->u.c.offset;
    qevent.op = event->aio_lio_opcode == IO_CMD_PREAD ? eo_read : eo_write;

    callback->on_io_complete(&qevent);

    // Free the iocb structure
    delete event;
}

void linux_io_calls_t::process_requests() {
    if (n_pending > TARGET_IO_QUEUE_DEPTH)
        return;

    int res = 0;
    while(!io_requests.queue.empty()) {
        res = io_requests.process_request_batch();
        if(res < 0)
            break;
    }
    guarantee_xerr(res >= 0 || res == -EAGAIN, -res, "Could not submit IO request");
}

linux_io_calls_t::queue_t::queue_t(linux_io_calls_t *parent)
    : parent(parent)
{
    queue.reserve(MAX_CONCURRENT_IO_REQUESTS);
}

int linux_io_calls_t::queue_t::process_request_batch() {
    // Submit a batch
    int res = 0;
    if(queue.size() > 0) {
        res = io_submit(parent->aio_context,
                        std::min(queue.size(), size_t(TARGET_IO_QUEUE_DEPTH / 2)),
                        &queue[0]);
        if (res > 0) {
            // TODO: erase will cause the vector to shift elements in
            // the back. Perhaps we should optimize this somehow.
            queue.erase(queue.begin(), queue.begin() + res);
            parent->n_pending += res;
        } else if (res < 0 && (-res) != EAGAIN) {
            logERR("io_submit failed: (%d) %s\n", -res, strerror(-res));
        }
    }
    return res;
}

linux_io_calls_t::queue_t::~queue_t() {
    assert(queue.size() == 0);
}
<|endoftext|>


<commit_before>#include "serverConnection.h"
#include <vector>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#include <libwatcher/message.h>
#include <libwatcher/messageStatus.h>

#include "messageFactory.h"
#include "dataMarshaller.h"
#include "watcherd.h"
#include "writeDBMessageHandler.h"
#include "watcherdConfig.h"

using namespace std;
using namespace boost::asio;

namespace {
    using namespace watcher::event;

    /* Grr, because there is no std::copy_if have to use the negation with
     * remove_copy_if() */
    bool not_feeder_message(const MessagePtr& m)
    {
        return !isFeederEvent(m->type);
    }
}

namespace watcher {
    using namespace event;

    INIT_LOGGER(ServerConnection, "Connection.ServerConnection");

    ServerConnection::ServerConnection(Watcherd& w, boost::asio::io_service& io_service) :
        Connection(io_service),
        watcher(w),
        strand_(io_service),
        write_strand_(io_service)
    {
        TRACE_ENTER();
        TRACE_EXIT();
    }

    ServerConnection::~ServerConnection()
    {
        TRACE_ENTER();
        //shared_from_this() not allowed in destructor
        //watcher.unsubscribe(shared_from_this());
        TRACE_EXIT();
    }

    /** Initialization point for start of new ServerConnection thread. */
    void ServerConnection::run()
    {
        /*
         * Pull endpoint info out of the socket and make it available via the
         * Connection::getPeerAddr() member function.
         */
        boost::asio::ip::tcp::endpoint ep = getSocket().remote_endpoint();
        endpoint_addr_ = ep.address().to_string();
        endpoint_port_ = ep.port();

        start();
    }

    void ServerConnection::start()
    {
        TRACE_ENTER();
        boost::asio::async_read(
                theSocket,
                boost::asio::buffer(incomingBuffer, DataMarshaller::header_length),
                strand_.wrap(
                    boost::bind(
                        &ServerConnection::handle_read_header,
                        shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred)));
        TRACE_EXIT();
    }

    void ServerConnection::handle_read_header(const boost::system::error_code& e, size_t bytes_transferred)
    {
        TRACE_ENTER();
        if (!e)
        {
            LOG_DEBUG("Read " << bytes_transferred << " bytes.");

            size_t payloadSize;
            unsigned short numOfMessages;
            if (!DataMarshaller::unmarshalHeader(incomingBuffer.begin(), bytes_transferred, payloadSize, numOfMessages))
            {
                LOG_ERROR("Error parsing incoming message header.");

                if (conn_type == gui)
                    watcher.unsubscribe(shared_from_this());
            }
            else
            {
                LOG_DEBUG("Reading packet payload of " << payloadSize << " bytes.");

                boost::asio::async_read(
                        theSocket,
                        boost::asio::buffer(
                            incomingBuffer,
                            payloadSize),  // Should incoming buffer be new'd()?
                        strand_.wrap(
                            boost::bind(
                                &ServerConnection::handle_read_payload,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred,
                                numOfMessages)));
            }
        }
        else
        {
            if (e==boost::asio::error::eof)
            {
                LOG_DEBUG("Received empty message from clienti or client closed connection.");
                LOG_INFO("Connection to client closed.");

            }
            else
            {
                LOG_ERROR("Error reading socket: " << e.message());
            }

            // unsubscribe to event stream, otherwise it will hold a
            // shared_ptr open
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }
        TRACE_EXIT();
    }

    void ServerConnection::handle_read_payload(const boost::system::error_code& e, size_t bytes_transferred, unsigned short numOfMessages)
    {
        TRACE_ENTER();

        if (!e)
        {
            vector<MessagePtr> arrivedMessages;
            if (DataMarshaller::unmarshalPayload(arrivedMessages, numOfMessages, incomingBuffer.begin(), bytes_transferred))
            {
                LOG_INFO("Recvd " << arrivedMessages.size() << " message" <<
                         (arrivedMessages.size()>1?"s":"") << " from " <<
                         getPeerAddr());

                /*
                 * If this connection type is unknown or gui, then traverse the
                 * list of arrived messages.  For GUI clients, look for the
                 * STOP_MESSAGE to unsubscribe from the event stream.
                 *
                 * For unknown clients, infer the type from the message
                 * received:
                 * START_MESSAGE => gui
                 * isFeederMessage => feeder
                 */
                if (conn_type == unknown || conn_type == gui) {
                    BOOST_FOREACH(MessagePtr i, arrivedMessages) {
                        if (conn_type == gui) {
                           if (i->type == STOP_MESSAGE_TYPE)
                            watcher.unsubscribe(shared_from_this());
                        } else if (conn_type == unknown) {
                            if (i->type == START_MESSAGE_TYPE) {
                                /* Client is requesting the live stream of events. */
                                watcher.subscribe(shared_from_this());
                                conn_type = gui;
                            } else if (isFeederEvent(i->type)) {
                                conn_type = feeder;

                                /*
                                 * This connection is a watcher test daemon.  Add a message handler to write
                                 * its event stream to the database.
                                 */
                                std::string path;
                                if (watcher.config().lookupValue(dbPath, path))
                                    addMessageHandler(MessageHandlerPtr(new WriteDBMessageHandler(path)));
                                else
                                    LOG_ERROR("unable to lookup \"" << dbPath << " in server configuration");
                            }
                        }
                    }
                }

                /* Flag indicating whether to continue reading from this
                 * connection. */
                bool fail = false;

                BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers) {
                    if (mh->handleMessagesArrive(shared_from_this(), arrivedMessages)) {
                        fail = true;
                        LOG_DEBUG("Message handler told us to close this connection.");
                    }
                }

                if (!fail) {
                    // initiate request to read next message
                    LOG_DEBUG("Waiting for next message.");
                    start();
                }

                if (conn_type == feeder) {
                    /* relay feeder message to any client requesting the live stream.
                     * Warning: currently there is no check to make sure that a client doesn't
                     * receive a message it just sent.  This should be OK since we are just
                     * relaying feeder messages only, and the GUIs should not be sending
                     * them. */
                    vector<MessagePtr> feeder;
                    remove_copy_if(arrivedMessages.begin(), arrivedMessages.end(), back_inserter(feeder), not_feeder_message);
                    if (! feeder.empty()) {
                        LOG_DEBUG("Sending " << feeder.size() << " feeder messages to clients.");
                        watcher.sendMessage(feeder);
                    }
                }
            }
        }
        else
        {
            LOG_WARN("Did not understand incoming message.");

            // unsubscribe to event stream, otherwise it will hold a
            // shared_ptr open
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // If an error occurs then no new asynchronous operations are started. This
        // means that all shared_ptr references to the ServerConnection object will
        // disappear and the object will be destroyed automatically after this
        // handler returns. The ServerConnection class's destructor closes the socket.

        TRACE_EXIT();
    }

    void ServerConnection::handle_write(const boost::system::error_code& e, MessagePtr message)
    {
        TRACE_ENTER();

        if (!e)
        {
            LOG_DEBUG("Successfully sent message to client: " << message);

            BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers)
            {
#if 0
                /* melkins
                 * The reads and writes to the socket are asynchronous, so
                 * we should never be waiting for something to be read as
                 * a result of a write.
                 */
                if(waitForResponse) // someone already said they wanted a response, so ignore ret val for others
                    mh->handleMessageSent(message);
                else
                    waitForResponse=mh->handleMessageSent(message);
#endif
                    mh->handleMessageSent(message);
            }

            // melkins
            // start() calls async_read(), which is not what we want to do here
            /*
            if(waitForResponse)
                start();
                */
        }
        else
        {
            LOG_WARN("Error while sending response to client: " << e);
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // No new asynchronous operations are started. This means that all shared_ptr
        // references to the connection object will disappear and the object will be
        // destroyed automatically after this handler returns. The connection class's
        // destructor closes the socket.

        /* NOTE: The refcount will not go to zero so long as there is an
         * async_read operation also oustanding. */

        TRACE_EXIT();
    }

    /** Send a single message to this connected client. */
    void ServerConnection::sendMessage(MessagePtr msg)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msg, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?  asio::tcp::socket() is listed at not
        // shared thread safe
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msg)));
        TRACE_EXIT();
    }

    /** Send a set of messages to this connected client. */
    void ServerConnection::sendMessage(const std::vector<MessagePtr>& msgs)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msgs, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msgs.front())));
        TRACE_EXIT();
    }

}
<commit_msg>initialiaze conn_type in ctor<commit_after>#include "serverConnection.h"
#include <vector>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#include <libwatcher/message.h>
#include <libwatcher/messageStatus.h>

#include "messageFactory.h"
#include "dataMarshaller.h"
#include "watcherd.h"
#include "writeDBMessageHandler.h"
#include "watcherdConfig.h"

using namespace std;
using namespace boost::asio;

namespace {
    using namespace watcher::event;

    /* Grr, because there is no std::copy_if have to use the negation with
     * remove_copy_if() */
    bool not_feeder_message(const MessagePtr& m)
    {
        return !isFeederEvent(m->type);
    }
}

namespace watcher {
    using namespace event;

    INIT_LOGGER(ServerConnection, "Connection.ServerConnection");

    ServerConnection::ServerConnection(Watcherd& w, boost::asio::io_service& io_service) :
        Connection(io_service),
        watcher(w),
        strand_(io_service),
        write_strand_(io_service),
        conn_type(unknown)
    {
        TRACE_ENTER();
        TRACE_EXIT();
    }

    ServerConnection::~ServerConnection()
    {
        TRACE_ENTER();
        //shared_from_this() not allowed in destructor
        //watcher.unsubscribe(shared_from_this());
        TRACE_EXIT();
    }

    /** Initialization point for start of new ServerConnection thread. */
    void ServerConnection::run()
    {
        /*
         * Pull endpoint info out of the socket and make it available via the
         * Connection::getPeerAddr() member function.
         */
        boost::asio::ip::tcp::endpoint ep = getSocket().remote_endpoint();
        endpoint_addr_ = ep.address().to_string();
        endpoint_port_ = ep.port();

        start();
    }

    void ServerConnection::start()
    {
        TRACE_ENTER();
        boost::asio::async_read(
                theSocket,
                boost::asio::buffer(incomingBuffer, DataMarshaller::header_length),
                strand_.wrap(
                    boost::bind(
                        &ServerConnection::handle_read_header,
                        shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred)));
        TRACE_EXIT();
    }

    void ServerConnection::handle_read_header(const boost::system::error_code& e, size_t bytes_transferred)
    {
        TRACE_ENTER();
        if (!e)
        {
            LOG_DEBUG("Read " << bytes_transferred << " bytes.");

            size_t payloadSize;
            unsigned short numOfMessages;
            if (!DataMarshaller::unmarshalHeader(incomingBuffer.begin(), bytes_transferred, payloadSize, numOfMessages))
            {
                LOG_ERROR("Error parsing incoming message header.");

                if (conn_type == gui)
                    watcher.unsubscribe(shared_from_this());
            }
            else
            {
                LOG_DEBUG("Reading packet payload of " << payloadSize << " bytes.");

                boost::asio::async_read(
                        theSocket,
                        boost::asio::buffer(
                            incomingBuffer,
                            payloadSize),  // Should incoming buffer be new'd()?
                        strand_.wrap(
                            boost::bind(
                                &ServerConnection::handle_read_payload,
                                shared_from_this(),
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred,
                                numOfMessages)));
            }
        }
        else
        {
            if (e==boost::asio::error::eof)
            {
                LOG_DEBUG("Received empty message from clienti or client closed connection.");
                LOG_INFO("Connection to client closed.");

            }
            else
            {
                LOG_ERROR("Error reading socket: " << e.message());
            }

            // unsubscribe to event stream, otherwise it will hold a
            // shared_ptr open
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }
        TRACE_EXIT();
    }

    void ServerConnection::handle_read_payload(const boost::system::error_code& e, size_t bytes_transferred, unsigned short numOfMessages)
    {
        TRACE_ENTER();

        if (!e)
        {
            vector<MessagePtr> arrivedMessages;
            if (DataMarshaller::unmarshalPayload(arrivedMessages, numOfMessages, incomingBuffer.begin(), bytes_transferred))
            {
                LOG_INFO("Recvd " << arrivedMessages.size() << " message" <<
                         (arrivedMessages.size()>1?"s":"") << " from " <<
                         getPeerAddr());

                /*
                 * If this connection type is unknown or gui, then traverse the
                 * list of arrived messages.  For GUI clients, look for the
                 * STOP_MESSAGE to unsubscribe from the event stream.
                 *
                 * For unknown clients, infer the type from the message
                 * received:
                 * START_MESSAGE => gui
                 * isFeederMessage => feeder
                 */
                if (conn_type == unknown || conn_type == gui) {
                    BOOST_FOREACH(MessagePtr i, arrivedMessages) {
                        if (conn_type == gui) {
                           if (i->type == STOP_MESSAGE_TYPE)
                            watcher.unsubscribe(shared_from_this());
                        } else if (conn_type == unknown) {
                            if (i->type == START_MESSAGE_TYPE) {
                                /* Client is requesting the live stream of events. */
                                watcher.subscribe(shared_from_this());
                                conn_type = gui;
                            } else if (isFeederEvent(i->type)) {
                                conn_type = feeder;

                                /*
                                 * This connection is a watcher test daemon.  Add a message handler to write
                                 * its event stream to the database.
                                 */
                                std::string path;
                                if (watcher.config().lookupValue(dbPath, path))
                                    addMessageHandler(MessageHandlerPtr(new WriteDBMessageHandler(path)));
                                else
                                    LOG_ERROR("unable to lookup \"" << dbPath << " in server configuration");
                            }
                        }
                    }
                }

                /* Flag indicating whether to continue reading from this
                 * connection. */
                bool fail = false;

                BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers) {
                    if (mh->handleMessagesArrive(shared_from_this(), arrivedMessages)) {
                        fail = true;
                        LOG_DEBUG("Message handler told us to close this connection.");
                    }
                }

                if (!fail) {
                    // initiate request to read next message
                    LOG_DEBUG("Waiting for next message.");
                    start();
                }

                if (conn_type == feeder) {
                    /* relay feeder message to any client requesting the live stream.
                     * Warning: currently there is no check to make sure that a client doesn't
                     * receive a message it just sent.  This should be OK since we are just
                     * relaying feeder messages only, and the GUIs should not be sending
                     * them. */
                    vector<MessagePtr> feeder;
                    remove_copy_if(arrivedMessages.begin(), arrivedMessages.end(), back_inserter(feeder), not_feeder_message);
                    if (! feeder.empty()) {
                        LOG_DEBUG("Sending " << feeder.size() << " feeder messages to clients.");
                        watcher.sendMessage(feeder);
                    }
                }
            }
        }
        else
        {
            LOG_WARN("Did not understand incoming message.");

            // unsubscribe to event stream, otherwise it will hold a
            // shared_ptr open
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // If an error occurs then no new asynchronous operations are started. This
        // means that all shared_ptr references to the ServerConnection object will
        // disappear and the object will be destroyed automatically after this
        // handler returns. The ServerConnection class's destructor closes the socket.

        TRACE_EXIT();
    }

    void ServerConnection::handle_write(const boost::system::error_code& e, MessagePtr message)
    {
        TRACE_ENTER();

        if (!e)
        {
            LOG_DEBUG("Successfully sent message to client: " << message);

            BOOST_FOREACH(MessageHandlerPtr mh, messageHandlers)
            {
#if 0
                /* melkins
                 * The reads and writes to the socket are asynchronous, so
                 * we should never be waiting for something to be read as
                 * a result of a write.
                 */
                if(waitForResponse) // someone already said they wanted a response, so ignore ret val for others
                    mh->handleMessageSent(message);
                else
                    waitForResponse=mh->handleMessageSent(message);
#endif
                    mh->handleMessageSent(message);
            }

            // melkins
            // start() calls async_read(), which is not what we want to do here
            /*
            if(waitForResponse)
                start();
                */
        }
        else
        {
            LOG_WARN("Error while sending response to client: " << e);
            if (conn_type == gui)
                watcher.unsubscribe(shared_from_this());
        }

        // No new asynchronous operations are started. This means that all shared_ptr
        // references to the connection object will disappear and the object will be
        // destroyed automatically after this handler returns. The connection class's
        // destructor closes the socket.

        /* NOTE: The refcount will not go to zero so long as there is an
         * async_read operation also oustanding. */

        TRACE_EXIT();
    }

    /** Send a single message to this connected client. */
    void ServerConnection::sendMessage(MessagePtr msg)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msg, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?  asio::tcp::socket() is listed at not
        // shared thread safe
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msg)));
        TRACE_EXIT();
    }

    /** Send a set of messages to this connected client. */
    void ServerConnection::sendMessage(const std::vector<MessagePtr>& msgs)
    {
        TRACE_ENTER();
        DataMarshaller::NetworkMarshalBuffers outBuffers;
        DataMarshaller::marshalPayload(msgs, outBuffers);

        /// FIXME melkins 2004-04-19
        // is it safe to call async_write and async_read from different
        // threads at the same time?
        async_write(theSocket,
                    outBuffers,
                    write_strand_.wrap( boost::bind( &ServerConnection::handle_write,
                                               shared_from_this(),
                                               placeholders::error,
                                               msgs.front())));
        TRACE_EXIT();
    }

}
<|endoftext|>