<commit_before>// @(#)root/cont:$Name:  $:$Id: TProcessID.cxx,v 1.1 2001/10/01 10:29:08 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TProcessID                                                           //
//                                                                      //
// ROOT session descriptor (unique in time and space)                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TProcessID.h"
#include "TROOT.h"
#include "TFile.h"
#include "TObjArray.h"
#include "TSystem.h"
#include "TDatime.h"

ClassImp(TProcessID)

// A TProcessID identifies a ROOT job in a unique way in time and space.
// The TProcessID title is made of :
//    - The process pid
//    - The system hostname
//    - The system time in clock units
//
// A TProcessID is automatically created by the TROOT constructor.
// When a TFile contains referenced objects (see TObjectRef), the TProcessID
// object is written to the file.
// If a file has been written in multiple sessions (same machine or not),
// a TProcessID is written for each session.
// These objects are used by the class TObjectRef to uniquely identified
// any TObject pointed by a TObjectRef.
//
// When a referenced object is read from a file (its bit kIsReferenced is set),
// this object is entered into the TExmap *fMap of the corresponding TProcessID.
// Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
// accessible via gROOT->GetListOfProcessIDs() (for all files).
// When this object is deleted, it is removed from the map via the cleanup
// mechanism invoked by the TObject destructor.
//______________________________________________________________________________
TProcessID::TProcessID()
{
   fMap = 0;
   gROOT->GetListOfCleanups()->Add(this);
}

//______________________________________________________________________________
TProcessID::TProcessID(Int_t pid)
{
   char name[20];
   char title[100];
   TDatime d;
   sprintf(name,"ProcessID%d",pid);
   sprintf(title,"pid%d_%s_%s",gSystem->GetPid(),gSystem->HostName(),d.AsString());
   SetName(name);
   SetTitle(title);
   fMap = 0;
   fCount = 0;
   gROOT->GetListOfCleanups()->Add(this);
}

//______________________________________________________________________________
TProcessID::~TProcessID()
{

   delete fMap;
   fMap = 0;
   gROOT->GetListOfProcessIDs()->Remove(this);
   gROOT->GetListOfCleanups()->Remove(this);
}
//______________________________________________________________________________
TProcessID::TProcessID(const TProcessID &ref)
{
   // TProcessID copy ctor.
}

//______________________________________________________________________________
Int_t TProcessID::DecrementCount()
{

   // the reference fCount is used to delete the TProcessID
   // in the TFile destructor when fCount = 0
   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

//______________________________________________________________________________
Int_t TProcessID::IncrementCount()
{

   fCount++;
   return fCount;
}

//______________________________________________________________________________
TObject *TProcessID::GetObjectWithID(Long_t uid)
{
   //returns the TObject with unique identifier uid in the TExMap
   if (!fMap) return 0;
   Long_t id = fMap->GetValue(uid);
   return (TObject *)id;
}

//______________________________________________________________________________
void TProcessID::PutObjectWithID(Long_t uid, TObject *obj)
{

   //stores the object with its key uid in the TExmap.
   //The object uniqueid is set as well as its kMustCleanup bit
   if (!fMap) fMap = new TExMap(100);
   Long_t id = (Long_t)obj;
   obj->SetBit(kMustCleanup);
   obj->SetUniqueID(UInt_t(uid));
   if (!fMap->GetValue(uid)) fMap->Add(uid,id);
}

//______________________________________________________________________________
TProcessID  *TProcessID::ReadProcessID(Int_t pidf, TFile *file)
{
// static function

   //The TProcessID with number pidf is read from file.
   //If the object is not already entered in the gROOT list, it is added.

   if (!file) return 0;
   TObjArray *pids = file->GetListOfProcessIDs();
   TProcessID *pid = (TProcessID *)pids->UncheckedAt(pidf);
   if (pid) return pid;

   //check if fProcessIDs[uid] is set in file
   //if not set, read the process uid from file
   char pidname[32];
   sprintf(pidname,"ProcessID%d",pidf);
   pid = (TProcessID *)file->Get(pidname);
   if (!pid) {
      file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
      return 0;
   }
      //check that a similar pid is not already registered in gROOT
   TIter next(gROOT->GetListOfProcessIDs());
   TProcessID *apid;
   while ((apid=(TProcessID *)next())) {
      if (strcmp(apid->GetTitle(),pid->GetTitle())) continue;
      gROOT->GetListOfProcessIDs()->Add(pid);
      break;
   }
   pids->AddAt(pid,pidf);
   pid->IncrementCount();
   return pid;
}

//______________________________________________________________________________
void TProcessID::RecursiveRemove(TObject *obj)
{
   // called by the object destructor
   if (!fMap) return;
   Long_t uid = (Long_t)obj->GetUniqueID();
   fMap->Remove(uid);
}
<commit_msg>Use new class TUUID to identify the current process.<commit_after>// @(#)root/cont:$Name:  $:$Id: TProcessID.cxx,v 1.2 2001/10/01 14:15:51 brun Exp $
// Author: Rene Brun   28/09/2001

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TProcessID                                                           //
//                                                                      //
// ROOT session descriptor (unique in time and space)                   //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TProcessID.h"
#include "TROOT.h"
#include "TFile.h"
#include "TObjArray.h"
#include "TSystem.h"
#include "TUUID.h"

ClassImp(TProcessID)

// A TProcessID identifies a ROOT job in a unique way in time and space.
// The TProcessID title is made of :
//    - The process pid
//    - The system hostname
//    - The system time in clock units
//
// A TProcessID is automatically created by the TROOT constructor.
// When a TFile contains referenced objects (see TObjectRef), the TProcessID
// object is written to the file.
// If a file has been written in multiple sessions (same machine or not),
// a TProcessID is written for each session.
// These objects are used by the class TObjectRef to uniquely identified
// any TObject pointed by a TObjectRef.
//
// When a referenced object is read from a file (its bit kIsReferenced is set),
// this object is entered into the TExmap *fMap of the corresponding TProcessID.
// Each TFile has a list of TProcessIDs (see TFile::fProcessIDs) also
// accessible via gROOT->GetListOfProcessIDs() (for all files).
// When this object is deleted, it is removed from the map via the cleanup
// mechanism invoked by the TObject destructor.
//______________________________________________________________________________
TProcessID::TProcessID()
{
   fCount = 0;
   fMap = 0;
   gROOT->GetListOfCleanups()->Add(this);
}

//______________________________________________________________________________
TProcessID::TProcessID(Int_t pid)
{
   char name[20];
   sprintf(name,"ProcessID%d",pid);
   SetName(name);
   TUUID u;
   SetTitle(u.AsString());
   fMap = 0;
   fCount = 0;
   gROOT->GetListOfCleanups()->Add(this);
}

//______________________________________________________________________________
TProcessID::~TProcessID()
{

   delete fMap;
   fMap = 0;
   gROOT->GetListOfProcessIDs()->Remove(this);
   gROOT->GetListOfCleanups()->Remove(this);
}
//______________________________________________________________________________
TProcessID::TProcessID(const TProcessID &ref)
{
   // TProcessID copy ctor.
}

//______________________________________________________________________________
Int_t TProcessID::DecrementCount()
{

   // the reference fCount is used to delete the TProcessID
   // in the TFile destructor when fCount = 0
   fCount--;
   if (fCount < 0) fCount = 0;
   return fCount;
}

//______________________________________________________________________________
Int_t TProcessID::IncrementCount()
{

   fCount++;
   return fCount;
}

//______________________________________________________________________________
TObject *TProcessID::GetObjectWithID(Long_t uid)
{
   //returns the TObject with unique identifier uid in the TExMap
   if (!fMap) return 0;
   Long_t id = fMap->GetValue(uid);
   return (TObject *)id;
}

//______________________________________________________________________________
void TProcessID::PutObjectWithID(Long_t uid, TObject *obj)
{

   //stores the object with its key uid in the TExmap.
   //The object uniqueid is set as well as its kMustCleanup bit
   if (!fMap) fMap = new TExMap(100);
   Long_t id = (Long_t)obj;
   obj->SetBit(kMustCleanup);
   obj->SetUniqueID(UInt_t(uid));
   if (!fMap->GetValue(uid)) fMap->Add(uid,id);
}

//______________________________________________________________________________
TProcessID  *TProcessID::ReadProcessID(Int_t pidf, TFile *file)
{
// static function

   //The TProcessID with number pidf is read from file.
   //If the object is not already entered in the gROOT list, it is added.

   if (!file) return 0;
   TObjArray *pids = file->GetListOfProcessIDs();
   TProcessID *pid = (TProcessID *)pids->UncheckedAt(pidf);
   if (pid) return pid;

   //check if fProcessIDs[uid] is set in file
   //if not set, read the process uid from file
   char pidname[32];
   sprintf(pidname,"ProcessID%d",pidf);
   pid = (TProcessID *)file->Get(pidname);
   if (!pid) {
      file->Error("ReadProcessID","Cannot find %s in file %s",pidname,file->GetName());
      return 0;
   }
      //check that a similar pid is not already registered in gROOT
   TIter next(gROOT->GetListOfProcessIDs());
   TProcessID *apid;
   while ((apid=(TProcessID *)next())) {
      if (strcmp(apid->GetTitle(),pid->GetTitle())) continue;
      gROOT->GetListOfProcessIDs()->Add(pid);
      break;
   }
   pids->AddAt(pid,pidf);
   pid->IncrementCount();
   return pid;
}

//______________________________________________________________________________
void TProcessID::RecursiveRemove(TObject *obj)
{
   // called by the object destructor
   if (!fMap) return;
   Long_t uid = (Long_t)obj->GetUniqueID();
   fMap->Remove(uid);
}
<|endoftext|>


<commit_before>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "lb_config.hxx"
#include "lb_cluster.hxx"
#include "filtered_socket.hxx"
#include "client_balancer.hxx"
#include "address_sticky.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "GException.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"
#include "net/StaticSocketAddress.hxx"
#include "util/Cancellable.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool &pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *handler;
    void *handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    CancellablePointer cancel_connect;

    bool got_inbound_data, got_outbound_data;

    LbTcpConnection(struct pool &_pool, EventLoop &event_loop,
                    Stock *_pipe_stock,
                    SocketDescriptor &&fd, FdType fd_type,
                    const SocketFilter *filter, void *filter_ctx,
                    const LbTcpConnectionHandler &_handler, void *ctx);

    void DestroyInbound();
    void DestroyOutbound();
    void Destroy();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(std::exception_ptr ep) override;
};

