<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "filestormetrics.h"
#include <vespa/metrics/loadmetric.hpp>
#include <vespa/metrics/summetric.hpp>
#include <sstream>

namespace storage {

using metrics::MetricSet;
using metrics::LoadTypeSet;

FileStorThreadMetrics::Op::Op(const std::string& id, const std::string& name, MetricSet* owner)
    : MetricSet(id, {}, name + " load in filestor thread", owner),
      _name(name),
      count("count", {{"yamasdefault"}}, "Number of requests processed.", this),
      latency("latency", {{"yamasdefault"}}, "Latency of successful requests.", this),
      failed("failed", {{"yamasdefault"}}, "Number of failed requests.", this)
{ }

FileStorThreadMetrics::Op::~Op() = default;

MetricSet *
FileStorThreadMetrics::Op::clone(std::vector<Metric::UP>& ownerList,
                                 CopyType copyType,
                                 MetricSet* owner,
                                 bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Op*) (new Op(getName(), _name, owner))->assignValues(*this);
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::OpWithRequestSize(const std::string& id, const std::string& name, MetricSet* owner)
        : BaseOp(id, name, owner),
          request_size("request_size", {}, "Size of requests, in bytes", this)
{
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::~OpWithRequestSize() = default;

// FIXME this has very non-intuitive semantics, ending up with copy&paste patterns
template <typename BaseOp>
MetricSet*
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::clone(
        std::vector<Metric::UP>& ownerList,
        CopyType copyType,
        MetricSet* owner,
        bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return static_cast<OpWithRequestSize<BaseOp>*>((new OpWithRequestSize<BaseOp>(this->getName(), this->_name, owner))
            ->assignValues(*this));
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::OpWithTestAndSetFailed(const std::string& id, const std::string& name, MetricSet* owner)
    : BaseOp(id, name, owner),
      test_and_set_failed("test_and_set_failed", {{"yamasdefault"}},
                           "Number of times operations were failed due to a "
                           "test-and-set condition mismatch", this)
{
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::~OpWithTestAndSetFailed() = default;

// FIXME this has very non-intuitive semantics, ending up with copy&paste patterns (yet again...)
template <typename BaseOp>
MetricSet*
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::clone(
        std::vector<Metric::UP>& ownerList,
        CopyType copyType,
        MetricSet* owner,
        bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return static_cast<OpWithTestAndSetFailed<BaseOp>*>((new OpWithTestAndSetFailed<BaseOp>(this->getName(), this->_name, owner))
            ->assignValues(*this));
}

FileStorThreadMetrics::OpWithNotFound::OpWithNotFound(const std::string& id, const std::string& name, MetricSet* owner)
    : Op(id, name, owner),
      notFound("not_found", {},
               "Number of requests that could not be completed due to source document not found.",
               this)
{ }

FileStorThreadMetrics::OpWithNotFound::~OpWithNotFound() = default;

MetricSet *
FileStorThreadMetrics::OpWithNotFound::clone(std::vector<Metric::UP>& ownerList,
                                             CopyType copyType,
                                             MetricSet* owner,
                                             bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (OpWithNotFound*)
            (new OpWithNotFound(getName(), _name, owner))
                ->assignValues(*this);
}

FileStorThreadMetrics::Update::Update(MetricSet* owner)
    : OpWithTestAndSetFailed("update", "Update", owner),
      latencyRead("latency_read", {}, "Latency of the source read in the request.", this)
{ }

FileStorThreadMetrics::Update::~Update() = default;

MetricSet *
FileStorThreadMetrics::Update::clone(std::vector<Metric::UP>& ownerList,
                                     CopyType copyType,
                                     MetricSet* owner,
                                     bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Update*) (new Update(owner))->assignValues(*this);
}

FileStorThreadMetrics::Visitor::Visitor(MetricSet* owner)
    : Op("visit", "Visit", owner),
      documentsPerIterate("docs", {}, "Number of entries read per iterate call", this)
{ }

FileStorThreadMetrics::Visitor::~Visitor() = default;

MetricSet *
FileStorThreadMetrics::Visitor::clone(std::vector<Metric::UP>& ownerList,
                                      CopyType copyType,
                                      MetricSet* owner,
                                      bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Visitor*) (new Visitor(owner))->assignValues(*this);
}

FileStorThreadMetrics::FileStorThreadMetrics(const std::string& name, const std::string& desc, const LoadTypeSet& lt)
    : MetricSet(name, {{"filestor"},{"partofsum"}}, desc),
      operations("operations", {}, "Number of operations processed.", this),
      failedOperations("failedoperations", {}, "Number of operations throwing exceptions.", this),
      put(lt, PutMetricType("put", "Put"), this),
      get(lt, GetMetricType("get", "Get"), this),
      remove(lt, RemoveMetricType("remove", "Remove"), this),
      removeLocation(lt, Op("remove_location", "Remove location"), this),
      statBucket(lt, Op("stat_bucket", "Stat bucket"), this),
      update(lt, Update(), this),
      revert(lt, OpWithNotFound("revert", "Revert"), this),
      createIterator("createiterator", {}, this),
      visit(lt, Visitor(), this),
      multiOp(lt, Op("multioperations", "The number of multioperations that have been created"), this),
      createBuckets("createbuckets", "Number of buckets that has been created.", this),
      deleteBuckets("deletebuckets", "Number of buckets that has been deleted.", this),
      repairs("bucketverified", "Number of times buckets have been checked.", this),
      repairFixed("bucketfixed", {}, "Number of times bucket has been fixed because of corruption", this),
      recheckBucketInfo("recheckbucketinfo",
                        "Number of times bucket info has been explicitly "
                        "rechecked due to buckets being marked modified by "
                        "the persistence provider",
                        this),
      splitBuckets("splitbuckets", "Number of times buckets have been split.", this),
      joinBuckets("joinbuckets", "Number of times buckets have been joined.", this),
      setBucketStates("setbucketstates", "Number of times buckets have been activated or deactivated.", this),
      movedBuckets("movedbuckets", "Number of buckets moved between disks", this),
      readBucketList("readbucketlist", "Number of read bucket list requests", this),
      readBucketInfo("readbucketinfo", "Number of read bucket info requests", this),
      internalJoin("internaljoin", "Number of joins to join buckets on multiple disks during "
                                   "storage initialization.", this),
      mergeBuckets("mergebuckets", "Number of times buckets have been merged.", this),
      getBucketDiff("getbucketdiff", "Number of getbucketdiff commands that have been processed.", this),
      applyBucketDiff("applybucketdiff", "Number of applybucketdiff commands that have been processed.", this),
      getBucketDiffReply("getbucketdiffreply", {}, "Number of getbucketdiff replies that have been processed.", this),
      applyBucketDiffReply("applybucketdiffreply", {}, "Number of applybucketdiff replies that have been processed.", this),
      merge_handler_metrics(this),
      batchingSize("batchingsize", {}, "Number of operations batched per bucket (only counts "
                   "batches of size > 1)", this)
{ }

FileStorThreadMetrics::~FileStorThreadMetrics() = default;

FileStorStripeMetrics::FileStorStripeMetrics(const std::string& name, const std::string& description,
                                             const LoadTypeSet& loadTypes)
    : MetricSet(name, {{"partofsum"}}, description),
      averageQueueWaitingTime(loadTypes,
                              metrics::DoubleAverageMetric("averagequeuewait", {},
                                                           "Average time an operation spends in input queue."),
                              this)
{
}

FileStorStripeMetrics::~FileStorStripeMetrics() = default;

FileStorDiskMetrics::FileStorDiskMetrics(const std::string& name, const std::string& description,
                                         const metrics::LoadTypeSet& loadTypes, MetricSet* owner)
    : MetricSet(name, {{"partofsum"}}, description, owner),
      sumThreads("allthreads", {{"sum"}}, "", this),
      sumStripes("allstripes", {{"sum"}}, "", this),
      averageQueueWaitingTime(loadTypes,
                              metrics::DoubleAverageMetric("averagequeuewait", {},
                                                           "Average time an operation spends in input queue."),
                              this),
      queueSize("queuesize", {}, "Size of input message queue.", this),
      pendingMerges("pendingmerge", {}, "Number of buckets currently being merged.", this),
      waitingForLockHitRate("waitingforlockrate", {},
              "Amount of times a filestor thread has needed to wait for "
              "lock to take next message in queue.", this),
      lockWaitTime("lockwaittime", {}, "Amount of time waiting used waiting for lock.", this)
{
    pendingMerges.unsetOnZeroValue();
    waitingForLockHitRate.unsetOnZeroValue();
}

FileStorDiskMetrics::~FileStorDiskMetrics() = default;

void
FileStorDiskMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t numStripes, uint32_t threadsPerDisk)
{
    threads.clear();
    threads.resize(threadsPerDisk);
    for (uint32_t i=0; i<threadsPerDisk; ++i) {
        std::ostringstream desc;
        std::ostringstream name;
        name << "thread" << i;
        desc << "Thread " << i << '/' << threadsPerDisk;
        threads[i] = std::make_shared<FileStorThreadMetrics>(name.str(), desc.str(), loadTypes);
        registerMetric(*threads[i]);
        sumThreads.addMetricToSum(*threads[i]);
    }
    stripes.clear();
    stripes.resize(numStripes);
    for (uint32_t i=0; i<numStripes; ++i) {
        std::ostringstream desc;
        std::ostringstream name;
        name << "stripe" << i;
        desc << "Stripe " << i << '/' << numStripes;
        stripes[i] = std::make_shared<FileStorStripeMetrics>(name.str(), desc.str(), loadTypes);
        registerMetric(*stripes[i]);
        sumStripes.addMetricToSum(*stripes[i]);
    }
}

FileStorMetrics::FileStorMetrics(const LoadTypeSet&)
    : MetricSet("filestor", {{"filestor"}}, ""),
      sum("alldisks", {{"sum"}}, "", this),
      directoryEvents("directoryevents", {}, "Number of directory events received.", this),
      partitionEvents("partitionevents", {}, "Number of partition events received.", this),
      diskEvents("diskevents", {}, "Number of disk events received.", this),
      bucket_db_init_latency("bucket_db_init_latency", {}, "Time taken (in ms) to initialize bucket databases with "
                                                           "information from the persistence provider", this)
{ }

FileStorMetrics::~FileStorMetrics() = default;

void FileStorMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t numStripes, uint32_t threadsPerDisk)
{
    if (disk) {
        throw vespalib::IllegalStateException("Can't initialize disk twice", VESPA_STRLOC);
    }
    // Currently FileStorHandlerImpl expects metrics to exist for
    // disks that are not in use too.
    disk = std::make_shared<FileStorDiskMetrics>( "disk_0", "Disk 0", loadTypes, this);
    sum.addMetricToSum(*disk);
    disk->initDiskMetrics(loadTypes, numStripes, threadsPerDisk);
}

}

