<commit_before>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Thomas Grass
 *          Andreas Hansson
 *          Sascha Bischoff
 */

#include <sstream>

#include "base/random.hh"
#include "cpu/testers/traffic_gen/traffic_gen.hh"
#include "debug/Checkpoint.hh"
#include "debug/TrafficGen.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

using namespace std;

TrafficGen::TrafficGen(const TrafficGenParams* p)
    : MemObject(p),
      system(p->system),
      masterID(system->getMasterId(name())),
      configFile(p->config_file),
      elasticReq(p->elastic_req),
      nextTransitionTick(0),
      nextPacketTick(0),
      port(name() + ".port", *this),
      retryPkt(NULL),
      retryPktTick(0),
      updateEvent(this),
      drainManager(NULL)
{
}

TrafficGen*
TrafficGenParams::create()
{
    return new TrafficGen(this);
}

BaseMasterPort&
TrafficGen::getMasterPort(const string& if_name, PortID idx)
{
    if (if_name == "port") {
        return port;
    } else {
        return MemObject::getMasterPort(if_name, idx);
    }
}

void
TrafficGen::init()
{
    if (!port.isConnected())
        fatal("The port of %s is not connected!\n", name());

    // if the system is in timing mode active the request generator
    if (system->isTimingMode()) {
        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");

        parseConfig();

        // enter initial state
        enterState(currState);
    } else {
        DPRINTF(TrafficGen,
                "Traffic generator is only active in timing mode\n");
    }
}

void
TrafficGen::initState()
{
    // when not restoring from a checkpoint, make sure we kick things off
    if (system->isTimingMode()) {
        // call nextPacketTick on the state to advance it
        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
    } else {
        DPRINTF(TrafficGen,
                "Traffic generator is only active in timing mode\n");
    }
}

unsigned int
TrafficGen::drain(DrainManager *dm)
{
    if (retryPkt == NULL) {
        // shut things down
        nextPacketTick = MaxTick;
        nextTransitionTick = MaxTick;
        deschedule(updateEvent);
        return 0;
    } else {
        drainManager = dm;
        return 1;
    }
}

void
TrafficGen::serialize(ostream &os)
{
    DPRINTF(Checkpoint, "Serializing TrafficGen\n");

    // save ticks of the graph event if it is scheduled
    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;

    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);

    SERIALIZE_SCALAR(nextEvent);

    SERIALIZE_SCALAR(nextTransitionTick);

    SERIALIZE_SCALAR(nextPacketTick);

    SERIALIZE_SCALAR(currState);
}

void
TrafficGen::unserialize(Checkpoint* cp, const string& section)
{
    // restore scheduled events
    Tick nextEvent;
    UNSERIALIZE_SCALAR(nextEvent);
    if (nextEvent != 0) {
        schedule(updateEvent, nextEvent);
    }

    UNSERIALIZE_SCALAR(nextTransitionTick);

    UNSERIALIZE_SCALAR(nextPacketTick);

    // @todo In the case of a stateful generator state such as the
    // trace player we would also have to restore the position in the
    // trace playback and the tick offset
    UNSERIALIZE_SCALAR(currState);
}

void
TrafficGen::update()
{
    // if we have reached the time for the next state transition, then
    // perform the transition
    if (curTick() >= nextTransitionTick) {
        transition();
    } else {
        assert(curTick() >= nextPacketTick);
        // get the next packet and try to send it
        PacketPtr pkt = states[currState]->getNextPacket();
        numPackets++;
        if (!port.sendTimingReq(pkt)) {
            retryPkt = pkt;
            retryPktTick = curTick();
        }
    }

    // if we are waiting for a retry, do not schedule any further
    // events, in the case of a transition or a successful send, go
    // ahead and determine when the next update should take place
    if (retryPkt == NULL) {
        // schedule next update event based on either the next execute
        // tick or the next transition, which ever comes first
        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
        schedule(updateEvent, nextEventTick);
    }
}

void
TrafficGen::parseConfig()
{
    // keep track of the transitions parsed to create the matrix when
    // done
    vector<Transition> transitions;

    // open input file
    ifstream infile;
    infile.open(configFile.c_str(), ifstream::in);
    if (!infile.is_open()) {
        fatal("Traffic generator %s config file not found at %s\n",
              name(), configFile);
    }

    // read line by line and determine the action based on the first
    // keyword
    string keyword;
    string line;

    while (getline(infile, line).good()) {
        // see if this line is a comment line, and if so skip it
        if (line.find('#') != 1) {
            // create an input stream for the tokenization
            istringstream is(line);

            // determine the keyword
            is >> keyword;

            if (keyword == "STATE") {
                // parse the behaviour of this state
                uint32_t id;
                Tick duration;
                string mode;

                is >> id >> duration >> mode;

                if (mode == "TRACE") {
                    string traceFile;
                    Addr addrOffset;

                    is >> traceFile >> addrOffset;

                    states[id] = new TraceGen(name(), masterID, duration,
                                              traceFile, addrOffset);
                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
                } else if (mode == "IDLE") {
                    states[id] = new IdleGen(name(), masterID, duration);
                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
                } else if (mode == "LINEAR" || mode == "RANDOM") {
                    uint32_t read_percent;
                    Addr start_addr;
                    Addr end_addr;
                    Addr blocksize;
                    Tick min_period;
                    Tick max_period;
                    Addr data_limit;

                    is >> read_percent >> start_addr >> end_addr >>
                        blocksize >> min_period >> max_period >> data_limit;

                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
                            " period %d to %d, %d%% reads\n",
                            mode, start_addr, end_addr, blocksize, min_period,
                            max_period, read_percent);

                    if (port.deviceBlockSize() &&
                        blocksize > port.deviceBlockSize())
                        fatal("TrafficGen %s block size (%d) is larger than "
                              "port block size (%d)\n", name(),
                              blocksize, port.deviceBlockSize());

                    if (read_percent > 100)
                        fatal("%s cannot have more than 100% reads", name());

                    if (mode == "LINEAR") {
                        states[id] = new LinearGen(name(), masterID,
                                                   duration, start_addr,
                                                   end_addr, blocksize,
                                                   min_period, max_period,
                                                   read_percent, data_limit);
                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
                    } else if (mode == "RANDOM") {
                        states[id] = new RandomGen(name(), masterID,
                                                   duration, start_addr,
                                                   end_addr, blocksize,
                                                   min_period, max_period,
                                                   read_percent, data_limit);
                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
                    }
                } else {
                    fatal("%s: Unknown traffic generator mode: %s",
                          name(), mode);
                }
            } else if (keyword == "TRANSITION") {
                Transition transition;

                is >> transition.from >> transition.to >> transition.p;

                transitions.push_back(transition);

                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
                        transition.to);
            } else if (keyword == "INIT") {
                // set the initial state as the active state
                is >> currState;

                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
            }
        }
    }

    // resize and populate state transition matrix
    transitionMatrix.resize(states.size());
    for (size_t i = 0; i < states.size(); i++) {
        transitionMatrix[i].resize(states.size());
    }

    for (vector<Transition>::iterator t = transitions.begin();
         t != transitions.end(); ++t) {
        transitionMatrix[t->from][t->to] = t->p;
    }

    // ensure the egress edges do not have a probability larger than
    // one
    for (size_t i = 0; i < states.size(); i++) {
        double sum = 0;
        for (size_t j = 0; j < states.size(); j++) {
            sum += transitionMatrix[i][j];
        }

        // avoid comparing floating point numbers
        if (abs(sum - 1.0) > 0.001)
            fatal("%s has transition probability != 1 for state %d\n",
                  name(), i);
    }

    // close input file
    infile.close();
}

void
TrafficGen::transition()
{
    // exit the current state
    states[currState]->exit();

    // determine next state
    double p = random_mt.gen_real1();
    assert(currState < transitionMatrix.size());
    double cumulative = 0.0;
    size_t i = 0;
    do {
        cumulative += transitionMatrix[currState][i];
        ++i;
    } while (cumulative < p && i < transitionMatrix[currState].size());

    enterState(i - 1);
}

void
TrafficGen::enterState(uint32_t newState)
{
    DPRINTF(TrafficGen, "Transition to state %d\n", newState);

    currState = newState;
    // we could have been delayed and not transitioned on the exact
    // tick when we were supposed to (due to back pressure when
    // sending a packet)
    nextTransitionTick = curTick() + states[currState]->duration;
    states[currState]->enter();
}

void
TrafficGen::recvRetry()
{
    assert(retryPkt != NULL);

    DPRINTF(TrafficGen, "Received retry\n");
    numRetries++;
    // attempt to send the packet, and if we are successful start up
    // the machinery again
    if (port.sendTimingReq(retryPkt)) {
        retryPkt = NULL;
        // remember how much delay was incurred due to back-pressure
        // when sending the request, we also use this to derive
        // the tick for the next packet
        Tick delay = curTick() - retryPktTick;
        retryPktTick = 0;
        retryTicks += delay;

        if (drainManager == NULL) {
            // packet is sent, so find out when the next one is due
            nextPacketTick = states[currState]->nextPacketTick(elasticReq,
                                                               delay);
            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
            schedule(updateEvent, std::max(curTick(), nextEventTick));
        } else {
            // shut things down
            nextPacketTick = MaxTick;
            nextTransitionTick = MaxTick;
            drainManager->signalDrainDone();
            // Clear the drain event once we're done with it.
            drainManager = NULL;
        }
    }
}

void
TrafficGen::regStats()
{
    // Initialise all the stats
    using namespace Stats;

    numPackets
        .name(name() + ".numPackets")
        .desc("Number of packets generated");

    numRetries
        .name(name() + ".numRetries")
        .desc("Number of retries");

    retryTicks
        .name(name() + ".retryTicks")
        .desc("Time spent waiting due to back-pressure (ticks)");
}

bool
TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
{
    delete pkt->req;
    delete pkt;

    return true;
}
<commit_msg>cpu: Check that minimum TrafficGen period is less than max period<commit_after>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Thomas Grass
 *          Andreas Hansson
 *          Sascha Bischoff
 */

#include <sstream>

#include "base/random.hh"
#include "cpu/testers/traffic_gen/traffic_gen.hh"
#include "debug/Checkpoint.hh"
#include "debug/TrafficGen.hh"
#include "sim/stats.hh"
#include "sim/system.hh"

