<commit_before>/* discovery.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 26 Dec 2013
   FreeBSD-style copyright and disclaimer apply

   Implementation of the node discovery database.

*/

#include "discovery.h"
#include "pack.h"
#include "lockless/bits.h"

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

template<>
struct Pack<Address>
{
    static size_t size(const Address& value)
    {
        return packedSizeAll(value.host, value.port);
    }

    static void pack(const Address& value, PackIt first, PackIt last)
    {
        packAll(first, last, value.host, value.port);
    }

    static Address unpack(ConstPackIt first, ConstPackIt last)
    {
        Address value;
        unpackAll(first, last, value.host, value.port);
        return std::move(value);
    }
};

/******************************************************************************/
/* NODE                                                                       */
/******************************************************************************/

bool
DistributedDiscovery::Node::
operator<(const Node& other) const
{
    size_t n = std::min(addrs.size(), other.addrs.size());
    for (size_t i = 0; i < n; ++i) {
        if (addrs[i] < other.addrs[i]) return true;
        if (other.addrs[i] < addrs[i]) return false;
    }

    if (addrs.size() < other.addrs.size()) return true;
    if (other.addrs.size() < addrs.size()) return false;

    return expiration < other.expiration;
}

/******************************************************************************/
/* WATCH                                                                      */
/******************************************************************************/

DistributedDiscovery::Watch::
Watch(WatchFn watch) : watch(std::move(watch))
{
    static std::atomic<WatchHandle> handleCounter{0};
    handle = ++handleCounter;
}

/******************************************************************************/
/* LIST                                                                       */
/******************************************************************************/

template<typename T>
auto
DistributedDiscovery::List<T>::
find(const T& value) -> iterator
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    return res.first == res.second ? list.end() : res.first;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
count(const T& value) const
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    return res.first != res.second;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
insert(T value)
{
    if (count(value)) return false;

    list.emplace_back(std::move(value));
    std::sort(list.begin(), list.end());

    return true;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
erase(const T& value)
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    if (res.first == res.second) return false;

    list.erase(res.first);
    return true;
}

/******************************************************************************/
/* PROTOCOL                                                                   */
/******************************************************************************/

namespace msg {

static const std::string Init =  "_slick_disc_";
static constexpr uint32_t Version = 1;

typedef uint16_t Type;
static constexpr Type Keys  = 1;
static constexpr Type Query = 2;
static constexpr Type Nodes = 3;
static constexpr Type Get   = 4;
static constexpr Type Data  = 5;

} // namespace msg

/******************************************************************************/
/* DISTRIBUTED DISCOVERY                                                      */
/******************************************************************************/

size_t
DistributedDiscovery::
timerPeriod()
{
    enum { BasePeriod = 60 };
    std::uniform_int_distribution<size_t>dist(BasePeriod, BasePeriod * 2);
    return dist(rng);
}

DistributedDiscovery::
DistributedDiscovery(const std::vector<Address>& seed, Port port) :
    keyTTL_(DefaultKeyTTL),
    nodeTTL_(DefaultNodeTTL),
    rng(lockless::wall()),
    endpoint(port),
    timer(timerPeriod()) // \todo add randomness
{
    using namespace std::placeholders;

    endpoint.onPayload = bind(&DistributedDiscovery::onPayload, this, _1, _2);
    endpoint.onNewConnection = bind(&DistributedDiscovery::onConnect, this, _1);
    endpoint.onLostConnection = bind(&DistributedDiscovery::onDisconnect, this, _1);
    poller.add(endpoint);

    retracts.onOperation = std::bind(&DistributedDiscovery::retract, this, _1);
    poller.add(retracts);

    publishes.onOperation = std::bind(&DistributedDiscovery::publish, this, _1, _2);
    poller.add(publishes);

    typedef void (DistributedDiscovery::*DiscoverFn) (const std::string&, Watch&&);
    DiscoverFn discoverFn = &DistributedDiscovery::discover;
    discovers.onOperation = std::bind(discoverFn, this, _1, _2);
    poller.add(discovers);

    forgets.onOperation = std::bind(&DistributedDiscovery::forget, this, _1, _2);
    poller.add(forgets);

    timer.onTimer = bind(&DistributedDiscovery::onTimer, this, _1);
    poller.add(timer);

    for (auto& addr : seed)
        nodes.insert(Node({ addr }, nodeTTL_));
}

void
DistributedDiscovery::
poll()
{
    isPollThread.set();
    poller.poll();
}

void
DistributedDiscovery::
shutdown()
{
    isPollThread.unset();
    retracts.poll();
    publishes.poll();
    discovers.poll();
    forgets.poll();
}

void
DistributedDiscovery::
onPayload(ConnectionHandle handle, Payload&& data)
{
    auto& conn = connections[handle];
    auto it = data.cbegin(), last = data.cend();

    if (!conn) it = onInit(conn, it, last);

    while (it != last) {
        msg::Type type;
        it = unpack(type, it, last);

        switch(type) {
        case msg::Keys:  it = onKeys(conn, it, last); break;
        case msg::Query: it = onQuery(conn, it, last); break;
        case msg::Nodes: it = onNodes(conn, it, last); break;
        case msg::Get:   it = onGet(conn, it, last); break;
        case msg::Data:  it = onData(conn, it, last); break;
        default: assert(false);
        };
    }
}

Discovery::WatchHandle
DistributedDiscovery::
discover(const std::string& key, const WatchFn& fn)
{
    Watch watch(fn);
    auto handle = watch.handle;
    discover(key, std::move(watch));
    return handle;
}

void
DistributedDiscovery::
discover(const std::string& key, Watch&& watch)
{
    if (!isPollThread()) {
        discovers.defer(key, watch);
        return;
    }

    if (!watches.count(key)) {
        std::vector<QueryItem> items = { key };
        endpoint.broadcast(packAll(msg::Query, endpoint.interfaces(), items));
    }

    watches[key].insert(std::move(watch));

    for (const auto& node : keys[key])
        doGet(key, node.addrs);
}

void
DistributedDiscovery::
forget(const std::string& key, WatchHandle handle)
{
    if (!isPollThread()) {
        forgets.defer(key, handle);
        return;
    }

    auto& list = watches[key];
    list.erase(Watch(handle));

    if (list.empty())
        watches.erase(key);
}

void
DistributedDiscovery::
publish(const std::string& key, Payload&& data)
{
    if (!isPollThread()) {
        publishes.defer(key, std::move(data));
        return;
    }

    this->data[key] = std::move(data);

    std::vector<KeyItem> items;
    items.emplace_back(key, endpoint.interfaces(), keyTTL_);

    endpoint.broadcast(packAll(msg::Keys, items));
}

void
DistributedDiscovery::
retract(const std::string& key)
{
    if (!isPollThread()) {
        retracts.defer(key);
        return;
    }

    data.erase(key);
}

void
DistributedDiscovery::
onConnect(ConnectionHandle handle)
{
    auto& conn = connections[handle];
    conn.handle = handle;

    endpoint.send(handle, packAll(msg::Init, msg::Version));
}

void
DistributedDiscovery::
onDisconnect(ConnectionHandle handle)
{
    connections.erase(handle);
}

ConstPackIt
DistributedDiscovery::
onInit(ConnState& conn, ConstPackIt it, ConstPackIt last)
{
    std::string init;
    it = unpackAll(it, last, init, conn.version);

    if (init != msg::Init) {
        endpoint.disconnect(conn.handle);
        return last;
    }

    assert(conn.version == msg::Version);

    if (!data.empty()) {
        std::vector<KeyItem> items;
        for (const auto& key : data)
            items.emplace_back(key.first, endpoint.interfaces(), keyTTL_);

        endpoint.send(conn.handle, packAll(msg::Keys, items));
    }

    if (!watches.empty()) {
        std::vector<QueryItem> items;
        for (const auto& watch : watches)
            items.emplace_back(watch.first);

        auto msg = packAll(msg::Query, endpoint.interfaces(), items);
        endpoint.send(conn.handle, std::move(msg));
    }

    if (!nodes.empty()) {
        std::vector<NodeItem> items;
        items.emplace_back(endpoint.interfaces(), nodeTTL_);

        double now = lockless::wall();
        size_t picks = lockless::log2(nodes.size());

        for (const auto& node : nodes.pickRandom(rng, picks))
            items.emplace_back(node.addrs, node.ttl(now));

        endpoint.send(conn.handle, packAll(msg::Nodes, items));
    }

    return it;
}

ConstPackIt
DistributedDiscovery::
onKeys(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<KeyItem> items;
    it = unpack(items, it, last);

    double now = lockless::wall();
    std::vector<KeyItem> toForward;

    for (auto& item : items) {
        std::string key;
        NodeLocation node;
        size_t ttl;
        std::tie(key, node, ttl) = std::move(item);

        auto& list = keys[key];

        Node value(node, ttl, now);
        auto it = list.find(value);
        if (it != list.end()) {
            it->setTTL(ttl, now);
            continue;
        }

        list.insert(value);
        toForward.emplace_back(key, node, ttl);

        if (watches.count(key)) doGet(key, node);
    }

    if (!toForward.empty())
        endpoint.broadcast(packAll(msg::Keys, toForward));

    return it;
}

ConstPackIt
DistributedDiscovery::
onQuery(ConnState& conn, ConstPackIt it, ConstPackIt last)
{
    NodeLocation node;
    std::vector<QueryItem> items;
    it = unpackAll(it, last, node, items);

    std::vector<QueryItem> toForward;

    for (const auto& key : items) {
        auto it = keys.find(key);

        if (it != keys.end()) {
            std::vector<KeyItem> items;
            for (const auto& node : it->second) {
                if (!node.ttl()) continue;
                items.emplace_back(key, node.addrs, node.ttl());
            }

            endpoint.send(conn.handle, packAll(msg::Keys, items));
        }

        else {
            keys[key] = List<Node>();
            toForward.emplace_back(key);
        }
    }

    if (!toForward.empty())
        endpoint.broadcast(packAll(msg::Query, toForward));

    return it;
}

ConstPackIt
DistributedDiscovery::
onNodes(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<NodeItem> items;
    it = unpack(items, it, last);

    double now = lockless::wall();
    std::vector<NodeItem> toForward;

    for (auto& item : items) {
        NodeLocation node;
        size_t ttl;
        std::tie(node, ttl) = std::move(item);

        Node value(node, ttl, now);

        auto it = nodes.find(value);
        if (it != nodes.end()) {
            it->setTTL(ttl, now);
            continue;
        }

        nodes.insert(value);
        toForward.emplace_back(node, ttl);
    }

    if (!toForward.empty())
        endpoint.broadcast(packAll(msg::Nodes, toForward));

    return it;
}

void
DistributedDiscovery::
doGet(const std::string& key, const std::vector<Address>& addrs)
{
    ConnectionHandle handle = endpoint.connect(addrs);
    if (!handle) return;

    std::vector<std::string> items = { key };
    endpoint.send(handle, packAll(msg::Get, items));
}

ConstPackIt
DistributedDiscovery::
onGet(ConnState& conn, ConstPackIt it, ConstPackIt last)
{

    std::vector<std::string> items;
    it = unpack(items, it, last);

    std::vector<DataItem> reply;
    reply.reserve(items.size());

    for (const auto& key : items) {
        auto it = data.find(key);
        if (it == data.end()) continue;

        reply.emplace_back(key, it->second);
    }

    if (!reply.empty())
        endpoint.send(conn.handle, packAll(msg::Data, reply));

    return it;
}