template class metrics::LoadMetric<storage::FileStorThreadMetrics::Op>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::OpWithNotFound>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::Update>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::Visitor>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::PutMetricType>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::GetMetricType>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::RemoveMetricType>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Op>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::OpWithNotFound>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Update>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Visitor>;
<commit_msg>Use simple assert instead<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "filestormetrics.h"
#include <vespa/metrics/loadmetric.hpp>
#include <vespa/metrics/summetric.hpp>
#include <sstream>

namespace storage {

using metrics::MetricSet;
using metrics::LoadTypeSet;

FileStorThreadMetrics::Op::Op(const std::string& id, const std::string& name, MetricSet* owner)
    : MetricSet(id, {}, name + " load in filestor thread", owner),
      _name(name),
      count("count", {{"yamasdefault"}}, "Number of requests processed.", this),
      latency("latency", {{"yamasdefault"}}, "Latency of successful requests.", this),
      failed("failed", {{"yamasdefault"}}, "Number of failed requests.", this)
{ }

FileStorThreadMetrics::Op::~Op() = default;

MetricSet *
FileStorThreadMetrics::Op::clone(std::vector<Metric::UP>& ownerList,
                                 CopyType copyType,
                                 MetricSet* owner,
                                 bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Op*) (new Op(getName(), _name, owner))->assignValues(*this);
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::OpWithRequestSize(const std::string& id, const std::string& name, MetricSet* owner)
        : BaseOp(id, name, owner),
          request_size("request_size", {}, "Size of requests, in bytes", this)
{
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::~OpWithRequestSize() = default;

// FIXME this has very non-intuitive semantics, ending up with copy&paste patterns
template <typename BaseOp>
MetricSet*
FileStorThreadMetrics::OpWithRequestSize<BaseOp>::clone(
        std::vector<Metric::UP>& ownerList,
        CopyType copyType,
        MetricSet* owner,
        bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return static_cast<OpWithRequestSize<BaseOp>*>((new OpWithRequestSize<BaseOp>(this->getName(), this->_name, owner))
            ->assignValues(*this));
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::OpWithTestAndSetFailed(const std::string& id, const std::string& name, MetricSet* owner)
    : BaseOp(id, name, owner),
      test_and_set_failed("test_and_set_failed", {{"yamasdefault"}},
                           "Number of times operations were failed due to a "
                           "test-and-set condition mismatch", this)
{
}

template <typename BaseOp>
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::~OpWithTestAndSetFailed() = default;

// FIXME this has very non-intuitive semantics, ending up with copy&paste patterns (yet again...)
template <typename BaseOp>
MetricSet*
FileStorThreadMetrics::OpWithTestAndSetFailed<BaseOp>::clone(
        std::vector<Metric::UP>& ownerList,
        CopyType copyType,
        MetricSet* owner,
        bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return static_cast<OpWithTestAndSetFailed<BaseOp>*>((new OpWithTestAndSetFailed<BaseOp>(this->getName(), this->_name, owner))
            ->assignValues(*this));
}

FileStorThreadMetrics::OpWithNotFound::OpWithNotFound(const std::string& id, const std::string& name, MetricSet* owner)
    : Op(id, name, owner),
      notFound("not_found", {},
               "Number of requests that could not be completed due to source document not found.",
               this)
{ }

FileStorThreadMetrics::OpWithNotFound::~OpWithNotFound() = default;

MetricSet *
FileStorThreadMetrics::OpWithNotFound::clone(std::vector<Metric::UP>& ownerList,
                                             CopyType copyType,
                                             MetricSet* owner,
                                             bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (OpWithNotFound*)
            (new OpWithNotFound(getName(), _name, owner))
                ->assignValues(*this);
}

FileStorThreadMetrics::Update::Update(MetricSet* owner)
    : OpWithTestAndSetFailed("update", "Update", owner),
      latencyRead("latency_read", {}, "Latency of the source read in the request.", this)
{ }

FileStorThreadMetrics::Update::~Update() = default;

MetricSet *
FileStorThreadMetrics::Update::clone(std::vector<Metric::UP>& ownerList,
                                     CopyType copyType,
                                     MetricSet* owner,
                                     bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Update*) (new Update(owner))->assignValues(*this);
}

FileStorThreadMetrics::Visitor::Visitor(MetricSet* owner)
    : Op("visit", "Visit", owner),
      documentsPerIterate("docs", {}, "Number of entries read per iterate call", this)
{ }

FileStorThreadMetrics::Visitor::~Visitor() = default;

MetricSet *
FileStorThreadMetrics::Visitor::clone(std::vector<Metric::UP>& ownerList,
                                      CopyType copyType,
                                      MetricSet* owner,
                                      bool includeUnused) const
{
    if (copyType == INACTIVE) {
        return MetricSet::clone(ownerList, INACTIVE, owner, includeUnused);
    }
    return (Visitor*) (new Visitor(owner))->assignValues(*this);
}

FileStorThreadMetrics::FileStorThreadMetrics(const std::string& name, const std::string& desc, const LoadTypeSet& lt)
    : MetricSet(name, {{"filestor"},{"partofsum"}}, desc),
      operations("operations", {}, "Number of operations processed.", this),
      failedOperations("failedoperations", {}, "Number of operations throwing exceptions.", this),
      put(lt, PutMetricType("put", "Put"), this),
      get(lt, GetMetricType("get", "Get"), this),
      remove(lt, RemoveMetricType("remove", "Remove"), this),
      removeLocation(lt, Op("remove_location", "Remove location"), this),
      statBucket(lt, Op("stat_bucket", "Stat bucket"), this),
      update(lt, Update(), this),
      revert(lt, OpWithNotFound("revert", "Revert"), this),
      createIterator("createiterator", {}, this),
      visit(lt, Visitor(), this),
      multiOp(lt, Op("multioperations", "The number of multioperations that have been created"), this),
      createBuckets("createbuckets", "Number of buckets that has been created.", this),
      deleteBuckets("deletebuckets", "Number of buckets that has been deleted.", this),
      repairs("bucketverified", "Number of times buckets have been checked.", this),
      repairFixed("bucketfixed", {}, "Number of times bucket has been fixed because of corruption", this),
      recheckBucketInfo("recheckbucketinfo",
                        "Number of times bucket info has been explicitly "
                        "rechecked due to buckets being marked modified by "
                        "the persistence provider",
                        this),
      splitBuckets("splitbuckets", "Number of times buckets have been split.", this),
      joinBuckets("joinbuckets", "Number of times buckets have been joined.", this),
      setBucketStates("setbucketstates", "Number of times buckets have been activated or deactivated.", this),
      movedBuckets("movedbuckets", "Number of buckets moved between disks", this),
      readBucketList("readbucketlist", "Number of read bucket list requests", this),
      readBucketInfo("readbucketinfo", "Number of read bucket info requests", this),
      internalJoin("internaljoin", "Number of joins to join buckets on multiple disks during "
                                   "storage initialization.", this),
      mergeBuckets("mergebuckets", "Number of times buckets have been merged.", this),
      getBucketDiff("getbucketdiff", "Number of getbucketdiff commands that have been processed.", this),
      applyBucketDiff("applybucketdiff", "Number of applybucketdiff commands that have been processed.", this),
      getBucketDiffReply("getbucketdiffreply", {}, "Number of getbucketdiff replies that have been processed.", this),
      applyBucketDiffReply("applybucketdiffreply", {}, "Number of applybucketdiff replies that have been processed.", this),
      merge_handler_metrics(this),
      batchingSize("batchingsize", {}, "Number of operations batched per bucket (only counts "
                   "batches of size > 1)", this)
{ }

FileStorThreadMetrics::~FileStorThreadMetrics() = default;

FileStorStripeMetrics::FileStorStripeMetrics(const std::string& name, const std::string& description,
                                             const LoadTypeSet& loadTypes)
    : MetricSet(name, {{"partofsum"}}, description),
      averageQueueWaitingTime(loadTypes,
                              metrics::DoubleAverageMetric("averagequeuewait", {},
                                                           "Average time an operation spends in input queue."),
                              this)
{
}

FileStorStripeMetrics::~FileStorStripeMetrics() = default;

FileStorDiskMetrics::FileStorDiskMetrics(const std::string& name, const std::string& description,
                                         const metrics::LoadTypeSet& loadTypes, MetricSet* owner)
    : MetricSet(name, {{"partofsum"}}, description, owner),
      sumThreads("allthreads", {{"sum"}}, "", this),
      sumStripes("allstripes", {{"sum"}}, "", this),
      averageQueueWaitingTime(loadTypes,
                              metrics::DoubleAverageMetric("averagequeuewait", {},
                                                           "Average time an operation spends in input queue."),
                              this),
      queueSize("queuesize", {}, "Size of input message queue.", this),
      pendingMerges("pendingmerge", {}, "Number of buckets currently being merged.", this),
      waitingForLockHitRate("waitingforlockrate", {},
              "Amount of times a filestor thread has needed to wait for "
              "lock to take next message in queue.", this),
      lockWaitTime("lockwaittime", {}, "Amount of time waiting used waiting for lock.", this)
{
    pendingMerges.unsetOnZeroValue();
    waitingForLockHitRate.unsetOnZeroValue();
}

FileStorDiskMetrics::~FileStorDiskMetrics() = default;

void
FileStorDiskMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t numStripes, uint32_t threadsPerDisk)
{
    threads.clear();
    threads.resize(threadsPerDisk);
    for (uint32_t i=0; i<threadsPerDisk; ++i) {
        std::ostringstream desc;
        std::ostringstream name;
        name << "thread" << i;
        desc << "Thread " << i << '/' << threadsPerDisk;
        threads[i] = std::make_shared<FileStorThreadMetrics>(name.str(), desc.str(), loadTypes);
        registerMetric(*threads[i]);
        sumThreads.addMetricToSum(*threads[i]);
    }
    stripes.clear();
    stripes.resize(numStripes);
    for (uint32_t i=0; i<numStripes; ++i) {
        std::ostringstream desc;
        std::ostringstream name;
        name << "stripe" << i;
        desc << "Stripe " << i << '/' << numStripes;
        stripes[i] = std::make_shared<FileStorStripeMetrics>(name.str(), desc.str(), loadTypes);
        registerMetric(*stripes[i]);
        sumStripes.addMetricToSum(*stripes[i]);
    }
}

FileStorMetrics::FileStorMetrics(const LoadTypeSet&)
    : MetricSet("filestor", {{"filestor"}}, ""),
      sum("alldisks", {{"sum"}}, "", this),
      directoryEvents("directoryevents", {}, "Number of directory events received.", this),
      partitionEvents("partitionevents", {}, "Number of partition events received.", this),
      diskEvents("diskevents", {}, "Number of disk events received.", this),
      bucket_db_init_latency("bucket_db_init_latency", {}, "Time taken (in ms) to initialize bucket databases with "
                                                           "information from the persistence provider", this)
{ }

FileStorMetrics::~FileStorMetrics() = default;

void FileStorMetrics::initDiskMetrics(const LoadTypeSet& loadTypes, uint32_t numStripes, uint32_t threadsPerDisk)
{
    assert( ! disk);
    // Currently FileStorHandlerImpl expects metrics to exist for
    // disks that are not in use too.
    disk = std::make_shared<FileStorDiskMetrics>( "disk_0", "Disk 0", loadTypes, this);
    sum.addMetricToSum(*disk);
    disk->initDiskMetrics(loadTypes, numStripes, threadsPerDisk);
}

}