using namespace std;

TrafficGen::TrafficGen(const TrafficGenParams* p)
    : MemObject(p),
      system(p->system),
      masterID(system->getMasterId(name())),
      configFile(p->config_file),
      elasticReq(p->elastic_req),
      nextTransitionTick(0),
      nextPacketTick(0),
      port(name() + ".port", *this),
      retryPkt(NULL),
      retryPktTick(0),
      updateEvent(this),
      drainManager(NULL)
{
}

TrafficGen*
TrafficGenParams::create()
{
    return new TrafficGen(this);
}

BaseMasterPort&
TrafficGen::getMasterPort(const string& if_name, PortID idx)
{
    if (if_name == "port") {
        return port;
    } else {
        return MemObject::getMasterPort(if_name, idx);
    }
}

void
TrafficGen::init()
{
    if (!port.isConnected())
        fatal("The port of %s is not connected!\n", name());

    // if the system is in timing mode active the request generator
    if (system->isTimingMode()) {
        DPRINTF(TrafficGen, "Timing mode, activating request generator\n");

        parseConfig();

        // enter initial state
        enterState(currState);
    } else {
        DPRINTF(TrafficGen,
                "Traffic generator is only active in timing mode\n");
    }
}

void
TrafficGen::initState()
{
    // when not restoring from a checkpoint, make sure we kick things off
    if (system->isTimingMode()) {
        // call nextPacketTick on the state to advance it
        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
        schedule(updateEvent, std::min(nextPacketTick, nextTransitionTick));
    } else {
        DPRINTF(TrafficGen,
                "Traffic generator is only active in timing mode\n");
    }
}

unsigned int
TrafficGen::drain(DrainManager *dm)
{
    if (retryPkt == NULL) {
        // shut things down
        nextPacketTick = MaxTick;
        nextTransitionTick = MaxTick;
        deschedule(updateEvent);
        return 0;
    } else {
        drainManager = dm;
        return 1;
    }
}

void
TrafficGen::serialize(ostream &os)
{
    DPRINTF(Checkpoint, "Serializing TrafficGen\n");

    // save ticks of the graph event if it is scheduled
    Tick nextEvent = updateEvent.scheduled() ? updateEvent.when() : 0;

    DPRINTF(TrafficGen, "Saving nextEvent=%llu\n", nextEvent);

    SERIALIZE_SCALAR(nextEvent);

    SERIALIZE_SCALAR(nextTransitionTick);

    SERIALIZE_SCALAR(nextPacketTick);

    SERIALIZE_SCALAR(currState);
}

void
TrafficGen::unserialize(Checkpoint* cp, const string& section)
{
    // restore scheduled events
    Tick nextEvent;
    UNSERIALIZE_SCALAR(nextEvent);
    if (nextEvent != 0) {
        schedule(updateEvent, nextEvent);
    }

    UNSERIALIZE_SCALAR(nextTransitionTick);

    UNSERIALIZE_SCALAR(nextPacketTick);

    // @todo In the case of a stateful generator state such as the
    // trace player we would also have to restore the position in the
    // trace playback and the tick offset
    UNSERIALIZE_SCALAR(currState);
}

void
TrafficGen::update()
{
    // if we have reached the time for the next state transition, then
    // perform the transition
    if (curTick() >= nextTransitionTick) {
        transition();
    } else {
        assert(curTick() >= nextPacketTick);
        // get the next packet and try to send it
        PacketPtr pkt = states[currState]->getNextPacket();
        numPackets++;
        if (!port.sendTimingReq(pkt)) {
            retryPkt = pkt;
            retryPktTick = curTick();
        }
    }

    // if we are waiting for a retry, do not schedule any further
    // events, in the case of a transition or a successful send, go
    // ahead and determine when the next update should take place
    if (retryPkt == NULL) {
        // schedule next update event based on either the next execute
        // tick or the next transition, which ever comes first
        nextPacketTick = states[currState]->nextPacketTick(elasticReq, 0);
        Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
        DPRINTF(TrafficGen, "Next event scheduled at %lld\n", nextEventTick);
        schedule(updateEvent, nextEventTick);
    }
}

void
TrafficGen::parseConfig()
{
    // keep track of the transitions parsed to create the matrix when
    // done
    vector<Transition> transitions;

    // open input file
    ifstream infile;
    infile.open(configFile.c_str(), ifstream::in);
    if (!infile.is_open()) {
        fatal("Traffic generator %s config file not found at %s\n",
              name(), configFile);
    }

    // read line by line and determine the action based on the first
    // keyword
    string keyword;
    string line;

    while (getline(infile, line).good()) {
        // see if this line is a comment line, and if so skip it
        if (line.find('#') != 1) {
            // create an input stream for the tokenization
            istringstream is(line);

            // determine the keyword
            is >> keyword;

            if (keyword == "STATE") {
                // parse the behaviour of this state
                uint32_t id;
                Tick duration;
                string mode;

                is >> id >> duration >> mode;

                if (mode == "TRACE") {
                    string traceFile;
                    Addr addrOffset;

                    is >> traceFile >> addrOffset;

                    states[id] = new TraceGen(name(), masterID, duration,
                                              traceFile, addrOffset);
                    DPRINTF(TrafficGen, "State: %d TraceGen\n", id);
                } else if (mode == "IDLE") {
                    states[id] = new IdleGen(name(), masterID, duration);
                    DPRINTF(TrafficGen, "State: %d IdleGen\n", id);
                } else if (mode == "LINEAR" || mode == "RANDOM") {
                    uint32_t read_percent;
                    Addr start_addr;
                    Addr end_addr;
                    Addr blocksize;
                    Tick min_period;
                    Tick max_period;
                    Addr data_limit;

                    is >> read_percent >> start_addr >> end_addr >>
                        blocksize >> min_period >> max_period >> data_limit;

                    DPRINTF(TrafficGen, "%s, addr %x to %x, size %d,"
                            " period %d to %d, %d%% reads\n",
                            mode, start_addr, end_addr, blocksize, min_period,
                            max_period, read_percent);

                    if (port.deviceBlockSize() &&
                        blocksize > port.deviceBlockSize())
                        fatal("TrafficGen %s block size (%d) is larger than "
                              "port block size (%d)\n", name(),
                              blocksize, port.deviceBlockSize());

                    if (read_percent > 100)
                        fatal("%s cannot have more than 100% reads", name());

                    if (min_period > max_period)
                        fatal("%s cannot have min_period > max_period", name());

                    if (mode == "LINEAR") {
                        states[id] = new LinearGen(name(), masterID,
                                                   duration, start_addr,
                                                   end_addr, blocksize,
                                                   min_period, max_period,
                                                   read_percent, data_limit);
                        DPRINTF(TrafficGen, "State: %d LinearGen\n", id);
                    } else if (mode == "RANDOM") {
                        states[id] = new RandomGen(name(), masterID,
                                                   duration, start_addr,
                                                   end_addr, blocksize,
                                                   min_period, max_period,
                                                   read_percent, data_limit);
                        DPRINTF(TrafficGen, "State: %d RandomGen\n", id);
                    }
                } else {
                    fatal("%s: Unknown traffic generator mode: %s",
                          name(), mode);
                }
            } else if (keyword == "TRANSITION") {
                Transition transition;

                is >> transition.from >> transition.to >> transition.p;

                transitions.push_back(transition);

                DPRINTF(TrafficGen, "Transition: %d -> %d\n", transition.from,
                        transition.to);
            } else if (keyword == "INIT") {
                // set the initial state as the active state
                is >> currState;

                DPRINTF(TrafficGen, "Initial state: %d\n", currState);
            }
        }
    }

    // resize and populate state transition matrix
    transitionMatrix.resize(states.size());
    for (size_t i = 0; i < states.size(); i++) {
        transitionMatrix[i].resize(states.size());
    }

    for (vector<Transition>::iterator t = transitions.begin();
         t != transitions.end(); ++t) {
        transitionMatrix[t->from][t->to] = t->p;
    }

    // ensure the egress edges do not have a probability larger than
    // one
    for (size_t i = 0; i < states.size(); i++) {
        double sum = 0;
        for (size_t j = 0; j < states.size(); j++) {
            sum += transitionMatrix[i][j];
        }

        // avoid comparing floating point numbers
        if (abs(sum - 1.0) > 0.001)
            fatal("%s has transition probability != 1 for state %d\n",
                  name(), i);
    }

    // close input file
    infile.close();
}

void
TrafficGen::transition()
{
    // exit the current state
    states[currState]->exit();

    // determine next state
    double p = random_mt.gen_real1();
    assert(currState < transitionMatrix.size());
    double cumulative = 0.0;
    size_t i = 0;
    do {
        cumulative += transitionMatrix[currState][i];
        ++i;
    } while (cumulative < p && i < transitionMatrix[currState].size());

    enterState(i - 1);
}

void
TrafficGen::enterState(uint32_t newState)
{
    DPRINTF(TrafficGen, "Transition to state %d\n", newState);

    currState = newState;
    // we could have been delayed and not transitioned on the exact
    // tick when we were supposed to (due to back pressure when
    // sending a packet)
    nextTransitionTick = curTick() + states[currState]->duration;
    states[currState]->enter();
}

void
TrafficGen::recvRetry()
{
    assert(retryPkt != NULL);

    DPRINTF(TrafficGen, "Received retry\n");
    numRetries++;
    // attempt to send the packet, and if we are successful start up
    // the machinery again
    if (port.sendTimingReq(retryPkt)) {
        retryPkt = NULL;
        // remember how much delay was incurred due to back-pressure
        // when sending the request, we also use this to derive
        // the tick for the next packet
        Tick delay = curTick() - retryPktTick;
        retryPktTick = 0;
        retryTicks += delay;

        if (drainManager == NULL) {
            // packet is sent, so find out when the next one is due
            nextPacketTick = states[currState]->nextPacketTick(elasticReq,
                                                               delay);
            Tick nextEventTick = std::min(nextPacketTick, nextTransitionTick);
            schedule(updateEvent, std::max(curTick(), nextEventTick));
        } else {
            // shut things down
            nextPacketTick = MaxTick;
            nextTransitionTick = MaxTick;
            drainManager->signalDrainDone();
            // Clear the drain event once we're done with it.
            drainManager = NULL;
        }
    }
}