ConstPackIt
DistributedDiscovery::
onData(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<DataItem> items;
    it = unpack(items, it, last);

    for (auto& item : items) {
        std::string key;
        Payload payload;
        std::tie(key, payload) = std::move(item);

        auto it = watches.find(key);
        if (it == watches.end()) continue;

        for (const auto& watch : it->second)
            watch.watch(watch.handle, payload);
    }

    return it;
}

void
DistributedDiscovery::
onTimer(size_t)
{
    expireNodes(nodes);
    expireKeys();
    rotateConnections();
}

void
DistributedDiscovery::
expireNodes(List<Node>& list)
{
    while (!list.empty()) {
        const auto& node = list.pickRandom(rng);
        if (node.ttl()) return;
        list.erase(node);
    }
}

void
DistributedDiscovery::
expireKeys()
{
    std::vector<std::string> toRemove;

    for (auto& key : keys) {
        auto& list = key.second;

        expireNodes(list);
        if (!list.empty()) continue;

        toRemove.emplace_back(key.first);
    }

    for (const auto& key : toRemove)
        keys.erase(key);
}

void
DistributedDiscovery::
rotateConnections()
{
    size_t targetSize = lockless::log2(nodes.size());
    size_t disconnects =
        std::min(lockless::log2(targetSize), connections.size());

    if (connections.size() - disconnects > targetSize)
        disconnects = connections.size() - targetSize;

    for (size_t i = 0; i < disconnects; ++i) {
        std::uniform_int_distribution<size_t> dist(0, connections.size() -1);

        auto it = connections.begin();
        std::advance(it, dist(rng));

        endpoint.disconnect(it->second.handle);
    }

    size_t connects = targetSize - connections.size();
    for (const auto& node : nodes.pickRandom(rng, connects))
        endpoint.connect(node.addrs);
}

} // slick
<commit_msg>Query no longer propagates. Solves a lot of problems.<commit_after>/* discovery.cpp                                 -*- C++ -*-
   Rémi Attab (remi.attab@gmail.com), 26 Dec 2013
   FreeBSD-style copyright and disclaimer apply

   Implementation of the node discovery database.

*/

#include "discovery.h"
#include "pack.h"
#include "lockless/bits.h"

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

namespace slick {

/******************************************************************************/
/* ADDRESS                                                                    */
/******************************************************************************/

template<>
struct Pack<Address>
{
    static size_t size(const Address& value)
    {
        return packedSizeAll(value.host, value.port);
    }

    static void pack(const Address& value, PackIt first, PackIt last)
    {
        packAll(first, last, value.host, value.port);
    }

    static Address unpack(ConstPackIt first, ConstPackIt last)
    {
        Address value;
        unpackAll(first, last, value.host, value.port);
        return std::move(value);
    }
};

/******************************************************************************/
/* NODE                                                                       */
/******************************************************************************/

bool
DistributedDiscovery::Node::
operator<(const Node& other) const
{
    size_t n = std::min(addrs.size(), other.addrs.size());
    for (size_t i = 0; i < n; ++i) {
        if (addrs[i] < other.addrs[i]) return true;
        if (other.addrs[i] < addrs[i]) return false;
    }

    if (addrs.size() < other.addrs.size()) return true;
    if (other.addrs.size() < addrs.size()) return false;

    return expiration < other.expiration;
}

/******************************************************************************/
/* WATCH                                                                      */
/******************************************************************************/

DistributedDiscovery::Watch::
Watch(WatchFn watch) : watch(std::move(watch))
{
    static std::atomic<WatchHandle> handleCounter{0};
    handle = ++handleCounter;
}

/******************************************************************************/
/* LIST                                                                       */
/******************************************************************************/

template<typename T>
auto
DistributedDiscovery::List<T>::
find(const T& value) -> iterator
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    return res.first == res.second ? list.end() : res.first;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
count(const T& value) const
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    return res.first != res.second;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
insert(T value)
{
    if (count(value)) return false;

    list.emplace_back(std::move(value));
    std::sort(list.begin(), list.end());

    return true;
}

template<typename T>
bool
DistributedDiscovery::List<T>::
erase(const T& value)
{
    auto res = std::equal_range(list.begin(), list.end(), value);
    if (res.first == res.second) return false;

    list.erase(res.first);
    return true;
}

/******************************************************************************/
/* PROTOCOL                                                                   */
/******************************************************************************/

namespace msg {

static const std::string Init =  "_slick_disc_";
static constexpr uint32_t Version = 1;

typedef uint16_t Type;
static constexpr Type Keys  = 1;
static constexpr Type Query = 2;
static constexpr Type Nodes = 3;
static constexpr Type Get   = 4;
static constexpr Type Data  = 5;

} // namespace msg

/******************************************************************************/
/* DISTRIBUTED DISCOVERY                                                      */
/******************************************************************************/

size_t
DistributedDiscovery::
timerPeriod()
{
    enum { BasePeriod = 60 };
    std::uniform_int_distribution<size_t>dist(BasePeriod, BasePeriod * 2);
    return dist(rng);
}

DistributedDiscovery::
DistributedDiscovery(const std::vector<Address>& seed, Port port) :
    keyTTL_(DefaultKeyTTL),
    nodeTTL_(DefaultNodeTTL),
    rng(lockless::wall()),
    endpoint(port),
    timer(timerPeriod()) // \todo add randomness
{
    using namespace std::placeholders;

    endpoint.onPayload = bind(&DistributedDiscovery::onPayload, this, _1, _2);
    endpoint.onNewConnection = bind(&DistributedDiscovery::onConnect, this, _1);
    endpoint.onLostConnection = bind(&DistributedDiscovery::onDisconnect, this, _1);
    poller.add(endpoint);

    retracts.onOperation = std::bind(&DistributedDiscovery::retract, this, _1);
    poller.add(retracts);

    publishes.onOperation = std::bind(&DistributedDiscovery::publish, this, _1, _2);
    poller.add(publishes);

    typedef void (DistributedDiscovery::*DiscoverFn) (const std::string&, Watch&&);
    DiscoverFn discoverFn = &DistributedDiscovery::discover;
    discovers.onOperation = std::bind(discoverFn, this, _1, _2);
    poller.add(discovers);

    forgets.onOperation = std::bind(&DistributedDiscovery::forget, this, _1, _2);
    poller.add(forgets);

    timer.onTimer = bind(&DistributedDiscovery::onTimer, this, _1);
    poller.add(timer);

    for (auto& addr : seed)
        nodes.insert(Node({ addr }, nodeTTL_));
}

void
DistributedDiscovery::
poll()
{
    isPollThread.set();
    poller.poll();
}

void
DistributedDiscovery::
shutdown()
{
    isPollThread.unset();
    retracts.poll();
    publishes.poll();
    discovers.poll();
    forgets.poll();
}

void
DistributedDiscovery::
onPayload(ConnectionHandle handle, Payload&& data)
{
    auto& conn = connections[handle];
    auto it = data.cbegin(), last = data.cend();

    if (!conn) it = onInit(conn, it, last);

    while (it != last) {
        msg::Type type;
        it = unpack(type, it, last);

        switch(type) {
        case msg::Keys:  it = onKeys(conn, it, last); break;
        case msg::Query: it = onQuery(conn, it, last); break;
        case msg::Nodes: it = onNodes(conn, it, last); break;
        case msg::Get:   it = onGet(conn, it, last); break;
        case msg::Data:  it = onData(conn, it, last); break;
        default: assert(false);
        };
    }
}

Discovery::WatchHandle
DistributedDiscovery::
discover(const std::string& key, const WatchFn& fn)
{
    Watch watch(fn);
    auto handle = watch.handle;
    discover(key, std::move(watch));
    return handle;
}

void
DistributedDiscovery::
discover(const std::string& key, Watch&& watch)
{
    if (!isPollThread()) {
        discovers.defer(key, watch);
        return;
    }

    if (!watches.count(key)) {
        std::vector<QueryItem> items = { key };
        endpoint.broadcast(packAll(msg::Query, endpoint.interfaces(), items));
    }

    watches[key].insert(std::move(watch));

    for (const auto& node : keys[key])
        doGet(key, node.addrs);
}

void
DistributedDiscovery::
forget(const std::string& key, WatchHandle handle)
{
    if (!isPollThread()) {
        forgets.defer(key, handle);
        return;
    }

    auto& list = watches[key];
    list.erase(Watch(handle));

    if (list.empty())
        watches.erase(key);
}

void
DistributedDiscovery::
publish(const std::string& key, Payload&& data)
{
    if (!isPollThread()) {
        publishes.defer(key, std::move(data));
        return;
    }

    this->data[key] = std::move(data);

    std::vector<KeyItem> items;
    items.emplace_back(key, endpoint.interfaces(), keyTTL_);

    endpoint.broadcast(packAll(msg::Keys, items));
}

void
DistributedDiscovery::
retract(const std::string& key)
{
    if (!isPollThread()) {
        retracts.defer(key);
        return;
    }

    data.erase(key);
}

void
DistributedDiscovery::
onConnect(ConnectionHandle handle)
{
    auto& conn = connections[handle];
    conn.handle = handle;

    endpoint.send(handle, packAll(msg::Init, msg::Version));
}

void
DistributedDiscovery::
onDisconnect(ConnectionHandle handle)
{
    connections.erase(handle);
}

ConstPackIt
DistributedDiscovery::
onInit(ConnState& conn, ConstPackIt it, ConstPackIt last)
{
    std::string init;
    it = unpackAll(it, last, init, conn.version);

    if (init != msg::Init) {
        endpoint.disconnect(conn.handle);
        return last;
    }

    assert(conn.version == msg::Version);

    if (!data.empty()) {
        std::vector<KeyItem> items;
        for (const auto& key : data)
            items.emplace_back(key.first, endpoint.interfaces(), keyTTL_);

        endpoint.send(conn.handle, packAll(msg::Keys, items));
    }

    if (!watches.empty()) {
        std::vector<QueryItem> items;
        for (const auto& watch : watches)
            items.emplace_back(watch.first);

        auto msg = packAll(msg::Query, endpoint.interfaces(), items);
        endpoint.send(conn.handle, std::move(msg));
    }

    if (!nodes.empty()) {
        std::vector<NodeItem> items;
        items.emplace_back(endpoint.interfaces(), nodeTTL_);

        double now = lockless::wall();
        size_t picks = lockless::log2(nodes.size());

        for (const auto& node : nodes.pickRandom(rng, picks))
            items.emplace_back(node.addrs, node.ttl(now));

        endpoint.send(conn.handle, packAll(msg::Nodes, items));
    }

    return it;
}

ConstPackIt
DistributedDiscovery::
onKeys(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<KeyItem> items;
    it = unpack(items, it, last);

    double now = lockless::wall();
    std::vector<KeyItem> toForward;

    for (auto& item : items) {
        std::string key;
        NodeLocation node;
        size_t ttl;
        std::tie(key, node, ttl) = std::move(item);

        auto& list = keys[key];

        Node value(node, ttl, now);
        auto it = list.find(value);
        if (it != list.end()) {
            it->setTTL(ttl, now);
            continue;
        }

        list.insert(value);
        toForward.emplace_back(key, node, ttl);

        if (watches.count(key)) doGet(key, node);
    }

    if (!toForward.empty())
        endpoint.broadcast(packAll(msg::Keys, toForward));

    return it;
}

ConstPackIt
DistributedDiscovery::
onQuery(ConnState& conn, ConstPackIt it, ConstPackIt last)
{
    NodeLocation node;
    std::vector<QueryItem> items;
    it = unpackAll(it, last, node, items);

    for (const auto& key : items) {
        auto it = keys.find(key);
        if (it == keys.end()) continue;

        std::vector<KeyItem> items;
        for (const auto& node : it->second) {
            if (!node.ttl()) continue;
            items.emplace_back(key, node.addrs, node.ttl());
        }

        endpoint.send(conn.handle, packAll(msg::Keys, items));
    }

    return it;
}

ConstPackIt
DistributedDiscovery::
onNodes(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<NodeItem> items;
    it = unpack(items, it, last);

    double now = lockless::wall();
    std::vector<NodeItem> toForward;

    for (auto& item : items) {
        NodeLocation node;
        size_t ttl;
        std::tie(node, ttl) = std::move(item);

        Node value(node, ttl, now);

        auto it = nodes.find(value);
        if (it != nodes.end()) {
            it->setTTL(ttl, now);
            continue;
        }

        nodes.insert(value);
        toForward.emplace_back(node, ttl);
    }

    if (!toForward.empty())
        endpoint.broadcast(packAll(msg::Nodes, toForward));

    return it;
}

void
DistributedDiscovery::
doGet(const std::string& key, const std::vector<Address>& addrs)
{
    ConnectionHandle handle = endpoint.connect(addrs);
    if (!handle) return;

    std::vector<std::string> items = { key };
    endpoint.send(handle, packAll(msg::Get, items));
}

ConstPackIt
DistributedDiscovery::
onGet(ConnState& conn, ConstPackIt it, ConstPackIt last)
{

    std::vector<std::string> items;
    it = unpack(items, it, last);

    std::vector<DataItem> reply;
    reply.reserve(items.size());

    for (const auto& key : items) {
        auto it = data.find(key);
        if (it == data.end()) continue;

        reply.emplace_back(key, it->second);
    }

    if (!reply.empty())
        endpoint.send(conn.handle, packAll(msg::Data, reply));

    return it;
}

ConstPackIt
DistributedDiscovery::
onData(ConnState&, ConstPackIt it, ConstPackIt last)
{
    std::vector<DataItem> items;
    it = unpack(items, it, last);

    for (auto& item : items) {
        std::string key;
        Payload payload;
        std::tie(key, payload) = std::move(item);

        auto it = watches.find(key);
        if (it == watches.end()) continue;

        for (const auto& watch : it->second)
            watch.watch(watch.handle, payload);
    }

    return it;
}

void
DistributedDiscovery::
onTimer(size_t)
{
    expireNodes(nodes);
    expireKeys();
    rotateConnections();
}

void
DistributedDiscovery::
expireNodes(List<Node>& list)
{
    while (!list.empty()) {
        const auto& node = list.pickRandom(rng);
        if (node.ttl()) return;
        list.erase(node);
    }
}

void
DistributedDiscovery::
expireKeys()
{
    std::vector<std::string> toRemove;

    for (auto& key : keys) {
        auto& list = key.second;

        expireNodes(list);
        if (!list.empty()) continue;

        toRemove.emplace_back(key.first);
    }

    for (const auto& key : toRemove)
        keys.erase(key);
}

void
DistributedDiscovery::
rotateConnections()
{
    size_t targetSize = lockless::log2(nodes.size());
    size_t disconnects =
        std::min(lockless::log2(targetSize), connections.size());

    if (connections.size() - disconnects > targetSize)
        disconnects = connections.size() - targetSize;

    for (size_t i = 0; i < disconnects; ++i) {
        std::uniform_int_distribution<size_t> dist(0, connections.size() -1);

        auto it = connections.begin();
        std::advance(it, dist(rng));

        endpoint.disconnect(it->second.handle);
    }

    size_t connects = targetSize - connections.size();
    for (const auto& node : nodes.pickRandom(rng, connects))
        endpoint.connect(node.addrs);
}

} // slick
<|endoftext|>