template class metrics::LoadMetric<storage::FileStorThreadMetrics::Op>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::OpWithNotFound>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::Update>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::Visitor>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::PutMetricType>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::GetMetricType>;
template class metrics::LoadMetric<storage::FileStorThreadMetrics::RemoveMetricType>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Op>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::OpWithNotFound>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Update>;
template class metrics::SumMetric<storage::FileStorThreadMetrics::Visitor>;
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "feedconfigstore.h"
#include "feedstates.h"
#include "ifeedview.h"
#include "ireplayconfig.h"
#include "replaypacketdispatcher.h"
#include "replay_throttling_policy.h"
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcore/proton/common/replay_feed_token_factory.h>
#include <vespa/vespalib/util/idestructorcallback.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/shared_operation_throttler.h>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".proton.server.feedstates");

using search::transactionlog::Packet;
using search::transactionlog::client::RPC;
using search::SerialNum;
using vespalib::Executor;
using vespalib::makeLambdaTask;
using vespalib::IDestructorCallback;
using vespalib::SharedOperationThrottler;
using vespalib::make_string;
using proton::bucketdb::IBucketDBHandler;

namespace proton {

namespace {

const search::SerialNum REPLAY_PROGRESS_INTERVAL = 50000;

void
handleProgress(TlsReplayProgress &progress, SerialNum currentSerial)
{
    progress.updateCurrent(currentSerial);
    if (LOG_WOULD_LOG(event) && (LOG_WOULD_LOG(debug) ||
            (progress.getCurrent() % REPLAY_PROGRESS_INTERVAL == 0)))
    {
        EventLogger::transactionLogReplayProgress(progress.getDomainName(),
                                                  progress.getProgress(),
                                                  progress.getFirst(),
                                                  progress.getLast(),
                                                  progress.getCurrent());
    }
}

class TransactionLogReplayPacketHandler : public IReplayPacketHandler {
    IFeedView *& _feed_view_ptr;  // Pointer can be changed in executor thread.
    IBucketDBHandler &_bucketDBHandler;
    IReplayConfig &_replay_config;
    FeedConfigStore &_config_store;
    IIncSerialNum   &_inc_serial_num;
    CommitTimeTracker _commitTimeTracker;
    std::unique_ptr<SharedOperationThrottler> _throttler;
    std::unique_ptr<feedtoken::ReplayFeedTokenFactory> _replay_feed_token_factory;

    static std::unique_ptr<SharedOperationThrottler> make_throttler(const ReplayThrottlingPolicy& replay_throttling_policy) {
        auto& params = replay_throttling_policy.get_params();
        if (!params.has_value()) {
            return SharedOperationThrottler::make_unlimited_throttler();
        }
        return SharedOperationThrottler::make_dynamic_throttler(params.value());
    }

public:
    TransactionLogReplayPacketHandler(IFeedView *& feed_view_ptr,
                                      IBucketDBHandler &bucketDBHandler,
                                      IReplayConfig &replay_config,
                                      FeedConfigStore &config_store,
                                      const ReplayThrottlingPolicy& replay_throttling_policy,
                                      IIncSerialNum &inc_serial_num)
        : _feed_view_ptr(feed_view_ptr),
          _bucketDBHandler(bucketDBHandler),
          _replay_config(replay_config),
          _config_store(config_store),
          _inc_serial_num(inc_serial_num),
          _commitTimeTracker(5ms),
        _throttler(make_throttler(replay_throttling_policy)),
        _replay_feed_token_factory(std::make_unique<feedtoken::ReplayFeedTokenFactory>(false))
    { }

    ~TransactionLogReplayPacketHandler() override = default;

    FeedToken make_replay_feed_token(const FeedOperation& op) {
        SharedOperationThrottler::Token throttler_token = _throttler->blocking_acquire_one();
        return _replay_feed_token_factory->make_replay_feed_token(std::move(throttler_token), op);
    }

    void replay(const PutOperation &op) override {
        _feed_view_ptr->handlePut(make_replay_feed_token(op), op);
    }
    void replay(const RemoveOperation &op) override {
        _feed_view_ptr->handleRemove(make_replay_feed_token(op), op);
    }
    void replay(const UpdateOperation &op) override {
        _feed_view_ptr->handleUpdate(make_replay_feed_token(op), op);
    }
    void replay(const NoopOperation &) override {} // ignored
    void replay(const NewConfigOperation &op) override {
        _replay_config.replayConfig(op.getSerialNum());
    }

    void replay(const DeleteBucketOperation &op) override {
        _feed_view_ptr->handleDeleteBucket(op, make_replay_feed_token(op));
    }
    void replay(const SplitBucketOperation &op) override {
        _bucketDBHandler.handleSplit(op.getSerialNum(), op.getSource(),
                                     op.getTarget1(), op.getTarget2());
    }
    void replay(const JoinBucketsOperation &op) override {
        _bucketDBHandler.handleJoin(op.getSerialNum(), op.getSource1(),
                                    op.getSource2(), op.getTarget());
    }
    void replay(const PruneRemovedDocumentsOperation &op) override {
        _feed_view_ptr->handlePruneRemovedDocuments(op, make_replay_feed_token(op));
    }
    void replay(const MoveOperation &op) override {
        _feed_view_ptr->handleMove(op, make_replay_feed_token(op));
    }
    void replay(const CreateBucketOperation &) override {
    }
    void replay(const CompactLidSpaceOperation &op) override {
        _feed_view_ptr->handleCompactLidSpace(op, make_replay_feed_token(op));
    }
    NewConfigOperation::IStreamHandler &getNewConfigStreamHandler() override {
        return _config_store;
    }
    const document::DocumentTypeRepo &getDeserializeRepo() override {
        return *_feed_view_ptr->getDocumentTypeRepo();
    }
    void check_serial_num(search::SerialNum serial_num) override {
        auto exp_serial_num = _inc_serial_num.inc_serial_num();
        if (exp_serial_num != serial_num) {
            LOG(warning, "Expected replay serial number %" PRIu64 ", got serial number %" PRIu64, exp_serial_num, serial_num);
            assert(exp_serial_num == serial_num);
        }
    }
    void optionalCommit(search::SerialNum serialNum) override {
        if (_commitTimeTracker.needCommit()) {
            _feed_view_ptr->forceCommit(serialNum);
        }
    }
};

class PacketDispatcher {
public:
    PacketDispatcher(IReplayPacketHandler *packet_handler)
        : _packet_handler(packet_handler)
    {}

    void handlePacket(PacketWrapper & wrap);
private:
    void handleEntry(const Packet::Entry &entry);
    IReplayPacketHandler *_packet_handler;
};

void
PacketDispatcher::handlePacket(PacketWrapper & wrap)
{
    vespalib::nbostream_longlivedbuf handle(wrap.packet.getHandle().data(), wrap.packet.getHandle().size());
    while ( !handle.empty() ) {
        Packet::Entry entry;
        entry.deserialize(handle);
        handleEntry(entry);
        if (wrap.progress != nullptr) {
            handleProgress(*wrap.progress, entry.serial());
        }
    }
    wrap.result = RPC::OK;
    wrap.gate.countDown();
}

void
PacketDispatcher::handleEntry(const Packet::Entry &entry) {
    // Called by handlePacket() in executor thread.
    LOG(spam, "replay packet entry: entrySerial(%" PRIu64 "), entryType(%u)", entry.serial(), entry.type());

    auto entry_serial_num = entry.serial();
    _packet_handler->check_serial_num(entry_serial_num);
    ReplayPacketDispatcher dispatcher(*_packet_handler);
    dispatcher.replayEntry(entry);
    _packet_handler->optionalCommit(entry_serial_num);
}

}  // namespace

ReplayTransactionLogState::ReplayTransactionLogState(
        const vespalib::string &name,
        IFeedView *& feed_view_ptr,
        IBucketDBHandler &bucketDBHandler,
        IReplayConfig &replay_config,
        FeedConfigStore &config_store,
        const ReplayThrottlingPolicy &replay_throttling_policy,
        IIncSerialNum& inc_serial_num)
    : FeedState(REPLAY_TRANSACTION_LOG),
      _doc_type_name(name),
      _packet_handler(std::make_unique<TransactionLogReplayPacketHandler>(feed_view_ptr, bucketDBHandler, replay_config, config_store, replay_throttling_policy, inc_serial_num))
{ }

ReplayTransactionLogState::~ReplayTransactionLogState() = default;

void
ReplayTransactionLogState::receive(const PacketWrapper::SP &wrap, Executor &executor) {
    executor.execute(makeLambdaTask([this, wrap = wrap] () {
        PacketDispatcher dispatcher(_packet_handler.get());
        dispatcher.handlePacket(*wrap);
    }));
}

}  // namespace proton
<commit_msg>Enable tracking of replay feed tokens.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "feedconfigstore.h"
#include "feedstates.h"
#include "ifeedview.h"
#include "ireplayconfig.h"
#include "replaypacketdispatcher.h"
#include "replay_throttling_policy.h"
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/searchcore/proton/common/replay_feed_token_factory.h>
#include <vespa/vespalib/util/idestructorcallback.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/shared_operation_throttler.h>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".proton.server.feedstates");

using search::transactionlog::Packet;
using search::transactionlog::client::RPC;
using search::SerialNum;
using vespalib::Executor;
using vespalib::makeLambdaTask;
using vespalib::IDestructorCallback;
using vespalib::SharedOperationThrottler;
using vespalib::make_string;
using proton::bucketdb::IBucketDBHandler;

namespace proton {

namespace {

const search::SerialNum REPLAY_PROGRESS_INTERVAL = 50000;

void
handleProgress(TlsReplayProgress &progress, SerialNum currentSerial)
{
    progress.updateCurrent(currentSerial);
    if (LOG_WOULD_LOG(event) && (LOG_WOULD_LOG(debug) ||
            (progress.getCurrent() % REPLAY_PROGRESS_INTERVAL == 0)))
    {
        EventLogger::transactionLogReplayProgress(progress.getDomainName(),
                                                  progress.getProgress(),
                                                  progress.getFirst(),
                                                  progress.getLast(),
                                                  progress.getCurrent());
    }
}

class TransactionLogReplayPacketHandler : public IReplayPacketHandler {
    IFeedView *& _feed_view_ptr;  // Pointer can be changed in executor thread.
    IBucketDBHandler &_bucketDBHandler;
    IReplayConfig &_replay_config;
    FeedConfigStore &_config_store;
    IIncSerialNum   &_inc_serial_num;
    CommitTimeTracker _commitTimeTracker;
    std::unique_ptr<SharedOperationThrottler> _throttler;
    std::unique_ptr<feedtoken::ReplayFeedTokenFactory> _replay_feed_token_factory;