void
TrafficGen::regStats()
{
    // Initialise all the stats
    using namespace Stats;

    numPackets
        .name(name() + ".numPackets")
        .desc("Number of packets generated");

    numRetries
        .name(name() + ".numRetries")
        .desc("Number of retries");

    retryTicks
        .name(name() + ".retryTicks")
        .desc("Time spent waiting due to back-pressure (ticks)");
}

bool
TrafficGen::TrafficGenPort::recvTimingResp(PacketPtr pkt)
{
    delete pkt->req;
    delete pkt;

    return true;
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <iostream>
#include <string>
#include <array>
#include <tinyxml2.h>
#include "linalg/vector.h"
#include "linalg/point.h"
#include "linalg/transform.h"
#include "render/camera.h"
#include "render/render_target.h"
#include "geometry/sphere.h"
#include "load_scene.h"
#include "scene.h"

/*
 * Load the camera information from the element and return it
 * also passes back x & y image resolution through xres & yres
 */
Camera load_camera(tinyxml2::XMLElement *elem, int &w, int &h);
/*
 * Load object nodes in the XMLElement as children of the
 * passed node. Any geometry needed will be loaded from
 * the scene's geometry cache or added if it's missing
 */
void load_node(tinyxml2::XMLElement *elem, Node &node, Scene &scene);
/*
 * Read the x,y,z attributes of the XMLElement and return it
 */
void read_vector(tinyxml2::XMLElement *elem, Vector &v);
/*
 * Read the x,y,z attributes of the XMLElement and return it
 */
void read_point(tinyxml2::XMLElement *elem, Point &p);
/*
 * Read the value float attribute of the XMLElement and return it
 */
void read_float(tinyxml2::XMLElement *elem, float &f);

Scene load_scene(const std::string &file){
	using namespace tinyxml2;
	XMLDocument doc;
	XMLError err = doc.LoadFile(file.c_str());
	if (err != XML_SUCCESS){
		std::cerr << "load_scene Error: failed to open scene " << file << std::endl;
		std::exit(1);
	}
	XMLElement *xml = doc.FirstChildElement("xml");
	if (!xml){
		std::cerr << "load_scene Error: missing <xml> tag\n";
		std::exit(1);
	}
	XMLElement *scene_node = xml->FirstChildElement("scene");
	if (!scene_node){
		std::cerr << "load_scene Error: no scene found\n";
		std::exit(1);
	}
	XMLElement *cam = xml->FirstChildElement("camera");
	if (!cam){
		std::cerr << "load_scene Error: no camera found\n";
		std::exit(1);
	}
	int w = 0, h = 0;
	Camera camera = load_camera(cam, w, h);
	RenderTarget render_target{static_cast<size_t>(w), static_cast<size_t>(h)};
	Scene scene{std::move(camera), std::move(render_target)};
	load_node(scene_node, scene.get_root(), scene);
	return scene;
}
Camera load_camera(tinyxml2::XMLElement *elem, int &w, int &h){
	using namespace tinyxml2;
	Point pos, target;
	Vector up;
	float fov;
	for (XMLNode *c = elem->FirstChild(); c; c = c->NextSibling()){
		if (c->Value() == std::string{"position"}){
			read_point(c->ToElement(), pos);
		}
		else if (c->Value() == std::string{"target"}){
			read_point(c->ToElement(), target);
		}
		else if (c->Value() == std::string{"up"}){
			read_vector(c->ToElement(), up);
		}
		else if (c->Value() == std::string{"fov"}){
			read_float(c->ToElement(), fov);
		}
		else if (c->Value() == std::string{"width"}){
			c->ToElement()->QueryIntAttribute("value", &w);
		}
		else if (c->Value() == std::string{"height"}){
			c->ToElement()->QueryIntAttribute("value", &h);
		}
	}
	//Not quite sure what to pick for img_screen so we just do +/- w/20
	//and +/- h/20
	float screen_x = w / 20.f;
	float screen_y = h / 20.f;
	return Camera{Transform::look_at(pos, target, up),
		std::array<float, 4>{-screen_x, screen_x, -screen_y, screen_y},
		fov, w, h};
}
void load_node(tinyxml2::XMLElement *elem, Node &node, Scene &scene){
	using namespace tinyxml2;
	auto &children = node.get_children();
	for (XMLNode *c = elem->FirstChild(); c; c = c->NextSibling()){
		if (c->Value() == std::string{"object"}){
			XMLElement *e = c->ToElement();
			std::string name = e->Attribute("name");
			const char *t = e->Attribute("type");
			std::cout << "Object with name " << name << " found\n";
			Geometry *geom = nullptr;
			if (t){
				std::string type = t;
				std::cout << "type: " << type << std::endl;
				//Check if the geometry is in our cache, if not load it
				auto &gcache = scene.get_geom_cache();
				if (!gcache.get(type)){
					if (type == "sphere"){
						gcache.add(type, std::unique_ptr<Geometry>(new Sphere(Point{0, 0, 0}, 1)));
					}
				}
				geom = gcache.get(type);
			}
			//Push the new child on and assign its geometry, the transform will
			//be setup in further iterations when we read the scale/translate elements
			children.push_back(std::make_shared<Node>(geom, Transform{}, name));
			load_node(e, *children.back(), scene);
		}
		else if (c->Value() == std::string{"scale"}){
			//Query both uniform and non-uniform scaling possibilities
			float f = 1;
			Vector v{1, 1, 1};
			read_float(c->ToElement(), f);
			read_vector(c->ToElement(), v);
			v *= f;
			std::cout << "Scaling of " << v << " applied\n";
			auto &transform = node.get_transform();
			transform = Transform::scale(v.x, v.y, v.z) * transform;
		}
		else if (c->Value() == std::string{"translate"}){
			Vector v;
			read_vector(c->ToElement(), v);
			std::cout << "Translation of " << v << " applied\n";
			auto &transform = node.get_transform();
			transform = Transform::translate(v) * transform;
		}
	}
}
void read_vector(tinyxml2::XMLElement *elem, Vector &v){
	elem->QueryFloatAttribute("x", &v.x);
	elem->QueryFloatAttribute("y", &v.y);
	elem->QueryFloatAttribute("z", &v.z);
}
void read_point(tinyxml2::XMLElement *elem, Point &p){
	elem->QueryFloatAttribute("x", &p.x);
	elem->QueryFloatAttribute("y", &p.y);
	elem->QueryFloatAttribute("z", &p.z);
}
void read_float(tinyxml2::XMLElement *elem, float &f){
	elem->QueryFloatAttribute("value", &f);
}

<commit_msg>Properly compute screen space extent of image based on fov<commit_after>#include <cstdlib>
#include <iostream>
#include <string>
#include <array>
#include <tinyxml2.h>
#include "linalg/util.h"
#include "linalg/vector.h"
#include "linalg/point.h"
#include "linalg/transform.h"
#include "render/camera.h"
#include "render/render_target.h"
#include "geometry/sphere.h"
#include "load_scene.h"
#include "scene.h"

/*
 * Load the camera information from the element and return it
 * also passes back x & y image resolution through xres & yres
 */
Camera load_camera(tinyxml2::XMLElement *elem, int &w, int &h);
/*
 * Load object nodes in the XMLElement as children of the
 * passed node. Any geometry needed will be loaded from
 * the scene's geometry cache or added if it's missing
 */
void load_node(tinyxml2::XMLElement *elem, Node &node, Scene &scene);
/*
 * Read the x,y,z attributes of the XMLElement and return it
 */
void read_vector(tinyxml2::XMLElement *elem, Vector &v);
/*
 * Read the x,y,z attributes of the XMLElement and return it
 */
void read_point(tinyxml2::XMLElement *elem, Point &p);
/*
 * Read the value float attribute of the XMLElement and return it
 */
void read_float(tinyxml2::XMLElement *elem, float &f);

Scene load_scene(const std::string &file){
	using namespace tinyxml2;
	XMLDocument doc;
	XMLError err = doc.LoadFile(file.c_str());
	if (err != XML_SUCCESS){
		std::cerr << "load_scene Error: failed to open scene " << file << std::endl;
		std::exit(1);
	}
	XMLElement *xml = doc.FirstChildElement("xml");
	if (!xml){
		std::cerr << "load_scene Error: missing <xml> tag\n";
		std::exit(1);
	}
	XMLElement *scene_node = xml->FirstChildElement("scene");
	if (!scene_node){
		std::cerr << "load_scene Error: no scene found\n";
		std::exit(1);
	}
	XMLElement *cam = xml->FirstChildElement("camera");
	if (!cam){
		std::cerr << "load_scene Error: no camera found\n";
		std::exit(1);
	}
	int w = 0, h = 0;
	Camera camera = load_camera(cam, w, h);
	RenderTarget render_target{static_cast<size_t>(w), static_cast<size_t>(h)};
	Scene scene{std::move(camera), std::move(render_target)};
	load_node(scene_node, scene.get_root(), scene);
	return scene;
}
Camera load_camera(tinyxml2::XMLElement *elem, int &w, int &h){
	using namespace tinyxml2;
	Point pos, target;
	Vector up;
	float fov;
	for (XMLNode *c = elem->FirstChild(); c; c = c->NextSibling()){
		if (c->Value() == std::string{"position"}){
			read_point(c->ToElement(), pos);
		}
		else if (c->Value() == std::string{"target"}){
			read_point(c->ToElement(), target);
		}
		else if (c->Value() == std::string{"up"}){
			read_vector(c->ToElement(), up);
		}
		else if (c->Value() == std::string{"fov"}){
			read_float(c->ToElement(), fov);
		}
		else if (c->Value() == std::string{"width"}){
			c->ToElement()->QueryIntAttribute("value", &w);
		}
		else if (c->Value() == std::string{"height"}){
			c->ToElement()->QueryIntAttribute("value", &h);
		}
	}
	//Compute x & y dimens of the screen
	float screen_x = 2 * std::tan(radians(fov) / 2.f);
	float screen_y = screen_x * w / static_cast<float>(h);
	//We want the screen centered along (0, 0) so we do +/- screen_x/y / 2
	screen_x /= 2;
	screen_y /= 2;
	return Camera{Transform::look_at(pos, target, up),
		std::array<float, 4>{-screen_x, screen_x, -screen_y, screen_y},
		fov, w, h};
}
void load_node(tinyxml2::XMLElement *elem, Node &node, Scene &scene){
	using namespace tinyxml2;
	auto &children = node.get_children();
	for (XMLNode *c = elem->FirstChild(); c; c = c->NextSibling()){
		if (c->Value() == std::string{"object"}){
			XMLElement *e = c->ToElement();
			std::string name = e->Attribute("name");
			const char *t = e->Attribute("type");
			std::cout << "Object with name " << name << " found\n";
			Geometry *geom = nullptr;
			if (t){
				std::string type = t;
				std::cout << "type: " << type << std::endl;
				//Check if the geometry is in our cache, if not load it
				auto &gcache = scene.get_geom_cache();
				if (!gcache.get(type)){
					if (type == "sphere"){
						gcache.add(type, std::unique_ptr<Geometry>(new Sphere(Point{0, 0, 0}, 1)));
					}
				}
				geom = gcache.get(type);
			}
			//Push the new child on and assign its geometry, the transform will
			//be setup in further iterations when we read the scale/translate elements
			children.push_back(std::make_shared<Node>(geom, Transform{}, name));
			load_node(e, *children.back(), scene);
		}
		else if (c->Value() == std::string{"scale"}){
			//Query both uniform and non-uniform scaling possibilities
			float f = 1;
			Vector v{1, 1, 1};
			read_float(c->ToElement(), f);
			read_vector(c->ToElement(), v);
			v *= f;
			std::cout << "Scaling of " << v << " applied\n";
			auto &transform = node.get_transform();
			transform = Transform::scale(v.x, v.y, v.z) * transform;
		}
		else if (c->Value() == std::string{"translate"}){
			Vector v;
			read_vector(c->ToElement(), v);
			std::cout << "Translation of " << v << " applied\n";
			auto &transform = node.get_transform();
			transform = Transform::translate(v) * transform;
		}
	}
}
void read_vector(tinyxml2::XMLElement *elem, Vector &v){
	elem->QueryFloatAttribute("x", &v.x);
	elem->QueryFloatAttribute("y", &v.y);
	elem->QueryFloatAttribute("z", &v.z);
}
void read_point(tinyxml2::XMLElement *elem, Point &p){
	elem->QueryFloatAttribute("x", &p.x);
	elem->QueryFloatAttribute("y", &p.y);
	elem->QueryFloatAttribute("z", &p.z);
}
void read_float(tinyxml2::XMLElement *elem, float &f){
	elem->QueryFloatAttribute("value", &f);
}

<|endoftext|>


<commit_before>/* $Id$ */
//____________________________________
/////////////////////////////////////////////////////////////////////////
//
// Class AliHBTPairCut:
//
// implements cut on the pair of particles
// more info: http://alisoft.cern.ch/people/skowron/analyzer/index.html
// Author: Piotr.Skowronski@cern.ch
//-------------------------------------------------------------------

#include "AliHBTPairCut.h"
#include "AliHBTPair.h"
#include "AliHBTParticleCut.h"
#include "AliHBTTrackPoints.h"
#include "AliHBTClusterMap.h"

ClassImp(AliHBTPairCut)
const Int_t AliHBTPairCut::fgkMaxCuts = 50;
/**********************************************************/

AliHBTPairCut::AliHBTPairCut():
  fNCuts(0)
{
  //constructor
  fFirstPartCut = new AliHBTEmptyParticleCut(); //empty cuts
  fSecondPartCut= new AliHBTEmptyParticleCut(); //empty cuts

  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
}
/**********************************************************/

AliHBTPairCut::AliHBTPairCut(const AliHBTPairCut& in):
 TNamed(in)
{
  //copy constructor
  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
  fNCuts = in.fNCuts;

  fFirstPartCut = (AliHBTParticleCut*)in.fFirstPartCut->Clone();
  fSecondPartCut = (AliHBTParticleCut*)in.fSecondPartCut->Clone();

  for (Int_t i = 0;i<fNCuts;i++)
    {
      fCuts[i] = (AliHbtBasePairCut*)in.fCuts[i]->Clone();//create new object (clone) and rember pointer to it
    }
}
/**********************************************************/

AliHBTPairCut&  AliHBTPairCut::operator=(const AliHBTPairCut& in)
{
  //assignment operator
  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
  fNCuts = in.fNCuts;

  fFirstPartCut = (AliHBTParticleCut*)in.fFirstPartCut->Clone();
  fSecondPartCut = (AliHBTParticleCut*)in.fSecondPartCut->Clone();

  for (Int_t i = 0;i<fNCuts;i++)
    {
      fCuts[i] = (AliHbtBasePairCut*)in.fCuts[i]->Clone();//create new object (clone) and rember pointer to it
    }
  return * this;
}
/**********************************************************/

AliHBTPairCut::~AliHBTPairCut()
{
  //destructor
  if (fFirstPartCut != fSecondPartCut)
    {
      delete fSecondPartCut;
    }
  delete fFirstPartCut;
  for (Int_t i = 0;i<fNCuts;i++)
    {
      delete fCuts[i];
    }
  delete []fCuts;
}
/**********************************************************/

/**********************************************************/

void AliHBTPairCut::AddBasePairCut(AliHbtBasePairCut* basecut)
{
  //adds the base pair cut (cut on one value)

  if (!basecut) return;
  if( fNCuts == (fgkMaxCuts-1) )
    {
      Warning("AddBasePairCut","Not enough place for another cut");
      return;
    }
  fCuts[fNCuts++]=basecut;
}
/**********************************************************/

Bool_t AliHBTPairCut::Pass(AliHBTPair* pair) const
{
  //methods which checks if given pair meets all criteria of the cut
  //if it meets returns FALSE
  //if NOT   returns    TRUE
  if(!pair)
    {
      Warning("Pass","No Pasaran! We never accept NULL pointers");
      return kTRUE;
    }

  //check particle's cuts
  if( ( fFirstPartCut->Pass( pair->Particle1()) ) ||
      ( fSecondPartCut->Pass(pair->Particle2()) )   )
    {
      return kTRUE;
    }
  return PassPairProp(pair);
}
/**********************************************************/

Bool_t AliHBTPairCut::PassPairProp(AliHBTPair* pair) const
{
  //methods which checks if given pair meets all criteria of the cut
  //if it meets returns FALSE
  //if NOT   returns    TRUE
  //examine all base pair cuts
  for (Int_t i = 0;i<fNCuts;i++)
    {
      if ( (fCuts[i]->Pass(pair)) ) return kTRUE; //if one of the cuts reject, then reject
    }
  return kFALSE;
}
/**********************************************************/

void AliHBTPairCut::SetFirstPartCut(AliHBTParticleCut* cut)
{
  // set cut for the first particle
  if(!cut)
    {
      Error("SetFirstPartCut","argument is NULL");
      return;
    }
  delete fFirstPartCut;
  fFirstPartCut = (AliHBTParticleCut*)cut->Clone();

}
/**********************************************************/

void AliHBTPairCut::SetSecondPartCut(AliHBTParticleCut* cut)
{
  // set cut for the second particle
  if(!cut)
    {
      Error("SetSecondPartCut","argument is NULL");
      return;
    }
  delete fSecondPartCut;
  fSecondPartCut = (AliHBTParticleCut*)cut->Clone();
}
/**********************************************************/

void AliHBTPairCut::SetPartCut(AliHBTParticleCut* cut)
{
  //sets the the same cut on both particles
  if(!cut)
    {
      Error("SetFirstPartCut","argument is NULL");
      return;
    }
  if (fFirstPartCut == fSecondPartCut) fSecondPartCut = 0x0;

  delete fFirstPartCut;
  fFirstPartCut = (AliHBTParticleCut*)cut->Clone();

  delete fSecondPartCut; //even if null should not be harmful
  fSecondPartCut = fFirstPartCut;
}
/**********************************************************/

void AliHBTPairCut::SetQInvRange(Double_t min, Double_t max)
{
  // set range of accepted invariant masses
  AliHBTQInvCut* cut= (AliHBTQInvCut*)FindCut(kHbtPairCutPropQInv);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQInvCut(min,max);
}
/**********************************************************/
void AliHBTPairCut::SetQOutCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QOut in CMS
  AliHBTQOutCMSLCCut* cut= (AliHBTQOutCMSLCCut*)FindCut(kHbtPairCutPropQOutCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQOutCMSLCCut(min,max);
}

/**********************************************************/
void AliHBTPairCut::SetQSideCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QSide in CMS
  AliHBTQSideCMSLCCut* cut= (AliHBTQSideCMSLCCut*)FindCut(kHbtPairCutPropQSideCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQSideCMSLCCut(min,max);
}

/**********************************************************/
void AliHBTPairCut::SetQLongCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QLong in CMS
  AliHBTQLongCMSLCCut* cut= (AliHBTQLongCMSLCCut*)FindCut(kHbtPairCutPropQLongCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQLongCMSLCCut(min,max);
}

/**********************************************************/

void AliHBTPairCut::SetKtRange(Double_t min, Double_t max)
{
  // set range of accepted Kt (?)
  AliHBTKtCut* cut= (AliHBTKtCut*)FindCut(kHbtPairCutPropKt);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTKtCut(min,max);
}
/**********************************************************/

void AliHBTPairCut::SetKStarRange(Double_t min, Double_t max)
{
  // set range of accepted KStar (?)
  AliHBTKStarCut* cut= (AliHBTKStarCut*)FindCut(kHbtPairCutPropKStar);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTKStarCut(min,max);
}
/**********************************************************/
void AliHBTPairCut::SetAvSeparationRange(Double_t min, Double_t max)
{
  //sets avarage separation cut ->Anti-Merging cut
  AliHbtBasePairCut* cut= FindCut(kHbtPairCutPropAvSepar);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTAvSeparationCut(min,max);
}
/**********************************************************/

void AliHBTPairCut::SetClusterOverlapRange(Double_t min,Double_t max)
{
  //sets cluster overlap factor cut ->Anti-Splitting cut
  //cluster overlap factor ranges between
  // -0.5 (in all padrows both tracks have cluters)
  // and 1 (in all padrows one track has cluter and second has not)
  // When Overlap Factor is 1 this pair of tracks in highly probable to be
  // splitted track: one particle that is recontructed twise
  // STAR uses range from -0.5 to 0.6

  AliHbtBasePairCut* cut= FindCut(kHbtPairCutPropClOverlap);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTCluterOverlapCut(min,max);
}

AliHbtBasePairCut* AliHBTPairCut::FindCut(AliHBTPairCutProperty property)
{
  // Find the cut corresponding to "property"
  for (Int_t i = 0;i<fNCuts;i++)
    {
      if (fCuts[i]->GetProperty() == property)
	return fCuts[i]; //we found the cut we were searching for
    }

  return 0x0; //we did not found this cut

}
/**********************************************************/

void AliHBTPairCut::Streamer(TBuffer &b)
{
  // Stream all objects in the array to or from the I/O buffer.

  UInt_t R__s, R__c;
  if (b.IsReading())
    {
      Version_t v = b.ReadVersion(&R__s, &R__c);
      if (v > -1)
       {
          delete fFirstPartCut;
          delete fSecondPartCut;
          fFirstPartCut = 0x0;
          fSecondPartCut = 0x0;
          TObject::Streamer(b);
          b >> fFirstPartCut;
          b >> fSecondPartCut;
          b >> fNCuts;
          for (Int_t i = 0;i<fNCuts;i++)
           {
             b >> fCuts[i];
           }
        }
      b.CheckByteCount(R__s, R__c,AliHBTPairCut::IsA());
    }
  else
    {
      R__c = b.WriteVersion(AliHBTPairCut::IsA(), kTRUE);
      TObject::Streamer(b);

//      printf("Streamer Cut 1 %#x Cut 2 %#x\n",fFirstPartCut,fSecondPartCut);
//      this->Dump();
//      fFirstPartCut->Dump();

      b << fFirstPartCut;
      b << fSecondPartCut;
      b << fNCuts;
      for (Int_t i = 0;i<fNCuts;i++)
        {
          b << fCuts[i];
        }
      b.SetByteCount(R__c, kTRUE);
    }
}

ClassImp(AliHBTEmptyPairCut)

void AliHBTEmptyPairCut::Streamer(TBuffer &b)
{
//streamer for empty pair cut
  AliHBTPairCut::Streamer(b);
}

ClassImp(AliHbtBasePairCut)
ClassImp(AliHBTQInvCut)
ClassImp(AliHBTKtCut)
ClassImp(AliHBTQSideCMSLCCut)
ClassImp(AliHBTQOutCMSLCCut)
ClassImp(AliHBTQLongCMSLCCut)
ClassImp(AliHBTAvSeparationCut)

Double_t AliHBTAvSeparationCut::GetValue(AliHBTPair* pair) const
{
  //chacks if avarage distance of two tracks is in given range
  AliHBTTrackPoints* tpts1 = pair->Particle1()->GetTrackPoints();
  if ( tpts1 == 0x0)
   {
     Warning("GetValue","Track 1 does not have Track Points. Pair NOT Passed.");
     return -10e5;
   }

  AliHBTTrackPoints* tpts2 = pair->Particle2()->GetTrackPoints();
  if ( tpts2 == 0x0)
   {
     Warning("GetValue","Track 2 does not have Track Points. Pair NOT Passed.");
     return -10e5;
   }

  return tpts1->AvarageDistance(*tpts2);
}

ClassImp(AliHBTCluterOverlapCut)

Double_t  AliHBTCluterOverlapCut::GetValue(AliHBTPair* pair) const
{
  //Returns Cluter Overlap Factor
  //It ranges between -0.5 (in all padrows both tracks have cluters)
  // and 1 (in all padrows one track has cluter and second has not)
  // When Overlap Factor is 1 this pair of tracks in highly probable to be
  // splitted track: one particle that is recontructed twise

  AliHBTClusterMap* cm1 = pair->Particle1()->GetClusterMap();
  if ( cm1 == 0x0)
   {
     Warning("GetValue","Track 1 does not have Cluster Map. Returning -0.5.");
     return -.5;
   }

  AliHBTClusterMap* cm2 = pair->Particle2()->GetClusterMap();
  if ( cm2 == 0x0)
   {
     Warning("GetValue","Track 2 does not have Cluster Map. Returning -0.5.");
     return -.5;
   }
  return cm1->GetOverlapFactor(*cm2);
}
<commit_msg>Correction for Anti-Merging cut - if Track Points are not available such a pair is accpepted<commit_after>/* $Id$ */
//____________________________________
/////////////////////////////////////////////////////////////////////////
//
// Class AliHBTPairCut:
//
// implements cut on the pair of particles
// more info: http://alisoft.cern.ch/people/skowron/analyzer/index.html
// Author: Piotr.Skowronski@cern.ch
//-------------------------------------------------------------------

#include "AliHBTPairCut.h"
#include "AliHBTPair.h"
#include "AliHBTParticleCut.h"
#include "AliHBTTrackPoints.h"
#include "AliHBTClusterMap.h"

ClassImp(AliHBTPairCut)
const Int_t AliHBTPairCut::fgkMaxCuts = 50;
/**********************************************************/

AliHBTPairCut::AliHBTPairCut():
  fNCuts(0)
{
  //constructor
  fFirstPartCut = new AliHBTEmptyParticleCut(); //empty cuts
  fSecondPartCut= new AliHBTEmptyParticleCut(); //empty cuts

  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
}
/**********************************************************/

AliHBTPairCut::AliHBTPairCut(const AliHBTPairCut& in):
 TNamed(in)
{
  //copy constructor
  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
  fNCuts = in.fNCuts;

  fFirstPartCut = (AliHBTParticleCut*)in.fFirstPartCut->Clone();
  fSecondPartCut = (AliHBTParticleCut*)in.fSecondPartCut->Clone();

  for (Int_t i = 0;i<fNCuts;i++)
    {
      fCuts[i] = (AliHbtBasePairCut*)in.fCuts[i]->Clone();//create new object (clone) and rember pointer to it
    }
}
/**********************************************************/

AliHBTPairCut&  AliHBTPairCut::operator=(const AliHBTPairCut& in)
{
  //assignment operator
  fCuts = new AliHbtBasePairCut*[fgkMaxCuts];
  fNCuts = in.fNCuts;

  fFirstPartCut = (AliHBTParticleCut*)in.fFirstPartCut->Clone();
  fSecondPartCut = (AliHBTParticleCut*)in.fSecondPartCut->Clone();

  for (Int_t i = 0;i<fNCuts;i++)
    {
      fCuts[i] = (AliHbtBasePairCut*)in.fCuts[i]->Clone();//create new object (clone) and rember pointer to it
    }
  return * this;
}
/**********************************************************/

AliHBTPairCut::~AliHBTPairCut()
{
  //destructor
  if (fFirstPartCut != fSecondPartCut)
    {
      delete fSecondPartCut;
    }
  delete fFirstPartCut;
  for (Int_t i = 0;i<fNCuts;i++)
    {
      delete fCuts[i];
    }
  delete []fCuts;
}
/**********************************************************/

/**********************************************************/

void AliHBTPairCut::AddBasePairCut(AliHbtBasePairCut* basecut)
{
  //adds the base pair cut (cut on one value)

  if (!basecut) return;
  if( fNCuts == (fgkMaxCuts-1) )
    {
      Warning("AddBasePairCut","Not enough place for another cut");
      return;
    }
  fCuts[fNCuts++]=basecut;
}
/**********************************************************/

Bool_t AliHBTPairCut::Pass(AliHBTPair* pair) const
{
  //methods which checks if given pair meets all criteria of the cut
  //if it meets returns FALSE
  //if NOT   returns    TRUE
  if(!pair)
    {
      Warning("Pass","No Pasaran! We never accept NULL pointers");
      return kTRUE;
    }

  //check particle's cuts
  if( ( fFirstPartCut->Pass( pair->Particle1()) ) ||
      ( fSecondPartCut->Pass(pair->Particle2()) )   )
    {
      return kTRUE;
    }
  return PassPairProp(pair);
}
/**********************************************************/

Bool_t AliHBTPairCut::PassPairProp(AliHBTPair* pair) const
{
  //methods which checks if given pair meets all criteria of the cut
  //if it meets returns FALSE
  //if NOT   returns    TRUE
  //examine all base pair cuts
  for (Int_t i = 0;i<fNCuts;i++)
    {
      if ( (fCuts[i]->Pass(pair)) ) return kTRUE; //if one of the cuts reject, then reject
    }
  return kFALSE;
}
/**********************************************************/

void AliHBTPairCut::SetFirstPartCut(AliHBTParticleCut* cut)
{
  // set cut for the first particle
  if(!cut)
    {
      Error("SetFirstPartCut","argument is NULL");
      return;
    }
  delete fFirstPartCut;
  fFirstPartCut = (AliHBTParticleCut*)cut->Clone();

}
/**********************************************************/

void AliHBTPairCut::SetSecondPartCut(AliHBTParticleCut* cut)
{
  // set cut for the second particle
  if(!cut)
    {
      Error("SetSecondPartCut","argument is NULL");
      return;
    }
  delete fSecondPartCut;
  fSecondPartCut = (AliHBTParticleCut*)cut->Clone();
}
/**********************************************************/

void AliHBTPairCut::SetPartCut(AliHBTParticleCut* cut)
{
  //sets the the same cut on both particles
  if(!cut)
    {
      Error("SetFirstPartCut","argument is NULL");
      return;
    }
  if (fFirstPartCut == fSecondPartCut) fSecondPartCut = 0x0;

  delete fFirstPartCut;
  fFirstPartCut = (AliHBTParticleCut*)cut->Clone();

  delete fSecondPartCut; //even if null should not be harmful
  fSecondPartCut = fFirstPartCut;
}
/**********************************************************/

void AliHBTPairCut::SetQInvRange(Double_t min, Double_t max)
{
  // set range of accepted invariant masses
  AliHBTQInvCut* cut= (AliHBTQInvCut*)FindCut(kHbtPairCutPropQInv);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQInvCut(min,max);
}
/**********************************************************/
void AliHBTPairCut::SetQOutCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QOut in CMS
  AliHBTQOutCMSLCCut* cut= (AliHBTQOutCMSLCCut*)FindCut(kHbtPairCutPropQOutCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQOutCMSLCCut(min,max);
}

/**********************************************************/
void AliHBTPairCut::SetQSideCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QSide in CMS
  AliHBTQSideCMSLCCut* cut= (AliHBTQSideCMSLCCut*)FindCut(kHbtPairCutPropQSideCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQSideCMSLCCut(min,max);
}

/**********************************************************/
void AliHBTPairCut::SetQLongCMSLRange(Double_t min, Double_t max)
{
  // set range of accepted QLong in CMS
  AliHBTQLongCMSLCCut* cut= (AliHBTQLongCMSLCCut*)FindCut(kHbtPairCutPropQLongCMSLC);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTQLongCMSLCCut(min,max);
}

/**********************************************************/

void AliHBTPairCut::SetKtRange(Double_t min, Double_t max)
{
  // set range of accepted Kt (?)
  AliHBTKtCut* cut= (AliHBTKtCut*)FindCut(kHbtPairCutPropKt);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTKtCut(min,max);
}
/**********************************************************/

void AliHBTPairCut::SetKStarRange(Double_t min, Double_t max)
{
  // set range of accepted KStar (?)
  AliHBTKStarCut* cut= (AliHBTKStarCut*)FindCut(kHbtPairCutPropKStar);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTKStarCut(min,max);
}
/**********************************************************/
void AliHBTPairCut::SetAvSeparationRange(Double_t min, Double_t max)
{
  //sets avarage separation cut ->Anti-Merging cut
  AliHbtBasePairCut* cut= FindCut(kHbtPairCutPropAvSepar);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTAvSeparationCut(min,max);
}
/**********************************************************/

void AliHBTPairCut::SetClusterOverlapRange(Double_t min,Double_t max)
{
  //sets cluster overlap factor cut ->Anti-Splitting cut
  //cluster overlap factor ranges between
  // -0.5 (in all padrows both tracks have cluters)
  // and 1 (in all padrows one track has cluter and second has not)
  // When Overlap Factor is 1 this pair of tracks in highly probable to be
  // splitted track: one particle that is recontructed twise
  // STAR uses range from -0.5 to 0.6

  AliHbtBasePairCut* cut= FindCut(kHbtPairCutPropClOverlap);
  if(cut) cut->SetRange(min,max);
  else fCuts[fNCuts++] = new AliHBTCluterOverlapCut(min,max);
}

AliHbtBasePairCut* AliHBTPairCut::FindCut(AliHBTPairCutProperty property)
{
  // Find the cut corresponding to "property"
  for (Int_t i = 0;i<fNCuts;i++)
    {
      if (fCuts[i]->GetProperty() == property)
	return fCuts[i]; //we found the cut we were searching for
    }

  return 0x0; //we did not found this cut

}
/**********************************************************/

void AliHBTPairCut::Streamer(TBuffer &b)
{
  // Stream all objects in the array to or from the I/O buffer.

  UInt_t R__s, R__c;
  if (b.IsReading())
    {
      Version_t v = b.ReadVersion(&R__s, &R__c);
      if (v > -1)
       {
          delete fFirstPartCut;
          delete fSecondPartCut;
          fFirstPartCut = 0x0;
          fSecondPartCut = 0x0;
          TObject::Streamer(b);
          b >> fFirstPartCut;
          b >> fSecondPartCut;
          b >> fNCuts;
          for (Int_t i = 0;i<fNCuts;i++)
           {
             b >> fCuts[i];
           }
        }
      b.CheckByteCount(R__s, R__c,AliHBTPairCut::IsA());
    }
  else
    {
      R__c = b.WriteVersion(AliHBTPairCut::IsA(), kTRUE);
      TObject::Streamer(b);

//      printf("Streamer Cut 1 %#x Cut 2 %#x\n",fFirstPartCut,fSecondPartCut);
//      this->Dump();
//      fFirstPartCut->Dump();

      b << fFirstPartCut;
      b << fSecondPartCut;
      b << fNCuts;
      for (Int_t i = 0;i<fNCuts;i++)
        {
          b << fCuts[i];
        }
      b.SetByteCount(R__c, kTRUE);
    }
}

ClassImp(AliHBTEmptyPairCut)

void AliHBTEmptyPairCut::Streamer(TBuffer &b)
{
//streamer for empty pair cut
  AliHBTPairCut::Streamer(b);
}

ClassImp(AliHbtBasePairCut)
ClassImp(AliHBTQInvCut)
ClassImp(AliHBTKtCut)
ClassImp(AliHBTQSideCMSLCCut)
ClassImp(AliHBTQOutCMSLCCut)
ClassImp(AliHBTQLongCMSLCCut)
ClassImp(AliHBTAvSeparationCut)

Double_t AliHBTAvSeparationCut::GetValue(AliHBTPair* pair) const
{
  //chacks if avarage distance of two tracks is in given range
  AliHBTTrackPoints* tpts1 = pair->Particle1()->GetTrackPoints();
  if ( tpts1 == 0x0)
   {//it could be simulated pair
//     Warning("GetValue","Track 1 does not have Track Points. Pair NOT Passed.");
     return 10e5;
   }

  AliHBTTrackPoints* tpts2 = pair->Particle2()->GetTrackPoints();
  if ( tpts2 == 0x0)
   {
//     Warning("GetValue","Track 2 does not have Track Points. Pair NOT Passed.");
     return 10e5;
   }

  return tpts1->AvarageDistance(*tpts2);
}

ClassImp(AliHBTCluterOverlapCut)

Double_t  AliHBTCluterOverlapCut::GetValue(AliHBTPair* pair) const
{
  //Returns Cluter Overlap Factor
  //It ranges between -0.5 (in all padrows both tracks have cluters)
  // and 1 (in all padrows one track has cluter and second has not)
  // When Overlap Factor is 1 this pair of tracks in highly probable to be
  // splitted track: one particle that is recontructed twise

  AliHBTClusterMap* cm1 = pair->Particle1()->GetClusterMap();
  if ( cm1 == 0x0)
   {
     Warning("GetValue","Track 1 does not have Cluster Map. Returning -0.5.");
     return -.5;
   }

  AliHBTClusterMap* cm2 = pair->Particle2()->GetClusterMap();
  if ( cm2 == 0x0)
   {
     Warning("GetValue","Track 2 does not have Cluster Map. Returning -0.5.");
     return -.5;
   }
  return cm1->GetOverlapFactor(*cm2);
}
<|endoftext|>


<commit_before>#include<iostream>
#include"atoms.hpp"
#include<boost/shared_ptr.hpp>
#include<boost/scoped_ptr.hpp>

boost::shared_ptr<Atom> NILATOM;

boost::scoped_ptr<Globals> globals;

int main(int argc, const char* argv[]){
	globals.reset(new Globals());
	NILATOM = globals->lookup("nil");

	/*tests!*/
	/*make sure NILATOM is a global atom*/
	GlobalAtom* p = dynamic_cast<GlobalAtom*>(&*NILATOM);
	if(p != NULL){
		std::cout << "NIL ATOM: ";
		p->emit();
		std::cout << std::endl;
	} else {
		std::cout << "NIL ATOM not global!" << std::endl;
	}
	/*make sure NILATOM is looked up again*/
	boost::shared_ptr<Atom> tmp(globals->lookup("nil"));
	if(tmp != NILATOM){
		std::cout << "Eh??" << std::endl;
		p = dynamic_cast<GlobalAtom*>(&*tmp);
		if(p != NULL){
			std::cout << "Other 'nil atom: ";
			p->emit();
			std::cout << std::endl;
		} else {
			std::cout << "Other 'nil atom isn't even global!"
				<< std::endl;
		}
	} else {
		std::cout << "Got NILATOM on lookup again!" << std::endl;
	}
}

<commit_msg>Added TATOM<commit_after>#include<iostream>
#include"atoms.hpp"
#include<boost/shared_ptr.hpp>
#include<boost/scoped_ptr.hpp>

boost::shared_ptr<Atom> NILATOM;
boost::shared_ptr<Atom> TATOM;

boost::scoped_ptr<Globals> globals;

int main(int argc, const char* argv[]){
	globals.reset(new Globals());
	NILATOM = globals->lookup("nil");
	TATOM = globals->lookup("t");

	/*tests!*/
	/*make sure NILATOM is a global atom*/
	GlobalAtom* p = dynamic_cast<GlobalAtom*>(&*NILATOM);
	if(p != NULL){
		std::cout << "NIL ATOM: ";
		p->emit();
		std::cout << std::endl;
	} else {
		std::cout << "NIL ATOM not global!" << std::endl;
	}
	/*make sure NILATOM is looked up again*/
	boost::shared_ptr<Atom> tmp(globals->lookup("nil"));
	if(tmp != NILATOM){
		std::cout << "Eh??" << std::endl;
		p = dynamic_cast<GlobalAtom*>(&*tmp);
		if(p != NULL){
			std::cout << "Other 'nil atom: ";
			p->emit();
			std::cout << std::endl;
		} else {
			std::cout << "Other 'nil atom isn't even global!"
				<< std::endl;
		}
	} else {
		std::cout << "Got NILATOM on lookup again!" << std::endl;
	}
	/*make sure NILATOM != TATOM*/
	if(NILATOM != TATOM){
		std::cout << "As expected, 'nil is not 't" << std::endl;
	} else {
		std::cout << "Not good: 'nil is somehow 't!" << std::endl;
	}
}

<|endoftext|>


<commit_before>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

#include "scheduler.h"
#include "env.h"

namespace kl {

namespace {
const int kLoggingThreadWaitTimeout = 5000;
const int kEpollThreadWaitTimeout = 5000;
const int kWorkerThreadWaitTimeout = 5000;
}

// REQUIRES: num_of_worker_threads >= 1
Scheduler::Scheduler(size_t num_of_worker_threads)
    : num_of_worker_threads_(num_of_worker_threads), enable_epoll_(true),
      stop_(false), task_queues_(nullptr), queue_round_robin_(0) {}

Scheduler::Scheduler(size_t num_of_worker_threads, bool enable_epoll)
    : num_of_worker_threads_(num_of_worker_threads),
      enable_epoll_(enable_epoll), stop_(false), task_queues_(nullptr),
      queue_round_robin_(0) {}

Scheduler::~Scheduler() {
  if (task_queues_) {
    delete[] task_queues_;
    task_queues_ = nullptr;
  }
}

kl::Status Scheduler::LaunchLoggingThread() {
  logger_ = std::make_unique<kl::logging::Logger>([this](const char *message) {
    logging_queue_.Push(std::string(message));
  });
  sync_.Add();
  std::thread([this] {
    kl::env::Defer defer([this] { sync_.Done(); });
    while (!stop_) {
      auto next = logging_queue_.Pop(kLoggingThreadWaitTimeout);
      if (!next && logging_queue_.Closed()) {
        break;
      }
      if (next) {
        std::string &message = *next;
        std::fprintf(stderr, "%s", message.c_str());
      }
    }
  }).detach();
  return kl::Ok();
}

kl::Status Scheduler::RegisterEpollEvent(int fd, uint32_t events,
                                         EpollHandler &&callback) {
  if (!enable_epoll_) {
    return kl::Err("epoll not enabled");
  }
  auto status = epoll_.AddFd(fd, events);
  if (!status) {
    return status;
  }
  callback_mapping_rwlock_.WLock();
  callbacks_.insert(std::make_pair(fd, std::move(callback)));
  callback_mapping_rwlock_.WUnlock();
  return kl::Ok();
}

kl::Status Scheduler::UnregisterEpollEvent(int fd) {
  if (!enable_epoll_) {
    return kl::Err("epoll not enabled");
  }
  auto status = epoll_.DelFd(fd);
  if (!status) {
    return status;
  }
  callback_mapping_rwlock_.WLock();
  callbacks_.erase(fd);
  callback_mapping_rwlock_.WUnlock();
  return kl::Ok();
}

void Scheduler::SubmitTask(std::function<void(void)> &&task) {
  size_t current = PickQueue();
  assert(current < num_of_worker_threads_);
  task_queues_[current].Push(std::move(task));
}

size_t Scheduler::PickQueue() {
  size_t current = queue_round_robin_++;
  return current % num_of_worker_threads_;
}

kl::Status Scheduler::LaunchEpollThread() {
  sync_.Add();
  std::thread([this] {
    kl::env::Defer defer([this] { sync_.Done(); });
    while (!stop_) {
      auto wait = epoll_.Wait(64, kEpollThreadWaitTimeout);
      if (!wait) {
        Stop(wait.Err().ToCString());
        break;
      }
      for (const auto &event : *wait) {
        int fd = event.data.fd;
        uint32_t events = event.events;
        kl::rwlock::ReadGuard _(&callback_mapping_rwlock_);
        auto iter = callbacks_.find(fd);
        if (iter == callbacks_.end()) {
          KL_DEBUG_L(Logger(), "callback for fd %d is empty");
          continue;
        }
        SubmitTask([ handler = iter->second, events ] { handler(events); });
      }
    }
  }).detach();
  return kl::Ok();
}

void Scheduler::WorkerRoutine(size_t id) {
  kl::env::Defer defer([this] { sync_.Done(); });
  while (!stop_) {
    auto task = task_queues_[id].Pop(kWorkerThreadWaitTimeout);
    if (!task && task_queues_[id].Closed()) {
      break;
    }
    if (task) {
      try {
        (*task)();
      } catch (const std::exception &e) {
        KL_ERROR_L(Logger(), e.what());
      }
    }
  }
}

kl::logging::Logger &Scheduler::Logger() {
  assert(logger_);
  return *logger_;
}

kl::Status Scheduler::Go() {
  auto status = LaunchLoggingThread();
  if (!status) {
    Stop(status.Err().ToCString());
    return status;
  }
  if (enable_epoll_) {
    status = LaunchEpollThread();
    if (!status) {
      Stop(status.Err().ToCString());
      return status;
    }
  }
  status = LaunchWorkerThreads();
  if (!status) {
    Stop(status.Err().ToCString());
    return status;
  }
  sync_.Wait();
  if (!exit_reason_.empty()) {
    return kl::Err(exit_reason_.c_str());
  }
  return kl::Ok();
}

void Scheduler::Stop(const char *reason) {
  if (reason) {
    exit_reason_mutex_.lock();
    exit_reason_ = reason;
    exit_reason_mutex_.unlock();
  }
  stop_.store(true);
}

kl::Status Scheduler::LaunchWorkerThreads() {
  task_queues_ =
      new kl::Chan<std::function<void(void)>>[num_of_worker_threads_];
  for (size_t i = 0; i < num_of_worker_threads_; ++i) {
    sync_.Add();
    std::thread([this, i] { WorkerRoutine(i); }).detach();
  }
  return kl::Ok();
}

}  // namespace kl
<commit_msg>minor fix<commit_after>// Copyright (c) 2017 Kai Luo <gluokai@gmail.com>. All rights reserved.
// Use of this source code is governed by the BSD license that can be found in
// the LICENSE file.

#include "scheduler.h"
#include "env.h"

namespace kl {

namespace {
const int kLoggingThreadWaitTimeout = 5000;
const int kEpollThreadWaitTimeout = 5000;
const int kWorkerThreadWaitTimeout = 5000;
}

// REQUIRES: num_of_worker_threads >= 1
Scheduler::Scheduler(size_t num_of_worker_threads)
    : num_of_worker_threads_(num_of_worker_threads), enable_epoll_(true),
      stop_(false), task_queues_(nullptr), queue_round_robin_(0) {
  task_queues_ =
      new kl::Chan<std::function<void(void)>>[num_of_worker_threads_];
  logger_ = std::make_unique<kl::logging::Logger>([this](const char *message) {
    logging_queue_.Push(std::string(message));
  });
}

Scheduler::Scheduler(size_t num_of_worker_threads, bool enable_epoll)
    : num_of_worker_threads_(num_of_worker_threads),
      enable_epoll_(enable_epoll), stop_(false), task_queues_(nullptr),
      queue_round_robin_(0) {
  task_queues_ =
      new kl::Chan<std::function<void(void)>>[num_of_worker_threads_];
  logger_ = std::make_unique<kl::logging::Logger>([this](const char *message) {
    logging_queue_.Push(std::string(message));
  });
}

Scheduler::~Scheduler() {
  if (task_queues_) {
    delete[] task_queues_;
    task_queues_ = nullptr;
  }
}

kl::Status Scheduler::LaunchLoggingThread() {
  sync_.Add();
  std::thread([this] {
    kl::env::Defer defer([this] { sync_.Done(); });
    while (!stop_) {
      auto next = logging_queue_.Pop(kLoggingThreadWaitTimeout);
      if (!next && logging_queue_.Closed()) {
        break;
      }
      if (next) {
        std::string &message = *next;
        std::fprintf(stderr, "%s", message.c_str());
      }
    }
  }).detach();
  return kl::Ok();
}

kl::Status Scheduler::RegisterEpollEvent(int fd, uint32_t events,
                                         EpollHandler &&callback) {
  if (!enable_epoll_) {
    return kl::Err("epoll not enabled");
  }
  auto status = epoll_.AddFd(fd, events);
  if (!status) {
    return status;
  }
  callback_mapping_rwlock_.WLock();
  callbacks_.insert(std::make_pair(fd, std::move(callback)));
  callback_mapping_rwlock_.WUnlock();
  return kl::Ok();
}

kl::Status Scheduler::UnregisterEpollEvent(int fd) {
  if (!enable_epoll_) {
    return kl::Err("epoll not enabled");
  }
  auto status = epoll_.DelFd(fd);
  if (!status) {
    return status;
  }
  callback_mapping_rwlock_.WLock();
  callbacks_.erase(fd);
  callback_mapping_rwlock_.WUnlock();
  return kl::Ok();
}

void Scheduler::SubmitTask(std::function<void(void)> &&task) {
  size_t current = PickQueue();
  assert(current < num_of_worker_threads_);
  task_queues_[current].Push(std::move(task));
}

size_t Scheduler::PickQueue() {
  size_t current = queue_round_robin_++;
  return current % num_of_worker_threads_;
}

kl::Status Scheduler::LaunchEpollThread() {
  sync_.Add();
  std::thread([this] {
    kl::env::Defer defer([this] { sync_.Done(); });
    while (!stop_) {
      auto wait = epoll_.Wait(64, kEpollThreadWaitTimeout);
      if (!wait) {
        Stop(wait.Err().ToCString());
        break;
      }
      for (const auto &event : *wait) {
        int fd = event.data.fd;
        uint32_t events = event.events;
        kl::rwlock::ReadGuard _(&callback_mapping_rwlock_);
        auto iter = callbacks_.find(fd);
        if (iter == callbacks_.end()) {
          KL_DEBUG_L(Logger(), "callback for fd %d is empty");
          continue;
        }
        SubmitTask([ handler = iter->second, events ] { handler(events); });
      }
    }
  }).detach();
  return kl::Ok();
}

void Scheduler::WorkerRoutine(size_t id) {
  kl::env::Defer defer([this] { sync_.Done(); });
  while (!stop_) {
    auto task = task_queues_[id].Pop(kWorkerThreadWaitTimeout);
    if (!task && task_queues_[id].Closed()) {
      break;
    }
    if (task) {
      try {
        (*task)();
      } catch (const std::exception &e) {
        KL_ERROR_L(Logger(), e.what());
      }
    }
  }
}

kl::logging::Logger &Scheduler::Logger() {
  assert(logger_);
  return *logger_;
}

kl::Status Scheduler::Go() {
  auto status = LaunchLoggingThread();
  if (!status) {
    Stop(status.Err().ToCString());
    return status;
  }
  if (enable_epoll_) {
    status = LaunchEpollThread();
    if (!status) {
      Stop(status.Err().ToCString());
      return status;
    }
  }
  status = LaunchWorkerThreads();
  if (!status) {
    Stop(status.Err().ToCString());
    return status;
  }
  sync_.Wait();
  if (!exit_reason_.empty()) {
    return kl::Err(exit_reason_.c_str());
  }
  return kl::Ok();
}

void Scheduler::Stop(const char *reason) {
  if (reason) {
    exit_reason_mutex_.lock();
    exit_reason_ = reason;
    exit_reason_mutex_.unlock();
  }
  stop_.store(true);
}

kl::Status Scheduler::LaunchWorkerThreads() {
  for (size_t i = 0; i < num_of_worker_threads_; ++i) {
    sync_.Add();
    std::thread([this, i] { WorkerRoutine(i); }).detach();
  }
  return kl::Ok();
}

}  // namespace kl
<|endoftext|>


<commit_before>/**
 * @file   Strings.hpp
 * @brief  Strings class prototype.
 * @author zer0
 * @date   2016-04-04
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/Noncopyable.hpp>

#define CHECK_GNUC_CXX_REGEX
#include <libtbag/predef.hpp>

#include <string>
#include <vector>
#include <regex>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace string {

template <typename T>
constexpr T const * charOrWidechar(char const * c, wchar_t const * w);

template <>
constexpr char const * charOrWidechar<char>(char const * c, wchar_t const * w)
{
    return c;
}

template <>
constexpr wchar_t const * charOrWidechar<wchar_t>(char const * c, wchar_t const * w)
{
    return w;
}

#ifndef CHAR_OR_WIDECHAR
#define CHAR_OR_WIDECHAR(type, str) ::libtbag::string::charOrWidechar<type>(str, L##str)
#endif

/**
 * Separate tokens.
 *
 * @param source    [in] Original string.
 * @param delimiter [in] Delimiter string.
 *
 * @return
 *  Token vector.
 */
template <typename CharType = char>
std::vector<std::basic_string<CharType> > splitTokens(
          std::basic_string<CharType> const & source
        , std::basic_string<CharType> const & delimiter)
{
    using String = std::basic_string<CharType>;
    using StringVector = std::vector<String>;

    StringVector result;
    String token;

    size_t start = 0;
    size_t end   = source.find(delimiter);

    while (end != String::npos) {
        token = source.substr(start, end - start);
        if (token.empty() == false) {
            result.push_back(token);
        }

        // Calculate next token index.
        start = end + delimiter.length();
        end   = source.find(delimiter, start);
    }

    // Last token.
    token = source.substr(start, end);
    if (token.empty() == false) {
        result.push_back(token);
    }

    return result;
}

} // namespace string