<commit_before>#include <signal.h>

#include <glog/logging.h>

#include <iostream>
#include <string>
#include <sstream>

#include <boost/bind.hpp>

#include <mesos/executor.hpp>

#include <process/process.hpp>
#include <process/protobuf.hpp>

#include "common/fatal.hpp"
#include "common/lock.hpp"
#include "common/logging.hpp"
#include "common/type_utils.hpp"
#include "common/uuid.hpp"

#include "messages/messages.hpp"

using namespace mesos;
using namespace mesos::internal;

using boost::bind;
using boost::cref;

using process::UPID;

using std::string;

namespace mesos { namespace internal {

class ExecutorProcess : public ProtobufProcess<ExecutorProcess>
{
public:
  ExecutorProcess(const UPID& _slave,
                  MesosExecutorDriver* _driver,
                  Executor* _executor,
                  const FrameworkID& _frameworkId,
                  const ExecutorID& _executorId,
                  bool _local)
    : slave(_slave),
      driver(_driver),
      executor(_executor),
      frameworkId(_frameworkId),
      executorId(_executorId),
      local(_local)
  {
    installProtobufHandler<ExecutorRegisteredMessage>(
        &ExecutorProcess::registered,
        &ExecutorRegisteredMessage::args);

    installProtobufHandler<RunTaskMessage>(
        &ExecutorProcess::runTask,
        &RunTaskMessage::task);

    installProtobufHandler<KillTaskMessage>(
        &ExecutorProcess::killTask,
        &KillTaskMessage::task_id);

    installProtobufHandler<FrameworkToExecutorMessage>(
        &ExecutorProcess::frameworkMessage,
        &FrameworkToExecutorMessage::slave_id,
        &FrameworkToExecutorMessage::framework_id,
        &FrameworkToExecutorMessage::executor_id,
        &FrameworkToExecutorMessage::data);

    installProtobufHandler<ShutdownMessage>(
        &ExecutorProcess::shutdown);

    installMessageHandler(process::EXITED, &ExecutorProcess::exited);
  }

  virtual ~ExecutorProcess() {}

protected:
  virtual void operator () ()
  {
    VLOG(1) << "Executor started at: " << self();

    link(slave);

    // Register with slave.
    RegisterExecutorMessage message;
    message.mutable_framework_id()->MergeFrom(frameworkId);
    message.mutable_executor_id()->MergeFrom(executorId);
    send(slave, message);

    do { if (serve() == process::TERMINATE) break; } while (true);
  }

  void registered(const ExecutorArgs& args)
  {
    VLOG(1) << "Executor registered on slave " << args.slave_id();
    slaveId = args.slave_id();
    process::invoke(bind(&Executor::init, executor, driver, cref(args)));
  }

  void runTask(const TaskDescription& task)
  {
    VLOG(1) << "Executor asked to run task '" << task.task_id() << "'";

    process::invoke(bind(&Executor::launchTask, executor, driver,
                         cref(task)));
  }

  void killTask(const TaskID& taskId)
  {
    VLOG(1) << "Executor asked to kill task '" << taskId << "'";
    process::invoke(bind(&Executor::killTask, executor, driver,
                         cref(taskId)));
  }

  void frameworkMessage(const SlaveID& slaveId,
			const FrameworkID& frameworkId,
			const ExecutorID& executorId,
			const string& data)
  {
    VLOG(1) << "Executor received framework message";
    process::invoke(bind(&Executor::frameworkMessage, executor, driver,
                         cref(data)));
  }

  void shutdown()
  {
    VLOG(1) << "Executor asked to shutdown";
    // TODO(benh): Any need to invoke driver.stop?
    process::invoke(bind(&Executor::shutdown, executor, driver));
    if (!local) {
      exit(0);
    } else {
      process::terminate(self());
    }
  }

  void exited()
  {
    VLOG(1) << "Slave exited, trying to shutdown";

    // TODO: Pass an argument to shutdown to tell it this is abnormal?
    process::invoke(bind(&Executor::shutdown, executor, driver));

    // This is a pretty bad state ... no slave is left. Rather
    // than exit lets kill our process group (which includes
    // ourself) hoping to clean up any processes this executor
    // launched itself.
    // TODO(benh): Maybe do a SIGTERM and then later do a SIGKILL?
    if (!local) {
      killpg(0, SIGKILL);
    } else {
      process::terminate(self());
    }
  }

private:
  friend class mesos::MesosExecutorDriver;

  UPID slave;
  MesosExecutorDriver* driver;
  Executor* executor;
  FrameworkID frameworkId;
  ExecutorID executorId;
  SlaveID slaveId;
  bool local;
};

}} // namespace mesos { namespace internal {

// Implementation of C++ API.

MesosExecutorDriver::MesosExecutorDriver(Executor* _executor)
  : executor(_executor), running(false)
{
  // Create mutex and condition variable
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init(&mutex, &attr);
  pthread_mutexattr_destroy(&attr);
  pthread_cond_init(&cond, 0);

  // TODO(benh): Initialize glog.

  // Initialize libprocess library (but not glog, done above).
  process::initialize(false);
}

MesosExecutorDriver::~MesosExecutorDriver()
{
  // Just as in SchedulerProcess, we might wait here indefinitely if
  // MesosExecutorDriver::stop has not been invoked.
  process::wait(process->self());
  delete process;

  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&cond);
}

int MesosExecutorDriver::start()
{
  Lock lock(&mutex);

  if (running) {
    return -1;
  }

  // Set stream buffering mode to flush on newlines so that we capture logs
  // from user processes even when output is redirected to a file.
  setvbuf(stdout, 0, _IOLBF, 0);
  setvbuf(stderr, 0, _IOLBF, 0);

  bool local;

  UPID slave;
  FrameworkID frameworkId;
  ExecutorID executorId;

  char* value;
  std::istringstream iss;

  /* Check if this is local (for example, for testing). */
  value = getenv("MESOS_LOCAL");

  if (value != NULL) {
    local = true;
  } else {
    local = false;
  }

  /* Get slave PID from environment. */
  value = getenv("MESOS_SLAVE_PID");

  if (value == NULL) {
    fatal("expecting MESOS_SLAVE_PID in environment");
  }

  slave = UPID(value);

  if (!slave) {
    fatal("cannot parse MESOS_SLAVE_PID");
  }

  /* Get framework ID from environment. */
  value = getenv("MESOS_FRAMEWORK_ID");

  if (value == NULL) {
    fatal("expecting MESOS_FRAMEWORK_ID in environment");
  }

  frameworkId.set_value(value);

  /* Get executor ID from environment. */
  value = getenv("MESOS_EXECUTOR_ID");

  if (value == NULL) {
    fatal("expecting MESOS_EXECUTOR_ID in environment");
  }

  executorId.set_value(value);

  process =
    new ExecutorProcess(slave, this, executor, frameworkId, executorId, local);

  process::spawn(process);

  running = true;

  return 0;
}

int MesosExecutorDriver::stop()
{
  Lock lock(&mutex);

  if (!running) {
    return -1;
  }

  CHECK(process != NULL);

  process::terminate(process->self());

  running = false;

  pthread_cond_signal(&cond);

  return 0;
}

int MesosExecutorDriver::join()
{
  Lock lock(&mutex);

  while (running) {
    pthread_cond_wait(&cond, &mutex);
  }

  return 0;
}

int MesosExecutorDriver::run()
{
  int ret = start();
  return ret != 0 ? ret : join();
}