    static std::unique_ptr<SharedOperationThrottler> make_throttler(const ReplayThrottlingPolicy& replay_throttling_policy) {
        auto& params = replay_throttling_policy.get_params();
        if (!params.has_value()) {
            return SharedOperationThrottler::make_unlimited_throttler();
        }
        return SharedOperationThrottler::make_dynamic_throttler(params.value());
    }

public:
    TransactionLogReplayPacketHandler(IFeedView *& feed_view_ptr,
                                      IBucketDBHandler &bucketDBHandler,
                                      IReplayConfig &replay_config,
                                      FeedConfigStore &config_store,
                                      const ReplayThrottlingPolicy& replay_throttling_policy,
                                      IIncSerialNum &inc_serial_num)
        : _feed_view_ptr(feed_view_ptr),
          _bucketDBHandler(bucketDBHandler),
          _replay_config(replay_config),
          _config_store(config_store),
          _inc_serial_num(inc_serial_num),
          _commitTimeTracker(5ms),
        _throttler(make_throttler(replay_throttling_policy)),
        _replay_feed_token_factory(std::make_unique<feedtoken::ReplayFeedTokenFactory>(true))
    { }

    ~TransactionLogReplayPacketHandler() override = default;

    FeedToken make_replay_feed_token(const FeedOperation& op) {
        SharedOperationThrottler::Token throttler_token = _throttler->blocking_acquire_one();
        return _replay_feed_token_factory->make_replay_feed_token(std::move(throttler_token), op);
    }

    void replay(const PutOperation &op) override {
        _feed_view_ptr->handlePut(make_replay_feed_token(op), op);
    }
    void replay(const RemoveOperation &op) override {
        _feed_view_ptr->handleRemove(make_replay_feed_token(op), op);
    }
    void replay(const UpdateOperation &op) override {
        _feed_view_ptr->handleUpdate(make_replay_feed_token(op), op);
    }
    void replay(const NoopOperation &) override {} // ignored
    void replay(const NewConfigOperation &op) override {
        _replay_config.replayConfig(op.getSerialNum());
    }

    void replay(const DeleteBucketOperation &op) override {
        _feed_view_ptr->handleDeleteBucket(op, make_replay_feed_token(op));
    }
    void replay(const SplitBucketOperation &op) override {
        _bucketDBHandler.handleSplit(op.getSerialNum(), op.getSource(),
                                     op.getTarget1(), op.getTarget2());
    }
    void replay(const JoinBucketsOperation &op) override {
        _bucketDBHandler.handleJoin(op.getSerialNum(), op.getSource1(),
                                    op.getSource2(), op.getTarget());
    }
    void replay(const PruneRemovedDocumentsOperation &op) override {
        _feed_view_ptr->handlePruneRemovedDocuments(op, make_replay_feed_token(op));
    }
    void replay(const MoveOperation &op) override {
        _feed_view_ptr->handleMove(op, make_replay_feed_token(op));
    }
    void replay(const CreateBucketOperation &) override {
    }
    void replay(const CompactLidSpaceOperation &op) override {
        _feed_view_ptr->handleCompactLidSpace(op, make_replay_feed_token(op));
    }
    NewConfigOperation::IStreamHandler &getNewConfigStreamHandler() override {
        return _config_store;
    }
    const document::DocumentTypeRepo &getDeserializeRepo() override {
        return *_feed_view_ptr->getDocumentTypeRepo();
    }
    void check_serial_num(search::SerialNum serial_num) override {
        auto exp_serial_num = _inc_serial_num.inc_serial_num();
        if (exp_serial_num != serial_num) {
            LOG(warning, "Expected replay serial number %" PRIu64 ", got serial number %" PRIu64, exp_serial_num, serial_num);
            assert(exp_serial_num == serial_num);
        }
    }
    void optionalCommit(search::SerialNum serialNum) override {
        if (_commitTimeTracker.needCommit()) {
            _feed_view_ptr->forceCommit(serialNum);
        }
    }
};

class PacketDispatcher {
public:
    PacketDispatcher(IReplayPacketHandler *packet_handler)
        : _packet_handler(packet_handler)
    {}

    void handlePacket(PacketWrapper & wrap);
private:
    void handleEntry(const Packet::Entry &entry);
    IReplayPacketHandler *_packet_handler;
};

void
PacketDispatcher::handlePacket(PacketWrapper & wrap)
{
    vespalib::nbostream_longlivedbuf handle(wrap.packet.getHandle().data(), wrap.packet.getHandle().size());
    while ( !handle.empty() ) {
        Packet::Entry entry;
        entry.deserialize(handle);
        handleEntry(entry);
        if (wrap.progress != nullptr) {
            handleProgress(*wrap.progress, entry.serial());
        }
    }
    wrap.result = RPC::OK;
    wrap.gate.countDown();
}

void
PacketDispatcher::handleEntry(const Packet::Entry &entry) {
    // Called by handlePacket() in executor thread.
    LOG(spam, "replay packet entry: entrySerial(%" PRIu64 "), entryType(%u)", entry.serial(), entry.type());

    auto entry_serial_num = entry.serial();
    _packet_handler->check_serial_num(entry_serial_num);
    ReplayPacketDispatcher dispatcher(*_packet_handler);
    dispatcher.replayEntry(entry);
    _packet_handler->optionalCommit(entry_serial_num);
}

}  // namespace

ReplayTransactionLogState::ReplayTransactionLogState(
        const vespalib::string &name,
        IFeedView *& feed_view_ptr,
        IBucketDBHandler &bucketDBHandler,
        IReplayConfig &replay_config,
        FeedConfigStore &config_store,
        const ReplayThrottlingPolicy &replay_throttling_policy,
        IIncSerialNum& inc_serial_num)
    : FeedState(REPLAY_TRANSACTION_LOG),
      _doc_type_name(name),
      _packet_handler(std::make_unique<TransactionLogReplayPacketHandler>(feed_view_ptr, bucketDBHandler, replay_config, config_store, replay_throttling_policy, inc_serial_num))
{ }

ReplayTransactionLogState::~ReplayTransactionLogState() = default;

void
ReplayTransactionLogState::receive(const PacketWrapper::SP &wrap, Executor &executor) {
    executor.execute(makeLambdaTask([this, wrap = wrap] () {
        PacketDispatcher dispatcher(_packet_handler.get());
        dispatcher.handlePacket(*wrap);
    }));
}

}  // namespace proton
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "feedconfigstore.h"
#include "feedstates.h"
#include "ifeedview.h"
#include "ireplayconfig.h"
#include "replaypacketdispatcher.h"
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/vespalib/util/idestructorcallback.h>
#include <vespa/vespalib/util/closuretask.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".proton.server.feedstates");

using search::transactionlog::Packet;
using search::transactionlog::client::RPC;
using search::SerialNum;
using vespalib::Executor;
using vespalib::makeClosure;
using vespalib::makeLambdaTask;
using vespalib::makeTask;
using vespalib::make_string;
using proton::bucketdb::IBucketDBHandler;

namespace proton {

namespace {
typedef vespalib::Closure1<const Packet::Entry &>::UP EntryHandler;

const search::SerialNum REPLAY_PROGRESS_INTERVAL = 50000;

void
handleProgress(TlsReplayProgress &progress, SerialNum currentSerial)
{
    progress.updateCurrent(currentSerial);
    if (LOG_WOULD_LOG(event) && (LOG_WOULD_LOG(debug) ||
            (progress.getCurrent() % REPLAY_PROGRESS_INTERVAL == 0)))
    {
        EventLogger::transactionLogReplayProgress(progress.getDomainName(),
                                                  progress.getProgress(),
                                                  progress.getFirst(),
                                                  progress.getLast(),
                                                  progress.getCurrent());
    }
}

void
handlePacket(PacketWrapper & wrap, EntryHandler entryHandler)
{
    vespalib::nbostream_longlivedbuf handle(wrap.packet.getHandle().data(), wrap.packet.getHandle().size());
    while ( !handle.empty() ) {
        Packet::Entry entry;
        entry.deserialize(handle);
        entryHandler->call(entry);
        if (wrap.progress != nullptr) {
            handleProgress(*wrap.progress, entry.serial());
        }
    }
    wrap.result = RPC::OK;
    wrap.gate.countDown();
}

class TransactionLogReplayPacketHandler : public IReplayPacketHandler {
    IFeedView *& _feed_view_ptr;  // Pointer can be changed in executor thread.
    IBucketDBHandler &_bucketDBHandler;
    IReplayConfig &_replay_config;
    FeedConfigStore &_config_store;
    IIncSerialNum   &_inc_serial_num;
    CommitTimeTracker _commitTimeTracker;

public:
    TransactionLogReplayPacketHandler(IFeedView *& feed_view_ptr,
                                      IBucketDBHandler &bucketDBHandler,
                                      IReplayConfig &replay_config,
                                      FeedConfigStore &config_store,
                                      IIncSerialNum &inc_serial_num)
        : _feed_view_ptr(feed_view_ptr),
          _bucketDBHandler(bucketDBHandler),
          _replay_config(replay_config),
          _config_store(config_store),
          _inc_serial_num(inc_serial_num),
          _commitTimeTracker(5ms)
    { }

    ~TransactionLogReplayPacketHandler() override = default;

    void replay(const PutOperation &op) override {
        _feed_view_ptr->handlePut(FeedToken(), op);
    }
    void replay(const RemoveOperation &op) override {
        _feed_view_ptr->handleRemove(FeedToken(), op);
    }
    void replay(const UpdateOperation &op) override {
        _feed_view_ptr->handleUpdate(FeedToken(), op);
    }
    void replay(const NoopOperation &) override {} // ignored
    void replay(const NewConfigOperation &op) override {
        _replay_config.replayConfig(op.getSerialNum());
    }