/**
 * String utility class.
 *
 * @author zer0
 * @date   2016-04-04
 *
 * @remarks
 *  Without using the inline function, class for using static method.
 *
 * @translate{ko, inline function을 사용하지 않고\, static method를 사용하기 위한 클래스.}
 */
template <typename CharType = char>
class BaseStrings : public Noncopyable
{
public:
    using ValueType = CharType;
    using String    = std::basic_string<ValueType>;

    using Regex = std::basic_regex<ValueType>;
    using RegexTokenIterator = std::regex_token_iterator<typename String::const_iterator>;

    static_assert(std::is_pod<ValueType>::value
            , "Character type of BaseStrings must be a POD");
    static_assert(std::is_same<ValueType, typename String::value_type>::value
            , "String::value_type must be the same type as ValueType");

public:
    constexpr BaseStrings() noexcept = default;
    ~BaseStrings() noexcept = default;

public:
    /**
     * Regex based token.
     */
    static std::vector<String> splitMatch(String const & source
                                        , Regex  const & match) {
        auto itr = RegexTokenIterator(source.begin(), source.end(), match);
        auto end = RegexTokenIterator();

        std::vector<String> result;
        while (itr != end) {
            result.push_back(itr->str());
            ++itr;
        }
        return result;
    }

    static std::vector<String> splitMatch(String const & source
                                        , String const & match) {
        return splitMatch(source, Regex(match));
    }

// Regexp utilities.
public:
    static String replaceRegex(String const & path
                             , String const & regex
                             , String const & replace) {
        return std::regex_replace(path, Regex(regex), replace);
    }