int MesosExecutorDriver::sendStatusUpdate(const TaskStatus& status)
{
  Lock lock(&mutex);

  if (!running) {
    //executor->error(this, EINVAL, "Executor has exited");
    return -1;
  }

  CHECK(process != NULL);

  // TODO(benh): Do a dispatch to Executor first?
  StatusUpdateMessage message;
  StatusUpdate* update = message.mutable_update();
  update->mutable_framework_id()->MergeFrom(process->frameworkId);
  update->mutable_executor_id()->MergeFrom(process->executorId);
  update->mutable_slave_id()->MergeFrom(process->slaveId);
  update->mutable_status()->MergeFrom(status);
  update->set_timestamp(process->elapsedTime());
  update->set_uuid(UUID::random().toBytes());
  message.set_reliable(false);
  process->send(process->slave, message);

  return 0;
}

int MesosExecutorDriver::sendFrameworkMessage(const string& data)
{
  Lock lock(&mutex);

  if (!running) {
    //executor->error(this, EINVAL, "Executor has exited");
    return -1;
  }

  CHECK(process != NULL);

  // TODO(benh): Do a dispatch to Executor first?
  ExecutorToFrameworkMessage message;
  message.mutable_slave_id()->MergeFrom(process->slaveId);
  message.mutable_framework_id()->MergeFrom(process->frameworkId);
  message.mutable_executor_id()->MergeFrom(process->executorId);
  message.set_data(data);
  process->send(process->slave, message);

  return 0;
}
<commit_msg>Made exec.cpp use namespace process like the others for readability.<commit_after>#include <signal.h>

#include <glog/logging.h>

#include <iostream>
#include <string>
#include <sstream>

#include <boost/bind.hpp>

#include <mesos/executor.hpp>

#include <process/dispatch.hpp>
#include <process/process.hpp>
#include <process/protobuf.hpp>

#include "common/fatal.hpp"
#include "common/lock.hpp"
#include "common/logging.hpp"
#include "common/type_utils.hpp"
#include "common/uuid.hpp"

#include "messages/messages.hpp"

using namespace mesos;
using namespace mesos::internal;

using namespace process;

using boost::bind;
using boost::cref;

using std::string;

namespace mesos { namespace internal {

class ExecutorProcess : public ProtobufProcess<ExecutorProcess>
{
public:
  ExecutorProcess(const UPID& _slave,
                  MesosExecutorDriver* _driver,
                  Executor* _executor,
                  const FrameworkID& _frameworkId,
                  const ExecutorID& _executorId,
                  bool _local)
    : slave(_slave),
      driver(_driver),
      executor(_executor),
      frameworkId(_frameworkId),
      executorId(_executorId),
      local(_local)
  {
    installProtobufHandler<ExecutorRegisteredMessage>(
        &ExecutorProcess::registered,
        &ExecutorRegisteredMessage::args);

    installProtobufHandler<RunTaskMessage>(
        &ExecutorProcess::runTask,
        &RunTaskMessage::task);

    installProtobufHandler<KillTaskMessage>(
        &ExecutorProcess::killTask,
        &KillTaskMessage::task_id);

    installProtobufHandler<FrameworkToExecutorMessage>(
        &ExecutorProcess::frameworkMessage,
        &FrameworkToExecutorMessage::slave_id,
        &FrameworkToExecutorMessage::framework_id,
        &FrameworkToExecutorMessage::executor_id,
        &FrameworkToExecutorMessage::data);

    installProtobufHandler<ShutdownMessage>(
        &ExecutorProcess::shutdown);

    installMessageHandler(EXITED, &ExecutorProcess::exited);
  }

  virtual ~ExecutorProcess() {}

protected:
  virtual void operator () ()
  {
    VLOG(1) << "Executor started at: " << self();

    link(slave);

    // Register with slave.
    RegisterExecutorMessage message;
    message.mutable_framework_id()->MergeFrom(frameworkId);
    message.mutable_executor_id()->MergeFrom(executorId);
    send(slave, message);

    do { if (serve() == TERMINATE) break; } while (true);
  }

  void registered(const ExecutorArgs& args)
  {
    VLOG(1) << "Executor registered on slave " << args.slave_id();
    slaveId = args.slave_id();
    invoke(bind(&Executor::init, executor, driver, cref(args)));
  }

  void runTask(const TaskDescription& task)
  {
    VLOG(1) << "Executor asked to run task '" << task.task_id() << "'";

    invoke(bind(&Executor::launchTask, executor, driver, cref(task)));
  }

  void killTask(const TaskID& taskId)
  {
    VLOG(1) << "Executor asked to kill task '" << taskId << "'";
    invoke(bind(&Executor::killTask, executor, driver, cref(taskId)));
  }

  void frameworkMessage(const SlaveID& slaveId,
			const FrameworkID& frameworkId,
			const ExecutorID& executorId,
			const string& data)
  {
    VLOG(1) << "Executor received framework message";
    invoke(bind(&Executor::frameworkMessage, executor, driver, cref(data)));
  }

  void shutdown()
  {
    VLOG(1) << "Executor asked to shutdown";
    // TODO(benh): Any need to invoke driver.stop?
    invoke(bind(&Executor::shutdown, executor, driver));
    if (!local) {
      exit(0);
    } else {
      terminate(this);
    }
  }

  void exited()
  {
    VLOG(1) << "Slave exited, trying to shutdown";

    // TODO: Pass an argument to shutdown to tell it this is abnormal?
    invoke(bind(&Executor::shutdown, executor, driver));

    // This is a pretty bad state ... no slave is left. Rather
    // than exit lets kill our process group (which includes
    // ourself) hoping to clean up any processes this executor
    // launched itself.
    // TODO(benh): Maybe do a SIGTERM and then later do a SIGKILL?
    if (!local) {
      killpg(0, SIGKILL);
    } else {
      terminate(this);
    }
  }

  void sendStatusUpdate(const TaskStatus& status)
  {
    StatusUpdateMessage message;
    StatusUpdate* update = message.mutable_update();
    update->mutable_framework_id()->MergeFrom(frameworkId);
    update->mutable_executor_id()->MergeFrom(executorId);
    update->mutable_slave_id()->MergeFrom(slaveId);
    update->mutable_status()->MergeFrom(status);
    update->set_timestamp(elapsedTime());
    update->set_uuid(UUID::random().toBytes());
    message.set_reliable(false);
    send(slave, message);
  }

  void sendFrameworkMessage(const string& data)
  {
    ExecutorToFrameworkMessage message;
    message.mutable_slave_id()->MergeFrom(slaveId);
    message.mutable_framework_id()->MergeFrom(frameworkId);
    message.mutable_executor_id()->MergeFrom(executorId);
    message.set_data(data);
    send(slave, message);
  }

private:
  friend class mesos::MesosExecutorDriver;

  UPID slave;
  MesosExecutorDriver* driver;
  Executor* executor;
  FrameworkID frameworkId;
  ExecutorID executorId;
  SlaveID slaveId;
  bool local;
};

}} // namespace mesos { namespace internal {

// Implementation of C++ API.

MesosExecutorDriver::MesosExecutorDriver(Executor* _executor)
  : executor(_executor), running(false)
{
  // Create mutex and condition variable
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  pthread_mutex_init(&mutex, &attr);
  pthread_mutexattr_destroy(&attr);
  pthread_cond_init(&cond, 0);

  // TODO(benh): Initialize glog.

  // Initialize libprocess library (but not glog, done above).
  process::initialize(false);
}

MesosExecutorDriver::~MesosExecutorDriver()
{
  // Just as in SchedulerProcess, we might wait here indefinitely if
  // MesosExecutorDriver::stop has not been invoked.
  wait(process);
  delete process;

  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&cond);
}

int MesosExecutorDriver::start()
{
  Lock lock(&mutex);

  if (running) {
    return -1;
  }

  // Set stream buffering mode to flush on newlines so that we capture logs
  // from user processes even when output is redirected to a file.
  setvbuf(stdout, 0, _IOLBF, 0);
  setvbuf(stderr, 0, _IOLBF, 0);

  bool local;

  UPID slave;
  FrameworkID frameworkId;
  ExecutorID executorId;

  char* value;
  std::istringstream iss;

  /* Check if this is local (for example, for testing). */
  value = getenv("MESOS_LOCAL");

  if (value != NULL) {
    local = true;
  } else {
    local = false;
  }

  /* Get slave PID from environment. */
  value = getenv("MESOS_SLAVE_PID");

  if (value == NULL) {
    fatal("expecting MESOS_SLAVE_PID in environment");
  }

  slave = UPID(value);

  if (!slave) {
    fatal("cannot parse MESOS_SLAVE_PID");
  }

  /* Get framework ID from environment. */
  value = getenv("MESOS_FRAMEWORK_ID");

  if (value == NULL) {
    fatal("expecting MESOS_FRAMEWORK_ID in environment");
  }

  frameworkId.set_value(value);

  /* Get executor ID from environment. */
  value = getenv("MESOS_EXECUTOR_ID");

  if (value == NULL) {
    fatal("expecting MESOS_EXECUTOR_ID in environment");
  }

  executorId.set_value(value);

  process =
    new ExecutorProcess(slave, this, executor, frameworkId, executorId, local);

  spawn(process);

  running = true;

  return 0;
}

int MesosExecutorDriver::stop()
{
  Lock lock(&mutex);

  if (!running) {
    return -1;
  }

  CHECK(process != NULL);
  terminate(process);
  running = false;
  pthread_cond_signal(&cond);

  return 0;
}

int MesosExecutorDriver::join()
{
  Lock lock(&mutex);

  while (running) {
    pthread_cond_wait(&cond, &mutex);
  }

  return 0;
}

int MesosExecutorDriver::run()
{
  int ret = start();
  return ret != 0 ? ret : join();
}

int MesosExecutorDriver::sendStatusUpdate(const TaskStatus& status)
{
  Lock lock(&mutex);

  if (!running) {
    //executor->error(this, EINVAL, "Executor has exited");
    return -1;
  }

  CHECK(process != NULL);
  dispatch(process, &ExecutorProcess::sendStatusUpdate, status);

  return 0;
}