    void replay(const DeleteBucketOperation &op) override {
        _feed_view_ptr->handleDeleteBucket(op);
    }
    void replay(const SplitBucketOperation &op) override {
        _bucketDBHandler.handleSplit(op.getSerialNum(), op.getSource(),
                                     op.getTarget1(), op.getTarget2());
    }
    void replay(const JoinBucketsOperation &op) override {
        _bucketDBHandler.handleJoin(op.getSerialNum(), op.getSource1(),
                                    op.getSource2(), op.getTarget());
    }
    void replay(const PruneRemovedDocumentsOperation &op) override {
        _feed_view_ptr->handlePruneRemovedDocuments(op);
    }
    void replay(const MoveOperation &op) override {
        _feed_view_ptr->handleMove(op, vespalib::IDestructorCallback::SP());
    }
    void replay(const CreateBucketOperation &) override {
    }
    void replay(const CompactLidSpaceOperation &op) override {
        _feed_view_ptr->handleCompactLidSpace(op);
    }
    NewConfigOperation::IStreamHandler &getNewConfigStreamHandler() override {
        return _config_store;
    }
    const document::DocumentTypeRepo &getDeserializeRepo() override {
        return *_feed_view_ptr->getDocumentTypeRepo();
    }
    void check_serial_num(search::SerialNum serial_num) override {
        auto exp_serial_num = _inc_serial_num.inc_serial_num();
        if (exp_serial_num != serial_num) {
            LOG(warning, "Expected replay serial number %" PRIu64 ", got serial number %" PRIu64, exp_serial_num, serial_num);
            assert(exp_serial_num == serial_num);
        }
    }
    void optionalCommit(search::SerialNum serialNum) override {
        if (_commitTimeTracker.needCommit()) {
            _feed_view_ptr->forceCommit(serialNum);
        }
    }
};

void startDispatch(IReplayPacketHandler *packet_handler, const Packet::Entry &entry) {
    // Called by handlePacket() in executor thread.
    LOG(spam, "replay packet entry: entrySerial(%" PRIu64 "), entryType(%u)", entry.serial(), entry.type());

    auto entry_serial_num = entry.serial();
    packet_handler->check_serial_num(entry_serial_num);
    ReplayPacketDispatcher dispatcher(*packet_handler);
    dispatcher.replayEntry(entry);
    packet_handler->optionalCommit(entry_serial_num);
}

}  // namespace

ReplayTransactionLogState::ReplayTransactionLogState(
        const vespalib::string &name,
        IFeedView *& feed_view_ptr,
        IBucketDBHandler &bucketDBHandler,
        IReplayConfig &replay_config,
        FeedConfigStore &config_store,
        IIncSerialNum& inc_serial_num)
    : FeedState(REPLAY_TRANSACTION_LOG),
      _doc_type_name(name),
      _packet_handler(std::make_unique<TransactionLogReplayPacketHandler>(feed_view_ptr, bucketDBHandler, replay_config, config_store, inc_serial_num))
{ }

ReplayTransactionLogState::~ReplayTransactionLogState() = default;

void
ReplayTransactionLogState::receive(const PacketWrapper::SP &wrap, Executor &executor) {
    EntryHandler closure = makeClosure(&startDispatch, _packet_handler.get());
    executor.execute(makeLambdaTask([wrap = wrap, dispatch = std::move(closure)] () mutable { handlePacket(*wrap, std::move(dispatch)); }));
}

}  // namespace proton
<commit_msg>Simplify by avoiding closure.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "feedconfigstore.h"
#include "feedstates.h"
#include "ifeedview.h"
#include "ireplayconfig.h"
#include "replaypacketdispatcher.h"
#include <vespa/searchcore/proton/bucketdb/ibucketdbhandler.h>
#include <vespa/searchcore/proton/feedoperation/operations.h>
#include <vespa/searchcore/proton/common/eventlogger.h>
#include <vespa/vespalib/util/idestructorcallback.h>
#include <vespa/vespalib/util/lambdatask.h>
#include <cassert>

#include <vespa/log/log.h>
LOG_SETUP(".proton.server.feedstates");

using search::transactionlog::Packet;
using search::transactionlog::client::RPC;
using search::SerialNum;
using vespalib::Executor;
using vespalib::makeLambdaTask;
using vespalib::make_string;
using proton::bucketdb::IBucketDBHandler;

namespace proton {

namespace {

const search::SerialNum REPLAY_PROGRESS_INTERVAL = 50000;

void
handleProgress(TlsReplayProgress &progress, SerialNum currentSerial)
{
    progress.updateCurrent(currentSerial);
    if (LOG_WOULD_LOG(event) && (LOG_WOULD_LOG(debug) ||
            (progress.getCurrent() % REPLAY_PROGRESS_INTERVAL == 0)))
    {
        EventLogger::transactionLogReplayProgress(progress.getDomainName(),
                                                  progress.getProgress(),
                                                  progress.getFirst(),
                                                  progress.getLast(),
                                                  progress.getCurrent());
    }
}

class TransactionLogReplayPacketHandler : public IReplayPacketHandler {
    IFeedView *& _feed_view_ptr;  // Pointer can be changed in executor thread.
    IBucketDBHandler &_bucketDBHandler;
    IReplayConfig &_replay_config;
    FeedConfigStore &_config_store;
    IIncSerialNum   &_inc_serial_num;
    CommitTimeTracker _commitTimeTracker;

public:
    TransactionLogReplayPacketHandler(IFeedView *& feed_view_ptr,
                                      IBucketDBHandler &bucketDBHandler,
                                      IReplayConfig &replay_config,
                                      FeedConfigStore &config_store,
                                      IIncSerialNum &inc_serial_num)
        : _feed_view_ptr(feed_view_ptr),
          _bucketDBHandler(bucketDBHandler),
          _replay_config(replay_config),
          _config_store(config_store),
          _inc_serial_num(inc_serial_num),
          _commitTimeTracker(5ms)
    { }

    ~TransactionLogReplayPacketHandler() override = default;

    void replay(const PutOperation &op) override {
        _feed_view_ptr->handlePut(FeedToken(), op);
    }
    void replay(const RemoveOperation &op) override {
        _feed_view_ptr->handleRemove(FeedToken(), op);
    }
    void replay(const UpdateOperation &op) override {
        _feed_view_ptr->handleUpdate(FeedToken(), op);
    }
    void replay(const NoopOperation &) override {} // ignored
    void replay(const NewConfigOperation &op) override {
        _replay_config.replayConfig(op.getSerialNum());
    }

    void replay(const DeleteBucketOperation &op) override {
        _feed_view_ptr->handleDeleteBucket(op);
    }
    void replay(const SplitBucketOperation &op) override {
        _bucketDBHandler.handleSplit(op.getSerialNum(), op.getSource(),
                                     op.getTarget1(), op.getTarget2());
    }
    void replay(const JoinBucketsOperation &op) override {
        _bucketDBHandler.handleJoin(op.getSerialNum(), op.getSource1(),
                                    op.getSource2(), op.getTarget());
    }
    void replay(const PruneRemovedDocumentsOperation &op) override {
        _feed_view_ptr->handlePruneRemovedDocuments(op);
    }
    void replay(const MoveOperation &op) override {
        _feed_view_ptr->handleMove(op, vespalib::IDestructorCallback::SP());
    }
    void replay(const CreateBucketOperation &) override {
    }
    void replay(const CompactLidSpaceOperation &op) override {
        _feed_view_ptr->handleCompactLidSpace(op);
    }
    NewConfigOperation::IStreamHandler &getNewConfigStreamHandler() override {
        return _config_store;
    }
    const document::DocumentTypeRepo &getDeserializeRepo() override {
        return *_feed_view_ptr->getDocumentTypeRepo();
    }
    void check_serial_num(search::SerialNum serial_num) override {
        auto exp_serial_num = _inc_serial_num.inc_serial_num();
        if (exp_serial_num != serial_num) {
            LOG(warning, "Expected replay serial number %" PRIu64 ", got serial number %" PRIu64, exp_serial_num, serial_num);
            assert(exp_serial_num == serial_num);
        }
    }
    void optionalCommit(search::SerialNum serialNum) override {
        if (_commitTimeTracker.needCommit()) {
            _feed_view_ptr->forceCommit(serialNum);
        }
    }
};

class PacketDispatcher {
public:
    PacketDispatcher(IReplayPacketHandler *packet_handler)
        : _packet_handler(packet_handler)
    {}

    void handlePacket(PacketWrapper & wrap);
private:
    void handleEntry(const Packet::Entry &entry);
    IReplayPacketHandler *_packet_handler;
};

void
PacketDispatcher::handlePacket(PacketWrapper & wrap)
{
    vespalib::nbostream_longlivedbuf handle(wrap.packet.getHandle().data(), wrap.packet.getHandle().size());
    while ( !handle.empty() ) {
        Packet::Entry entry;
        entry.deserialize(handle);
        handleEntry(entry);
        if (wrap.progress != nullptr) {
            handleProgress(*wrap.progress, entry.serial());
        }
    }
    wrap.result = RPC::OK;
    wrap.gate.countDown();
}

void
PacketDispatcher::handleEntry(const Packet::Entry &entry) {
    // Called by handlePacket() in executor thread.
    LOG(spam, "replay packet entry: entrySerial(%" PRIu64 "), entryType(%u)", entry.serial(), entry.type());

    auto entry_serial_num = entry.serial();
    _packet_handler->check_serial_num(entry_serial_num);
    ReplayPacketDispatcher dispatcher(*_packet_handler);
    dispatcher.replayEntry(entry);
    _packet_handler->optionalCommit(entry_serial_num);
}

}  // namespace

ReplayTransactionLogState::ReplayTransactionLogState(
        const vespalib::string &name,
        IFeedView *& feed_view_ptr,
        IBucketDBHandler &bucketDBHandler,
        IReplayConfig &replay_config,
        FeedConfigStore &config_store,
        IIncSerialNum& inc_serial_num)
    : FeedState(REPLAY_TRANSACTION_LOG),
      _doc_type_name(name),
      _packet_handler(std::make_unique<TransactionLogReplayPacketHandler>(feed_view_ptr, bucketDBHandler, replay_config, config_store, inc_serial_num))
{ }

ReplayTransactionLogState::~ReplayTransactionLogState() = default;

void
ReplayTransactionLogState::receive(const PacketWrapper::SP &wrap, Executor &executor) {
    executor.execute(makeLambdaTask([this, wrap = wrap] () {
        PacketDispatcher dispatcher(_packet_handler.get());
        dispatcher.handlePacket(*wrap);
    }));
}

}  // namespace proton
<|endoftext|>


<commit_before>/**
 * \file
 * \brief SysTick_Handler() for ARMv6-M and ARMv7-M
 *
 * \author Copyright (C) 2014-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/internal/scheduler/getScheduler.hpp"
#include "distortos/internal/scheduler/Scheduler.hpp"

#include "distortos/architecture/requestContextSwitch.hpp"

#ifdef CONFIG_CHECK_STACK_GUARD_SYSTEM_TICK_ENABLE

#include "distortos/chip/CMSIS-proxy.h"

#include "distortos/FATAL_ERROR.h"

#endif	// def CONFIG_CHECK_STACK_GUARD_SYSTEM_TICK_ENABLE

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief SysTick_Handler() for ARMv6-M and ARMv7-M
 *
 * Tick interrupt of scheduler. This function also checks stack pointer range when this functionality is enabled - if
 * the check fails, FATAL_ERROR() is called.
 */

extern "C" void SysTick_Handler()
{
	auto& scheduler = distortos::internal::getScheduler();

#ifdef CONFIG_CHECK_STACK_GUARD_SYSTEM_TICK_ENABLE

	const auto stackPointer = reinterpret_cast<const void*>(__get_PSP());
	if (scheduler.getCurrentThreadControlBlock().getStack().checkStackPointer(stackPointer) == false)
		FATAL_ERROR("Stack overflow detected!");

#endif	// def CONFIG_CHECK_STACK_GUARD_SYSTEM_TICK_ENABLE

	const auto contextSwitchRequired = scheduler.tickInterruptHandler();
	if (contextSwitchRequired == true)
		distortos::architecture::requestContextSwitch();
}
<commit_msg>Fix conditions for stack pointer range checks in SysTick_Handler()<commit_after>/**
 * \file
 * \brief SysTick_Handler() for ARMv6-M and ARMv7-M
 *
 * \author Copyright (C) 2014-2017 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "distortos/internal/scheduler/getScheduler.hpp"
#include "distortos/internal/scheduler/Scheduler.hpp"

#include "distortos/architecture/requestContextSwitch.hpp"

#ifdef CONFIG_CHECK_STACK_POINTER_RANGE_SYSTEM_TICK_ENABLE

#include "distortos/chip/CMSIS-proxy.h"

#include "distortos/FATAL_ERROR.h"

#endif	// def CONFIG_CHECK_STACK_POINTER_RANGE_SYSTEM_TICK_ENABLE

/*---------------------------------------------------------------------------------------------------------------------+
| global functions
+---------------------------------------------------------------------------------------------------------------------*/