    static String removeRegex(String const & path
                            , String const & regex) {
        return replaceRegex(path, regex, String());
    }

    static bool isMatch(String const & original
                      , Regex  const & regex) {
        return std::regex_match(original, regex);
    }

    static bool isMatch(String const & original
                      , String const & regex) {
        return isMatch(original, Regex(regex));
    }

public:
    static constexpr ValueType const CHAR_SPACE  = ' ';
    static constexpr ValueType const CHAR_TAB    = '\t';
    static constexpr ValueType const CHAR_RETURN = '\n';

    static String trimLeft(String const & str) {
        auto itr = str.begin();
        for (; itr != str.end(); ++itr) {
            if (*itr != CHAR_SPACE && *itr != CHAR_TAB && *itr != CHAR_RETURN) {
                break;
            }
        }
        return String(itr, str.end());
    }

    static String trimRight(String const & str) {
        auto itr = str.rbegin();
        for (; itr != str.rend(); ++itr) {
            if (*itr != CHAR_SPACE && *itr != CHAR_TAB && *itr != CHAR_RETURN) {
                break;
            }
        }
        return String(str.begin(), itr.base());
    }

    static String trim(String const & str) {
        return trimRight(trimLeft(str));
    }
};

using Strings = BaseStrings<char>;
using WideStrings = BaseStrings<wchar_t>;

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__