int MesosExecutorDriver::sendFrameworkMessage(const string& data)
{
  Lock lock(&mutex);

  if (!running) {
    //executor->error(this, EINVAL, "Executor has exited");
    return -1;
  }

  CHECK(process != NULL);
  dispatch(process, &ExecutorProcess::sendFrameworkMessage, data);

  return 0;
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2006-2014, Arvid Norberg
All rights reserved.

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 author 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.

*/

#include <boost/version.hpp>
#include <boost/bind.hpp>
#include "libtorrent/assert.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file_storage.hpp" // for file_entry

namespace libtorrent
{
	file_pool::file_pool(int size)
		: m_size(size)
		, m_low_prio_io(true)
	{
	}

	file_pool::~file_pool()
	{
	}

#ifdef TORRENT_WINDOWS
	void set_low_priority(file_handle const& f)
	{
		// file prio is only supported on vista and up
		// so load the functions dynamically
		typedef enum _FILE_INFO_BY_HANDLE_CLASS {
			FileBasicInfo,
			FileStandardInfo,
			FileNameInfo,
			FileRenameInfo,
			FileDispositionInfo,
			FileAllocationInfo,
			FileEndOfFileInfo,
			FileStreamInfo,
			FileCompressionInfo,
			FileAttributeTagInfo,
			FileIdBothDirectoryInfo,
			FileIdBothDirectoryRestartInfo,
			FileIoPriorityHintInfo,
			FileRemoteProtocolInfo,
			MaximumFileInfoByHandleClass
		} FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS;

		typedef enum _PRIORITY_HINT {
			IoPriorityHintVeryLow = 0,
			IoPriorityHintLow,
			IoPriorityHintNormal,
			MaximumIoPriorityHintType
		} PRIORITY_HINT;

		typedef struct _FILE_IO_PRIORITY_HINT_INFO {
			PRIORITY_HINT PriorityHint;
		} FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO;

		typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize);
		static SetFileInformationByHandle_t SetFileInformationByHandle = NULL;

		static bool failed_kernel_load = false;

		if (failed_kernel_load) return;

		if (SetFileInformationByHandle == NULL)
		{
			HMODULE kernel32 = LoadLibraryA("kernel32.dll");
			if (kernel32 == NULL)
			{
				failed_kernel_load = true;
				return;
			}

			SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle");
			if (SetFileInformationByHandle == NULL)
			{
				failed_kernel_load = true;
				return;
			}
		}

		TORRENT_ASSERT(SetFileInformationByHandle);

		FILE_IO_PRIORITY_HINT_INFO io_hint;
		io_hint.PriorityHint = IoPriorityHintLow;
		SetFileInformationByHandle(f->native_handle(),
			FileIoPriorityHintInfo, &io_hint, sizeof(io_hint));
	}
#endif // TORRENT_WINDOWS

	file_handle file_pool::open_file(void* st, std::string const& p
		, int file_index, file_storage const& fs, int m, error_code& ec)
	{
		mutex::scoped_lock l(m_mutex);

#if TORRENT_USE_ASSERTS
		// we're not allowed to open a file
		// from a deleted storage!
		TORRENT_ASSERT(std::find(m_deleted_storages.begin(), m_deleted_storages.end(), std::make_pair(fs.name(), (void const*)&fs))
			== m_deleted_storages.end());
#endif

		TORRENT_ASSERT(st != 0);
		TORRENT_ASSERT(is_complete(p));
		TORRENT_ASSERT((m & file::rw_mask) == file::read_only
			|| (m & file::rw_mask) == file::read_write);
		file_set::iterator i = m_files.find(std::make_pair(st, file_index));
		if (i != m_files.end())
		{
			lru_file_entry& e = i->second;
			e.last_use = time_now();

			if (e.key != st && ((e.mode & file::rw_mask) != file::read_only
				|| (m & file::rw_mask) != file::read_only))
			{
				// this means that another instance of the storage
				// is using the exact same file.
#if BOOST_VERSION >= 103500
				ec = errors::file_collision;
#endif
				return file_handle();
			}

			e.key = st;
			// if we asked for a file in write mode,
			// and the cached file is is not opened in
			// write mode, re-open it
			if ((((e.mode & file::rw_mask) != file::read_write)
				&& ((m & file::rw_mask) == file::read_write))
				|| (e.mode & file::random_access) != (m & file::random_access))
			{
				// close the file before we open it with
				// the new read/write privilages, since windows may
				// file opening a file twice. However, since there may
				// be outstanding operations on it, we can't close the
				// file, we can only delete our reference to it.
				// if this is the only reference to the file, it will be closed
				e.file_ptr = boost::make_shared<file>();

				std::string full_path = fs.file_path(file_index, p);
				if (!e.file_ptr->open(full_path, m, ec))
				{
					m_files.erase(i);
					return file_handle();
				}
#ifdef TORRENT_WINDOWS
				if (m_low_prio_io)
					set_low_priority(e.file_ptr);
#endif

				TORRENT_ASSERT(e.file_ptr->is_open());
				e.mode = m;
			}
			return e.file_ptr;
		}

		lru_file_entry e;
		e.file_ptr = boost::make_shared<file>();
		if (!e.file_ptr)
		{
			ec = error_code(ENOMEM, get_posix_category());
			return e.file_ptr;
		}
		std::string full_path = fs.file_path(file_index, p);
		if (!e.file_ptr->open(full_path, m, ec))
			return file_handle();
#ifdef TORRENT_WINDOWS
		if (m_low_prio_io)
			set_low_priority(e.file_ptr);
#endif
		e.mode = m;
		e.key = st;
		m_files.insert(std::make_pair(std::make_pair(st, file_index), e));
		TORRENT_ASSERT(e.file_ptr->is_open());

		file_handle file_ptr = e.file_ptr;

		// the file is not in our cache
		if ((int)m_files.size() >= m_size)
		{
			// the file cache is at its maximum size, close
			// the least recently used (lru) file from it
			remove_oldest(l);
		}
		return file_ptr;
	}

	void file_pool::get_status(std::vector<pool_file_status>* files, void* st) const
	{
		mutex::scoped_lock l(m_mutex);

		file_set::const_iterator start = m_files.lower_bound(std::make_pair(st, 0));
		file_set::const_iterator end = m_files.upper_bound(std::make_pair(st, INT_MAX));

		for (file_set::const_iterator i = start; i != end; ++i)
		{
			pool_file_status s;
			s.file_index = i->first.second;
			s.open_mode = i->second.mode;
			s.last_use = i->second.last_use;
			files->push_back(s);
		}
	}

	void file_pool::remove_oldest(mutex::scoped_lock& l)
	{
		file_set::iterator i = std::min_element(m_files.begin(), m_files.end()
			, boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _1))
				< boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _2)));
		if (i == m_files.end()) return;

		file_handle file_ptr = i->second.file_ptr;
		m_files.erase(i);

		// closing a file may be long running operation (mac os x)
		l.unlock();
		file_ptr.reset();
		l.lock();
	}

	void file_pool::release(void* st, int file_index)
	{
		mutex::scoped_lock l(m_mutex);

		file_set::iterator i = m_files.find(std::make_pair(st, file_index));
		if (i == m_files.end()) return;

		file_handle file_ptr = i->second.file_ptr;
		m_files.erase(i);

		// closing a file may be long running operation (mac os x)
		l.unlock();
		file_ptr.reset();
	}

	// closes files belonging to the specified
	// storage. If 0 is passed, all files are closed
	void file_pool::release(void* st)
	{
		mutex::scoped_lock l(m_mutex);

		if (st == 0)
		{
			file_set tmp;
			tmp.swap(m_files);
			l.unlock();
			return;
		}

		std::vector<file_handle> to_close;
		for (file_set::iterator i = m_files.begin();
			i != m_files.end();)
		{
			if (i->second.key == st)
			{
				to_close.push_back(i->second.file_ptr);
				m_files.erase(i++);
			}
			else
				++i;
		}
		l.unlock();
		// the files are closed here
	}

#if TORRENT_USE_ASSERTS
	void file_pool::mark_deleted(file_storage const& fs)
	{
		mutex::scoped_lock l(m_mutex);
		m_deleted_storages.push_back(std::make_pair(fs.name(), (void const*)&fs));
		if(m_deleted_storages.size() > 100)
			m_deleted_storages.erase(m_deleted_storages.begin());
	}

	bool file_pool::assert_idle_files(void* st) const
	{
		mutex::scoped_lock l(m_mutex);

		for (file_set::const_iterator i = m_files.begin();
			i != m_files.end(); ++i)
		{
			if (i->second.key == st && !i->second.file_ptr.unique())
				return false;
		}
		return true;
	}
#endif

	void file_pool::resize(int size)
	{
		mutex::scoped_lock l(m_mutex);

		TORRENT_ASSERT(size > 0);

		if (size == m_size) return;
		m_size = size;
		if (int(m_files.size()) <= m_size) return;

		// close the least recently used files
		while (int(m_files.size()) > m_size)
			remove_oldest(l);
	}

}

<commit_msg>move closing of files outside of file pool mutex<commit_after>/*

Copyright (c) 2006-2014, Arvid Norberg
All rights reserved.

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 author 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.

*/

#include <boost/version.hpp>
#include <boost/bind.hpp>
#include "libtorrent/assert.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/error_code.hpp"
#include "libtorrent/file_storage.hpp" // for file_entry

namespace libtorrent
{
	file_pool::file_pool(int size)
		: m_size(size)
		, m_low_prio_io(true)
	{
	}

	file_pool::~file_pool()
	{
	}

#ifdef TORRENT_WINDOWS
	void set_low_priority(file_handle const& f)
	{
		// file prio is only supported on vista and up
		// so load the functions dynamically
		typedef enum _FILE_INFO_BY_HANDLE_CLASS {
			FileBasicInfo,
			FileStandardInfo,
			FileNameInfo,
			FileRenameInfo,
			FileDispositionInfo,
			FileAllocationInfo,
			FileEndOfFileInfo,
			FileStreamInfo,
			FileCompressionInfo,
			FileAttributeTagInfo,
			FileIdBothDirectoryInfo,
			FileIdBothDirectoryRestartInfo,
			FileIoPriorityHintInfo,
			FileRemoteProtocolInfo,
			MaximumFileInfoByHandleClass
		} FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS;

		typedef enum _PRIORITY_HINT {
			IoPriorityHintVeryLow = 0,
			IoPriorityHintLow,
			IoPriorityHintNormal,
			MaximumIoPriorityHintType
		} PRIORITY_HINT;

		typedef struct _FILE_IO_PRIORITY_HINT_INFO {
			PRIORITY_HINT PriorityHint;
		} FILE_IO_PRIORITY_HINT_INFO, *PFILE_IO_PRIORITY_HINT_INFO;

		typedef BOOL (WINAPI *SetFileInformationByHandle_t)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize);
		static SetFileInformationByHandle_t SetFileInformationByHandle = NULL;

		static bool failed_kernel_load = false;

		if (failed_kernel_load) return;

		if (SetFileInformationByHandle == NULL)
		{
			HMODULE kernel32 = LoadLibraryA("kernel32.dll");
			if (kernel32 == NULL)
			{
				failed_kernel_load = true;
				return;
			}

			SetFileInformationByHandle = (SetFileInformationByHandle_t)GetProcAddress(kernel32, "SetFileInformationByHandle");
			if (SetFileInformationByHandle == NULL)
			{
				failed_kernel_load = true;
				return;
			}
		}

		TORRENT_ASSERT(SetFileInformationByHandle);

		FILE_IO_PRIORITY_HINT_INFO io_hint;
		io_hint.PriorityHint = IoPriorityHintLow;
		SetFileInformationByHandle(f->native_handle(),
			FileIoPriorityHintInfo, &io_hint, sizeof(io_hint));
	}