static constexpr timeval write_timeout = { 30, 0 };

void
LbTcpConnection::DestroyInbound()
{
    if (inbound.IsConnected())
        inbound.Close();

    inbound.Destroy();
}

void
LbTcpConnection::DestroyOutbound()
{
    if (outbound.IsConnected())
        outbound.Close();

    outbound.Destroy();
}

void
LbTcpConnection::Destroy()
{
    if (inbound.IsValid())
        DestroyInbound();

    if (cancel_connect)
        cancel_connect.Cancel();
    else if (outbound.IsValid())
        DestroyOutbound();

    this->~LbTcpConnection();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->cancel_connect)
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        tcp->Destroy();
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        tcp->Destroy();
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(std::exception_ptr ep, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->exception("Error", ep, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        tcp->Destroy();
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(std::exception_ptr ep, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->exception("Error", ep, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    cancel_connect = nullptr;

    outbound.Init(fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    DestroyInbound();
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(std::exception_ptr ep)
{
    DestroyInbound();
    handler->exception("Connect error", ep, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              SocketAddress remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

inline
LbTcpConnection::LbTcpConnection(struct pool &_pool, EventLoop &event_loop,
                                 Stock *_pipe_stock,
                                 SocketDescriptor &&fd, FdType fd_type,
                                 const SocketFilter *filter, void *filter_ctx,
                                 const LbTcpConnectionHandler &_handler, void *ctx)
    :pool(_pool), pipe_stock(_pipe_stock),
     handler(&_handler), handler_ctx(ctx),
     inbound(event_loop), outbound(event_loop)
{
    inbound.Init(fd.Steal(), fd_type,
                 nullptr, &write_timeout,
                 filter, filter_ctx,
                 inbound_buffered_socket_handler, this);
    /* TODO
    inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */
}

void
lb_tcp_new(struct pool &pool, EventLoop &event_loop, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           const LbClusterConfig &cluster,
           LbClusterMap &clusters,
           Balancer &balancer,
           const LbTcpConnectionHandler &handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(pool, pool, event_loop,
                                             pipe_stock,
                                             std::move(fd), fd_type,
                                             filter, filter_ctx,
                                             handler, ctx);

    unsigned session_sticky = lb_tcp_sticky(cluster.address_list,
                                            remote_address);

    SocketAddress bind_address = SocketAddress::Null();
    StaticSocketAddress bind_address_buffer;

    if (cluster.transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetPort() != 0) {
            bind_address_buffer = bind_address;
            if (bind_address_buffer.SetPort(0))
                bind_address = bind_address_buffer;
        }
    }

    *tcp_r = tcp;

    if (cluster.HasZeroConf()) {
        /* TODO: generalize the Zeroconf code, implement sticky */

        auto *cluster2 = clusters.Find(cluster.name);
        if (cluster2 == nullptr) {
            tcp->DestroyInbound();
            handler.error("Zeroconf error", "Zeroconf cluster not found", ctx);
            return;
        }

        const auto member = cluster2->Pick();
        if (member.first == nullptr) {
            tcp->DestroyInbound();
            handler.error("Zeroconf error", "Zeroconf cluster is empty", ctx);
            return;
        }

        const auto address = member.second;
        assert(address.IsDefined());

        client_socket_new(event_loop, pool,
                          address.GetFamily(), SOCK_STREAM, 0,
                          cluster.transparent_source, bind_address,
                          address,
                          20,
                          *tcp,
                          tcp->cancel_connect);
        return;
    }

    client_balancer_connect(event_loop, pool, balancer,
                            cluster.transparent_source,
                            bind_address,
                            session_sticky,
                            &cluster.address_list,
                            20,
                            *tcp,
                            tcp->cancel_connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    tcp->Destroy();
}
<commit_msg>lb_tcp: make handler attributes "const"<commit_after>/*
 * Handler for raw TCP connections.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "lb_tcp.hxx"
#include "lb_config.hxx"
#include "lb_cluster.hxx"
#include "filtered_socket.hxx"
#include "client_balancer.hxx"
#include "address_sticky.hxx"
#include "direct.hxx"
#include "pool.hxx"
#include "GException.hxx"
#include "net/ConnectSocket.hxx"
#include "net/SocketDescriptor.hxx"
#include "net/SocketAddress.hxx"
#include "net/StaticSocketAddress.hxx"
#include "util/Cancellable.hxx"

#include <unistd.h>
#include <errno.h>

struct LbTcpConnection final : ConnectSocketHandler {
    struct pool &pool;
    Stock *pipe_stock;

    const LbTcpConnectionHandler *const handler;
    void *const handler_ctx;

    FilteredSocket inbound;

    BufferedSocket outbound;

    CancellablePointer cancel_connect;

    bool got_inbound_data, got_outbound_data;

    LbTcpConnection(struct pool &_pool, EventLoop &event_loop,
                    Stock *_pipe_stock,
                    SocketDescriptor &&fd, FdType fd_type,
                    const SocketFilter *filter, void *filter_ctx,
                    const LbTcpConnectionHandler &_handler, void *ctx);

    void DestroyInbound();
    void DestroyOutbound();
    void Destroy();

    /* virtual methods from class ConnectSocketHandler */
    void OnSocketConnectSuccess(SocketDescriptor &&fd) override;
    void OnSocketConnectTimeout() override;
    void OnSocketConnectError(std::exception_ptr ep) override;
};

static constexpr timeval write_timeout = { 30, 0 };

void
LbTcpConnection::DestroyInbound()
{
    if (inbound.IsConnected())
        inbound.Close();

    inbound.Destroy();
}

void
LbTcpConnection::DestroyOutbound()
{
    if (outbound.IsConnected())
        outbound.Close();

    outbound.Destroy();
}

void
LbTcpConnection::Destroy()
{
    if (inbound.IsValid())
        DestroyInbound();

    if (cancel_connect)
        cancel_connect.Cancel();
    else if (outbound.IsValid())
        DestroyOutbound();

    this->~LbTcpConnection();
}

/*
 * inbound BufferedSocketHandler
 *
 */

static BufferedResult
inbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = true;

    if (tcp->cancel_connect)
        /* outbound is not yet connected */
        return BufferedResult::BLOCKING;

    if (!tcp->outbound.IsValid()) {
        tcp->Destroy();
        tcp->handler->error("Send error", "Broken socket", tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    ssize_t nbytes = tcp->outbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->inbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        tcp->Destroy();
        tcp->handler->_errno("Send failed", errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
inbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return false;
}

static bool
inbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = false;

    if (!tcp->outbound.Read(false))
        return false;

    if (!tcp->got_outbound_data)
        tcp->inbound.UnscheduleWrite();
    return true;
}

static bool
inbound_buffered_socket_drained(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    if (!tcp->outbound.IsValid()) {
        /* now that inbound's output buffers are drained, we can
           finally close the connection (postponed from
           outbound_buffered_socket_end()) */
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return false;
    }

    return true;
}

static enum write_result
inbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
inbound_buffered_socket_error(std::exception_ptr ep, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->exception("Error", ep, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler inbound_buffered_socket_handler = {
    inbound_buffered_socket_data,
    nullptr, // TODO: inbound_buffered_socket_direct,
    inbound_buffered_socket_closed,
    nullptr,
    nullptr,
    inbound_buffered_socket_write,
    inbound_buffered_socket_drained,
    nullptr,
    inbound_buffered_socket_broken,
    inbound_buffered_socket_error,
};

/*
 * outbound buffered_socket_handler
 *
 */

static BufferedResult
outbound_buffered_socket_data(const void *buffer, size_t size, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_outbound_data = true;

    ssize_t nbytes = tcp->inbound.Write(buffer, size);
    if (nbytes > 0) {
        tcp->outbound.Consumed(nbytes);
        return (size_t)nbytes == size
            ? BufferedResult::OK
            : BufferedResult::PARTIAL;
    }

    switch ((enum write_result)nbytes) {
        int save_errno;

    case WRITE_SOURCE_EOF:
        assert(false);
        gcc_unreachable();

    case WRITE_ERRNO:
        save_errno = errno;
        tcp->Destroy();
        tcp->handler->_errno("Send failed", save_errno, tcp->handler_ctx);
        return BufferedResult::CLOSED;

    case WRITE_BLOCKING:
        return BufferedResult::BLOCKING;

    case WRITE_DESTROYED:
        return BufferedResult::CLOSED;

    case WRITE_BROKEN:
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);
        return BufferedResult::CLOSED;
    }

    assert(false);
    gcc_unreachable();
}

static bool
outbound_buffered_socket_closed(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Close();
    return true;
}

static void
outbound_buffered_socket_end(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->outbound.Destroy();

    tcp->inbound.UnscheduleWrite();

    if (tcp->inbound.IsDrained()) {
        /* all output buffers to "inbound" are drained; close the
           connection, because there's nothing left to do */
        tcp->Destroy();
        tcp->handler->eof(tcp->handler_ctx);

        /* nothing will be done if the buffers are not yet drained;
           we're waiting for inbound_buffered_socket_drained() to be
           called */
    }
}

static bool
outbound_buffered_socket_write(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->got_inbound_data = false;

    if (!tcp->inbound.Read(false))
        return false;

    if (!tcp->got_inbound_data)
        tcp->outbound.UnscheduleWrite();
    return true;
}

static enum write_result
outbound_buffered_socket_broken(void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->eof(tcp->handler_ctx);
    return WRITE_DESTROYED;
}

static void
outbound_buffered_socket_error(std::exception_ptr ep, void *ctx)
{
    LbTcpConnection *tcp = (LbTcpConnection *)ctx;

    tcp->Destroy();
    tcp->handler->exception("Error", ep, tcp->handler_ctx);
}

static constexpr BufferedSocketHandler outbound_buffered_socket_handler = {
    outbound_buffered_socket_data,
    nullptr, // TODO: outbound_buffered_socket_direct,
    outbound_buffered_socket_closed,
    nullptr,
    outbound_buffered_socket_end,
    outbound_buffered_socket_write,
    nullptr,
    nullptr,
    outbound_buffered_socket_broken,
    outbound_buffered_socket_error,
};

/*
 * ConnectSocketHandler
 *
 */

void
LbTcpConnection::OnSocketConnectSuccess(SocketDescriptor &&fd)
{
    cancel_connect = nullptr;

    outbound.Init(fd.Steal(), FdType::FD_TCP,
                  nullptr, &write_timeout,
                  outbound_buffered_socket_handler, this);

    /* TODO
    outbound.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0 &&
        (istream_direct_mask_to(inbound.base.base.fd_type) & FdType::FD_PIPE) != 0;
    */

    if (inbound.Read(false))
        outbound.Read(false);
}

void
LbTcpConnection::OnSocketConnectTimeout()
{
    DestroyInbound();
    handler->error("Connect error", "Timeout", handler_ctx);
}

void
LbTcpConnection::OnSocketConnectError(std::exception_ptr ep)
{
    DestroyInbound();
    handler->exception("Connect error", ep, handler_ctx);
}

/*
 * constructor
 *
 */

gcc_pure
static unsigned
lb_tcp_sticky(const AddressList &address_list,
              SocketAddress remote_address)
{
    switch (address_list.sticky_mode) {
    case StickyMode::NONE:
    case StickyMode::FAILOVER:
        break;

    case StickyMode::SOURCE_IP:
        return socket_address_sticky(remote_address);

    case StickyMode::SESSION_MODULO:
    case StickyMode::COOKIE:
    case StickyMode::JVM_ROUTE:
        /* not implemented here */
        break;
    }

    return 0;
}

inline
LbTcpConnection::LbTcpConnection(struct pool &_pool, EventLoop &event_loop,
                                 Stock *_pipe_stock,
                                 SocketDescriptor &&fd, FdType fd_type,
                                 const SocketFilter *filter, void *filter_ctx,
                                 const LbTcpConnectionHandler &_handler, void *ctx)
    :pool(_pool), pipe_stock(_pipe_stock),
     handler(&_handler), handler_ctx(ctx),
     inbound(event_loop), outbound(event_loop)
{
    inbound.Init(fd.Steal(), fd_type,
                 nullptr, &write_timeout,
                 filter, filter_ctx,
                 inbound_buffered_socket_handler, this);
    /* TODO
    inbound.base.direct = pipe_stock != nullptr &&
        (ISTREAM_TO_PIPE & fd_type) != 0 &&
        (ISTREAM_TO_TCP & FdType::FD_PIPE) != 0;
    */
}

void
lb_tcp_new(struct pool &pool, EventLoop &event_loop, Stock *pipe_stock,
           SocketDescriptor &&fd, FdType fd_type,
           const SocketFilter *filter, void *filter_ctx,
           SocketAddress remote_address,
           const LbClusterConfig &cluster,
           LbClusterMap &clusters,
           Balancer &balancer,
           const LbTcpConnectionHandler &handler, void *ctx,
           LbTcpConnection **tcp_r)
{
    auto *tcp = NewFromPool<LbTcpConnection>(pool, pool, event_loop,
                                             pipe_stock,
                                             std::move(fd), fd_type,
                                             filter, filter_ctx,
                                             handler, ctx);

    unsigned session_sticky = lb_tcp_sticky(cluster.address_list,
                                            remote_address);

    SocketAddress bind_address = SocketAddress::Null();
    StaticSocketAddress bind_address_buffer;

    if (cluster.transparent_source) {
        bind_address = remote_address;

        /* reset the port to 0 to allow the kernel to choose one */
        if (bind_address.GetPort() != 0) {
            bind_address_buffer = bind_address;
            if (bind_address_buffer.SetPort(0))
                bind_address = bind_address_buffer;
        }
    }

    *tcp_r = tcp;

    if (cluster.HasZeroConf()) {
        /* TODO: generalize the Zeroconf code, implement sticky */

        auto *cluster2 = clusters.Find(cluster.name);
        if (cluster2 == nullptr) {
            tcp->DestroyInbound();
            handler.error("Zeroconf error", "Zeroconf cluster not found", ctx);
            return;
        }

        const auto member = cluster2->Pick();
        if (member.first == nullptr) {
            tcp->DestroyInbound();
            handler.error("Zeroconf error", "Zeroconf cluster is empty", ctx);
            return;
        }

        const auto address = member.second;
        assert(address.IsDefined());

        client_socket_new(event_loop, pool,
                          address.GetFamily(), SOCK_STREAM, 0,
                          cluster.transparent_source, bind_address,
                          address,
                          20,
                          *tcp,
                          tcp->cancel_connect);
        return;
    }

    client_balancer_connect(event_loop, pool, balancer,
                            cluster.transparent_source,
                            bind_address,
                            session_sticky,
                            &cluster.address_list,
                            20,
                            *tcp,
                            tcp->cancel_connect);
}

void
lb_tcp_close(LbTcpConnection *tcp)
{
    tcp->Destroy();
}
<|endoftext|>


<commit_before>/* **** **** **** **** **** **** **** **** *
 *
 *         _/        _/        _/
 *        _/_/_/    _/_/_/    _/_/_/
 *       _/    _/  _/    _/  _/    _/
 *      _/    _/  _/    _/  _/    _/
 *     _/_/_/    _/_/_/    _/_/_/
 *
 * bit by bit
 * bbb/core/type/logic.hpp
 *
 * author: ISHII 2bit
 * mail:   2bit@backspace.tokyo
 *
 * **** **** **** **** **** **** **** **** */

#pragma once

#include <bbb/core.hpp>

namespace bbb {
    namespace logic {
        namespace detail {
            template <template <typename> class pred, typename ... arguments>
            struct all;
            template <template <typename> class pred, typename argument, typename ... arguments>
            struct all<pred, argument, arguments ...> {
                using type = typename std::conditional<
                    pred<argument>::value,
                    typename all<pred, arguments ...>::type,
                    std::false_type
                >::type;
            };
            template <template <typename> class pred, typename argument>
            struct all<pred, argument> {
                using type = pred<argument>;
            };
            template <template <typename> class pred>
            struct all<pred> {
                using type = std::true_type;
            };

            template <template <typename> class pred, typename ... arguments>
            struct any;
            template <template <typename> class pred, typename argument, typename ... arguments>
            struct any<pred, argument, arguments ...> {
                using type = typename std::conditional<
                    pred<argument>::value,
                    std::true_type,
                    typename any<pred, arguments ...>::type
                >::type;
            };
            template <template <typename> class pred, typename argument>
            struct any<pred, argument> {
                using type = pred<argument>;
            };
            template <template <typename> class pred>
            struct any<pred> {
                using type = std::true_type;
            };
        };

        template <template <typename> class pred, typename ... arguments>
        using all_t = typename detail::all<pred, arguments ...>::type;

        template <template <typename> class pred, typename ... arguments>
        using any_t = typename detail::any<pred, arguments ...>::type;
    };

    using namespace logic;
};
<commit_msg>fix and add unit_test<commit_after>/* **** **** **** **** **** **** **** **** *
 *
 *         _/        _/        _/
 *        _/_/_/    _/_/_/    _/_/_/
 *       _/    _/  _/    _/  _/    _/
 *      _/    _/  _/    _/  _/    _/
 *     _/_/_/    _/_/_/    _/_/_/
 *
 * bit by bit
 * bbb/core/type/logic.hpp
 *
 * author: ISHII 2bit
 * mail:   2bit@backspace.tokyo
 *
 * **** **** **** **** **** **** **** **** */

#pragma once

#include <bbb/core.hpp>

namespace bbb {
    namespace logic {
        namespace detail {
            template <template <typename> class pred, typename ... arguments>
            struct all;
            template <template <typename> class pred, typename argument, typename ... arguments>
            struct all<pred, argument, arguments ...> {
                using type = typename std::conditional<
                    pred<argument>::value,
                    typename all<pred, arguments ...>::type,
                    std::false_type
                >::type;
            };
            template <template <typename> class pred>
            struct all<pred> {
                using type = std::true_type;
            };

            template <template <typename> class pred, typename ... arguments>
            struct any;
            template <template <typename> class pred, typename argument, typename ... arguments>
            struct any<pred, argument, arguments ...> {
                using type = typename std::conditional<
                    pred<argument>::value,
                    std::true_type,
                    typename any<pred, arguments ...>::type
                >::type;
            };
            template <template <typename> class pred>
            struct any<pred> {
                using type = std::false_type;
            };
        };

        template <template <typename> class pred, typename ... arguments>
        using all = detail::all<pred, arguments ...>;
        template <template <typename> class pred, typename ... arguments>
        using all_t = get_type<all<pred, arguments ...>>;

        template <template <typename> class pred, typename ... arguments>
        using any = detail::any<pred, arguments ...>;
        template <template <typename> class pred, typename ... arguments>
        using any_t = get_type<any<pred, arguments ...>>;

#if BBB_EXEC_UNIT_TEST
        namespace logic_test {
            template <typename rhs>
            using eq = std::is_same<int, rhs>;

            using test1 = unit_test::assert<
                all_t<eq, int, float>,
                std::false_type
            >;
            using test2 = unit_test::assert<
                all_t<eq, int, int>,
                std::true_type
            >;
            using test3 = unit_test::assert<
                any_t<eq, int, int>,
                std::true_type
            >;
            using test4 = unit_test::assert<
                any_t<eq, int, float>,
                std::true_type
            >;
            using test5 = unit_test::assert<
                any_t<eq, char, float>,
                std::false_type
            >;
        }
#endif
    };

    using namespace logic;
};
<|endoftext|>


<commit_before>#include "dsa/message.h"
#include "dsa/network.h"

#include "../test/sdk/async_test.h"
#include "../test/sdk/test_config.h"

#include "network/tcp/tcp_client.h"
#include "network/tcp/tcp_server.h"

#include <chrono>
#include <ctime>

#include <atomic>

#include <boost/program_options.hpp>

using high_resolution_clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::high_resolution_clock::time_point;

using namespace dsa;
namespace opts = boost::program_options;

class MockNode : public NodeModel {
 public:
  explicit MockNode(LinkStrandRef strand) : NodeModel(std::move(strand)){};
};

int main(int argc, const char *argv[]) {
  opts::options_description desc{"Options"};
  desc.add_options()("help,h", "Help screen")(
      "client,c", opts::value<int>()->default_value(2), "Number of Clients")(
      "time,t", opts::value<int>()->default_value(20), "Time (seconds)")(
      "encode-value,e", opts::bool_switch(), "Encode value before sending")(
      "decode-value,d", opts::bool_switch(), "Decode value after receiving")(
      "num-message,n", opts::value<int>()->default_value(100000),
      "Minimal number of messages to send in each iteration");

  opts::variables_map variables;
  opts::store(opts::parse_command_line(argc, argv, desc), variables);
  opts::notify(variables);

  if (variables.count("help")) {
    std::cout << desc << '\n';
    return 0;
  }

  int run_time = 20;
  int client_count = 2;
  const int MAX_CLIENT_COUNT = 256;

  client_count = variables["client"].as<int>();
  run_time = variables["time"].as<int>();

  if (client_count > MAX_CLIENT_COUNT || client_count < 1) {
    std::cout << "invalid Number of Clients, ( 1 ~ 255 )";
    return 0;
  }
  bool encode_value = variables["encode-value"].as<bool>();
  bool decode_value = variables["decode-value"].as<bool>();
  int min_send_num = variables["num-message"].as<int>();
  ;
  std::cout << std::endl << "benchmark with " << client_count << " clients";

  App app;

  app.async_start(4);  // client_count * 2 + 2);

  TestConfig server_config(app);

  MockNode *root_node = new MockNode(server_config.strand);

  server_config.get_link_config()->set_stream_acceptor(
      make_unique_<NodeStateManager>(ref_<MockNode>(root_node)));

  //  auto tcp_server(new TcpServer(server_config));
  auto tcp_server = make_shared_<TcpServer>(server_config);
  tcp_server->start();

  std::vector<WrapperConfig> client_configs;
  std::vector<shared_ptr_<TcpClient>> clients;
  std::atomic_int receive_count[MAX_CLIENT_COUNT];

  SubscribeOptions initial_options;
  initial_options.qos = QosLevel::_1;
  initial_options.queue_size = 655360;

  for (int i = 0; i < client_count; ++i) {
    client_configs.emplace_back(server_config.get_client_config(app));
    clients.emplace_back(make_shared_<TcpClient>(client_configs[i]));
    clients[i]->connect();

    wait_for_bool(500, *client_configs[i].strand,
                  [&]() { return clients[i]->get_session().is_connected(); });

    receive_count[i] = 0;

    std::atomic_int &count = receive_count[i];

    clients[i]->get_session().requester.subscribe(
        "",
        [&](ref_<const SubscribeResponseMessage> &&msg,
            IncomingSubscribeStream &stream) {
          count.fetch_add(1);
          if (decode_value) {
            msg->get_value();
          }
        },
        initial_options);
  }

  int64_t msg_per_second = 300000;

  boost::posix_time::milliseconds interval(10);
  boost::asio::deadline_timer timer(app.io_service(), interval);

  int print_count = 0;  // print every 100 timer visit;

  auto ts = high_resolution_clock::now();
  int total_ms = 0;

  int total_message = 0;

  SubscribeResponseMessageCRef cached_message =
      make_ref_<SubscribeResponseMessage>(Variant(0));

  std::function<void(const boost::system::error_code &)> tick;
  tick = [&](const boost::system::error_code &error) {

    if (!error) {
      server_config.strand->dispatch([&]() {
        auto ts2 = high_resolution_clock::now();

        auto ms =
            std::chrono::duration_cast<std::chrono::milliseconds>(ts2 - ts)
                .count();

        if (ms > 0) {
          ts = ts2;
          int count = 0;
          for (int i = 0; i < client_count; ++i) {
            count += receive_count[i];
            receive_count[i] = 0;
          }
          total_message += count;

          count /= client_count;

          print_count += ms;
          if (print_count > 2000) {
            print_count = 0;
            std::cout << std::endl
                      << "message per second: "
                      << (msg_per_second * client_count)
                      << "  current: " << count * 1000 / ms << " x "
                      << client_count << ", interval " << ms;
          }

          msg_per_second =
              (count * 1000 + msg_per_second * total_ms) / (total_ms + ms);
          total_ms += ms/2;
          if (total_ms > 5000) total_ms = 5000;

          long tosend_per_second = msg_per_second;
          if (tosend_per_second < min_send_num)
            tosend_per_second = min_send_num;
          // send a little bit more than the current speed,
          // limited message queue size should handle the extra messages
          long num_message = tosend_per_second * ms / (800 + total_ms / 50);
          if (encode_value) {
            for (int i = 0; i < num_message; ++i) {
              root_node->set_value(Variant(i));
            }
          } else {
            for (int i = 0; i < num_message; ++i) {
              root_node->set_message(copy_ref_(cached_message));
            }
          }
        }
        timer.async_wait(tick);
      });
    }
  };

  timer.async_wait(tick);

  std::cout << std::endl << "run benchmark for " << run_time << " seconds";
  boost::this_thread::sleep(boost::posix_time::seconds(run_time));
  std::cout << std::endl << "end benchmark";
  timer.cancel();
  std::cout << std::endl << "total message: " << total_message;

  Server::close_in_strand(tcp_server);
  for (int i = 0; i < client_count; ++i) {
    Client::close_in_strand(clients[i]);
  }

  app.close();

  wait_for_bool(500, [&]() { return app.is_stopped(); });

  if (!app.is_stopped()) {
    app.force_stop();
  }

  app.wait();
  return 0;
}
<commit_msg>send 5k message/second as minimal in throughput benchmark<commit_after>#include "dsa/message.h"
#include "dsa/network.h"

#include "../test/sdk/async_test.h"
#include "../test/sdk/test_config.h"

#include "network/tcp/tcp_client.h"
#include "network/tcp/tcp_server.h"

#include <chrono>
#include <ctime>

#include <atomic>

#include <boost/program_options.hpp>

using high_resolution_clock = std::chrono::high_resolution_clock;
using time_point = std::chrono::high_resolution_clock::time_point;

using namespace dsa;
namespace opts = boost::program_options;

class MockNode : public NodeModel {
 public:
  explicit MockNode(LinkStrandRef strand) : NodeModel(std::move(strand)){};
};

int main(int argc, const char *argv[]) {
  opts::options_description desc{"Options"};
  desc.add_options()("help,h", "Help screen")(
      "client,c", opts::value<int>()->default_value(2), "Number of Clients")(
      "time,t", opts::value<int>()->default_value(20), "Time (seconds)")(
      "encode-value,e", opts::bool_switch(), "Encode value before sending")(
      "decode-value,d", opts::bool_switch(), "Decode value after receiving")(
      "num-message,n", opts::value<int>()->default_value(5000),
      "Minimal number of messages to send in each iteration");

  opts::variables_map variables;
  opts::store(opts::parse_command_line(argc, argv, desc), variables);
  opts::notify(variables);

  if (variables.count("help")) {
    std::cout << desc << '\n';
    return 0;
  }

  int run_time = 20;
  int client_count = 2;
  const int MAX_CLIENT_COUNT = 256;

  client_count = variables["client"].as<int>();
  run_time = variables["time"].as<int>();

  if (client_count > MAX_CLIENT_COUNT || client_count < 1) {
    std::cout << "invalid Number of Clients, ( 1 ~ 255 )";
    return 0;
  }
  bool encode_value = variables["encode-value"].as<bool>();
  bool decode_value = variables["decode-value"].as<bool>();
  int min_send_num = variables["num-message"].as<int>();
  ;
  std::cout << std::endl << "benchmark with " << client_count << " clients";

  App app;

  app.async_start(4);  // client_count * 2 + 2);

  TestConfig server_config(app);

  MockNode *root_node = new MockNode(server_config.strand);

  server_config.get_link_config()->set_stream_acceptor(
      make_unique_<NodeStateManager>(ref_<MockNode>(root_node)));

  //  auto tcp_server(new TcpServer(server_config));
  auto tcp_server = make_shared_<TcpServer>(server_config);
  tcp_server->start();

  std::vector<WrapperConfig> client_configs;
  std::vector<shared_ptr_<TcpClient>> clients;
  std::atomic_int receive_count[MAX_CLIENT_COUNT];

  SubscribeOptions initial_options;
  initial_options.qos = QosLevel::_1;
  initial_options.queue_size = 655360;

  for (int i = 0; i < client_count; ++i) {
    client_configs.emplace_back(server_config.get_client_config(app));
    clients.emplace_back(make_shared_<TcpClient>(client_configs[i]));
    clients[i]->connect();

    wait_for_bool(500, *client_configs[i].strand,
                  [&]() { return clients[i]->get_session().is_connected(); });

    receive_count[i] = 0;

    std::atomic_int &count = receive_count[i];

    clients[i]->get_session().requester.subscribe(
        "",
        [&](ref_<const SubscribeResponseMessage> &&msg,
            IncomingSubscribeStream &stream) {
          count.fetch_add(1);
          if (decode_value) {
            msg->get_value();
          }
        },
        initial_options);
  }

  int64_t msg_per_second = 300000;

  boost::posix_time::milliseconds interval(10);
  boost::asio::deadline_timer timer(app.io_service(), interval);

  int print_count = 0;  // print every 100 timer visit;

  auto ts = high_resolution_clock::now();
  int total_ms = 0;

  int total_message = 0;

  SubscribeResponseMessageCRef cached_message =
      make_ref_<SubscribeResponseMessage>(Variant(0));

  std::function<void(const boost::system::error_code &)> tick;
  tick = [&](const boost::system::error_code &error) {

    if (!error) {
      server_config.strand->dispatch([&]() {
        auto ts2 = high_resolution_clock::now();

        auto ms =
            std::chrono::duration_cast<std::chrono::milliseconds>(ts2 - ts)
                .count();

        if (ms > 0) {
          ts = ts2;
          int count = 0;
          for (int i = 0; i < client_count; ++i) {
            count += receive_count[i];
            receive_count[i] = 0;
          }
          total_message += count;

          count /= client_count;

          print_count += ms;
          if (print_count > 2000) {
            print_count = 0;
            std::cout << std::endl
                      << "message per second: "
                      << (msg_per_second * client_count)
                      << "  current: " << count * 1000 / ms << " x "
                      << client_count << ", interval " << ms;
          }

          msg_per_second =
              (count * 1000 + msg_per_second * total_ms) / (total_ms + ms);
          total_ms += ms/2;
          if (total_ms > 5000) total_ms = 5000;

          long tosend_per_second = msg_per_second;
          if (tosend_per_second < min_send_num)
            tosend_per_second = min_send_num;
          // send a little bit more than the current speed,
          // limited message queue size should handle the extra messages
          long num_message = tosend_per_second * ms / (800 + total_ms / 50);
          if (encode_value) {
            for (int i = 0; i < num_message; ++i) {
              root_node->set_value(Variant(i));
            }
          } else {
            for (int i = 0; i < num_message; ++i) {
              root_node->set_message(copy_ref_(cached_message));
            }
          }
        }
        timer.async_wait(tick);
      });
    }
  };

  timer.async_wait(tick);

  std::cout << std::endl << "run benchmark for " << run_time << " seconds";
  boost::this_thread::sleep(boost::posix_time::seconds(run_time));
  std::cout << std::endl << "end benchmark";
  timer.cancel();
  std::cout << std::endl << "total message: " << total_message;

  Server::close_in_strand(tcp_server);
  for (int i = 0; i < client_count; ++i) {
    Client::close_in_strand(clients[i]);
  }

  app.close();

  wait_for_bool(500, [&]() { return app.is_stopped(); });

  if (!app.is_stopped()) {
    app.force_stop();
  }

  app.wait();
  return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mem_map.h"

#include <sys/mman.h>

#include "ScopedFd.h"
#include "stringprintf.h"
#include "utils.h"

#include <corkscrew/map_info.h>

#define USE_ASHMEM 1

#ifdef USE_ASHMEM
#include <cutils/ashmem.h>
#endif

namespace art {

#if !defined(NDEBUG)

static std::ostream& operator<<(std::ostream& os, map_info_t* rhs) {
  for (map_info_t* m = rhs; m != NULL; m = m->next) {
    os << StringPrintf("%08x-%08x %c%c %s\n",m->start, m->end,
                       m->is_readable ? 'r' : '-', m->is_executable ? 'x' : '-', m->name);
  }
  return os;
}

void CheckMapRequest(byte* addr, size_t byte_count) {
  if (addr == NULL) {
    return;
  }

  uint32_t base = reinterpret_cast<size_t>(addr);
  uint32_t limit = base + byte_count;

  map_info_t* map_info_list = load_map_info_list(getpid());
  for (map_info_t* m = map_info_list; m != NULL; m = m->next) {
    CHECK(!(base >= m->start && base < m->end)      // start of new within old
        && !(limit > m->start && limit < m->end)  // end of new within old
        && !(base <= m->start && limit > m->end)) // start/end of new includes all of old
        << StringPrintf("Requested region %08x-%08x overlaps with existing map %08x-%08x (%s)\n",
                        base, limit, m->start, m->end, m->name)
        << map_info_list;
  }
  free_map_info_list(map_info_list);
}

#else
static void CheckMapRequest(byte*, size_t) { }
#endif

MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot) {
  CHECK_NE(0U, byte_count);
  CHECK_NE(0, prot);
  size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
  CheckMapRequest(addr, page_aligned_byte_count);

#ifdef USE_ASHMEM
  ScopedFd fd(ashmem_create_region(name, page_aligned_byte_count));
  int flags = MAP_PRIVATE;
  if (fd.get() == -1) {
    PLOG(ERROR) << "ashmem_create_region failed (" << name << ")";
    return NULL;
  }
#else
  ScopedFd fd(-1);
  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
#endif

  byte* actual = reinterpret_cast<byte*>(mmap(addr, page_aligned_byte_count, prot, flags, fd.get(), 0));
  if (actual == MAP_FAILED) {
    PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_byte_count
                << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name;
    return NULL;
  }
  return new MemMap(actual, byte_count, actual, page_aligned_byte_count);
}

MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start) {
  CHECK_NE(0U, byte_count);
  CHECK_NE(0, prot);
  CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
  // Adjust 'offset' and 'byte_count' to be page-aligned.
  int page_offset = start % kPageSize;
  off_t page_aligned_offset = start - page_offset;
  size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
  CheckMapRequest(addr, page_aligned_byte_count);
  byte* actual = reinterpret_cast<byte*>(mmap(addr,
                                              page_aligned_byte_count,
                                              prot,
                                              flags,
                                              fd,
                                              page_aligned_offset));
  if (actual == MAP_FAILED) {
    PLOG(ERROR) << "mmap failed";
    return NULL;
  }
  return new MemMap(actual + page_offset, byte_count, actual, page_aligned_byte_count);
}

MemMap::~MemMap() {
  if (base_begin_ == NULL && base_size_ == 0) {
    return;
  }
  int result = munmap(base_begin_, base_size_);
  if (result == -1) {
    PLOG(FATAL) << "munmap failed";
  }
}

MemMap::MemMap(byte* begin, size_t size, void* base_begin, size_t base_size)
    : begin_(begin), size_(size), base_begin_(base_begin), base_size_(base_size) {
  CHECK(begin_ != NULL);
  CHECK_NE(size_, 0U);
  CHECK(base_begin_ != NULL);
  CHECK_NE(base_size_, 0U);
};

bool MemMap::Protect(int prot) {
  if (base_begin_ == NULL && base_size_ == 0) {
    return true;
  }

  if (mprotect(base_begin_, base_size_, prot) == 0) {
    return true;
  }

  PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(base_begin_) << ", " << base_size_ << ", "
              << prot << ") failed";
  return false;
}

}  // namespace art
<commit_msg>am 7bda1583: Fix the Mac build.<commit_after>/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mem_map.h"

#include <sys/mman.h>

#include "ScopedFd.h"
#include "stringprintf.h"
#include "utils.h"

#include <corkscrew/map_info.h>

#define USE_ASHMEM 1

#ifdef USE_ASHMEM
#include <cutils/ashmem.h>
#endif

namespace art {

#if !defined(NDEBUG)

static std::ostream& operator<<(std::ostream& os, map_info_t* rhs) {
  for (map_info_t* m = rhs; m != NULL; m = m->next) {
    os << StringPrintf("0x%08x-0x%08x %c%c %s\n",
                       static_cast<uint32_t>(m->start),
                       static_cast<uint32_t>(m->end),
                       m->is_readable ? 'r' : '-', m->is_executable ? 'x' : '-', m->name);
  }
  return os;
}

void CheckMapRequest(byte* addr, size_t byte_count) {
  if (addr == NULL) {
    return;
  }

  uint32_t base = reinterpret_cast<size_t>(addr);
  uint32_t limit = base + byte_count;

  map_info_t* map_info_list = load_map_info_list(getpid());
  for (map_info_t* m = map_info_list; m != NULL; m = m->next) {
    CHECK(!(base >= m->start && base < m->end)      // start of new within old
        && !(limit > m->start && limit < m->end)  // end of new within old
        && !(base <= m->start && limit > m->end)) // start/end of new includes all of old
        << StringPrintf("Requested region 0x%08x-0x%08x overlaps with existing map 0x%08x-0x%08x (%s)\n",
                        base, limit,
                        static_cast<uint32_t>(m->start), static_cast<uint32_t>(m->end), m->name)
        << map_info_list;
  }
  free_map_info_list(map_info_list);
}

#else
static void CheckMapRequest(byte*, size_t) { }
#endif

MemMap* MemMap::MapAnonymous(const char* name, byte* addr, size_t byte_count, int prot) {
  CHECK_NE(0U, byte_count);
  CHECK_NE(0, prot);
  size_t page_aligned_byte_count = RoundUp(byte_count, kPageSize);
  CheckMapRequest(addr, page_aligned_byte_count);

#ifdef USE_ASHMEM
  ScopedFd fd(ashmem_create_region(name, page_aligned_byte_count));
  int flags = MAP_PRIVATE;
  if (fd.get() == -1) {
    PLOG(ERROR) << "ashmem_create_region failed (" << name << ")";
    return NULL;
  }
#else
  ScopedFd fd(-1);
  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
#endif

  byte* actual = reinterpret_cast<byte*>(mmap(addr, page_aligned_byte_count, prot, flags, fd.get(), 0));
  if (actual == MAP_FAILED) {
    PLOG(ERROR) << "mmap(" << reinterpret_cast<void*>(addr) << ", " << page_aligned_byte_count
                << ", " << prot << ", " << flags << ", " << fd.get() << ", 0) failed for " << name;
    return NULL;
  }
  return new MemMap(actual, byte_count, actual, page_aligned_byte_count);
}

MemMap* MemMap::MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start) {
  CHECK_NE(0U, byte_count);
  CHECK_NE(0, prot);
  CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));
  // Adjust 'offset' and 'byte_count' to be page-aligned.
  int page_offset = start % kPageSize;
  off_t page_aligned_offset = start - page_offset;
  size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
  CheckMapRequest(addr, page_aligned_byte_count);
  byte* actual = reinterpret_cast<byte*>(mmap(addr,
                                              page_aligned_byte_count,
                                              prot,
                                              flags,
                                              fd,
                                              page_aligned_offset));
  if (actual == MAP_FAILED) {
    PLOG(ERROR) << "mmap failed";
    return NULL;
  }
  return new MemMap(actual + page_offset, byte_count, actual, page_aligned_byte_count);
}

MemMap::~MemMap() {
  if (base_begin_ == NULL && base_size_ == 0) {
    return;
  }
  int result = munmap(base_begin_, base_size_);
  if (result == -1) {
    PLOG(FATAL) << "munmap failed";
  }
}

MemMap::MemMap(byte* begin, size_t size, void* base_begin, size_t base_size)
    : begin_(begin), size_(size), base_begin_(base_begin), base_size_(base_size) {
  CHECK(begin_ != NULL);
  CHECK_NE(size_, 0U);
  CHECK(base_begin_ != NULL);
  CHECK_NE(base_size_, 0U);
};

bool MemMap::Protect(int prot) {
  if (base_begin_ == NULL && base_size_ == 0) {
    return true;
  }

  if (mprotect(base_begin_, base_size_, prot) == 0) {
    return true;
  }

  PLOG(ERROR) << "mprotect(" << reinterpret_cast<void*>(base_begin_) << ", " << base_size_ << ", "
              << prot << ") failed";
  return false;
}

}  // namespace art
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//mapnik
#include <mapnik/xml_tree.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/color_factory.hpp>
#include <mapnik/gamma_method.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/line_symbolizer.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/text_properties.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/raster_colorizer.hpp>

//boost
#include <boost/lexical_cast.hpp>

namespace mapnik
{

template <typename T>
inline boost::optional<T> fast_cast(xml_tree const& tree, std::string const& value)
{
    try
    {
        return boost::lexical_cast<T>(value);
    }
    catch (boost::bad_lexical_cast const& ex)
    {
        return boost::optional<T>();
    }
}

template <>
inline boost::optional<mapnik::boolean> fast_cast(xml_tree const& tree, std::string const& value)
{
    bool result;
    if (mapnik::util::string2bool(value, result))
        return boost::optional<mapnik::boolean>(result);
    return boost::optional<mapnik::boolean>();
}

template <>
inline boost::optional<int> fast_cast(xml_tree const& tree, std::string const& value)
{
    int result;
    if (mapnik::util::string2int(value, result))
        return boost::optional<int>(result);
    return boost::optional<int>();
}

template <>
inline boost::optional<double> fast_cast(xml_tree const& tree, std::string const& value)
{
    double result;
    if (mapnik::util::string2double(value, result))
        return boost::optional<double>(result);
    return boost::optional<double>();
}

template <>
inline boost::optional<float> fast_cast(xml_tree const& tree, std::string const& value)
{
    float result;
    if (mapnik::util::string2float(value, result))
        return boost::optional<float>(result);
    return boost::optional<float>();
}

template <>
inline boost::optional<std::string> fast_cast(xml_tree const& tree, std::string const& value)
{
    return value;
}

template <>
inline boost::optional<color> fast_cast(xml_tree const& tree, std::string const& value)
{
    return parse_color(value, tree.color_grammar);
}

template <>
inline boost::optional<expression_ptr> fast_cast(xml_tree const& tree, std::string const& value)
{
    return parse_expression(value, tree.expr_grammar);
}

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

class boolean;
template <typename T>
struct name_trait
{
    static std::string name()
    {
        return "<unknown>";
    }
    // missing name_trait for type ...
    // if you get here you are probably using a new type
    // in the XML file. Just add a name trait for the new
    // type below.
    BOOST_STATIC_ASSERT( sizeof(T) == 0 );
};

#define DEFINE_NAME_TRAIT( type, type_name )                            \
    template <>                                                         \
    struct name_trait<type>                                             \
    {                                                                   \
        static std::string name() { return std::string("type ") + type_name; } \
    };

DEFINE_NAME_TRAIT( double, "double")
DEFINE_NAME_TRAIT( float, "float")
DEFINE_NAME_TRAIT( unsigned, "unsigned")
DEFINE_NAME_TRAIT( boolean, "boolean")
#ifdef BIGINT
DEFINE_NAME_TRAIT( mapnik::value_integer, "long long" )
#else
DEFINE_NAME_TRAIT( mapnik::value_integer, "int" )
#endif
DEFINE_NAME_TRAIT( std::string, "string" )
DEFINE_NAME_TRAIT( color, "color" )
DEFINE_NAME_TRAIT(expression_ptr, "expression_ptr" )

template <typename ENUM, int MAX>
struct name_trait< mapnik::enumeration<ENUM, MAX> >
{
    typedef enumeration<ENUM, MAX> Enum;

    static std::string name()
    {
        std::string value_list("one of [");
        for (unsigned i = 0; i < Enum::MAX; ++i)
        {
            value_list += Enum::get_string( i );
            if ( i + 1 < Enum::MAX ) value_list += ", ";
        }
        value_list += "]";

        return value_list;
    }
};

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

xml_tree::xml_tree(std::string const& encoding)
    : node_(*this, "<root>"),
      file_(),
      tr_(encoding),
      color_grammar(),
      expr_grammar(tr_),
      path_expr_grammar(),
      transform_expr_grammar(expr_grammar),
      image_filters_grammar()
{
    node_.set_processed(true); //root node is always processed
}

void xml_tree::set_filename(std::string fn)
{
    file_ = fn;
}

std::string const& xml_tree::filename() const
{
    return file_;
}

xml_node &xml_tree::root()
{
    return node_;
}

const xml_node &xml_tree::root() const
{
    return node_;
}

/****************************************************************************/
xml_attribute::xml_attribute(std::string const& value_)
    : value(value_), processed(false)
{

}

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

node_not_found::node_not_found(std::string const& node_name)
    : node_name_(node_name)
{

}

const char* node_not_found::what() const throw()
{
    return ("Node "+node_name_+ "not found").c_str();
}

node_not_found::~node_not_found() throw()
{

}

attribute_not_found::attribute_not_found(
    std::string const& node_name,
    std::string const& attribute_name)
    :
    node_name_(node_name),
    attribute_name_(attribute_name)
{

}

const char* attribute_not_found::what() const throw()
{
    return ("Attribute '" + attribute_name_ +"' not found in node '"+node_name_+ "'").c_str();
}

attribute_not_found::~attribute_not_found() throw()
{

}

more_than_one_child::more_than_one_child(std::string const& node_name)
    : node_name_(node_name)
{

}

const char* more_than_one_child::what() const throw()
{
    return ("More than one child node in node '" + node_name_ +"'").c_str();
}

more_than_one_child::~more_than_one_child() throw()
{

}

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

xml_node::xml_node(xml_tree &tree, std::string const& name, unsigned line, bool is_text)
    : tree_(tree),
      name_(name),
      is_text_(is_text),
      line_(line),
      processed_(false)
{

}

std::string xml_node::xml_text = "<xmltext>";

std::string const& xml_node::name() const
{
    if (!is_text_)
        return name_;
    else
        return xml_text;
}

std::string const& xml_node::text() const
{
    if (is_text_)
    {
        processed_ = true;
        return name_;
    } else
    {
        throw config_error("text() called on non-text node", *this);
    }
}

std::string const& xml_node::filename() const
{
    return tree_.filename();
}

bool xml_node::is_text() const
{
    return is_text_;
}

bool xml_node::is(std::string const& name) const
{
    if (name_ == name)
    {
        processed_ = true;
        return true;
    }
    return false;
}

xml_node &xml_node::add_child(std::string const& name, unsigned line, bool is_text)
{
    children_.push_back(xml_node(tree_, name, line, is_text));
    return children_.back();
}

void xml_node::add_attribute(std::string const& name, std::string const& value)
{
    attributes_.insert(std::make_pair(name,xml_attribute(value)));
}

xml_node::attribute_map const& xml_node::get_attributes() const
{
    return attributes_;
}

void xml_node::set_processed(bool processed) const
{
    processed_ = processed;
}

bool xml_node::processed() const
{
    return processed_;
}

xml_node::const_iterator xml_node::begin() const
{
    return children_.begin();
}

xml_node::const_iterator xml_node::end() const
{
    return children_.end();
}

xml_node & xml_node::get_child(std::string const& name)
{
    std::list<xml_node>::iterator itr = children_.begin();
    std::list<xml_node>::iterator end = children_.end();
    for (; itr != end; itr++)
    {
        if (!(itr->is_text_) && itr->name_ == name)
        {
            itr->set_processed(true);
            return *itr;
        }
    }
    throw node_not_found(name);
}

xml_node const& xml_node::get_child(std::string const& name) const
{
    xml_node const* node = get_opt_child(name);
    if (!node) throw node_not_found(name);
    return *node;
}

xml_node const* xml_node::get_opt_child(std::string const& name) const
{
    const_iterator itr = children_.begin();
    const_iterator end = children_.end();
    for (; itr != end; itr++)
    {
        if (!(itr->is_text_) && itr->name_ == name)
        {
            itr->set_processed(true);
            return &(*itr);
        }
    }
    return 0;
}

bool xml_node::has_child(std::string const& name) const
{
    return get_opt_child(name) != 0;
}

template <typename T>
boost::optional<T> xml_node::get_opt_attr(std::string const& name) const
{
    std::map<std::string, xml_attribute>::const_iterator itr = attributes_.find(name);
    if (itr ==  attributes_.end()) return boost::optional<T>();
    itr->second.processed = true;
    boost::optional<T> result = fast_cast<T>(tree_, std::string(itr->second.value));
    if (!result)
    {
        throw config_error(std::string("Failed to parse attribute '") +
                           name + "'. Expected " + name_trait<T>::name() +
                           " but got '" + itr->second.value + "'", *this);
    }
    return result;
}

template <typename T>
T xml_node::get_attr(std::string const& name, T const& default_opt_value) const
{
    boost::optional<T> value = get_opt_attr<T>(name);
    if (value) return *value;
    return default_opt_value;
}

template <typename T>
T xml_node::get_attr(std::string const& name) const
{
    boost::optional<T> value = get_opt_attr<T>(name);
    if (value) return *value;
    throw attribute_not_found(name_, name);
}

std::string xml_node::get_text() const
{
    if (children_.size() == 0)
    {
        if (is_text_)
        {
            return name_;
        } else
        {
            return "";
        }
    }
    if (children_.size() == 1)
    {
        return children_.front().text();
    }
    throw more_than_one_child(name_);
}

template <typename T>
T xml_node::get_value() const
{
    boost::optional<T> result = fast_cast<T>(tree_, get_text());
    if (!result)
    {
        throw config_error(std::string("Failed to parse value. Expected ")
                           + name_trait<T>::name() +
                           " but got '" + get_text() + "'", *this);
    }
    return *result;
}

unsigned xml_node::line() const
{
    return line_;
}

std::string xml_node::line_to_string() const
{
    std::string number;
    util::to_string(number,line_);
    return number;
}

#define compile_get_opt_attr(T) template boost::optional<T> xml_node::get_opt_attr<T>(std::string const&) const
#define compile_get_attr(T) template T xml_node::get_attr<T>(std::string const&) const; template T xml_node::get_attr<T>(std::string const&, T const&) const
#define compile_get_value(T) template T xml_node::get_value<T>() const

compile_get_opt_attr(boolean);
compile_get_opt_attr(std::string);
compile_get_opt_attr(unsigned);
compile_get_opt_attr(mapnik::value_integer);
compile_get_opt_attr(float);
compile_get_opt_attr(double);
compile_get_opt_attr(color);
compile_get_opt_attr(gamma_method_e);
compile_get_opt_attr(line_join_e);
compile_get_opt_attr(line_cap_e);
compile_get_opt_attr(text_transform_e);
compile_get_opt_attr(label_placement_e);
compile_get_opt_attr(vertical_alignment_e);
compile_get_opt_attr(horizontal_alignment_e);
compile_get_opt_attr(justify_alignment_e);
compile_get_opt_attr(expression_ptr);
compile_get_attr(std::string);
compile_get_attr(filter_mode_e);
compile_get_attr(point_placement_e);
compile_get_attr(marker_placement_e);
compile_get_attr(marker_multi_policy_e);
compile_get_attr(pattern_alignment_e);
compile_get_attr(line_rasterizer_e);
compile_get_attr(colorizer_mode);
compile_get_attr(double);
compile_get_value(value_integer);
compile_get_value(double);
compile_get_value(expression_ptr);
} //ns mapnik
<commit_msg>+ update xml_tree to use xml_attrinbute_cast and remove fast_cast<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2012 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//mapnik
#include <mapnik/xml_tree.hpp>
#include <mapnik/xml_attribute_cast.hpp>
#include <mapnik/util/conversions.hpp>
#include <mapnik/enumeration.hpp>
#include <mapnik/color_factory.hpp>
#include <mapnik/gamma_method.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/line_symbolizer.hpp>
#include <mapnik/line_pattern_symbolizer.hpp>
#include <mapnik/polygon_pattern_symbolizer.hpp>
#include <mapnik/point_symbolizer.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/text_properties.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/raster_colorizer.hpp>

namespace mapnik
{

class boolean;
template <typename T>
struct name_trait
{
    static std::string name()
    {
        return "<unknown>";
    }
    // missing name_trait for type ...
    // if you get here you are probably using a new type
    // in the XML file. Just add a name trait for the new
    // type below.
    BOOST_STATIC_ASSERT( sizeof(T) == 0 );
};

#define DEFINE_NAME_TRAIT( type, type_name )                            \
    template <>                                                         \
    struct name_trait<type>                                             \
    {                                                                   \
        static std::string name() { return std::string("type ") + type_name; } \
    };

DEFINE_NAME_TRAIT( double, "double")
DEFINE_NAME_TRAIT( float, "float")
DEFINE_NAME_TRAIT( unsigned, "unsigned")
DEFINE_NAME_TRAIT( boolean, "boolean")
#ifdef BIGINT
DEFINE_NAME_TRAIT( mapnik::value_integer, "long long" )
#else
DEFINE_NAME_TRAIT( mapnik::value_integer, "int" )
#endif
DEFINE_NAME_TRAIT( std::string, "string" )
DEFINE_NAME_TRAIT( color, "color" )
DEFINE_NAME_TRAIT(expression_ptr, "expression_ptr" )

template <typename ENUM, int MAX>
struct name_trait< mapnik::enumeration<ENUM, MAX> >
{
    typedef enumeration<ENUM, MAX> Enum;

    static std::string name()
    {
        std::string value_list("one of [");
        for (unsigned i = 0; i < Enum::MAX; ++i)
        {
            value_list += Enum::get_string( i );
            if ( i + 1 < Enum::MAX ) value_list += ", ";
        }
        value_list += "]";

        return value_list;
    }
};

xml_tree::xml_tree(std::string const& encoding)
    : node_(*this, "<root>"),
      file_(),
      tr_(encoding),
      color_grammar(),
      expr_grammar(tr_),
      path_expr_grammar(),
      transform_expr_grammar(expr_grammar),
      image_filters_grammar()
{
    node_.set_processed(true); //root node is always processed
}

void xml_tree::set_filename(std::string fn)
{
    file_ = fn;
}

std::string const& xml_tree::filename() const
{
    return file_;
}

xml_node &xml_tree::root()
{
    return node_;
}

const xml_node &xml_tree::root() const
{
    return node_;
}

xml_attribute::xml_attribute(std::string const& value_)
    : value(value_), processed(false)
{

}

node_not_found::node_not_found(std::string const& node_name)
    : node_name_(node_name) {}

const char* node_not_found::what() const throw()
{
    return ("Node "+node_name_+ "not found").c_str();
}

node_not_found::~node_not_found() throw() {}

attribute_not_found::attribute_not_found(
    std::string const& node_name,
    std::string const& attribute_name)
    :
    node_name_(node_name),
    attribute_name_(attribute_name) {}

const char* attribute_not_found::what() const throw()
{
    return ("Attribute '" + attribute_name_ +"' not found in node '"+node_name_+ "'").c_str();
}

attribute_not_found::~attribute_not_found() throw() {}

more_than_one_child::more_than_one_child(std::string const& node_name)
    : node_name_(node_name) {}

const char* more_than_one_child::what() const throw()
{
    return ("More than one child node in node '" + node_name_ +"'").c_str();
}

more_than_one_child::~more_than_one_child() throw() {}

xml_node::xml_node(xml_tree &tree, std::string const& name, unsigned line, bool is_text)
    : tree_(tree),
      name_(name),
      is_text_(is_text),
      line_(line),
      processed_(false) {}

std::string xml_node::xml_text = "<xmltext>";

std::string const& xml_node::name() const
{
    if (!is_text_)
        return name_;
    else
        return xml_text;
}

std::string const& xml_node::text() const
{
    if (is_text_)
    {
        processed_ = true;
        return name_;
    } else
    {
        throw config_error("text() called on non-text node", *this);
    }
}

std::string const& xml_node::filename() const
{
    return tree_.filename();
}

bool xml_node::is_text() const
{
    return is_text_;
}

bool xml_node::is(std::string const& name) const
{
    if (name_ == name)
    {
        processed_ = true;
        return true;
    }
    return false;
}

xml_node &xml_node::add_child(std::string const& name, unsigned line, bool is_text)
{
    children_.push_back(xml_node(tree_, name, line, is_text));
    return children_.back();
}

void xml_node::add_attribute(std::string const& name, std::string const& value)
{
    attributes_.insert(std::make_pair(name,xml_attribute(value)));
}

xml_node::attribute_map const& xml_node::get_attributes() const
{
    return attributes_;
}

void xml_node::set_processed(bool processed) const
{
    processed_ = processed;
}

bool xml_node::processed() const
{
    return processed_;
}

xml_node::const_iterator xml_node::begin() const
{
    return children_.begin();
}

xml_node::const_iterator xml_node::end() const
{
    return children_.end();
}

xml_node & xml_node::get_child(std::string const& name)
{
    std::list<xml_node>::iterator itr = children_.begin();
    std::list<xml_node>::iterator end = children_.end();
    for (; itr != end; itr++)
    {
        if (!(itr->is_text_) && itr->name_ == name)
        {
            itr->set_processed(true);
            return *itr;
        }
    }
    throw node_not_found(name);
}

xml_node const& xml_node::get_child(std::string const& name) const
{
    xml_node const* node = get_opt_child(name);
    if (!node) throw node_not_found(name);
    return *node;
}

xml_node const* xml_node::get_opt_child(std::string const& name) const
{
    const_iterator itr = children_.begin();
    const_iterator end = children_.end();
    for (; itr != end; itr++)
    {
        if (!(itr->is_text_) && itr->name_ == name)
        {
            itr->set_processed(true);
            return &(*itr);
        }
    }
    return 0;
}

bool xml_node::has_child(std::string const& name) const
{
    return get_opt_child(name) != 0;
}

template <typename T>
boost::optional<T> xml_node::get_opt_attr(std::string const& name) const
{
    std::map<std::string, xml_attribute>::const_iterator itr = attributes_.find(name);
    if (itr ==  attributes_.end()) return boost::optional<T>();
    itr->second.processed = true;
    boost::optional<T> result = xml_attribute_cast<T>(tree_, std::string(itr->second.value));
    if (!result)
    {
        throw config_error(std::string("Failed to parse attribute '") +
                           name + "'. Expected " + name_trait<T>::name() +
                           " but got '" + itr->second.value + "'", *this);
    }
    return result;
}

template <typename T>
T xml_node::get_attr(std::string const& name, T const& default_opt_value) const
{
    boost::optional<T> value = get_opt_attr<T>(name);
    if (value) return *value;
    return default_opt_value;
}

template <typename T>
T xml_node::get_attr(std::string const& name) const
{
    boost::optional<T> value = get_opt_attr<T>(name);
    if (value) return *value;
    throw attribute_not_found(name_, name);
}

std::string xml_node::get_text() const
{
    if (children_.size() == 0)
    {
        if (is_text_)
        {
            return name_;
        } else
        {
            return "";
        }
    }
    if (children_.size() == 1)
    {
        return children_.front().text();
    }
    throw more_than_one_child(name_);
}

template <typename T>
T xml_node::get_value() const
{
    boost::optional<T> result = xml_attribute_cast<T>(tree_, get_text());
    if (!result)
    {
        throw config_error(std::string("Failed to parse value. Expected ")
                           + name_trait<T>::name() +
                           " but got '" + get_text() + "'", *this);
    }
    return *result;
}

unsigned xml_node::line() const
{
    return line_;
}

std::string xml_node::line_to_string() const
{
    std::string number;
    util::to_string(number,line_);
    return number;
}

#define compile_get_opt_attr(T) template boost::optional<T> xml_node::get_opt_attr<T>(std::string const&) const
#define compile_get_attr(T) template T xml_node::get_attr<T>(std::string const&) const; template T xml_node::get_attr<T>(std::string const&, T const&) const
#define compile_get_value(T) template T xml_node::get_value<T>() const

compile_get_opt_attr(boolean);
compile_get_opt_attr(std::string);
compile_get_opt_attr(unsigned);
compile_get_opt_attr(mapnik::value_integer);
compile_get_opt_attr(float);
compile_get_opt_attr(double);
compile_get_opt_attr(color);
compile_get_opt_attr(gamma_method_e);
compile_get_opt_attr(line_join_e);
compile_get_opt_attr(line_cap_e);
compile_get_opt_attr(text_transform_e);
compile_get_opt_attr(label_placement_e);
compile_get_opt_attr(vertical_alignment_e);
compile_get_opt_attr(horizontal_alignment_e);
compile_get_opt_attr(justify_alignment_e);
compile_get_opt_attr(expression_ptr);
compile_get_attr(std::string);
compile_get_attr(filter_mode_e);
compile_get_attr(point_placement_e);
compile_get_attr(marker_placement_e);
compile_get_attr(marker_multi_policy_e);
compile_get_attr(pattern_alignment_e);
compile_get_attr(line_rasterizer_e);
compile_get_attr(colorizer_mode);
compile_get_attr(double);
compile_get_value(value_integer);
compile_get_value(double);
compile_get_value(expression_ptr);
} //ns mapnik
<|endoftext|>


<commit_before>#include "PolyRegress.h"

// Least Squares Regression based on following discussion:
// http://stackoverflow.com/questions/24747643/3d-linear-regression
// coeffs = theta = inv(trans(X) * X) * trans(X) * y
// theta: vector of fit coefficients [a0 a1 a2 ... aN] for a0 + a1x + a2x^2 + ... + aNx^N
// X: matrix of inputs [[1 x1 x1^2 ... x1^N]
//                      [1 x2 x2^2 ... x2^N]
//                       ...
//                      [1 xM xM^2 ... xM^N]]
// y: vector of outputs [y1 y2 ... yM]
// M: number of input points
// degree = N: degree of polynomial to fit
PolyRegress::PolyRegress(vector<float> const &xVals, vector<float> const &yVals, int degree, bool useLog) {
	logVals = useLog;
	deg = degree;
	success = false;

	int M = yVals.size();
	if (M != xVals.size()) {
		return;
	}

	ColumnVector y(M);
	Matrix X(M, degree + 1);

	for (int i = 1; i <= M; ++i) {
		y(i) = yVals[i - 1];

		int j = 1;
		float curVal = 1;
		do {
			if (useLog && j > 1) {
				X(i, j) = log(curVal);
			}
			else {
				X(i, j) = curVal;
			}
			curVal *= xVals[i - 1];
		} while (j++ < degree + 1);
	}

	coeffs = (X.t() * X).i() * X.t() * y; //inv(trans(X) * X) * trans(X) * y

	success = true;
}

vector<float> PolyRegress::getCoeffs() {
	vector<float> result(deg + 1);

	for (int i = 0; i < deg + 1; ++i) {
		result[i] = coeffs(i + 1);
	}

	return result;
}

float PolyRegress::interpolate(float x) {
	float result = 0;
	float curX = 1;

	for (int i = 0; i < deg + 1; ++i) {
		if (logVals) {
			result += log(curX) * coeffs(i + 1);
		}
		else {
			result += curX * coeffs(i + 1);
		}
		curX *= x;
	}

	return result;
}<commit_msg>Fix to logarithmic regression<commit_after>#include "PolyRegress.h"

// Least Squares Regression based on following discussion:
// http://stackoverflow.com/questions/24747643/3d-linear-regression
// coeffs = theta = inv(trans(X) * X) * trans(X) * y
// theta: vector of fit coefficients [a0 a1 a2 ... aN] for a0 + a1x + a2x^2 + ... + aNx^N
// X: matrix of inputs [[1 x1 x1^2 ... x1^N]
//                      [1 x2 x2^2 ... x2^N]
//                       ...
//                      [1 xM xM^2 ... xM^N]]
// y: vector of outputs [y1 y2 ... yM]
// M: number of input points
// degree = N: degree of polynomial to fit
PolyRegress::PolyRegress(vector<float> const &xVals, vector<float> const &yVals, int degree, bool useLog) {
	logVals = useLog;
	deg = degree;
	success = false;

	int M = yVals.size();
	if (M != xVals.size()) {
		return;
	}

	ColumnVector y(M);
	Matrix X(M, degree + 1);

	for (int i = 1; i <= M; ++i) {
		y(i) = yVals[i - 1];

		int j = 1;
		float curVal = 1;
		do {
			X(i, j) = curVal;
			curVal *= (logVals) ? log(xVals[i - 1] + 1) : xVals[i - 1];
		} while (j++ < degree + 1);
	}

	coeffs = (X.t() * X).i() * X.t() * y; //inv(trans(X) * X) * trans(X) * y
	std::cout << coeffs << std::endl;

	success = true;
}

vector<float> PolyRegress::getCoeffs() {
	vector<float> result(deg + 1);

	for (int i = 0; i < deg + 1; ++i) {
		result[i] = coeffs(i + 1);
	}

	return result;
}

float PolyRegress::interpolate(float x) {
	float result = 0;
	float curX = 1;

	for (int i = 0; i < deg + 1; ++i) {
		result += curX * coeffs(i + 1);
		curX *= (logVals) ? log(x + 1) : x;
	}

	return result;
}<|endoftext|>