<commit_msg>Fix the string::splitTokens() method bug.<commit_after>/**
 * @file   Strings.hpp
 * @brief  Strings class prototype.
 * @author zer0
 * @date   2016-04-04
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/Noncopyable.hpp>
#include <libtbag/type.hpp>

#define CHECK_GNUC_CXX_REGEX
#include <libtbag/predef.hpp>

#include <string>
#include <vector>
#include <regex>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace string {

template <typename T>
constexpr T const * charOrWidechar(char const * c, wchar_t const * w);

template <>
constexpr char const * charOrWidechar<char>(char const * c, wchar_t const * w)
{
    return c;
}

template <>
constexpr wchar_t const * charOrWidechar<wchar_t>(char const * c, wchar_t const * w)
{
    return w;
}

#ifndef CHAR_OR_WIDECHAR
#define CHAR_OR_WIDECHAR(type, str) ::libtbag::string::charOrWidechar<type>(str, L##str)
#endif

/**
 * Separate tokens.
 *
 * @param source    [in] Original string.
 * @param delimiter [in] Delimiter string.
 *
 * @return
 *  Token vector.
 */
template <typename CharType = char>
std::vector<std::basic_string<typename libtbag::remove_cr<CharType>::type> > splitTokens(
          std::basic_string<typename libtbag::remove_cr<CharType>::type> const & source
        , std::basic_string<typename libtbag::remove_cr<CharType>::type> const & delimiter)
{
    using String = std::basic_string<typename libtbag::remove_cr<CharType>::type>;
    using StringVector = std::vector<String>;

    StringVector result;
    String token;

    size_t start = 0;
    size_t end   = source.find(delimiter);

    while (end != String::npos) {
        token = source.substr(start, end - start);
        if (token.empty() == false) {
            result.push_back(token);
        }

        // Calculate next token index.
        start = end + delimiter.length();
        end   = source.find(delimiter, start);
    }

    // Last token.
    token = source.substr(start, end);
    if (token.empty() == false) {
        result.push_back(token);
    }

    return result;
}

} // namespace string