#endif // TORRENT_WINDOWS

	file_handle file_pool::open_file(void* st, std::string const& p
		, int file_index, file_storage const& fs, int m, error_code& ec)
	{
		// potentially used to hold a reference to a file object that's
		// about to be destructed. If we have such object we assign it to
		// this member to be destructed after we release the mutex. On some
		// operating systems (such as OSX) closing a file may take a long
		// time. We don't want to hold the mutex for that.
		file_handle defer_destruction;

		mutex::scoped_lock l(m_mutex);

#if TORRENT_USE_ASSERTS
		// we're not allowed to open a file
		// from a deleted storage!
		TORRENT_ASSERT(std::find(m_deleted_storages.begin(), m_deleted_storages.end(), std::make_pair(fs.name(), (void const*)&fs))
			== m_deleted_storages.end());
#endif

		TORRENT_ASSERT(st != 0);
		TORRENT_ASSERT(is_complete(p));
		TORRENT_ASSERT((m & file::rw_mask) == file::read_only
			|| (m & file::rw_mask) == file::read_write);
		file_set::iterator i = m_files.find(std::make_pair(st, file_index));
		if (i != m_files.end())
		{
			lru_file_entry& e = i->second;
			e.last_use = time_now();

			if (e.key != st && ((e.mode & file::rw_mask) != file::read_only
				|| (m & file::rw_mask) != file::read_only))
			{
				// this means that another instance of the storage
				// is using the exact same file.
#if BOOST_VERSION >= 103500
				ec = errors::file_collision;
#endif
				return file_handle();
			}

			e.key = st;
			// if we asked for a file in write mode,
			// and the cached file is is not opened in
			// write mode, re-open it
			if ((((e.mode & file::rw_mask) != file::read_write)
				&& ((m & file::rw_mask) == file::read_write))
				|| (e.mode & file::random_access) != (m & file::random_access))
			{
				// close the file before we open it with
				// the new read/write privilages, since windows may
				// file opening a file twice. However, since there may
				// be outstanding operations on it, we can't close the
				// file, we can only delete our reference to it.
				// if this is the only reference to the file, it will be closed
				defer_destruction = e.file_ptr;
				e.file_ptr = boost::make_shared<file>();

				std::string full_path = fs.file_path(file_index, p);
				if (!e.file_ptr->open(full_path, m, ec))
				{
					m_files.erase(i);
					return file_handle();
				}
#ifdef TORRENT_WINDOWS
				if (m_low_prio_io)
					set_low_priority(e.file_ptr);
#endif

				TORRENT_ASSERT(e.file_ptr->is_open());
				e.mode = m;
			}
			return e.file_ptr;
		}

		lru_file_entry e;
		e.file_ptr = boost::make_shared<file>();
		if (!e.file_ptr)
		{
			ec = error_code(ENOMEM, get_posix_category());
			return e.file_ptr;
		}
		std::string full_path = fs.file_path(file_index, p);
		if (!e.file_ptr->open(full_path, m, ec))
			return file_handle();
#ifdef TORRENT_WINDOWS
		if (m_low_prio_io)
			set_low_priority(e.file_ptr);
#endif
		e.mode = m;
		e.key = st;
		m_files.insert(std::make_pair(std::make_pair(st, file_index), e));
		TORRENT_ASSERT(e.file_ptr->is_open());

		file_handle file_ptr = e.file_ptr;

		// the file is not in our cache
		if ((int)m_files.size() >= m_size)
		{
			// the file cache is at its maximum size, close
			// the least recently used (lru) file from it
			remove_oldest(l);
		}
		return file_ptr;
	}

	void file_pool::get_status(std::vector<pool_file_status>* files, void* st) const
	{
		mutex::scoped_lock l(m_mutex);

		file_set::const_iterator start = m_files.lower_bound(std::make_pair(st, 0));
		file_set::const_iterator end = m_files.upper_bound(std::make_pair(st, INT_MAX));

		for (file_set::const_iterator i = start; i != end; ++i)
		{
			pool_file_status s;
			s.file_index = i->first.second;
			s.open_mode = i->second.mode;
			s.last_use = i->second.last_use;
			files->push_back(s);
		}
	}

	void file_pool::remove_oldest(mutex::scoped_lock& l)
	{
		file_set::iterator i = std::min_element(m_files.begin(), m_files.end()
			, boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _1))
				< boost::bind(&lru_file_entry::last_use, boost::bind(&file_set::value_type::second, _2)));
		if (i == m_files.end()) return;

		file_handle file_ptr = i->second.file_ptr;
		m_files.erase(i);

		// closing a file may be long running operation (mac os x)
		l.unlock();
		file_ptr.reset();
		l.lock();
	}

	void file_pool::release(void* st, int file_index)
	{
		mutex::scoped_lock l(m_mutex);

		file_set::iterator i = m_files.find(std::make_pair(st, file_index));
		if (i == m_files.end()) return;

		file_handle file_ptr = i->second.file_ptr;
		m_files.erase(i);

		// closing a file may be long running operation (mac os x)
		l.unlock();
		file_ptr.reset();
	}

	// closes files belonging to the specified
	// storage. If 0 is passed, all files are closed
	void file_pool::release(void* st)
	{
		mutex::scoped_lock l(m_mutex);

		if (st == 0)
		{
			file_set tmp;
			tmp.swap(m_files);
			l.unlock();
			return;
		}

		std::vector<file_handle> to_close;
		for (file_set::iterator i = m_files.begin();
			i != m_files.end();)
		{
			if (i->second.key == st)
			{
				to_close.push_back(i->second.file_ptr);
				m_files.erase(i++);
			}
			else
				++i;
		}
		l.unlock();
		// the files are closed here
	}

#if TORRENT_USE_ASSERTS
	void file_pool::mark_deleted(file_storage const& fs)
	{
		mutex::scoped_lock l(m_mutex);
		m_deleted_storages.push_back(std::make_pair(fs.name(), (void const*)&fs));
		if(m_deleted_storages.size() > 100)
			m_deleted_storages.erase(m_deleted_storages.begin());
	}

	bool file_pool::assert_idle_files(void* st) const
	{
		mutex::scoped_lock l(m_mutex);

		for (file_set::const_iterator i = m_files.begin();
			i != m_files.end(); ++i)
		{
			if (i->second.key == st && !i->second.file_ptr.unique())
				return false;
		}
		return true;
	}
#endif

	void file_pool::resize(int size)
	{
		mutex::scoped_lock l(m_mutex);

		TORRENT_ASSERT(size > 0);

		if (size == m_size) return;
		m_size = size;
		if (int(m_files.size()) <= m_size) return;

		// close the least recently used files
		while (int(m_files.size()) > m_size)
			remove_oldest(l);
	}

}

<|endoftext|>


<commit_before>//2014.03.28 - 2014.03.30 - 2014.03.31 Gustaf - CTG.

/* EXERCISE 4-3 :

For our dynamically allocated strings, create a function replaceString that takes
three parameters, each of type arrayString: source, target, and replaceText.

The function replaces every occurrence of target in source with replaceText.

For example,
  if source points to an array containing "abcdabee",
  target points to "ab",
  and replaceText points to "xyz",
  then when the function ends, source should point to an array containing "xyzcdxyzee".

=== PLAN ===

OK - Diagram of the example.
- Traverse (go over) the SOURCE string and identify the initial and final positions of the TARGET string.
  ok- Identify one character.
  ok- Identify two characters.
  ok- Identify three characters.
  ok- Identify four characters.
  - Identify lots of characters.

*/

#include <iostream>

using namespace std;

typedef char *arrayString;

int main()
{
  cout << "Variable-Length String Manipulation. TEST identify the limits." << endl;

  int ARRAY_SIZE = 9;
  int posIni = -1, posFinal = -1;

  arrayString a = new char[ARRAY_SIZE];
  a[0] = 'a'; a[1] = 'b'; a[2] = 'c'; a[3] = 'd';
  a[4] = 'a'; a[5] = 'b'; a[6] = 'e'; a[7] = 'e'; a[8] = 0;

  cout << "Initial string: " << a << endl << endl;

  // --- Identifying one character.
  cout << "Identifying one character." << endl;

  char targetChar = 0;
  targetChar = 'b';
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    if (a[i] == targetChar)
    {
      posIni = i;
      cout << "Target - index: " << targetChar << " - " << posIni << endl;
      cout << endl;
    }

  }

  // --- Identifying two contiguous characters.
  cout << "Identifying two contiguous character." << endl;

  char targetArray[2] = {'a', 'b'};
  // char targetArray[2] = {'b', 'z'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (a[i] == targetArray[0]) )
    {
      posIni = i;
    }

    // Find second adjacent char, in next cicle.
    if ( (posIni != -1) && (i == (posIni + 1) ) && (a[i] == targetArray[1]) )
    {
      posFinal = i;

      cout << "Target - index: " << targetArray[0] << " - " << posIni << endl;
      cout << "Target - index: " << targetArray[1] << " - " << posFinal << endl;
      cout << endl;

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  // --- Identifying three contiguous characters.
  cout << "Identifying three contiguous character." << endl;

  char targetArrayThree[3] = {'a', 'b', 'c'};
  // char targetArrayThree[3] = {'b', 'e', 'e'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (a[i] == targetArrayThree[0]) )
    {
      posIni = i;
    }

    // Check second adjacent character.
    if ( (posIni != -1) && (i == (posIni + 1)) )
    {
      if (a[i] == targetArrayThree[1])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayThree[1])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check third adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posFinal + 1)) )
    {
      if (a[i] == targetArrayThree[2])
      {
        posFinal = i;

        cout << "Target initial - index: " << targetArrayThree[0] << " - " << posIni << endl;
        cout << "Target final   - index: " << targetArrayThree[2] << " - " << posFinal << endl;
        cout << endl;
      }

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  // --- Identifying four contiguous characters.
  cout << "Identifying four contiguous character." << endl;

  char targetArrayFour[4] = {'a', 'b', 'c', 'd'};
  // char targetArrayFour[4] = {'b', 'c', 'd', 'a'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (posFinal == -1) )
    {
      if (a[i] == targetArrayFour[0])
      {
        posIni = i;
      }
    }

    // Check second adjacent character.
    if ( (posIni != -1) && (posFinal == -1) && (i == (posIni + 1)) )
    {
      if (a[i] == targetArrayFour[1])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayFour[1])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check third adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posIni + 2)) )
    {
      if (a[i] == targetArrayFour[2])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayFour[2])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check fourth adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posIni + 3)) )
    {
      if (a[i] == targetArrayFour[3])
      {
        posFinal = i;

        cout << "Target initial - index: " << targetArrayFour[0] << " - " << posIni << endl;
        cout << "Target final   - index: " << targetArrayFour[3] << " - " << posFinal << endl;
        cout << endl;
      }

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  delete a; // Free memory of the dinamic array.

  cout << endl;
  return 0;
}
<commit_msg>Chapter 04 exercice 4-3 partial progress. Test of identify one to lots of characters.<commit_after>//2014.03.28 - 2014.03.30 - 2014.03.31 - 2014.04.01 Gustaf - CTG.

/* EXERCISE 4-3 :

For our dynamically allocated strings, create a function replaceString that takes
three parameters, each of type arrayString: source, target, and replaceText.

The function replaces every occurrence of target in source with replaceText.

For example,
  if source points to an array containing "abcdabee",
  target points to "ab",
  and replaceText points to "xyz",
  then when the function ends, source should point to an array containing "xyzcdxyzee".

=== PLAN ===

OK - Diagram of the example.
OK - Traverse (go over) the SOURCE string and identify the initial and final positions of the TARGET string.
  ok- Identify one character.
  ok- Identify two characters.
  ok- Identify three characters.
  ok- Identify four characters.
  ok- Identify FROM one TO lots of characters.

*/

#include <iostream>

using namespace std;

typedef char *arrayString;