/**
 * \brief SysTick_Handler() for ARMv6-M and ARMv7-M
 *
 * Tick interrupt of scheduler. This function also checks stack pointer range when this functionality is enabled - if
 * the check fails, FATAL_ERROR() is called.
 */

extern "C" void SysTick_Handler()
{
	auto& scheduler = distortos::internal::getScheduler();

#ifdef CONFIG_CHECK_STACK_POINTER_RANGE_SYSTEM_TICK_ENABLE

	const auto stackPointer = reinterpret_cast<const void*>(__get_PSP());
	if (scheduler.getCurrentThreadControlBlock().getStack().checkStackPointer(stackPointer) == false)
		FATAL_ERROR("Stack overflow detected!");

#endif	// def CONFIG_CHECK_STACK_POINTER_RANGE_SYSTEM_TICK_ENABLE

	const auto contextSwitchRequired = scheduler.tickInterruptHandler();
	if (contextSwitchRequired == true)
		distortos::architecture::requestContextSwitch();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pam.hxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: hr $ $Date: 2007-09-27 08:07:57 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _PAM_HXX
#define _PAM_HXX

#include <stddef.h>         // fuer MemPool
#ifndef _GEN_HXX //autogen
#include <tools/gen.hxx>
#endif
#ifndef _SVMEMPOOL_HXX //autogen
#include <tools/mempool.hxx>
#endif

#ifndef _CSHTYP_HXX
#include <cshtyp.hxx>       // fuer die Funktions-Definitionen
#endif
#ifndef _RING_HXX
#include <ring.hxx>         // Superklasse
#endif
#ifndef _INDEX_HXX
#include <index.hxx>        // fuer SwIndex
#endif
#ifndef _NDINDEX_HXX
#include <ndindex.hxx>      // fuer SwNodeIndex
#endif

#ifndef INCLUDED_SWDLLAPI_H
#include "swdllapi.h"
#endif

class SwFmt;
class SfxPoolItem;
class SfxItemSet;
class SwDoc;
class SwNode;
class SwCntntNode;
class SwPaM;

namespace com { namespace sun { namespace star { namespace util {
    struct SearchOptions;
} } } }

namespace utl {
    class TextSearch;
}

struct SwPosition
{
    SwNodeIndex nNode;
    SwIndex nContent;

    SwPosition( const SwNode& rNode );
    SwPosition( const SwNodeIndex &rNode );
    SwPosition( const SwNodeIndex &rNode, const SwIndex &rCntnt );
    /* @@@MAINTAINABILITY-HORROR@@@
       SwPosition desperately needs a constructor
       SwPosition( const SwNode& rNode, xub_StrLen nOffset );
    */

    SwPosition( const SwPosition & );
    SwPosition &operator=(const SwPosition &);

    // #111827#
    /**
       Returns the document this position is in.

       @return the document this position is in.
    */
    SwDoc * GetDoc() const;

    BOOL operator < (const SwPosition &) const;
    BOOL operator > (const SwPosition &) const;
    BOOL operator <=(const SwPosition &) const;
    BOOL operator >=(const SwPosition &) const;
    BOOL operator ==(const SwPosition &) const;
    BOOL operator !=(const SwPosition &) const;
};

// das Ergebnis eines Positions Vergleiches
enum SwComparePosition {
    POS_BEFORE,             // Pos1 liegt vor Pos2
    POS_BEHIND,             // Pos1 liegt hinter Pos2
    POS_INSIDE,             // Pos1 liegt vollstaendig in Pos2
    POS_OUTSIDE,            // Pos2 liegt vollstaendig in Pos1
    POS_EQUAL,              // Pos1 ist genauso gross wie Pos2
    POS_OVERLAP_BEFORE,     // Pos1 ueberlappt Pos2 am Anfang
    POS_OVERLAP_BEHIND,     // Pos1 ueberlappt Pos2 am Ende
    POS_COLLIDE_START,      // Pos1 Start stoesst an Pos2 Ende
    POS_COLLIDE_END         // Pos1 End stoesst an Pos2 Start
};
SwComparePosition ComparePosition(
            const SwPosition& rStt1, const SwPosition& rEnd1,
            const SwPosition& rStt2, const SwPosition& rEnd2 );

SwComparePosition ComparePosition(
            const unsigned long nStt1, const unsigned long nEnd1,
            const unsigned long nStt2, const unsigned long nEnd2 );

// SwPointAndMark / SwPaM
struct SwMoveFnCollection;
typedef SwMoveFnCollection* SwMoveFn;
SW_DLLPUBLIC extern SwMoveFn fnMoveForward; // SwPam::Move()/Find() default argument.
extern SwMoveFn fnMoveBackward;

typedef BOOL (*SwGoInDoc)( SwPaM& rPam, SwMoveFn fnMove );
extern SwGoInDoc fnGoDoc;
extern SwGoInDoc fnGoSection;
extern SwGoInDoc fnGoNode;
SW_DLLPUBLIC extern SwGoInDoc fnGoCntnt; // SwPam::Move() default argument.
extern SwGoInDoc fnGoCntntCells;
extern SwGoInDoc fnGoCntntSkipHidden;
extern SwGoInDoc fnGoCntntCellsSkipHidden;

void _InitPam();

class SwPaM : public Ring
{
    SwPosition aBound1;
    SwPosition aBound2;
    SwPosition *pPoint;
    SwPosition *pMark;
    BOOL bIsInFrontOfLabel;

    SwPaM* MakeRegion( SwMoveFn fnMove, const SwPaM * pOrigRg = 0 );

public:
    SwPaM( const SwPosition& rPos, SwPaM* pRing = 0 );
    SwPaM( const SwPosition& rMk, const SwPosition& rPt, SwPaM* pRing = 0 );
    SwPaM( const SwNodeIndex& rMk, const SwNodeIndex& rPt,
           long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = 0 );
    SwPaM( const SwNode& rMk, const SwNode& rPt,
           long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = 0 );
    SwPaM(  const SwNodeIndex& rMk, xub_StrLen nMkCntnt,
            const SwNodeIndex& rPt, xub_StrLen nPtCntnt, SwPaM* pRing = 0 );
    SwPaM(  const SwNode& rMk, xub_StrLen nMkCntnt,
            const SwNode& rPt, xub_StrLen nPtCntnt, SwPaM* pRing = 0 );
    SwPaM( const SwNode& rNd, xub_StrLen nCntnt = 0, SwPaM* pRing = 0 );
    SwPaM( const SwNodeIndex& rNd, xub_StrLen nCntnt = 0, SwPaM* pRing = 0 );
    virtual ~SwPaM();

    // @@@ semantic: no copy ctor.
    SwPaM( SwPaM & );
    // @@@ semantic: no copy assignment for super class Ring.
    SwPaM& operator=( const SwPaM & );

    // Bewegen des Cursors
    BOOL Move( SwMoveFn fnMove = fnMoveForward,
                    SwGoInDoc fnGo = fnGoCntnt );

    // Suchen
    BYTE Find(  const com::sun::star::util::SearchOptions& rSearchOpt,
                utl::TextSearch& rSTxt,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE);
    BOOL Find(  const SwFmt& rFmt,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE);
    BOOL Find(  const SfxPoolItem& rAttr, BOOL bValue = TRUE,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE );
    BOOL Find(  const SfxItemSet& rAttr, BOOL bNoColls = FALSE,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE );

    inline BOOL IsInFrontOfLabel() const { return bIsInFrontOfLabel; }
    inline void _SetInFrontOfLabel( BOOL bNew ) { bIsInFrontOfLabel = bNew; }

    virtual void SetMark();
    void DeleteMark() { pMark = pPoint; }
#ifdef PRODUCT
    void Exchange()
    {
        if(pPoint != pMark)
        {
            SwPosition *pTmp = pPoint;
            pPoint = pMark;
            pMark = pTmp;
        }
    }
#else
    void Exchange();
#endif
    /*
     * Undokumented Feature: Liefert zurueck, ob das Pam ueber
     * eine Selektion verfuegt oder nicht. Definition einer
     * Selektion: Point und Mark zeigen auf unterschiedliche
     * Puffer.
     */
    BOOL HasMark() const { return pPoint == pMark? FALSE : TRUE; }

    const SwPosition *GetPoint() const { return pPoint; }
          SwPosition *GetPoint()       { return pPoint; }
    const SwPosition *GetMark()  const { return pMark; }
          SwPosition *GetMark()        { return pMark; }

    const SwPosition *Start() const
                    { return (*pPoint) <= (*pMark)? pPoint: pMark; }
          SwPosition *Start()
                    { return (*pPoint) <= (*pMark)? pPoint: pMark; }
    const SwPosition *End()   const
                    { return (*pPoint) > (*pMark)? pPoint: pMark; }
          SwPosition *End()
                    { return (*pPoint) > (*pMark)? pPoint: pMark; }

    // erfrage vom SwPaM den aktuellen Node/ContentNode am SPoint / Mark
    SwNode* GetNode( BOOL bPoint = TRUE ) const
    {
        return &( bPoint ? pPoint->nNode : pMark->nNode ).GetNode();
    }
    SwCntntNode* GetCntntNode( BOOL bPoint = TRUE ) const
    {
        return ( bPoint ? pPoint->nNode : pMark->nNode ).GetNode().GetCntntNode();
    }

    /**
       Normalizes PaM, i.e. sort point and mark.

       @param bPointFirst TRUE: If the point is behind the mark then swap.
                          FALSE: If the mark is behind the point then swap.
    */
    SwPaM & Normalize(BOOL bPointFirst = TRUE);

    // erfrage vom SwPaM das Dokument, in dem er angemeldet ist
    SwDoc* GetDoc() const { return pPoint->nNode.GetNode().GetDoc(); }
    SwPosition& GetBound( BOOL bOne = TRUE )
    {   return bOne ? aBound1 : aBound2; }
    const SwPosition& GetBound( BOOL bOne = TRUE ) const
    {   return bOne ? aBound1 : aBound2; }

    // erfrage die Seitennummer auf der der Cursor steht
    USHORT GetPageNum( BOOL bAtPoint = TRUE, const Point* pLayPos = 0 );

    // steht in etwas geschuetztem oder in die Selektion umspannt
    // etwas geschuetztes.
    BOOL HasReadonlySel( bool bFormView ) const;

    BOOL ContainsPosition(const SwPosition & rPos)
    { return *Start() <= rPos && rPos <= *End(); }

    DECL_FIXEDMEMPOOL_NEWDEL(SwPaM);

    String GetTxt() const;
};

BOOL CheckNodesRange( const SwNodeIndex&, const SwNodeIndex&, BOOL );
BOOL GoInCntnt( SwPaM & rPam, SwMoveFn fnMove );

#endif  // _PAM_HXX
<commit_msg>INTEGRATION: CWS sw8u10bf05 (1.17.138); FILE MERGED 2008/01/11 08:03:28 ama 1.17.138.1: Fix #b6640846#: Don't skip empty paragraphs<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: pam.hxx,v $
 *
 *  $Revision: 1.18 $
 *
 *  last change: $Author: vg $ $Date: 2008-03-18 15:53:12 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _PAM_HXX
#define _PAM_HXX

#include <stddef.h>         // fuer MemPool
#ifndef _GEN_HXX //autogen
#include <tools/gen.hxx>
#endif
#ifndef _SVMEMPOOL_HXX //autogen
#include <tools/mempool.hxx>
#endif

#ifndef _CSHTYP_HXX
#include <cshtyp.hxx>       // fuer die Funktions-Definitionen
#endif
#ifndef _RING_HXX
#include <ring.hxx>         // Superklasse
#endif
#ifndef _INDEX_HXX
#include <index.hxx>        // fuer SwIndex
#endif
#ifndef _NDINDEX_HXX
#include <ndindex.hxx>      // fuer SwNodeIndex
#endif

#ifndef INCLUDED_SWDLLAPI_H
#include "swdllapi.h"
#endif

class SwFmt;
class SfxPoolItem;
class SfxItemSet;
class SwDoc;
class SwNode;
class SwCntntNode;
class SwPaM;

namespace com { namespace sun { namespace star { namespace util {
    struct SearchOptions;
} } } }

namespace utl {
    class TextSearch;
}

struct SwPosition
{
    SwNodeIndex nNode;
    SwIndex nContent;

    SwPosition( const SwNode& rNode );
    SwPosition( const SwNodeIndex &rNode );
    SwPosition( const SwNodeIndex &rNode, const SwIndex &rCntnt );
    /* @@@MAINTAINABILITY-HORROR@@@
       SwPosition desperately needs a constructor
       SwPosition( const SwNode& rNode, xub_StrLen nOffset );
    */

    SwPosition( const SwPosition & );
    SwPosition &operator=(const SwPosition &);

    // #111827#
    /**
       Returns the document this position is in.

       @return the document this position is in.
    */
    SwDoc * GetDoc() const;

    BOOL operator < (const SwPosition &) const;
    BOOL operator > (const SwPosition &) const;
    BOOL operator <=(const SwPosition &) const;
    BOOL operator >=(const SwPosition &) const;
    BOOL operator ==(const SwPosition &) const;
    BOOL operator !=(const SwPosition &) const;
};

// das Ergebnis eines Positions Vergleiches
enum SwComparePosition {
    POS_BEFORE,             // Pos1 liegt vor Pos2
    POS_BEHIND,             // Pos1 liegt hinter Pos2
    POS_INSIDE,             // Pos1 liegt vollstaendig in Pos2
    POS_OUTSIDE,            // Pos2 liegt vollstaendig in Pos1
    POS_EQUAL,              // Pos1 ist genauso gross wie Pos2
    POS_OVERLAP_BEFORE,     // Pos1 ueberlappt Pos2 am Anfang
    POS_OVERLAP_BEHIND,     // Pos1 ueberlappt Pos2 am Ende
    POS_COLLIDE_START,      // Pos1 Start stoesst an Pos2 Ende
    POS_COLLIDE_END         // Pos1 End stoesst an Pos2 Start
};
SwComparePosition ComparePosition(
            const SwPosition& rStt1, const SwPosition& rEnd1,
            const SwPosition& rStt2, const SwPosition& rEnd2 );

SwComparePosition ComparePosition(
            const unsigned long nStt1, const unsigned long nEnd1,
            const unsigned long nStt2, const unsigned long nEnd2 );

// SwPointAndMark / SwPaM
struct SwMoveFnCollection;
typedef SwMoveFnCollection* SwMoveFn;
SW_DLLPUBLIC extern SwMoveFn fnMoveForward; // SwPam::Move()/Find() default argument.
extern SwMoveFn fnMoveBackward;

typedef BOOL (*SwGoInDoc)( SwPaM& rPam, SwMoveFn fnMove );
extern SwGoInDoc fnGoDoc;
extern SwGoInDoc fnGoSection;
extern SwGoInDoc fnGoNode;
SW_DLLPUBLIC extern SwGoInDoc fnGoCntnt; // SwPam::Move() default argument.
extern SwGoInDoc fnGoCntntCells;
extern SwGoInDoc fnGoCntntSkipHidden;
extern SwGoInDoc fnGoCntntCellsSkipHidden;

void _InitPam();

class SwPaM : public Ring
{
    SwPosition aBound1;
    SwPosition aBound2;
    SwPosition *pPoint;
    SwPosition *pMark;
    BOOL bIsInFrontOfLabel;

    SwPaM* MakeRegion( SwMoveFn fnMove, const SwPaM * pOrigRg = 0 );

public:
    SwPaM( const SwPosition& rPos, SwPaM* pRing = 0 );
    SwPaM( const SwPosition& rMk, const SwPosition& rPt, SwPaM* pRing = 0 );
    SwPaM( const SwNodeIndex& rMk, const SwNodeIndex& rPt,
           long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = 0 );
    SwPaM( const SwNode& rMk, const SwNode& rPt,
           long nMkOffset = 0, long nPtOffset = 0, SwPaM* pRing = 0 );
    SwPaM(  const SwNodeIndex& rMk, xub_StrLen nMkCntnt,
            const SwNodeIndex& rPt, xub_StrLen nPtCntnt, SwPaM* pRing = 0 );
    SwPaM(  const SwNode& rMk, xub_StrLen nMkCntnt,
            const SwNode& rPt, xub_StrLen nPtCntnt, SwPaM* pRing = 0 );
    SwPaM( const SwNode& rNd, xub_StrLen nCntnt = 0, SwPaM* pRing = 0 );
    SwPaM( const SwNodeIndex& rNd, xub_StrLen nCntnt = 0, SwPaM* pRing = 0 );
    virtual ~SwPaM();

    // @@@ semantic: no copy ctor.
    SwPaM( SwPaM & );
    // @@@ semantic: no copy assignment for super class Ring.
    SwPaM& operator=( const SwPaM & );

    // Bewegen des Cursors
    BOOL Move( SwMoveFn fnMove = fnMoveForward,
                    SwGoInDoc fnGo = fnGoCntnt );

    // Suchen
    BYTE Find(  const com::sun::star::util::SearchOptions& rSearchOpt,
                utl::TextSearch& rSTxt,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE);
    BOOL Find(  const SwFmt& rFmt,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE);
    BOOL Find(  const SfxPoolItem& rAttr, BOOL bValue = TRUE,
                SwMoveFn fnMove = fnMoveForward,
                const SwPaM *pPam =0, BOOL bInReadOnly = FALSE );
    BOOL Find(  const SfxItemSet& rAttr, BOOL bNoColls,
                SwMoveFn fnMove,
                const SwPaM *pPam, BOOL bInReadOnly, BOOL bMoveFirst );

    inline BOOL IsInFrontOfLabel() const { return bIsInFrontOfLabel; }
    inline void _SetInFrontOfLabel( BOOL bNew ) { bIsInFrontOfLabel = bNew; }

    virtual void SetMark();
    void DeleteMark() { pMark = pPoint; }
#ifdef PRODUCT
    void Exchange()
    {
        if(pPoint != pMark)
        {
            SwPosition *pTmp = pPoint;
            pPoint = pMark;
            pMark = pTmp;
        }
    }
#else
    void Exchange();
#endif
    /*
     * Undokumented Feature: Liefert zurueck, ob das Pam ueber
     * eine Selektion verfuegt oder nicht. Definition einer
     * Selektion: Point und Mark zeigen auf unterschiedliche
     * Puffer.
     */
    BOOL HasMark() const { return pPoint == pMark? FALSE : TRUE; }

    const SwPosition *GetPoint() const { return pPoint; }
          SwPosition *GetPoint()       { return pPoint; }
    const SwPosition *GetMark()  const { return pMark; }
          SwPosition *GetMark()        { return pMark; }

    const SwPosition *Start() const
                    { return (*pPoint) <= (*pMark)? pPoint: pMark; }
          SwPosition *Start()
                    { return (*pPoint) <= (*pMark)? pPoint: pMark; }
    const SwPosition *End()   const
                    { return (*pPoint) > (*pMark)? pPoint: pMark; }
          SwPosition *End()
                    { return (*pPoint) > (*pMark)? pPoint: pMark; }

    // erfrage vom SwPaM den aktuellen Node/ContentNode am SPoint / Mark
    SwNode* GetNode( BOOL bPoint = TRUE ) const
    {
        return &( bPoint ? pPoint->nNode : pMark->nNode ).GetNode();
    }
    SwCntntNode* GetCntntNode( BOOL bPoint = TRUE ) const
    {
        return ( bPoint ? pPoint->nNode : pMark->nNode ).GetNode().GetCntntNode();
    }

    /**
       Normalizes PaM, i.e. sort point and mark.

       @param bPointFirst TRUE: If the point is behind the mark then swap.
                          FALSE: If the mark is behind the point then swap.
    */
    SwPaM & Normalize(BOOL bPointFirst = TRUE);

    // erfrage vom SwPaM das Dokument, in dem er angemeldet ist
    SwDoc* GetDoc() const { return pPoint->nNode.GetNode().GetDoc(); }
    SwPosition& GetBound( BOOL bOne = TRUE )
    {   return bOne ? aBound1 : aBound2; }
    const SwPosition& GetBound( BOOL bOne = TRUE ) const
    {   return bOne ? aBound1 : aBound2; }

    // erfrage die Seitennummer auf der der Cursor steht
    USHORT GetPageNum( BOOL bAtPoint = TRUE, const Point* pLayPos = 0 );

    // steht in etwas geschuetztem oder in die Selektion umspannt
    // etwas geschuetztes.
    BOOL HasReadonlySel( bool bFormView ) const;

    BOOL ContainsPosition(const SwPosition & rPos)
    { return *Start() <= rPos && rPos <= *End(); }

    DECL_FIXEDMEMPOOL_NEWDEL(SwPaM);

    String GetTxt() const;
};

BOOL CheckNodesRange( const SwNodeIndex&, const SwNodeIndex&, BOOL );
BOOL GoInCntnt( SwPaM & rPam, SwMoveFn fnMove );

#endif  // _PAM_HXX
<|endoftext|>


<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve SIM900 tests.
 */

#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>

#include "OTSIM900Link.h"

// Test the getter function definitely does what it should.
TEST(OTSIM900Link, getterFunction)
{
    const char SIM900_PIN[] = "1111";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, NULL, NULL, NULL);
    EXPECT_EQ(SIM900_PIN[0], SIM900Config.get((const uint8_t *)SIM900Config.PIN));
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 never accepts data or responds, eg like a dead card.
TEST(OTSIM900Link,basicsDeadCard)
{
    const bool verbose = false;

    class NULLSerialStream final : public Stream
      {
      public:
        void begin(unsigned long) { }
        void begin(unsigned long, uint8_t);
        void end();

        virtual size_t write(uint8_t c) override { if(verbose) { fprintf(stderr, "%c\n", (char) c); } return(0); }
        virtual int available() override { return(-1); }
        virtual int read() override { return(-1); }
        virtual int peek() override { return(-1); }
        virtual void flush() override { }
      };
    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);
    OTSIM900Link::OTSIM900Link<0, 0, 0, NULLSerialStream> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_EQ(OTSIM900Link::PANIC, l0._getState()) << "should keep trying to start with GET_STATE, RETRY_GET_STATE and START_UP";
    // ...
    l0.end();
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 accepts output, does not respond.
namespace B1
{
const bool verbose = true;

// Does a trivial simulation of SIM900, responding to start of 'A' of AT command.
// Exercises every major non-PANIC state of the OTSIM900Link implementation.
class TrivialSimulator final : public Stream
  {
  public:
    // Events exposed.
    static bool haveSeenCommandStart;

  private:
    // Command being collected from OTSIM900Link.
    bool waitingForCommand = true;
    bool collectingCommand = false;
    // Entire request starting "AT"; no trailing CR or LF stored.
    std::string command;

    // Reply (postfix) being returned to OTSIM900Link: empty if none.
    std::string reply;

  public:
    void begin(unsigned long) { }
    void begin(unsigned long, uint8_t);
    void end();

    virtual size_t write(uint8_t uc) override
      {
      const char c = (char)uc;
      if(waitingForCommand)
        {
        // Look for leading 'A' of 'AT' to start a command.
        if('A' == c)
          {
          waitingForCommand = false;
          collectingCommand = true;
          command = 'A';
          haveSeenCommandStart = true; // Note at least one command start.
          }
        }
      else
        {
        // Look for CR (or LF) to terminate a command.
        if(('\r' == c) || ('\n' == c))
          {
          waitingForCommand = true;
          collectingCommand = false;
          if(verbose) { fprintf(stderr, "command received: %s\n", command.c_str()); }
          // Respond to particular commands...
          if("AT" == command) { reply = "AT\r"; }  // Relevant states: GET_STATE, RETRY_GET_STATE, START_UP
          // DHD20161101: "No PIN" response (deliberately not typical SIM900 response) resulted in SIGSEGV from not checking getResponse() result for NULL.
          // Should futz/vary the response to check sensitivity.
          // TODO: have at least one response be expected SIM900 answer for no-PIN SIM.
          else if("AT+CPIN?" == command) { reply = (random() & 1) ? "No PIN\r" : "OK READY\r"; }  // Relevant states: CHECK_PIN
          else if("AT+CREG?" == command) { reply = (random() & 1) ? "+CREG: 0,0\r" : "+CREG: 0,5\r"; } // Relevant states: WAIT_FOR_REGISTRATION
          else if("AT+CSTT=apn" == command) { reply = (random() & 1) ? "gbfhs\r" : "AT+CSTT\r\n\r\nOK\r"; } // Relevant states: SET_APN
          }
        else if(collectingCommand) { command += c; }
        }
      if(verbose) { if(isprint(c)) { fprintf(stderr, "<%c\n", c); } else { fprintf(stderr, "< %d\n", (int)c); } }
      return(1);
      }
    virtual int read() override
        {
        if(0 == reply.size()) { return(-1); }
        const char c = reply[0];
        if(verbose) { if(isprint(c)) { fprintf(stderr, ">%c\n", c); } else { fprintf(stderr, "> %d\n", (int)c); } }
        reply.erase(0, 1);
        return(c);
        }
    virtual int available() override { return(-1); }
    virtual int peek() override { return(-1); }
    virtual void flush() override { }
  };
// Events exposed.
bool TrivialSimulator::haveSeenCommandStart;
}
TEST(OTSIM900Link,basicsSimpleSimulator)
{
//    const bool verbose = true;

    srandom(::testing::UnitTest::GetInstance()->random_seed()); // Seed random() for use in simulator; --gtest_shuffle will force it to change.

    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);

    ASSERT_FALSE(B1::TrivialSimulator::haveSeenCommandStart);
    OTSIM900Link::OTSIM900Link<0, 0, 0, B1::TrivialSimulator> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_TRUE(B1::TrivialSimulator::haveSeenCommandStart) << "should see some attempt to communicate with SIM900";
    EXPECT_LE(OTSIM900Link::WAIT_FOR_REGISTRATION, l0._getState()) << "should make it to at least WAIT_FOR_REGISTRATION";
    // ...
    l0.end();
}