/**
 * String utility class.
 *
 * @author zer0
 * @date   2016-04-04
 *
 * @remarks
 *  Without using the inline function, class for using static method.
 *
 * @translate{ko, inline function을 사용하지 않고\, static method를 사용하기 위한 클래스.}
 */
template <typename CharType = char>
class BaseStrings : public Noncopyable
{
public:
    using ValueType = CharType;
    using String    = std::basic_string<ValueType>;

    using Regex = std::basic_regex<ValueType>;
    using RegexTokenIterator = std::regex_token_iterator<typename String::const_iterator>;

    static_assert(std::is_pod<ValueType>::value
            , "Character type of BaseStrings must be a POD");
    static_assert(std::is_same<ValueType, typename String::value_type>::value
            , "String::value_type must be the same type as ValueType");

public:
    constexpr BaseStrings() noexcept = default;
    ~BaseStrings() noexcept = default;

public:
    /**
     * Regex based token.
     */
    static std::vector<String> splitMatch(String const & source
                                        , Regex  const & match) {
        auto itr = RegexTokenIterator(source.begin(), source.end(), match);
        auto end = RegexTokenIterator();

        std::vector<String> result;
        while (itr != end) {
            result.push_back(itr->str());
            ++itr;
        }
        return result;
    }

    static std::vector<String> splitMatch(String const & source
                                        , String const & match) {
        return splitMatch(source, Regex(match));
    }

// Regexp utilities.
public:
    static String replaceRegex(String const & path
                             , String const & regex
                             , String const & replace) {
        return std::regex_replace(path, Regex(regex), replace);
    }