int main()
{
  cout << "Variable-Length String Manipulation. TEST identify the limits." << endl;

  int ARRAY_SIZE = 9;
  int posIni = -1, posFinal = -1;

  arrayString a = new char[ARRAY_SIZE];
  a[0] = 'a'; a[1] = 'b'; a[2] = 'c'; a[3] = 'd';
  a[4] = 'a'; a[5] = 'b'; a[6] = 'e'; a[7] = 'e'; a[8] = 0;

  cout << "Initial string: " << a << endl << endl;

  // --- Identifying one character.
  cout << "Identifying one character." << endl;

  char targetChar = 0;
  targetChar = 'b';
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    if (a[i] == targetChar)
    {
      posIni = i;
      cout << "Target - index: " << targetChar << " - " << posIni << endl;
      cout << endl;
    }

  }

  // --- Identifying two contiguous characters.
  cout << "Identifying two contiguous characters." << endl;

  char targetArray[2] = {'a', 'b'};
  // char targetArray[2] = {'b', 'z'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (a[i] == targetArray[0]) )
    {
      posIni = i;
    }

    // Find second adjacent char, in next cicle.
    if ( (posIni != -1) && (i == (posIni + 1) ) && (a[i] == targetArray[1]) )
    {
      posFinal = i;

      cout << "Target - index: " << targetArray[0] << " - " << posIni << endl;
      cout << "Target - index: " << targetArray[1] << " - " << posFinal << endl;
      cout << endl;

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  // --- Identifying three contiguous characters.
  cout << "Identifying three contiguous characters." << endl;

  char targetArrayThree[3] = {'a', 'b', 'c'};
  // char targetArrayThree[3] = {'b', 'e', 'e'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (a[i] == targetArrayThree[0]) )
    {
      posIni = i;
    }

    // Check second adjacent character.
    if ( (posIni != -1) && (i == (posIni + 1)) )
    {
      if (a[i] == targetArrayThree[1])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayThree[1])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check third adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posFinal + 1)) )
    {
      if (a[i] == targetArrayThree[2])
      {
        posFinal = i;

        cout << "Target initial - index: " << targetArrayThree[0] << " - " << posIni << endl;
        cout << "Target final   - index: " << targetArrayThree[2] << " - " << posFinal << endl;
        cout << endl;
      }

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  // --- Identifying four contiguous characters.
  cout << "Identifying four contiguous characters." << endl;

  char targetArrayFour[4] = {'a', 'b', 'c', 'd'};
  // char targetArrayFour[4] = {'b', 'c', 'd', 'a'};

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first char
    if ( (posIni == -1) && (posFinal == -1) )
    {
      if (a[i] == targetArrayFour[0])
      {
        posIni = i;
      }
    }

    // Check second adjacent character.
    if ( (posIni != -1) && (posFinal == -1) && (i == (posIni + 1)) )
    {
      if (a[i] == targetArrayFour[1])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayFour[1])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check third adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posIni + 2)) )
    {
      if (a[i] == targetArrayFour[2])
      {
        posFinal = i;
      }

      if (a[i] != targetArrayFour[2])
      {
        posIni = -1, posFinal = -1; // Reset.
      }
    }

    // Check fourth adjacent character.
    if ( (posIni != -1) && (posFinal != -1) && (i == (posIni + 3)) )
    {
      if (a[i] == targetArrayFour[3])
      {
        posFinal = i;

        cout << "Target initial - index: " << targetArrayFour[0] << " - " << posIni << endl;
        cout << "Target final   - index: " << targetArrayFour[3] << " - " << posFinal << endl;
        cout << endl;
      }

      posIni = -1, posFinal = -1; // Reset.
    }

  }

  // --- Identifying lots of contiguous characters.

  // const int TARGET_SIZE = 9; char targetArrayLots[TARGET_SIZE] = {'b', 'c', 'd', 'a', 'b', 'c', 'e', 'f', 'g'};
  // const int TARGET_SIZE = 7; char targetArrayLots[TARGET_SIZE] = {'b', 'c', 'd', 'a', 'b', 'c', 'e'};
  const int TARGET_SIZE = 3; char targetArrayLots[TARGET_SIZE] = {'a', 'b', 'c'};
  // const int TARGET_SIZE = 2; char targetArrayLots[TARGET_SIZE] = {'a', 'b'};
  // const int TARGET_SIZE = 1; char targetArrayLots[TARGET_SIZE] = {'b'};

  cout << "Identifying " << TARGET_SIZE << " contiguous characters." << endl;

  a[0] = 'a'; a[1] = 'b'; a[2] = 'c'; a[3] = 'd';
  a[4] = 'a'; a[5] = 'b'; a[6] = 'c'; a[7] = 'e'; a[8] = 0;
  cout << "Initial string: " << a << endl << endl;

  posIni = -1, posFinal = -1;
  for (int i = 0; i < ARRAY_SIZE; ++i)
  {

    // Find first character.
    if ( (posIni == -1) && (posFinal == -1) && (a[i] == targetArrayLots[0]) )
    {
      posIni = i;

      // Handles special case of one character.
      if (TARGET_SIZE == 1)
      {
        cout << "Target initial/final - index: " << targetArrayLots[0] << " - " << posIni << endl;
        cout << endl;

        posIni = -1, posFinal = -1; // Reset.
      }

      // Handles cases from two characters until ...
      for (int j = 1; j < TARGET_SIZE; ++j)
      {

        // Check second adjacent character.
        if ( (posFinal == -1) && ( (i + j) == (posIni + j)) )
        {
          if (a[i + j] == targetArrayLots[j])
          {
            posFinal = i + j;
          }

          if (a[i + j] != targetArrayLots[j])
          {
            posIni = -1, posFinal = -1; // Reset.
          }
        }

        // Check next adjacent character. BUT not the last.
        if ( (posFinal != -1) && ((i + j) == (posIni + j)) && (j <= (TARGET_SIZE - 2)) )
        {
          if (a[i + j] == targetArrayLots[j])
          {
            posFinal = i + j;
          }

          if (a[i + j] != targetArrayLots[j])
          {
            posIni = -1, posFinal = -1; // Reset.
          }
        }

        // Check last adjacent character.
        if ( (posFinal != -1) && ((i + j) == (posIni + j)) && (j == (TARGET_SIZE - 1)) )
        {
          if (a[i + j] == targetArrayLots[j])
          {
            posFinal = i + j;

            cout << "Target initial - index: " << targetArrayLots[0] << " - " << posIni << endl;
            cout << "Target final   - index: " << targetArrayLots[j] << " - " << posFinal << endl;
            cout << endl;
          }

          posIni = -1, posFinal = -1; // Reset.
        }

      } // internal for
    } // if that check the first character.

  } // external for

  delete a; // Free memory of the dynamic array.

  cout << endl;
  return 0;
}
<|endoftext|>


<commit_before>#include "sys/time.h"
#include "gspan.h"
#include "seperator.h"
#include "database.h"

namespace gspan {

	void GSpan::execute(const char *seperator_type, const char *file_path, double support)
	{
		_m_seperator = new Seperator(seperator_type);
		_m_file_path = file_path;
		_m_support = support;

		Input gspan_input;

		_m_seperator->seperate(_m_file_path, gspan_input);

#ifdef DEBUG
		printf("seperate\n");
#endif
		if (GSPAN_SUCCESS != read_input(gspan_input)) {
			fprintf(stderr, "read input error!");
			exit(GSPAN_ERROR);
		}

#ifdef DEBUG
		printf("find_frequent\n");
#endif

		if (GSPAN_SUCCESS != find_frequent_nodes()) {
			fprintf(stderr, "find frequent nodes error!");
			exit(GSPAN_ERROR);
		}

		if (GSPAN_SUCCESS != reconstruct(gspan_input)) {
			fprintf(stderr, "find frequent nodes error!");
			exit(GSPAN_ERROR);
		}

		DataBase *database = DataBase::get_instance();
		_m_graphs = database->get_graph();

		timeval t1, t2;
		double elapsed_time = 0.0f;
		gettimeofday(&t1, NULL);

#ifdef DEBUG
		printf("project\n");
#endif

		if (GSPAN_SUCCESS != project()) {
			fprintf(stderr, "projection nodes error!");
			exit(GSPAN_ERROR);
		}

		gettimeofday(&t2, NULL);
		elapsed_time = (t2.tv_sec - t1.tv_sec) * 1000.0;
		elapsed_time += (t2.tv_usec - t1.tv_usec) / 1000.0;
		printf("elapsed time->execute %f\n", elapsed_time);
	}

	GSpanReturnCode GSpan::read_input(Input& input)
	{
		Graph graph;
		Vertice vertice;

		DataBase *database = DataBase::get_instance();

		uint32_t graph_idx = 0;
		uint32_t edge_id = 0;
		double total_edge = 0.0f;
		double density = 0.0f;

		for (size_t i = 0; i < input.size(); ++i) {
			if (input[i][0] == "t") {
				if (i != 0) {
					total_edge += edge_id;
					if (vertice.size() != 0) {
						density += 2.0f * edge_id / (vertice.size() * (vertice.size() - 1));
					}
					graph.set_nedges(edge_id);
					graph.set_vertice(vertice);
					edge_id = 0;
					database->push_graph(graph);
					graph.clear();
					vertice.clear();
				}

				char indicator, seperator;
				uint32_t idx;
				indicator = input[i][0][0];
				seperator = input[i][1][0];
				sscanf(input[i][2].c_str(), "%u", &idx);

				if (graph_idx != idx) {
					fprintf(stderr, "reading input warning! %u %u\n", graph_idx, idx);
				}
				graph.set_id(idx);

				++graph_idx;
			} else if (input[i][0] == "v") {
				char indicator;
				uint32_t id, label;
				indicator = input[i][0][0];
				sscanf(input[i][1].c_str(), "%u", &id);
				sscanf(input[i][2].c_str(), "%u", &label);

				struct vertex_t vertex;
				vertex.id = id;
				vertex.label = label;

				vertice.push_back(vertex);
			} else if (input[i][0] == "e") {
				char indicator;
				uint32_t from, to, label;
				indicator = input[i][0][0];
				sscanf(input[i][1].c_str(), "%u", &from);
				sscanf(input[i][2].c_str(), "%u", &to);
				sscanf(input[i][3].c_str(), "%u", &label);

				struct edge_t edge;
				edge.from = from;
				edge.to = to;
				edge.label = label;
				edge.id = edge_id;
				++edge_id;

				//first edge
				vertice[from].edges.push_back(edge);

				//second edge
				edge.from = to;
				edge.to = from;
				vertice[to].edges.push_back(edge);
			} else {
				fprintf(stderr, "reading input warning!");
			}
		}

		graph.set_vertice(vertice);
		database->push_graph(graph);

		printf("average graph size : %f\n", total_edge / database->size());
		printf("average density: %f\n", density / database->size());

		_m_nsupport = static_cast<uint32_t>(graph_idx * _m_support);

		return GSPAN_SUCCESS;
	}

}//namespace gspan

<commit_msg>update unique label set<commit_after>#include "sys/time.h"
#include "gspan.h"
#include "seperator.h"
#include "database.h"

namespace gspan {

	void GSpan::execute(const char *seperator_type, const char *file_path, double support)
	{
		_m_seperator = new Seperator(seperator_type);
		_m_file_path = file_path;
		_m_support = support;

		Input gspan_input;

		_m_seperator->seperate(_m_file_path, gspan_input);

#ifdef DEBUG
		printf("seperate\n");
#endif
		if (GSPAN_SUCCESS != read_input(gspan_input)) {
			fprintf(stderr, "read input error!");
			exit(GSPAN_ERROR);
		}

#ifdef DEBUG
		printf("find_frequent\n");
#endif

		if (GSPAN_SUCCESS != find_frequent_nodes()) {
			fprintf(stderr, "find frequent nodes error!");
			exit(GSPAN_ERROR);
		}

		if (GSPAN_SUCCESS != reconstruct(gspan_input)) {
			fprintf(stderr, "find frequent nodes error!");
			exit(GSPAN_ERROR);
		}

		DataBase *database = DataBase::get_instance();
		_m_graphs = database->get_graph();

		timeval t1, t2;
		double elapsed_time = 0.0f;
		gettimeofday(&t1, NULL);

#ifdef DEBUG
		printf("project\n");
#endif

		if (GSPAN_SUCCESS != project()) {
			fprintf(stderr, "projection nodes error!");
			exit(GSPAN_ERROR);
		}

		gettimeofday(&t2, NULL);
		elapsed_time = (t2.tv_sec - t1.tv_sec) * 1000.0;
		elapsed_time += (t2.tv_usec - t1.tv_usec) / 1000.0;
		printf("elapsed time->execute %f\n", elapsed_time);
	}