<commit_msg>TODO-1034: improved description of test goals<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2016
*/

/*
 * OTRadValve SIM900 tests.
 */

#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>

#include "OTSIM900Link.h"

// Test the getter function definitely does what it should.
TEST(OTSIM900Link, getterFunction)
{
    const char SIM900_PIN[] = "1111";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, NULL, NULL, NULL);
    EXPECT_EQ(SIM900_PIN[0], SIM900Config.get((const uint8_t *)SIM900Config.PIN));
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 never accepts data or responds, eg like a dead card.
TEST(OTSIM900Link,basicsDeadCard)
{
    const bool verbose = false;

    class NULLSerialStream final : public Stream
      {
      public:
        void begin(unsigned long) { }
        void begin(unsigned long, uint8_t);
        void end();

        virtual size_t write(uint8_t c) override { if(verbose) { fprintf(stderr, "%c\n", (char) c); } return(0); }
        virtual int available() override { return(-1); }
        virtual int read() override { return(-1); }
        virtual int peek() override { return(-1); }
        virtual void flush() override { }
      };
    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);
    OTSIM900Link::OTSIM900Link<0, 0, 0, NULLSerialStream> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_EQ(OTSIM900Link::PANIC, l0._getState()) << "should keep trying to start with GET_STATE, RETRY_GET_STATE and START_UP";
    // ...
    l0.end();
}