    static String removeRegex(String const & path
                            , String const & regex) {
        return replaceRegex(path, regex, String());
    }

    static bool isMatch(String const & original
                      , Regex  const & regex) {
        return std::regex_match(original, regex);
    }

    static bool isMatch(String const & original
                      , String const & regex) {
        return isMatch(original, Regex(regex));
    }

public:
    static constexpr ValueType const CHAR_SPACE  = ' ';
    static constexpr ValueType const CHAR_TAB    = '\t';
    static constexpr ValueType const CHAR_RETURN = '\n';

    static String trimLeft(String const & str) {
        auto itr = str.begin();
        for (; itr != str.end(); ++itr) {
            if (*itr != CHAR_SPACE && *itr != CHAR_TAB && *itr != CHAR_RETURN) {
                break;
            }
        }
        return String(itr, str.end());
    }

    static String trimRight(String const & str) {
        auto itr = str.rbegin();
        for (; itr != str.rend(); ++itr) {
            if (*itr != CHAR_SPACE && *itr != CHAR_TAB && *itr != CHAR_RETURN) {
                break;
            }
        }
        return String(str.begin(), itr.base());
    }

    static String trim(String const & str) {
        return trimRight(trimLeft(str));
    }
};

using Strings = BaseStrings<char>;
using WideStrings = BaseStrings<wchar_t>;

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_STRINGS_HPP__

<|endoftext|>