	GSpanReturnCode GSpan::read_input(Input& input)
	{
		Graph graph;
		Vertice vertice;

		DataBase *database = DataBase::get_instance();

		uint32_t graph_idx = 0;
		uint32_t edge_id = 0;
		double total_edge = 0.0f;
		double density = 0.0f;
		std::vector<std::set<uint32_t> > edge_label_set;

		for (size_t i = 0; i < input.size(); ++i) {
			if (input[i][0] == "t") {
				if (i != 0) {
					total_edge += edge_id;
					if (vertice.size() != 0) {
						density += 2.0f * edge_id / (vertice.size() * (vertice.size() - 1));
					}
					graph.set_nedges(edge_id);
					graph.set_vertice(vertice);
					edge_id = 0;
					database->push_graph(graph);
					graph.clear();
					vertice.clear();
				}

				edge_label_set.resize(edge_label_set.size() + 1);

				char indicator, seperator;
				uint32_t idx;
				indicator = input[i][0][0];
				seperator = input[i][1][0];
				sscanf(input[i][2].c_str(), "%u", &idx);

				if (graph_idx != idx) {
					fprintf(stderr, "reading input warning! %u %u\n", graph_idx, idx);
				}
				graph.set_id(idx);

				++graph_idx;
			} else if (input[i][0] == "v") {
				char indicator;
				uint32_t id, label;
				indicator = input[i][0][0];
				sscanf(input[i][1].c_str(), "%u", &id);
				sscanf(input[i][2].c_str(), "%u", &label);

				struct vertex_t vertex;
				vertex.id = id;
				vertex.label = label;

				vertice.push_back(vertex);
			} else if (input[i][0] == "e") {
				char indicator;
				uint32_t from, to, label;
				indicator = input[i][0][0];
				sscanf(input[i][1].c_str(), "%u", &from);
				sscanf(input[i][2].c_str(), "%u", &to);
				sscanf(input[i][3].c_str(), "%u", &label);

				//add label
				edge_label_set[graph_idx - 1].insert(label);

				struct edge_t edge;
				edge.from = from;
				edge.to = to;
				edge.label = label;
				edge.id = edge_id;
				++edge_id;

				//first edge
				vertice[from].edges.push_back(edge);

				//second edge
				edge.from = to;
				edge.to = from;
				vertice[to].edges.push_back(edge);
			} else {
				fprintf(stderr, "reading input warning!");
			}
		}

		graph.set_vertice(vertice);
		database->push_graph(graph);

		printf("average graph size : %f\n", total_edge / database->size());
		printf("average density: %f\n", density / database->size());
		for (size_t i = 0; i < graph_idx; ++i) {
			for (size_t j = i + 1; j < graph_idx; ++j) {
				printf("unique edge label set of %zu and %zu \n", i, j);
				printf("%zu : \n", i);
				for (std::set<uint32_t>::iterator edge_iterator = edge_label_set[i].begin();
						edge_iterator != edge_label_set[i].end(); ++edge_iterator) {
					if (edge_label_set[j].find(*edge_iterator) == edge_label_set[j].end()) {
						printf("%zu ", *edge_iterator);
					}
				}
				printf("\n");

				printf("%zu : \n", j);
				for (std::set<uint32_t>::iterator edge_iterator = edge_label_set[j].begin();
						edge_iterator != edge_label_set[j].end(); ++edge_iterator) {
					if (edge_label_set[i].find(*edge_iterator) == edge_label_set[i].end()) {
						printf("%zu ", *edge_iterator);
					}
				}
				printf("\n");
			}
		}

		_m_nsupport = static_cast<uint32_t>(graph_idx * _m_support);

		return GSPAN_SUCCESS;
	}

}//namespace gspan

<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 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.
 *
 ****************************************************************************/

#ifndef _uORBTest_UnitTest_hpp_
#define _uORBTest_UnitTest_hpp_
#include "../uORBCommon.hpp"
#include "../uORB.h"
#include <px4_time.h>

struct orb_test {
	int val;
	hrt_abstime time;
};
ORB_DECLARE(orb_test);
ORB_DECLARE(orb_multitest);

struct orb_test_medium {
	int val;
	hrt_abstime time;
	char junk[64];
};
ORB_DECLARE(orb_test_medium);
ORB_DECLARE(orb_test_medium_multi);
ORB_DECLARE(orb_test_medium_queue);
ORB_DECLARE(orb_test_medium_queue_poll);

struct orb_test_large {
	int val;
	hrt_abstime time;
	char junk[512];
};
ORB_DECLARE(orb_test_large);

namespace uORBTest
{
class UnitTest;
}

class uORBTest::UnitTest
{
public:

	// Singleton pattern
	static uORBTest::UnitTest &instance();
	~UnitTest() {}
	int test();
	template<typename S> int latency_test(orb_id_t T, bool print);
	int info();

private:
	UnitTest() : pubsubtest_passed(false), pubsubtest_print(false) {}

	// Disallow copy
	UnitTest(const uORBTest::UnitTest &) {};
	static int pubsubtest_threadEntry(char *const argv[]);
	int pubsublatency_main(void);

	static int pub_test_multi2_entry(char *const argv[]);
	int pub_test_multi2_main();

	volatile bool _thread_should_exit;

	bool pubsubtest_passed;
	bool pubsubtest_print;
	int pubsubtest_res = OK;

	orb_advert_t _pfd[4]; ///< used for test_multi and test_multi_reversed

	int test_single();

	/* These 3 depend on each other and must be called in this order */
	int test_multi();
	int test_multi_reversed();
	int test_unadvertise();

	int test_multi2();

	/* queuing tests */
	int test_queue();
	static int pub_test_queue_entry(char *const argv[]);
	int pub_test_queue_main();
	int test_queue_poll_notify();
	volatile int _num_messages_sent = 0;

	int test_fail(const char *fmt, ...);
	int test_note(const char *fmt, ...);
};

template<typename S>
int uORBTest::UnitTest::latency_test(orb_id_t T, bool print)
{
	test_note("---------------- LATENCY TEST ------------------");
	S t;
	t.val = 308;
	t.time = hrt_absolute_time();

	orb_advert_t pfd0 = orb_advertise(T, &t);

	if (pfd0 == nullptr) {
		return test_fail("orb_advertise failed (%i)", errno);
	}

	char *const args[1] = { NULL };

	pubsubtest_print = print;
	pubsubtest_passed = false;

	/* test pub / sub latency */

	// Can't pass a pointer in args, must be a null terminated
	// array of strings because the strings are copied to
	// prevent access if the caller data goes out of scope
	int pubsub_task = px4_task_spawn_cmd("uorb_latency",
					     SCHED_DEFAULT,
					     SCHED_PRIORITY_MAX - 5,
					     1500,
					     (px4_main_t)&uORBTest::UnitTest::pubsubtest_threadEntry,
					     args);

	/* give the test task some data */
	while (!pubsubtest_passed) {
		t.val = 308;
		t.time = hrt_absolute_time();

		if (PX4_OK != orb_publish(T, pfd0, &t)) {
			return test_fail("mult. pub0 timing fail");
		}

		/* simulate >800 Hz system operation */
		usleep(1000);
	}

	if (pubsub_task < 0) {
		return test_fail("failed launching task");
	}

	orb_unadvertise(pfd0);

	return pubsubtest_res;
}

#endif // _uORBTest_UnitTest_hpp_
<commit_msg>uORB: Header cleanup<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 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.
 *
 ****************************************************************************/

#ifndef _uORBTest_UnitTest_hpp_
#define _uORBTest_UnitTest_hpp_
#include "../uORBCommon.hpp"
#include "../uORB.h"
#include <px4_time.h>
#include <px4_tasks.h>

struct orb_test {
	int val;
	hrt_abstime time;
};
ORB_DECLARE(orb_test);
ORB_DECLARE(orb_multitest);

struct orb_test_medium {
	int val;
	hrt_abstime time;
	char junk[64];
};
ORB_DECLARE(orb_test_medium);
ORB_DECLARE(orb_test_medium_multi);
ORB_DECLARE(orb_test_medium_queue);
ORB_DECLARE(orb_test_medium_queue_poll);

struct orb_test_large {
	int val;
	hrt_abstime time;
	char junk[512];
};
ORB_DECLARE(orb_test_large);

namespace uORBTest
{
class UnitTest;
}

class uORBTest::UnitTest
{
public:

	// Singleton pattern
	static uORBTest::UnitTest &instance();
	~UnitTest() {}
	int test();
	template<typename S> int latency_test(orb_id_t T, bool print);
	int info();

private:
	UnitTest() : pubsubtest_passed(false), pubsubtest_print(false) {}

	// Disallow copy
	UnitTest(const uORBTest::UnitTest &) {};
	static int pubsubtest_threadEntry(char *const argv[]);
	int pubsublatency_main(void);

	static int pub_test_multi2_entry(char *const argv[]);
	int pub_test_multi2_main();

	volatile bool _thread_should_exit;

	bool pubsubtest_passed;
	bool pubsubtest_print;
	int pubsubtest_res = OK;

	orb_advert_t _pfd[4]; ///< used for test_multi and test_multi_reversed

	int test_single();

	/* These 3 depend on each other and must be called in this order */
	int test_multi();
	int test_multi_reversed();
	int test_unadvertise();

	int test_multi2();

	/* queuing tests */
	int test_queue();
	static int pub_test_queue_entry(char *const argv[]);
	int pub_test_queue_main();
	int test_queue_poll_notify();
	volatile int _num_messages_sent = 0;

	int test_fail(const char *fmt, ...);
	int test_note(const char *fmt, ...);
};

template<typename S>
int uORBTest::UnitTest::latency_test(orb_id_t T, bool print)
{
	test_note("---------------- LATENCY TEST ------------------");
	S t;
	t.val = 308;
	t.time = hrt_absolute_time();

	orb_advert_t pfd0 = orb_advertise(T, &t);

	if (pfd0 == nullptr) {
		return test_fail("orb_advertise failed (%i)", errno);
	}

	char *const args[1] = { NULL };

	pubsubtest_print = print;
	pubsubtest_passed = false;

	/* test pub / sub latency */

	// Can't pass a pointer in args, must be a null terminated
	// array of strings because the strings are copied to
	// prevent access if the caller data goes out of scope
	int pubsub_task = px4_task_spawn_cmd("uorb_latency",
					     SCHED_DEFAULT,
					     SCHED_PRIORITY_MAX - 5,
					     1500,
					     (px4_main_t)&uORBTest::UnitTest::pubsubtest_threadEntry,
					     args);

	/* give the test task some data */
	while (!pubsubtest_passed) {
		t.val = 308;
		t.time = hrt_absolute_time();

		if (PX4_OK != orb_publish(T, pfd0, &t)) {
			return test_fail("mult. pub0 timing fail");
		}

		/* simulate >800 Hz system operation */
		usleep(1000);
	}

	if (pubsub_task < 0) {
		return test_fail("failed launching task");
	}

	orb_unadvertise(pfd0);

	return pubsubtest_res;
}

#endif // _uORBTest_UnitTest_hpp_
<|endoftext|>