// Test for general sanity of OTSIM900Link.
// Make sure that an instance can be created and does not die horribly.
// Underlying simulated serial/SIM900 accepts output, does not respond.
namespace B1
{
const bool verbose = true;

// Does a trivial simulation of SIM900, responding to start of 'A' of AT command.
// Exercises every major non-PANIC state of the OTSIM900Link implementation.
// Is meant to mainly walk through all the normal expected SIM900 behaviour when all is well.
// Other test can look at error handling including unexpected/garbage responses.
class TrivialSimulator final : public Stream
  {
  public:
    // Events exposed.
    static bool haveSeenCommandStart;

  private:
    // Command being collected from OTSIM900Link.
    bool waitingForCommand = true;
    bool collectingCommand = false;
    // Entire request starting "AT"; no trailing CR or LF stored.
    std::string command;

    // Reply (postfix) being returned to OTSIM900Link: empty if none.
    std::string reply;

  public:
    void begin(unsigned long) { }
    void begin(unsigned long, uint8_t);
    void end();

    virtual size_t write(uint8_t uc) override
      {
      const char c = (char)uc;
      if(waitingForCommand)
        {
        // Look for leading 'A' of 'AT' to start a command.
        if('A' == c)
          {
          waitingForCommand = false;
          collectingCommand = true;
          command = 'A';
          haveSeenCommandStart = true; // Note at least one command start.
          }
        }
      else
        {
        // Look for CR (or LF) to terminate a command.
        if(('\r' == c) || ('\n' == c))
          {
          waitingForCommand = true;
          collectingCommand = false;
          if(verbose) { fprintf(stderr, "command received: %s\n", command.c_str()); }
          // Respond to particular commands...
          if("AT" == command) { reply = "AT\r"; }  // Relevant states: GET_STATE, RETRY_GET_STATE, START_UP
          // DHD20161101: "No PIN" response (deliberately not typical SIM900 response) resulted in SIGSEGV from not checking getResponse() result for NULL.
          // Should futz/vary the response to check sensitivity.
          // TODO: have at least one response be expected SIM900 answer for no-PIN SIM.
          else if("AT+CPIN?" == command) { reply = (random() & 1) ? "No PIN\r" : "OK READY\r"; }  // Relevant states: CHECK_PIN
          else if("AT+CREG?" == command) { reply = (random() & 1) ? "+CREG: 0,0\r" : "+CREG: 0,5\r"; } // Relevant states: WAIT_FOR_REGISTRATION
          else if("AT+CSTT=apn" == command) { reply = (random() & 1) ? "gbfhs\r" : "AT+CSTT\r\n\r\nOK\r"; } // Relevant states: SET_APN
          }
        else if(collectingCommand) { command += c; }
        }
      if(verbose) { if(isprint(c)) { fprintf(stderr, "<%c\n", c); } else { fprintf(stderr, "< %d\n", (int)c); } }
      return(1);
      }
    virtual int read() override
        {
        if(0 == reply.size()) { return(-1); }
        const char c = reply[0];
        if(verbose) { if(isprint(c)) { fprintf(stderr, ">%c\n", c); } else { fprintf(stderr, "> %d\n", (int)c); } }
        reply.erase(0, 1);
        return(c);
        }
    virtual int available() override { return(-1); }
    virtual int peek() override { return(-1); }
    virtual void flush() override { }
  };
// Events exposed.
bool TrivialSimulator::haveSeenCommandStart;
}
TEST(OTSIM900Link,basicsSimpleSimulator)
{
//    const bool verbose = true;

    srandom(::testing::UnitTest::GetInstance()->random_seed()); // Seed random() for use in simulator; --gtest_shuffle will force it to change.

    const char SIM900_PIN[] = "1111";
    const char SIM900_APN[] = "apn";
    const char SIM900_UDP_ADDR[] = "0.0.0.0"; // ORS server
    const char SIM900_UDP_PORT[] = "9999";
    const OTSIM900Link::OTSIM900LinkConfig_t SIM900Config(false, SIM900_PIN, SIM900_APN, SIM900_UDP_ADDR, SIM900_UDP_PORT);
    const OTRadioLink::OTRadioChannelConfig l0Config(&SIM900Config, true);

    ASSERT_FALSE(B1::TrivialSimulator::haveSeenCommandStart);
    OTSIM900Link::OTSIM900Link<0, 0, 0, B1::TrivialSimulator> l0;
    EXPECT_TRUE(l0.configure(1, &l0Config));
    EXPECT_TRUE(l0.begin());
    EXPECT_EQ(OTSIM900Link::GET_STATE, l0._getState());
    // Try to hang just by calling poll() repeatedly.
    for(int i = 0; i < 100; ++i) { l0.poll(); }
    EXPECT_TRUE(B1::TrivialSimulator::haveSeenCommandStart) << "should see some attempt to communicate with SIM900";
    EXPECT_LE(OTSIM900Link::WAIT_FOR_REGISTRATION, l0._getState()) << "should make it to at least WAIT_FOR_REGISTRATION";
    // ...
    l0.end();
}

<|endoftext|>