<commit_before>// Copyright (c) 2018-2020 Daniel Kraft
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <rpc/auxpow_miner.h>

#include <arith_uint256.h>
#include <auxpow.h>
#include <chainparams.h>
#include <net.h>
#include <node/context.h>
#include <rpc/blockchain.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <util/strencodings.h>
#include <util/time.h>
#include <validation.h>

#include <cassert>

namespace
{

void auxMiningCheck()
{
  if (!g_rpc_node->connman)
    throw JSONRPCError (RPC_CLIENT_P2P_DISABLED,
                        "Error: Peer-to-peer functionality missing or"
                        " disabled");

  if (g_rpc_node->connman->GetNodeCount (CConnman::CONNECTIONS_ALL) == 0
        && !Params ().MineBlocksOnDemand ())
    throw JSONRPCError (RPC_CLIENT_NOT_CONNECTED,
                        "Syscoin is not connected!");

  if (::ChainstateActive ().IsInitialBlockDownload ()
        && !Params ().MineBlocksOnDemand ())
    throw JSONRPCError (RPC_CLIENT_IN_INITIAL_DOWNLOAD,
                        "Syscoin is downloading blocks...");

  /* This should never fail, since the chain is already
     past the point of merge-mining start.  Check nevertheless.  */
  {
    LOCK (cs_main);
    const auto auxpowStart = Params ().GetConsensus ().nAuxpowStartHeight;
    if (::ChainActive ().Height () + 1 < auxpowStart)
      throw std::runtime_error ("mining auxblock method is not yet available");
  }
}

}  // anonymous namespace

const CBlock*
AuxpowMiner::getCurrentBlock (const CTxMemPool& mempool,
                              const CScript& scriptPubKey, uint256& target)
{
  AssertLockHeld (cs);
  const CBlock* pblockCur = nullptr;

  {
    LOCK (cs_main);
    CScriptID scriptID (scriptPubKey);
    auto iter = curBlocks.find(scriptID);
    if (iter != curBlocks.end())
      pblockCur = iter->second;

    if (pblockCur == nullptr
        || pindexPrev != ::ChainActive ().Tip ()
        || (mempool.GetTransactionsUpdated () != txUpdatedLast
            && GetTime () - startTime > 60))
      {
        if (pindexPrev != ::ChainActive ().Tip ())
          {
            /* Clear old blocks since they're obsolete now.  */
            blocks.clear ();
            templates.clear ();
            curBlocks.clear ();
          }

        /* Create new block with nonce = 0 and extraNonce = 1.  */
        std::unique_ptr<CBlockTemplate> newBlock
            = BlockAssembler (mempool, Params ()).CreateNewBlock (scriptPubKey);
        if (newBlock == nullptr)
          throw JSONRPCError (RPC_OUT_OF_MEMORY, "out of memory");

        /* Update state only when CreateNewBlock succeeded.  */
        txUpdatedLast = mempool.GetTransactionsUpdated ();
        pindexPrev = ::ChainActive ().Tip ();
        startTime = GetTime ();

        /* Finalise it by setting the version and building the merkle root.  */
        IncrementExtraNonce (&newBlock->block, pindexPrev, extraNonce);
        newBlock->block.SetAuxpowVersion (true);

        /* Save in our map of constructed blocks.  */
        pblockCur = &newBlock->block;
        curBlocks.emplace(scriptID, pblockCur);
        blocks[pblockCur->GetHash ()] = pblockCur;
        templates.push_back (std::move (newBlock));
      }
  }

  /* At this point, pblockCur is always initialised:  If we make it here
     without creating a new block above, it means that, in particular,
     pindexPrev == ::ChainActive ().Tip().  But for that to happen, we must
     already have created a pblockCur in a previous call, as pindexPrev is
     initialised only when pblockCur is.  */
  CHECK_NONFATAL(pblockCur);

  arith_uint256 arithTarget;
  bool fNegative, fOverflow;
  arithTarget.SetCompact (pblockCur->nBits, &fNegative, &fOverflow);
  if (fNegative || fOverflow || arithTarget == 0)
    throw std::runtime_error ("invalid difficulty bits in block");
  target = ArithToUint256 (arithTarget);

  return pblockCur;
}

const CBlock*
AuxpowMiner::lookupSavedBlock (const std::string& hashHex) const
{
  AssertLockHeld (cs);

  uint256 hash;
  hash.SetHex (hashHex);

  const auto iter = blocks.find (hash);
  if (iter == blocks.end ())
    throw JSONRPCError (RPC_INVALID_PARAMETER, "block hash unknown");

  return iter->second;
}

UniValue
AuxpowMiner::createAuxBlock (const CScript& scriptPubKey)
{
  auxMiningCheck ();
  LOCK (cs);

  const auto& mempool = EnsureMemPool ();

  uint256 target;
  const CBlock* pblock = getCurrentBlock (mempool, scriptPubKey, target);

  UniValue result(UniValue::VOBJ);
  result.pushKV ("hash", pblock->GetHash ().GetHex ());
  result.pushKV ("chainid", pblock->GetChainId ());
  result.pushKV ("previousblockhash", pblock->hashPrevBlock.GetHex ());
  result.pushKV ("coinbasevalue",
                 static_cast<int64_t> (pblock->vtx[0]->vout[0].nValue));
  result.pushKV ("bits", strprintf ("%08x", pblock->nBits));
  result.pushKV ("height", static_cast<int64_t> (pindexPrev->nHeight + 1));
  result.pushKV ("_target", HexStr (target.begin (), target.end ()));

  return result;
}

bool
AuxpowMiner::submitAuxBlock (const std::string& hashHex,
                             const std::string& auxpowHex) const
{
  auxMiningCheck ();

  std::shared_ptr<CBlock> shared_block;
  {
    LOCK (cs);
    const CBlock* pblock = lookupSavedBlock (hashHex);
    shared_block = std::make_shared<CBlock> (*pblock);
  }

  const std::vector<unsigned char> vchAuxPow = ParseHex (auxpowHex);
  CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
  std::unique_ptr<CAuxPow> pow(new CAuxPow ());
  ss >> *pow;
  shared_block->SetAuxpow (std::move (pow));
  CHECK_NONFATAL(shared_block->GetHash ().GetHex () == hashHex);

  return ProcessNewBlock (Params (), shared_block, true, nullptr);
}

AuxpowMiner&
AuxpowMiner::get ()
{
  static AuxpowMiner* instance = nullptr;
  static RecursiveMutex lock;

  LOCK (lock);
  if (instance == nullptr)
    instance = new AuxpowMiner ();

  return *instance;
}<commit_msg>include check.h<commit_after>// Copyright (c) 2018-2020 Daniel Kraft
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <rpc/auxpow_miner.h>

#include <arith_uint256.h>
#include <auxpow.h>
#include <chainparams.h>
#include <net.h>
#include <node/context.h>
#include <rpc/blockchain.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <util/strencodings.h>
#include <util/time.h>
#include <validation.h>

#include <cassert>
#include <util/check.h>
namespace
{

void auxMiningCheck()
{
  if (!g_rpc_node->connman)
    throw JSONRPCError (RPC_CLIENT_P2P_DISABLED,
                        "Error: Peer-to-peer functionality missing or"
                        " disabled");

  if (g_rpc_node->connman->GetNodeCount (CConnman::CONNECTIONS_ALL) == 0
        && !Params ().MineBlocksOnDemand ())
    throw JSONRPCError (RPC_CLIENT_NOT_CONNECTED,
                        "Syscoin is not connected!");

  if (::ChainstateActive ().IsInitialBlockDownload ()
        && !Params ().MineBlocksOnDemand ())
    throw JSONRPCError (RPC_CLIENT_IN_INITIAL_DOWNLOAD,
                        "Syscoin is downloading blocks...");

  /* This should never fail, since the chain is already
     past the point of merge-mining start.  Check nevertheless.  */
  {
    LOCK (cs_main);
    const auto auxpowStart = Params ().GetConsensus ().nAuxpowStartHeight;
    if (::ChainActive ().Height () + 1 < auxpowStart)
      throw std::runtime_error ("mining auxblock method is not yet available");
  }
}

}  // anonymous namespace

const CBlock*
AuxpowMiner::getCurrentBlock (const CTxMemPool& mempool,
                              const CScript& scriptPubKey, uint256& target)
{
  AssertLockHeld (cs);
  const CBlock* pblockCur = nullptr;

  {
    LOCK (cs_main);
    CScriptID scriptID (scriptPubKey);
    auto iter = curBlocks.find(scriptID);
    if (iter != curBlocks.end())
      pblockCur = iter->second;

    if (pblockCur == nullptr
        || pindexPrev != ::ChainActive ().Tip ()
        || (mempool.GetTransactionsUpdated () != txUpdatedLast
            && GetTime () - startTime > 60))
      {
        if (pindexPrev != ::ChainActive ().Tip ())
          {
            /* Clear old blocks since they're obsolete now.  */
            blocks.clear ();
            templates.clear ();
            curBlocks.clear ();
          }

        /* Create new block with nonce = 0 and extraNonce = 1.  */
        std::unique_ptr<CBlockTemplate> newBlock
            = BlockAssembler (mempool, Params ()).CreateNewBlock (scriptPubKey);
        if (newBlock == nullptr)
          throw JSONRPCError (RPC_OUT_OF_MEMORY, "out of memory");

        /* Update state only when CreateNewBlock succeeded.  */
        txUpdatedLast = mempool.GetTransactionsUpdated ();
        pindexPrev = ::ChainActive ().Tip ();
        startTime = GetTime ();

        /* Finalise it by setting the version and building the merkle root.  */
        IncrementExtraNonce (&newBlock->block, pindexPrev, extraNonce);
        newBlock->block.SetAuxpowVersion (true);

        /* Save in our map of constructed blocks.  */
        pblockCur = &newBlock->block;
        curBlocks.emplace(scriptID, pblockCur);
        blocks[pblockCur->GetHash ()] = pblockCur;
        templates.push_back (std::move (newBlock));
      }
  }

  /* At this point, pblockCur is always initialised:  If we make it here
     without creating a new block above, it means that, in particular,
     pindexPrev == ::ChainActive ().Tip().  But for that to happen, we must
     already have created a pblockCur in a previous call, as pindexPrev is
     initialised only when pblockCur is.  */
  CHECK_NONFATAL(pblockCur);

  arith_uint256 arithTarget;
  bool fNegative, fOverflow;
  arithTarget.SetCompact (pblockCur->nBits, &fNegative, &fOverflow);
  if (fNegative || fOverflow || arithTarget == 0)
    throw std::runtime_error ("invalid difficulty bits in block");
  target = ArithToUint256 (arithTarget);

  return pblockCur;
}

const CBlock*
AuxpowMiner::lookupSavedBlock (const std::string& hashHex) const
{
  AssertLockHeld (cs);

  uint256 hash;
  hash.SetHex (hashHex);

  const auto iter = blocks.find (hash);
  if (iter == blocks.end ())
    throw JSONRPCError (RPC_INVALID_PARAMETER, "block hash unknown");

  return iter->second;
}

UniValue
AuxpowMiner::createAuxBlock (const CScript& scriptPubKey)
{
  auxMiningCheck ();
  LOCK (cs);

  const auto& mempool = EnsureMemPool ();

  uint256 target;
  const CBlock* pblock = getCurrentBlock (mempool, scriptPubKey, target);

  UniValue result(UniValue::VOBJ);
  result.pushKV ("hash", pblock->GetHash ().GetHex ());
  result.pushKV ("chainid", pblock->GetChainId ());
  result.pushKV ("previousblockhash", pblock->hashPrevBlock.GetHex ());
  result.pushKV ("coinbasevalue",
                 static_cast<int64_t> (pblock->vtx[0]->vout[0].nValue));
  result.pushKV ("bits", strprintf ("%08x", pblock->nBits));
  result.pushKV ("height", static_cast<int64_t> (pindexPrev->nHeight + 1));
  result.pushKV ("_target", HexStr (target.begin (), target.end ()));

  return result;
}

bool
AuxpowMiner::submitAuxBlock (const std::string& hashHex,
                             const std::string& auxpowHex) const
{
  auxMiningCheck ();

  std::shared_ptr<CBlock> shared_block;
  {
    LOCK (cs);
    const CBlock* pblock = lookupSavedBlock (hashHex);
    shared_block = std::make_shared<CBlock> (*pblock);
  }

  const std::vector<unsigned char> vchAuxPow = ParseHex (auxpowHex);
  CDataStream ss(vchAuxPow, SER_GETHASH, PROTOCOL_VERSION);
  std::unique_ptr<CAuxPow> pow(new CAuxPow ());
  ss >> *pow;
  shared_block->SetAuxpow (std::move (pow));
  CHECK_NONFATAL(shared_block->GetHash ().GetHex () == hashHex);

  return ProcessNewBlock (Params (), shared_block, true, nullptr);
}

AuxpowMiner&
AuxpowMiner::get ()
{
  static AuxpowMiner* instance = nullptr;
  static RecursiveMutex lock;

  LOCK (lock);
  if (instance == nullptr)
    instance = new AuxpowMiner ();

  return *instance;
}<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_ATOM_HPP
#define CAF_ATOM_HPP

#include <string>
#include <type_traits>

#include "caf/detail/atom_val.hpp"

namespace caf {

/**
 * The value type of atoms.
 */
enum class atom_value : uint64_t {
  /** @cond PRIVATE */
  dirty_little_hack = 31337
  /** @endcond */

};

/**
 * Creates an atom from given string literal.
 */
template <size_t Size>
constexpr atom_value atom(char const (&str)[Size]) {
  // last character is the NULL terminator
  static_assert(Size <= 11, "only 10 characters are allowed");
  return static_cast<atom_value>(detail::atom_val(str, 0xF));
}

template <atom_value Value>
using atom_constant = std::integral_constant<atom_value, Value>;

} // namespace caf

#endif // CAF_ATOM_HPP
<commit_msg>Add aliases for common atom constant types<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_ATOM_HPP
#define CAF_ATOM_HPP

#include <string>
#include <type_traits>

#include "caf/detail/atom_val.hpp"

namespace caf {

/**
 * The value type of atoms.
 */
enum class atom_value : uint64_t {
  /** @cond PRIVATE */
  dirty_little_hack = 31337
  /** @endcond */
};

/**
 * Creates an atom from given string literal.
 */
template <size_t Size>
constexpr atom_value atom(char const (&str)[Size]) {
  // last character is the NULL terminator
  static_assert(Size <= 11, "only 10 characters are allowed");
  return static_cast<atom_value>(detail::atom_val(str, 0xF));
}

/**
 * Type alias for treating atom constants at compile-time
 * (cf. "Int-ToType" idiom).
 */
template <atom_value Value>
using atom_constant = std::integral_constant<atom_value, Value>;

/**
 * Generic 'GET' atom for request operations.
 */
using get_atom = atom_constant<atom("GET")>;

/**
 * Generic 'PUT' atom for request operations.
 */
using put_atom = atom_constant<atom("PUT")>;

/**
 * Generic 'OK' atom for response messages.
 */
using ok_atom = atom_constant<atom("OK")>;

/**
 * Generic 'ERROR' atom for response messages.
 */
using error_atom = atom_constant<atom("ERROR")>;

} // namespace caf

#endif // CAF_ATOM_HPP
<|endoftext|>


<commit_before>#include "capacitor.h"
#include "time_ordered_set.h"
#include "utils.h"
#include "timeutil.h"
#include <cassert>
#include <algorithm>
#include <list>
#include <map>
#include <limits>
#include <utility>
#include <mutex>

using namespace dariadb;
using namespace dariadb::storage;

class Capacitor::Private
{
public:
    typedef std::shared_ptr<TimeOrderedSet>   tos_ptr;
    typedef std::list<tos_ptr>                container;
    typedef std::map<dariadb::Id,container>   dict;
    typedef std::map<dariadb::Id, tos_ptr>    dict_last;

	Private(const size_t max_size, const AbstractStorage_ptr stor, const dariadb::Time write_window_deep):

		_max_size(max_size),
		_minTime(std::numeric_limits<dariadb::Time>::max()),
		_maxTime(std::numeric_limits<dariadb::Time>::min()),
		_bucks(),
        _last(),
		_stor(stor),
		_writed_count(0),
		_write_window_deep(write_window_deep)
    {

    }

	Private(const Private &other) :
		_max_size(other._max_size),
		_minTime(other._minTime),
		_maxTime(other._maxTime),
		_bucks(other._bucks),
		_last(other._last),
		_stor(other._stor),
		_writed_count(other._writed_count),
		_write_window_deep(other._write_window_deep)
    {}

    ~Private() {
    }

    tos_ptr alloc_new() {
        return std::make_shared<TimeOrderedSet>(_max_size);
    }

    bool is_valid_time(const dariadb::Time &t)const {
        auto now=dariadb::timeutil::current_time();
        auto past=(now-_write_window_deep);
        if(t< past){
            return false;
        }
        return true;
    }

	bool is_valid(const dariadb::Meas &m)const {
        return is_valid_time(m.time);
	}

    bool append(const dariadb::Meas&m) {
		std::lock_guard<std::mutex> lg(_mutex);

		if (!is_valid(m)) {
			return false;
		}

        tos_ptr target = get_target_to_write(m);

        if (target != nullptr) {
            target->append(m, true);
            _writed_count++;
            _minTime = std::min(_minTime, m.time);
            _maxTime = std::max(_maxTime, m.time);
        }

        for(auto &kv:_bucks){
            while(kv.second.size()>0){
                auto v=kv.second.front();
                if(is_valid_time(v->maxTime())){
                    break;
                }else{
                    flush_set(v);
                    kv.second.pop_front();
                }
            }
        }

        return true;
    }

    tos_ptr get_target_to_write(const Meas&m) {
        auto it=_last.find(m.id);
        if(it==_last.end()){
            auto n=alloc_new();
            _last[m.id]=n;
            _bucks[m.id].push_back(n);
            return n;
        }

        if ((maxTime() <= m.time) || (_last[m.id]->inInterval(m))) {
            if (_last[m.id]->is_full()) {
                auto n=alloc_new();
                _last[m.id]=n;
                _bucks[m.id].push_back(n);
            }
            return _last[m.id];
        }
        else {
            auto it = _bucks[m.id].rbegin();

            for (; it != _bucks[m.id].rend(); ++it) {
                auto b = *it;
                if ((b->inInterval(m))|| (b->maxTime()<m.time)) {
                    if(b->is_full()){
                        auto new_b = alloc_new();
                        _bucks[m.id].insert(it.base(),new_b);
                        return new_b;
                    }
                    return b;
                }
            }

            auto new_b = alloc_new();
            _bucks[m.id].push_front(new_b);
            return new_b;

        }
    }

    size_t size()const {
        return _bucks.size();
    }

    dariadb::Time minTime()const {
        return _minTime;
    }
    dariadb::Time maxTime()const {
        return _maxTime;
    }

    size_t writed_count()const {
        return _writed_count;
    }

    bool flush(){
		std::lock_guard<std::mutex> lg(_mutex);
        for (auto kv : _bucks) {
            for(auto v:kv.second){
                if(!flush_set(v)){
                    return false;
                }
            }
        }
        clear();
        return true;
    }

    bool flush_set(tos_ptr b){
        auto arr = b->as_array();
        auto stor_res = _stor->append(arr);
        _writed_count -= arr.size();
        if (stor_res.writed != arr.size()) {
            return false;
        }
        return true;
    }

    void clear(){
        this->_bucks.clear();
        _last.clear();
    }
protected:
    size_t _max_size;

    dariadb::Time _minTime;
    dariadb::Time _maxTime;

    dict _bucks;
    dict_last   _last;
    AbstractStorage_ptr _stor;
    size_t _writed_count;
	dariadb::Time _write_window_deep;
	std::mutex _mutex;
};

Capacitor::Capacitor():_Impl(new Capacitor::Private(0,nullptr,0))
{}

Capacitor::~Capacitor()
{}

Capacitor::Capacitor(const size_t max_size, const AbstractStorage_ptr stor, const dariadb::Time write_window_deep) :
	_Impl(new Capacitor::Private(max_size,stor,write_window_deep))
{}

Capacitor::Capacitor(const Capacitor & other): _Impl(new Capacitor::Private(*other._Impl))
{}

Capacitor::Capacitor(Capacitor && other): _Impl(std::move(other._Impl))
{}

void Capacitor::swap(Capacitor & other)throw(){
    std::swap(_Impl, other._Impl);
}

Capacitor& Capacitor::operator=(const Capacitor & other){
    if (this != &other) {
        Capacitor tmp(other);
        this->swap(tmp);
    }
    return *this;
}

Capacitor& Capacitor::operator=(Capacitor && other){
    this->swap(other);
    return *this;
}

bool Capacitor::append(const Meas & m){
    return _Impl->append(m);
}

size_t Capacitor::size()const {
    return _Impl->size();
}

dariadb::Time Capacitor::minTime()const {
    return _Impl->minTime();
}

dariadb::Time Capacitor::maxTime()const {
    return _Impl->maxTime();
}

size_t Capacitor::writed_count()const {
    return _Impl->writed_count();
}

bool Capacitor::flush() {//write all to storage;
    return _Impl->flush();
}

void Capacitor::clear() {
    return _Impl->clear();
}
<commit_msg>comment.<commit_after>#include "capacitor.h"
#include "time_ordered_set.h"
#include "utils.h"
#include "timeutil.h"
#include <cassert>
#include <algorithm>
#include <list>
#include <map>
#include <limits>
#include <utility>
#include <mutex>

using namespace dariadb;
using namespace dariadb::storage;

class Capacitor::Private
{
public:
    typedef std::shared_ptr<TimeOrderedSet>   tos_ptr;
    typedef std::list<tos_ptr>                container;
    typedef std::map<dariadb::Id,container>   dict;
    typedef std::map<dariadb::Id, tos_ptr>    dict_last;

	Private(const size_t max_size, const AbstractStorage_ptr stor, const dariadb::Time write_window_deep):

		_max_size(max_size),
		_minTime(std::numeric_limits<dariadb::Time>::max()),
		_maxTime(std::numeric_limits<dariadb::Time>::min()),
		_bucks(),
        _last(),
		_stor(stor),
		_writed_count(0),
		_write_window_deep(write_window_deep)
    {

    }

	Private(const Private &other) :
		_max_size(other._max_size),
		_minTime(other._minTime),
		_maxTime(other._maxTime),
		_bucks(other._bucks),
		_last(other._last),
		_stor(other._stor),
		_writed_count(other._writed_count),
		_write_window_deep(other._write_window_deep)
    {}

    ~Private() {
    }

    tos_ptr alloc_new() {
        return std::make_shared<TimeOrderedSet>(_max_size);
    }

    bool is_valid_time(const dariadb::Time &t)const {
        auto now=dariadb::timeutil::current_time();
        auto past=(now-_write_window_deep);
        if(t< past){
            return false;
        }
        return true;
    }

	bool is_valid(const dariadb::Meas &m)const {
        return is_valid_time(m.time);
	}

    bool append(const dariadb::Meas&m) {
		std::lock_guard<std::mutex> lg(_mutex);

		if (!is_valid(m)) {
			return false;
		}

        tos_ptr target = get_target_to_write(m);

        if (target != nullptr) {
            target->append(m, true);
            _writed_count++;
            _minTime = std::min(_minTime, m.time);
            _maxTime = std::max(_maxTime, m.time);
        }

        //flush old sets.
        for(auto &kv:_bucks){
            while(kv.second.size()>0){
                auto v=kv.second.front();
                if(is_valid_time(v->maxTime())){
                    break;
                }else{
                    flush_set(v);
                    kv.second.pop_front();
                }
            }
        }

        return true;
    }

    tos_ptr get_target_to_write(const Meas&m) {
        auto it=_last.find(m.id);
        if(it==_last.end()){
            auto n=alloc_new();
            _last[m.id]=n;
            _bucks[m.id].push_back(n);
            return n;
        }

        if ((maxTime() <= m.time) || (_last[m.id]->inInterval(m))) {
            if (_last[m.id]->is_full()) {
                auto n=alloc_new();
                _last[m.id]=n;
                _bucks[m.id].push_back(n);
            }
            return _last[m.id];
        }
        else {
            auto it = _bucks[m.id].rbegin();

            for (; it != _bucks[m.id].rend(); ++it) {
                auto b = *it;
                if ((b->inInterval(m))|| (b->maxTime()<m.time)) {
                    if(b->is_full()){
                        auto new_b = alloc_new();
                        _bucks[m.id].insert(it.base(),new_b);
                        return new_b;
                    }
                    return b;
                }
            }

            auto new_b = alloc_new();
            _bucks[m.id].push_front(new_b);
            return new_b;

        }
    }

    size_t size()const {
        return _bucks.size();
    }

    dariadb::Time minTime()const {
        return _minTime;
    }
    dariadb::Time maxTime()const {
        return _maxTime;
    }

    size_t writed_count()const {
        return _writed_count;
    }

    bool flush(){
		std::lock_guard<std::mutex> lg(_mutex);
        for (auto kv : _bucks) {
            for(auto v:kv.second){
                if(!flush_set(v)){
                    return false;
                }
            }
        }
        clear();
        return true;
    }

    bool flush_set(tos_ptr b){
        auto arr = b->as_array();
        auto stor_res = _stor->append(arr);
        _writed_count -= arr.size();
        if (stor_res.writed != arr.size()) {
            return false;
        }
        return true;
    }

    void clear(){
        this->_bucks.clear();
        _last.clear();
    }
protected:
    size_t _max_size;

    dariadb::Time _minTime;
    dariadb::Time _maxTime;

    dict _bucks;
    dict_last   _last;
    AbstractStorage_ptr _stor;
    size_t _writed_count;
	dariadb::Time _write_window_deep;
	std::mutex _mutex;
};

Capacitor::Capacitor():_Impl(new Capacitor::Private(0,nullptr,0))
{}

Capacitor::~Capacitor()
{}

Capacitor::Capacitor(const size_t max_size, const AbstractStorage_ptr stor, const dariadb::Time write_window_deep) :
	_Impl(new Capacitor::Private(max_size,stor,write_window_deep))
{}

Capacitor::Capacitor(const Capacitor & other): _Impl(new Capacitor::Private(*other._Impl))
{}

Capacitor::Capacitor(Capacitor && other): _Impl(std::move(other._Impl))
{}

void Capacitor::swap(Capacitor & other)throw(){
    std::swap(_Impl, other._Impl);
}

Capacitor& Capacitor::operator=(const Capacitor & other){
    if (this != &other) {
        Capacitor tmp(other);
        this->swap(tmp);
    }
    return *this;
}

Capacitor& Capacitor::operator=(Capacitor && other){
    this->swap(other);
    return *this;
}

bool Capacitor::append(const Meas & m){
    return _Impl->append(m);
}

size_t Capacitor::size()const {
    return _Impl->size();
}

dariadb::Time Capacitor::minTime()const {
    return _Impl->minTime();
}

dariadb::Time Capacitor::maxTime()const {
    return _Impl->maxTime();
}

size_t Capacitor::writed_count()const {
    return _Impl->writed_count();
}

bool Capacitor::flush() {//write all to storage;
    return _Impl->flush();
}

void Capacitor::clear() {
    return _Impl->clear();
}
<|endoftext|>


<commit_before>#include "rust_internal.h"
#include "memory_region.h"

// NB: please do not commit code with this uncommented. It's
// hugely expensive and should only be used as a last resort.
//
// #define TRACK_ALLOCATIONS

memory_region::memory_region(rust_srv *srv, bool synchronized) :
    _srv(srv), _parent(NULL), _live_allocations(0),
    _detailed_leaks(getenv("RUST_DETAILED_LEAKS") != NULL),
    _synchronized(synchronized) {
}

memory_region::memory_region(memory_region *parent) :
    _srv(parent->_srv), _parent(parent), _live_allocations(0),
    _detailed_leaks(parent->_detailed_leaks),
    _synchronized(parent->_synchronized) {
    // Nop.
}

void memory_region::free(void *mem) {
    // printf("free: ptr 0x%" PRIxPTR"\n", (uintptr_t) mem);
    if (_synchronized) { _lock.lock(); }
#ifdef TRACK_ALLOCATIONS
    if (_allocation_list.replace(mem, NULL) == false) {
        printf("free: ptr 0x%" PRIxPTR " is not in allocation_list\n",
            (uintptr_t) mem);
        _srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
    }
#endif
    if (_live_allocations < 1) {
        _srv->fatal("live_allocs < 1", __FILE__, __LINE__, "");
    }
    _live_allocations--;
    _srv->free(mem);
    if (_synchronized) { _lock.unlock(); }
}

void *
memory_region::realloc(void *mem, size_t size) {
    if (_synchronized) { _lock.lock(); }
    if (!mem) {
        _live_allocations++;
    }
    void *newMem = _srv->realloc(mem, size);
#ifdef TRACK_ALLOCATIONS
    if (_allocation_list.replace(mem, newMem) == false) {
        printf("realloc: ptr 0x%" PRIxPTR " is not in allocation_list\n",
            (uintptr_t) mem);
        _srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
    }
#endif
    if (_synchronized) { _lock.unlock(); }
    return newMem;
}

void *
memory_region::malloc(size_t size) {
    if (_synchronized) { _lock.lock(); }
    _live_allocations++;
    void *mem = _srv->malloc(size);
#ifdef TRACK_ALLOCATIONS
    _allocation_list.append(mem);
#endif
    // printf("malloc: ptr 0x%" PRIxPTR "\n", (uintptr_t) mem);
    if (_synchronized) { _lock.unlock(); }
    return mem;
}

void *
memory_region::calloc(size_t size) {
    if (_synchronized) { _lock.lock(); }
    _live_allocations++;
    void *mem = _srv->malloc(size);
    memset(mem, 0, size);
#ifdef TRACK_ALLOCATIONS
    _allocation_list.append(mem);
#endif
    if (_synchronized) { _lock.unlock(); }
    return mem;
}

memory_region::~memory_region() {
    if (_live_allocations == 0) {
        return;
    }
    char msg[128];
    snprintf(msg, sizeof(msg),
             "leaked memory in rust main loop (%" PRIuPTR " objects)",
             _live_allocations);
#ifdef TRACK_ALLOCATIONS
    if (_detailed_leaks) {
        for (size_t i = 0; i < _allocation_list.size(); i++) {
            if (_allocation_list[i] != NULL) {
                printf("allocation 0x%" PRIxPTR " was not freed\n",
                       (uintptr_t) _allocation_list[i]);
            }
        }
    }
#endif
    _srv->fatal(msg, __FILE__, __LINE__, "%d objects", _live_allocations);
}

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>rt: memory_region::free(NULL) shouldn't touch the live allocation count<commit_after>#include "rust_internal.h"
#include "memory_region.h"

// NB: please do not commit code with this uncommented. It's
// hugely expensive and should only be used as a last resort.
//
// #define TRACK_ALLOCATIONS

memory_region::memory_region(rust_srv *srv, bool synchronized) :
    _srv(srv), _parent(NULL), _live_allocations(0),
    _detailed_leaks(getenv("RUST_DETAILED_LEAKS") != NULL),
    _synchronized(synchronized) {
}

memory_region::memory_region(memory_region *parent) :
    _srv(parent->_srv), _parent(parent), _live_allocations(0),
    _detailed_leaks(parent->_detailed_leaks),
    _synchronized(parent->_synchronized) {
    // Nop.
}

void memory_region::free(void *mem) {
    // printf("free: ptr 0x%" PRIxPTR"\n", (uintptr_t) mem);
    if (!mem) { return; }
    if (_synchronized) { _lock.lock(); }
#ifdef TRACK_ALLOCATIONS
    if (_allocation_list.replace(mem, NULL) == false) {
        printf("free: ptr 0x%" PRIxPTR " is not in allocation_list\n",
            (uintptr_t) mem);
        _srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
    }
#endif
    if (_live_allocations < 1) {
        _srv->fatal("live_allocs < 1", __FILE__, __LINE__, "");
    }
    _live_allocations--;
    _srv->free(mem);
    if (_synchronized) { _lock.unlock(); }
}

void *
memory_region::realloc(void *mem, size_t size) {
    if (_synchronized) { _lock.lock(); }
    if (!mem) {
        _live_allocations++;
    }
    void *newMem = _srv->realloc(mem, size);
#ifdef TRACK_ALLOCATIONS
    if (_allocation_list.replace(mem, newMem) == false) {
        printf("realloc: ptr 0x%" PRIxPTR " is not in allocation_list\n",
            (uintptr_t) mem);
        _srv->fatal("not in allocation_list", __FILE__, __LINE__, "");
    }
#endif
    if (_synchronized) { _lock.unlock(); }
    return newMem;
}

void *
memory_region::malloc(size_t size) {
    if (_synchronized) { _lock.lock(); }
    _live_allocations++;
    void *mem = _srv->malloc(size);
#ifdef TRACK_ALLOCATIONS
    _allocation_list.append(mem);
#endif
    // printf("malloc: ptr 0x%" PRIxPTR "\n", (uintptr_t) mem);
    if (_synchronized) { _lock.unlock(); }
    return mem;
}

void *
memory_region::calloc(size_t size) {
    if (_synchronized) { _lock.lock(); }
    _live_allocations++;
    void *mem = _srv->malloc(size);
    memset(mem, 0, size);
#ifdef TRACK_ALLOCATIONS
    _allocation_list.append(mem);
#endif
    if (_synchronized) { _lock.unlock(); }
    return mem;
}

memory_region::~memory_region() {
    if (_live_allocations == 0) {
        return;
    }
    char msg[128];
    snprintf(msg, sizeof(msg),
             "leaked memory in rust main loop (%" PRIuPTR " objects)",
             _live_allocations);
#ifdef TRACK_ALLOCATIONS
    if (_detailed_leaks) {
        for (size_t i = 0; i < _allocation_list.size(); i++) {
            if (_allocation_list[i] != NULL) {
                printf("allocation 0x%" PRIxPTR " was not freed\n",
                       (uintptr_t) _allocation_list[i]);
            }
        }
    }
#endif
    _srv->fatal(msg, __FILE__, __LINE__, "%d objects", _live_allocations);
}

//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Axel Isouard <axel@isouard.fr>
 *
 * 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 <cstring>
#include <memory>
#include <iostream>
#include <webrtc/api/test/fakeconstraints.h>
#include "common.h"
#include "globals.h"
#include "rtccertificate.h"
#include "rtccreatesessiondescriptionobserver.h"
#include "rtcpeerconnection.h"
#include "rtcpeerconnectionobserver.h"

Nan::Persistent<FunctionTemplate> RTCPeerConnection::constructor;

static const char sRTCPeerConnection[] = "RTCPeerConnection";

static const char kCreateOffer[] = "createOffer";
static const char kGenerateCertificate[] = "generateCertificate";

static const char kName[] = "name";
static const char kRSA[] = "RSASSA-PKCS1-v1_5";
static const char kHash[] = "hash";
static const char kSHA256[] = "SHA-256";
static const char kModulusLength[] = "modulusLength";
static const char kPublicExponent[] = "publicExponent";
static const char kECDSA[] = "ECDSA";
static const char kNamedCurve[] = "namedCurve";
static const char kP256[] = "P-256";

static const char kConnectionState[] = "connectionState";
static const char kCurrentLocalDescription[] = "currentLocalDescription";
static const char kCurrentRemoteDescription[] = "currentRemoteDescription";
static const char kIceConnectionState[] = "iceConnectionState";
static const char kIceGatheringState[] = "iceGatheringState";
static const char kPendingLocalDescription[] = "pendingLocalDescription";
static const char kPendingRemoteDescription[] = "pendingRemoteDescription";
static const char kSignalingState[] = "signalingState";

static const char kNew[] = "new";
static const char kChecking[] = "checking";
static const char kConnected[] = "connected";
static const char kCompleted[] = "completed";
static const char kFailed[] = "failed";
static const char kDisconnected[] = "disconnected";
static const char kClosed[] = "closed";
static const char kUnknown[] = "unknown";

static const char kStable[] = "stable";
static const char kHaveLocalOffer[] = "have-local-offer";
static const char kHaveLocalPranswer[] = "have-local-pranswer";
static const char kHaveRemoteOffer[] = "have-remote-offer";
static const char kHaveRemotePranswer[] = "have-remote-pranswer";

static const char kIceRestart[] = "iceRestart";

static const char eCurve[] = "EcKeyGenParams: Unrecognized namedCurve";
static const char eHash[] = "Algorithm: Unrecognized hash";
static const char eName[] = "Algorithm: Unrecognized name";
static const char eUnsupported[] = "The 1st argument provided is an "
    "AlgorithmIdentifier with a supported algorithm name, but the parameters "
    "are not supported.";

static const char eFailure[] = "Failed to generate the certificate.";

NAN_MODULE_INIT(RTCPeerConnection::Init) {
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->SetClassName(LOCAL_STRING(sRTCPeerConnection));
  ctor->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetMethod(ctor, kGenerateCertificate, GenerateCertificate);

  Local<ObjectTemplate> prototype = ctor->InstanceTemplate();
  Nan::SetMethod(prototype, kCreateOffer, CreateOffer);

  Local<ObjectTemplate> tpl = ctor->InstanceTemplate();
  Nan::SetAccessor(tpl, LOCAL_STRING(kConnectionState),
                   GetConnectionState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kCurrentLocalDescription),
                   GetCurrentLocalDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kCurrentRemoteDescription),
                   GetCurrentRemoteDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kIceConnectionState),
                   GetIceConnectionState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kIceGatheringState),
                   GetIceGatheringState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kPendingLocalDescription),
                   GetPendingLocalDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kPendingRemoteDescription),
                   GetPendingRemoteDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kSignalingState),
                   GetSignalingState);

  constructor.Reset(ctor);
  Nan::Set(target, LOCAL_STRING(sRTCPeerConnection), ctor->GetFunction());
}

RTCPeerConnection::RTCPeerConnection(
    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection) :
    _peerConnection(peerConnection) {
}

RTCPeerConnection::~RTCPeerConnection() {
  _peerConnection = NULL;
}

NAN_METHOD(RTCPeerConnection::New) {
  webrtc::FakeConstraints constraints;
  webrtc::PeerConnectionInterface::RTCConfiguration config;
  webrtc::PeerConnectionInterface::IceServer server;
  server.uri = "stun:stun.l.google.com:19302";
  config.servers.push_back(server);

  constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
                          "true");

  webrtc::PeerConnectionObserver *obs = new RTCPeerConnectionObserver();

  rtc::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection =
      Globals::GetPeerConnectionFactory()->CreatePeerConnection(
          config, &constraints, NULL, NULL, obs);

  RTCPeerConnection *rtcPeerConnection = new RTCPeerConnection(peerConnection);
  rtcPeerConnection->Wrap(info.This());

  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(RTCPeerConnection::CreateOffer) {
  METHOD_HEADER("RTCPeerConnection", "createOffer");
  UNWRAP_OBJECT(RTCPeerConnection, object);

  bool iceRestart = false;
  unsigned char start = 0;
  rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;

  if (info.Length() < 2) {
    DECLARE_PROMISE_RESOLVER;

    if (info.Length() == 1) {
      ASSERT_REJECT_OBJECT_ARGUMENT(0, options);
      DECLARE_OBJECT_PROPERTY(options, kIceRestart, iceRestartVal);
      iceRestart = iceRestartVal->ToBoolean()->BooleanValue();
    }

    observer = RTCCreateSessionDescriptionObserver::Create(
        new Nan::Persistent<Promise::Resolver>(resolver));
  } else if (info.Length() > 1) {
    if (info.Length() > 2) {
      start = 1;

      ASSERT_OBJECT_ARGUMENT(0, options);
      DECLARE_OBJECT_PROPERTY(options, kIceRestart, iceRestartVal);
      iceRestart = iceRestartVal->ToBoolean()->BooleanValue();
    }

    ASSERT_FUNCTION_ARGUMENT(start, successCallback);
    ASSERT_FUNCTION_ARGUMENT(start + 1, failureCallback);

    observer = RTCCreateSessionDescriptionObserver::Create(
        new Nan::Persistent<Function>(successCallback),
        new Nan::Persistent<Function>(failureCallback));
  }

  object->_peerConnection->CreateOffer(observer, NULL);
}

NAN_GETTER(RTCPeerConnection::GetConnectionState) {
  info.GetReturnValue().Set(LOCAL_STRING("new"));
}

NAN_GETTER(RTCPeerConnection::GetCurrentLocalDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetCurrentRemoteDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetIceConnectionState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string iceConnectionState;
  webrtc::PeerConnectionInterface::IceConnectionState state =
      object->_peerConnection->ice_connection_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kIceConnectionNew:
      iceConnectionState = kNew;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionChecking:
      iceConnectionState = kChecking;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionConnected:
      iceConnectionState = kConnected;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
      iceConnectionState = kCompleted;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionFailed:
      iceConnectionState = kFailed;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
      iceConnectionState = kDisconnected;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionClosed:
      iceConnectionState = kClosed;
      break;

    default:
      iceConnectionState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(iceConnectionState));
}

NAN_GETTER(RTCPeerConnection::GetIceGatheringState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string iceGatheringState;
  webrtc::PeerConnectionInterface::IceGatheringState state =
      object->_peerConnection->ice_gathering_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kIceGatheringNew:
      iceGatheringState = kNew;
      break;

    case webrtc::PeerConnectionInterface::kIceGatheringGathering:
      iceGatheringState = kChecking;
      break;

    case webrtc::PeerConnectionInterface::kIceGatheringComplete:
      iceGatheringState = kConnected;
      break;

    default:
      iceGatheringState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(iceGatheringState));
}

NAN_GETTER(RTCPeerConnection::GetPendingLocalDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetPendingRemoteDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetSignalingState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string signalingState;
  webrtc::PeerConnectionInterface::SignalingState state =
      object->_peerConnection->signaling_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kStable:
      signalingState = kStable;
      break;

    case webrtc::PeerConnectionInterface::kHaveLocalOffer:
      signalingState = kHaveLocalOffer;
      break;

    case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer:
      signalingState = kHaveLocalPranswer;
      break;

    case webrtc::PeerConnectionInterface::kHaveRemoteOffer:
      signalingState = kHaveRemoteOffer;
      break;

    case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer:
      signalingState = kHaveRemotePranswer;
      break;

    default:
      signalingState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(signalingState));
}

RTCPeerConnection::GenerateCertificateWorker::GenerateCertificateWorker
    (Persistent<Promise::Resolver> *resolver, const rtc::KeyParams& params)
    : Nan::AsyncWorker(nullptr), _params(params), _resolver(resolver) {
}

void RTCPeerConnection::GenerateCertificateWorker::Execute() {
  rtc::RTCCertificateGenerator *generator = Globals::GetCertificateGenerator();

  _certificate = generator->GenerateCertificate(_params,
                                                rtc::Optional<uint64_t>());
}

void RTCPeerConnection::GenerateCertificateWorker::WorkComplete() {
  Nan::HandleScope scope;
  Local<Promise::Resolver> resolver = Nan::New(*_resolver);

  if (!_certificate.get()) {
    std::stringstream errorStream;

    errorStream << eFailure;
    resolver->Reject(Nan::TypeError(errorStream.str().c_str()));
  } else {
    resolver->Resolve(RTCCertificate::Create(_certificate));
  }

  Isolate::GetCurrent()->RunMicrotasks();
  _resolver->Reset();
  delete _resolver;
}

NAN_METHOD(RTCPeerConnection::GenerateCertificate) {
  METHOD_HEADER("RTCPeerConnection", "generateCertificate");
  DECLARE_PROMISE_RESOLVER;

  ASSERT_REJECT_SINGLE_ARGUMENT;
  ASSERT_REJECT_OBJECT_ARGUMENT(0, keygenAlgorithm);
  ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kName, nameVal);
  ASSERT_REJECT_PROPERTY_STRING(kName, nameVal, name);

  rtc::KeyParams keyParams;
  if (!stricmp(kECDSA, (*name))) {
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kNamedCurve, namedCurveVal);
    ASSERT_REJECT_PROPERTY_STRING(kNamedCurve, namedCurveVal, namedCurve);

    if (stricmp(kP256, *namedCurve)) {
      errorStream << eCurve;
      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    keyParams = rtc::KeyParams::ECDSA(rtc::ECCurve::EC_NIST_P256);
  } else if (!stricmp(kRSA, (*name))) {
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kHash, hashVal);
    ASSERT_REJECT_PROPERTY_STRING(kHash, hashVal, hash);

    if (stricmp(kSHA256, *hash)) {
      errorStream << eHash;
      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kModulusLength,
                                  modulusLengthVal);
    ASSERT_REJECT_PROPERTY_NUMBER(kModulusLength, modulusLengthVal);
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kPublicExponent,
                                  publicExponentVal);
    ASSERT_REJECT_PROPERTY_UINT8ARRAY(kPublicExponent, publicExponentVal,
                                      publicExponentArray);

    int modulus = modulusLengthVal->Int32Value();
    if (modulus < rtc::kRsaMinModSize || modulus > rtc::kRsaMaxModSize) {
      errorStream << eUnsupported;

      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    size_t publicExponentLength = publicExponentArray->Length();
    if (publicExponentLength < 1 || publicExponentLength > 4) {
      errorStream << eUnsupported;

      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    uint8_t publicExponent[4] = { 0, 0, 0, 0 };
    for (uint32_t i = 0; i < publicExponentLength; ++i) {
      publicExponent[i] =
          static_cast<uint8_t>(publicExponentArray->Get(i)->Uint32Value());
    }

    uint32_t publicExponentValue = (publicExponent[3] << 24)
                                   + (publicExponent[2] << 16)
                                   + (publicExponent[1] << 8)
                                   + publicExponent[0];

    keyParams = rtc::KeyParams::RSA(modulus, publicExponentValue);
  } else {
    errorStream << eName;
    resolver->Reject(Nan::GetCurrentContext(),
                     Nan::Error(errorStream.str().c_str()));
    return;
  }

  Nan::AsyncQueueWorker(new GenerateCertificateWorker(
      new Nan::Persistent<Promise::Resolver>(resolver), keyParams));
}
<commit_msg>CreateOffer: Set `iceRestart` MediaConstraint<commit_after>/*
 * Copyright (c) 2017 Axel Isouard <axel@isouard.fr>
 *
 * 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 <cstring>
#include <memory>
#include <iostream>
#include <webrtc/api/test/fakeconstraints.h>
#include "common.h"
#include "globals.h"
#include "rtccertificate.h"
#include "rtccreatesessiondescriptionobserver.h"
#include "rtcpeerconnection.h"
#include "rtcpeerconnectionobserver.h"

Nan::Persistent<FunctionTemplate> RTCPeerConnection::constructor;

static const char sRTCPeerConnection[] = "RTCPeerConnection";

static const char kCreateOffer[] = "createOffer";
static const char kGenerateCertificate[] = "generateCertificate";

static const char kName[] = "name";
static const char kRSA[] = "RSASSA-PKCS1-v1_5";
static const char kHash[] = "hash";
static const char kSHA256[] = "SHA-256";
static const char kModulusLength[] = "modulusLength";
static const char kPublicExponent[] = "publicExponent";
static const char kECDSA[] = "ECDSA";
static const char kNamedCurve[] = "namedCurve";
static const char kP256[] = "P-256";

static const char kConnectionState[] = "connectionState";
static const char kCurrentLocalDescription[] = "currentLocalDescription";
static const char kCurrentRemoteDescription[] = "currentRemoteDescription";
static const char kIceConnectionState[] = "iceConnectionState";
static const char kIceGatheringState[] = "iceGatheringState";
static const char kPendingLocalDescription[] = "pendingLocalDescription";
static const char kPendingRemoteDescription[] = "pendingRemoteDescription";
static const char kSignalingState[] = "signalingState";

static const char kNew[] = "new";
static const char kChecking[] = "checking";
static const char kConnected[] = "connected";
static const char kCompleted[] = "completed";
static const char kFailed[] = "failed";
static const char kDisconnected[] = "disconnected";
static const char kClosed[] = "closed";
static const char kUnknown[] = "unknown";

static const char kStable[] = "stable";
static const char kHaveLocalOffer[] = "have-local-offer";
static const char kHaveLocalPranswer[] = "have-local-pranswer";
static const char kHaveRemoteOffer[] = "have-remote-offer";
static const char kHaveRemotePranswer[] = "have-remote-pranswer";

static const char kIceRestart[] = "iceRestart";

static const char eCurve[] = "EcKeyGenParams: Unrecognized namedCurve";
static const char eHash[] = "Algorithm: Unrecognized hash";
static const char eName[] = "Algorithm: Unrecognized name";
static const char eUnsupported[] = "The 1st argument provided is an "
    "AlgorithmIdentifier with a supported algorithm name, but the parameters "
    "are not supported.";

static const char eFailure[] = "Failed to generate the certificate.";

NAN_MODULE_INIT(RTCPeerConnection::Init) {
  Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(New);
  ctor->SetClassName(LOCAL_STRING(sRTCPeerConnection));
  ctor->InstanceTemplate()->SetInternalFieldCount(1);

  Nan::SetMethod(ctor, kGenerateCertificate, GenerateCertificate);

  Local<ObjectTemplate> prototype = ctor->InstanceTemplate();
  Nan::SetMethod(prototype, kCreateOffer, CreateOffer);

  Local<ObjectTemplate> tpl = ctor->InstanceTemplate();
  Nan::SetAccessor(tpl, LOCAL_STRING(kConnectionState),
                   GetConnectionState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kCurrentLocalDescription),
                   GetCurrentLocalDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kCurrentRemoteDescription),
                   GetCurrentRemoteDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kIceConnectionState),
                   GetIceConnectionState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kIceGatheringState),
                   GetIceGatheringState);
  Nan::SetAccessor(tpl, LOCAL_STRING(kPendingLocalDescription),
                   GetPendingLocalDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kPendingRemoteDescription),
                   GetPendingRemoteDescription);
  Nan::SetAccessor(tpl, LOCAL_STRING(kSignalingState),
                   GetSignalingState);

  constructor.Reset(ctor);
  Nan::Set(target, LOCAL_STRING(sRTCPeerConnection), ctor->GetFunction());
}

RTCPeerConnection::RTCPeerConnection(
    rtc::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection) :
    _peerConnection(peerConnection) {
}

RTCPeerConnection::~RTCPeerConnection() {
  _peerConnection = NULL;
}

NAN_METHOD(RTCPeerConnection::New) {
  webrtc::FakeConstraints constraints;
  webrtc::PeerConnectionInterface::RTCConfiguration config;
  webrtc::PeerConnectionInterface::IceServer server;
  server.uri = "stun:stun.l.google.com:19302";
  config.servers.push_back(server);

  constraints.AddOptional(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
                          "true");

  webrtc::PeerConnectionObserver *obs = new RTCPeerConnectionObserver();

  rtc::scoped_refptr<webrtc::PeerConnectionInterface> peerConnection =
      Globals::GetPeerConnectionFactory()->CreatePeerConnection(
          config, &constraints, NULL, NULL, obs);

  RTCPeerConnection *rtcPeerConnection = new RTCPeerConnection(peerConnection);
  rtcPeerConnection->Wrap(info.This());

  info.GetReturnValue().Set(info.This());
}

NAN_METHOD(RTCPeerConnection::CreateOffer) {
  METHOD_HEADER("RTCPeerConnection", "createOffer");
  UNWRAP_OBJECT(RTCPeerConnection, object);

  bool iceRestart = false;
  unsigned char start = 0;
  rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;

  if (info.Length() < 2) {
    DECLARE_PROMISE_RESOLVER;

    if (info.Length() == 1) {
      ASSERT_REJECT_OBJECT_ARGUMENT(0, options);
      DECLARE_OBJECT_PROPERTY(options, kIceRestart, iceRestartVal);
      iceRestart = iceRestartVal->ToBoolean()->BooleanValue();
    }

    observer = RTCCreateSessionDescriptionObserver::Create(
        new Nan::Persistent<Promise::Resolver>(resolver));
  } else if (info.Length() > 1) {
    if (info.Length() > 2) {
      start = 1;

      ASSERT_OBJECT_ARGUMENT(0, options);
      DECLARE_OBJECT_PROPERTY(options, kIceRestart, iceRestartVal);
      iceRestart = iceRestartVal->ToBoolean()->BooleanValue();
    }

    ASSERT_FUNCTION_ARGUMENT(start, successCallback);
    ASSERT_FUNCTION_ARGUMENT(start + 1, failureCallback);

    observer = RTCCreateSessionDescriptionObserver::Create(
        new Nan::Persistent<Function>(successCallback),
        new Nan::Persistent<Function>(failureCallback));
  }

  webrtc::FakeConstraints constraints;
  constraints.AddOptional(webrtc::MediaConstraintsInterface::kIceRestart,
                          iceRestart);

  object->_peerConnection->CreateOffer(observer, &constraints);
}

NAN_GETTER(RTCPeerConnection::GetConnectionState) {
  info.GetReturnValue().Set(LOCAL_STRING("new"));
}

NAN_GETTER(RTCPeerConnection::GetCurrentLocalDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetCurrentRemoteDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetIceConnectionState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string iceConnectionState;
  webrtc::PeerConnectionInterface::IceConnectionState state =
      object->_peerConnection->ice_connection_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kIceConnectionNew:
      iceConnectionState = kNew;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionChecking:
      iceConnectionState = kChecking;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionConnected:
      iceConnectionState = kConnected;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionCompleted:
      iceConnectionState = kCompleted;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionFailed:
      iceConnectionState = kFailed;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionDisconnected:
      iceConnectionState = kDisconnected;
      break;

    case webrtc::PeerConnectionInterface::kIceConnectionClosed:
      iceConnectionState = kClosed;
      break;

    default:
      iceConnectionState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(iceConnectionState));
}

NAN_GETTER(RTCPeerConnection::GetIceGatheringState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string iceGatheringState;
  webrtc::PeerConnectionInterface::IceGatheringState state =
      object->_peerConnection->ice_gathering_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kIceGatheringNew:
      iceGatheringState = kNew;
      break;

    case webrtc::PeerConnectionInterface::kIceGatheringGathering:
      iceGatheringState = kChecking;
      break;

    case webrtc::PeerConnectionInterface::kIceGatheringComplete:
      iceGatheringState = kConnected;
      break;

    default:
      iceGatheringState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(iceGatheringState));
}

NAN_GETTER(RTCPeerConnection::GetPendingLocalDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetPendingRemoteDescription) {
  info.GetReturnValue().Set(Nan::Null());
}

NAN_GETTER(RTCPeerConnection::GetSignalingState) {
  UNWRAP_OBJECT(RTCPeerConnection, object);

  std::string signalingState;
  webrtc::PeerConnectionInterface::SignalingState state =
      object->_peerConnection->signaling_state();

  switch (state) {
    case webrtc::PeerConnectionInterface::kStable:
      signalingState = kStable;
      break;

    case webrtc::PeerConnectionInterface::kHaveLocalOffer:
      signalingState = kHaveLocalOffer;
      break;

    case webrtc::PeerConnectionInterface::kHaveLocalPrAnswer:
      signalingState = kHaveLocalPranswer;
      break;

    case webrtc::PeerConnectionInterface::kHaveRemoteOffer:
      signalingState = kHaveRemoteOffer;
      break;

    case webrtc::PeerConnectionInterface::kHaveRemotePrAnswer:
      signalingState = kHaveRemotePranswer;
      break;

    default:
      signalingState = kUnknown;
      break;
  }

  info.GetReturnValue().Set(LOCAL_STRING(signalingState));
}

RTCPeerConnection::GenerateCertificateWorker::GenerateCertificateWorker
    (Persistent<Promise::Resolver> *resolver, const rtc::KeyParams& params)
    : Nan::AsyncWorker(nullptr), _params(params), _resolver(resolver) {
}

void RTCPeerConnection::GenerateCertificateWorker::Execute() {
  rtc::RTCCertificateGenerator *generator = Globals::GetCertificateGenerator();

  _certificate = generator->GenerateCertificate(_params,
                                                rtc::Optional<uint64_t>());
}

void RTCPeerConnection::GenerateCertificateWorker::WorkComplete() {
  Nan::HandleScope scope;
  Local<Promise::Resolver> resolver = Nan::New(*_resolver);

  if (!_certificate.get()) {
    std::stringstream errorStream;

    errorStream << eFailure;
    resolver->Reject(Nan::TypeError(errorStream.str().c_str()));
  } else {
    resolver->Resolve(RTCCertificate::Create(_certificate));
  }

  Isolate::GetCurrent()->RunMicrotasks();
  _resolver->Reset();
  delete _resolver;
}

NAN_METHOD(RTCPeerConnection::GenerateCertificate) {
  METHOD_HEADER("RTCPeerConnection", "generateCertificate");
  DECLARE_PROMISE_RESOLVER;

  ASSERT_REJECT_SINGLE_ARGUMENT;
  ASSERT_REJECT_OBJECT_ARGUMENT(0, keygenAlgorithm);
  ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kName, nameVal);
  ASSERT_REJECT_PROPERTY_STRING(kName, nameVal, name);

  rtc::KeyParams keyParams;
  if (!stricmp(kECDSA, (*name))) {
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kNamedCurve, namedCurveVal);
    ASSERT_REJECT_PROPERTY_STRING(kNamedCurve, namedCurveVal, namedCurve);

    if (stricmp(kP256, *namedCurve)) {
      errorStream << eCurve;
      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    keyParams = rtc::KeyParams::ECDSA(rtc::ECCurve::EC_NIST_P256);
  } else if (!stricmp(kRSA, (*name))) {
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kHash, hashVal);
    ASSERT_REJECT_PROPERTY_STRING(kHash, hashVal, hash);

    if (stricmp(kSHA256, *hash)) {
      errorStream << eHash;
      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kModulusLength,
                                  modulusLengthVal);
    ASSERT_REJECT_PROPERTY_NUMBER(kModulusLength, modulusLengthVal);
    ASSERT_REJECT_OBJECT_PROPERTY(keygenAlgorithm, kPublicExponent,
                                  publicExponentVal);
    ASSERT_REJECT_PROPERTY_UINT8ARRAY(kPublicExponent, publicExponentVal,
                                      publicExponentArray);

    int modulus = modulusLengthVal->Int32Value();
    if (modulus < rtc::kRsaMinModSize || modulus > rtc::kRsaMaxModSize) {
      errorStream << eUnsupported;

      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    size_t publicExponentLength = publicExponentArray->Length();
    if (publicExponentLength < 1 || publicExponentLength > 4) {
      errorStream << eUnsupported;

      resolver->Reject(Nan::GetCurrentContext(),
                       Nan::Error(errorStream.str().c_str()));
      return;
    }

    uint8_t publicExponent[4] = { 0, 0, 0, 0 };
    for (uint32_t i = 0; i < publicExponentLength; ++i) {
      publicExponent[i] =
          static_cast<uint8_t>(publicExponentArray->Get(i)->Uint32Value());
    }

    uint32_t publicExponentValue = (publicExponent[3] << 24)
                                   + (publicExponent[2] << 16)
                                   + (publicExponent[1] << 8)
                                   + publicExponent[0];

    keyParams = rtc::KeyParams::RSA(modulus, publicExponentValue);
  } else {
    errorStream << eName;
    resolver->Reject(Nan::GetCurrentContext(),
                     Nan::Error(errorStream.str().c_str()));
    return;
  }

  Nan::AsyncQueueWorker(new GenerateCertificateWorker(
      new Nan::Persistent<Promise::Resolver>(resolver), keyParams));
}
<|endoftext|>


<commit_before>#include "runner/at_handler.hh"

#include <iostream>
#include <list>

#include "scheduler/tag.hh"

namespace runner
{

  struct AtJob
  {
    AtJob(rObject condition, rObject clause, rObject on_leave,
	  scheduler::tags_type tags) :
      condition_(condition),
      clause_(clause),
      on_leave_(on_leave),
      triggered_(false),
      tags_(tags)
      {
      }
    rObject condition_;
    rObject clause_;
    rObject on_leave_;
    bool triggered_;
    scheduler::tags_type tags_;
  };

  class AtHandler : public Runner
  {
  public:
    AtHandler(const Runner&);
    virtual ~AtHandler();
    virtual void work();
    virtual bool frozen() const;
    virtual bool blocked() const;
    void add_job(AtJob*);
  private:
    void complete_tags(const AtJob&);
    void rebuild_tags();
    std::list<AtJob*> jobs_;
    bool yielding;
    scheduler::tags_type tags_;
  };

  // There is only one at job handler. It will be created if it doesn't exist
  // and destroyed when it has no more jobs to handle.
  static AtHandler* at_job_handler;

  AtHandler::AtHandler(const Runner& model) :
    Runner(model, 0),
    yielding(false)
  {
  }

  AtHandler::~AtHandler()
  {
  }

  void
  AtHandler::work()
  {
    bool check_for_blocked = true;
    side_effect_free_set(true);

    while (true)
    {
      non_interruptible_set(true);
      yielding = false;

      // We have been woken up, either because we may have something to do
      // or because some tag conditions have changed.

      std::list<AtJob*> pending;
      swap(jobs_, pending);

      foreach (AtJob* job, pending)
      {
	bool blocked = false;
	bool frozen = false;
	foreach (const scheduler::rTag& tag, job->tags_)
	{
	  // Checking for a blocked state may be costly, so skip it when we
	  // know for sure that none of our at jobs can be blocked.
	  if (check_for_blocked)
	    blocked |= tag->blocked();
	  frozen  |= tag->frozen();
	}

	// If job has been blocked or stopped, we will not keep it any longer.
	if (blocked)
	{
	  delete job;
	  continue;
	}

	// If job has been frozen, we will not consider it for the moment.
	if (frozen)
	{
	  jobs_.push_back(job);
	  continue;
	}

	// Check the job condition and continue if it has not changed.
	bool new_state;
	try
	{
	  new_state =
	    object::is_true(urbi_call(*this, job->condition_, SYMBOL(eval)));
	}
	catch (const kernel::exception& ke)
	{
	  std::cerr << "at condition triggered an exception: " << ke.what()
		    << std::endl;
	  delete job;
	  continue;
	}
	catch (...)
	{
	  std::cerr << "at condition triggered an exception\n";
	  delete job;
	  continue;
	}
	if (new_state == job->triggered_)
	{
	  jobs_.push_back(job);
	  continue;
	}

	// There has been a change in the condition, act accordingly depending
	// on whether we have seen a rising or a falling edge and save the
	// condition evaluation result.
	rObject to_launch = new_state ? job->clause_ : job->on_leave_;
	if (to_launch != object::nil_class)
	{
	  // Temporarily install the needed tags as the current tags.
	  tags_set(job->tags_);
	  // We do not need to check for an exception here as "detach",
	  // which is the function being called, will not throw and any
	  // exception thrown in the detached runner will not be caught
	  // here anyway.
	  urbi_call(*this, to_launch, SYMBOL(eval));
	}
	job->triggered_ = new_state;
	jobs_.push_back(job);
      }

      // If we have no more jobs, we can destroy ourselves.
      if (jobs_.empty())
      {
	at_job_handler = 0;
	terminate_now();
      }

      non_interruptible_set(false);
      check_for_blocked = false;
      yielding = true;

      // Go to sleep
      try
      {
	// We want to appear blocked only when explicitly yielding and
	// catching the exception. If, by mistake, a condition evaluation
	// yields and is blocked, we do not want it to get the bogus
        // exception.
	yield_until_things_changed();
      }
      catch (const scheduler::BlockedException& e)
      {
	// We have at least one "at" job which needs to be blocked.
	check_for_blocked = true;
      }
      catch (const kernel::exception& e)
      {
	std::cerr << "at job handler exited with exception " << e.what()
		  << std::endl;
	throw;
      }
      catch (...)
      {
	std::cerr << "at job handler exited with unknown exception\n";
	throw;
      }
    }
  }

  bool
  AtHandler::frozen() const
  {
    return false;
  }

  bool
  AtHandler::blocked() const
  {
    if (!yielding)
      return false;
    foreach (const scheduler::rTag& t, tags_)
      if (t->blocked())
	return true;
    return false;
  }

  void
  AtHandler::add_job(AtJob* job)
  {
    jobs_.push_back(job);
    complete_tags(*job);
  }

  void
  AtHandler::rebuild_tags()
  {
    tags_.clear();
    foreach (const AtJob* job, jobs_)
      complete_tags(*job);
  }

  void
  AtHandler::complete_tags(const AtJob& job)
  {
    foreach (scheduler::rTag t, job.tags_)
    {
      foreach (const scheduler::rTag& u, tags_)
	if (t == u)
	  goto already_found;
      tags_.push_back(t);
    already_found:
      ;
    }
  }

  void
  register_at_job(const runner::Runner& starter,
		  rObject condition,
		  rObject clause,
		  rObject on_leave)
  {
    if (!at_job_handler)
    {
      at_job_handler = new AtHandler(starter);
      at_job_handler->start_job();
    }
    AtJob* job = new AtJob(condition,
			   clause,
			   on_leave,
			   starter.tags_get());
    at_job_handler->add_job(job);
  }

} // namespace runner
<commit_msg>If at jobs have been removed from the handler, rebuild tags.<commit_after>#include "runner/at_handler.hh"

#include <iostream>
#include <list>

#include "scheduler/tag.hh"

namespace runner
{

  struct AtJob
  {
    AtJob(rObject condition, rObject clause, rObject on_leave,
	  scheduler::tags_type tags) :
      condition_(condition),
      clause_(clause),
      on_leave_(on_leave),
      triggered_(false),
      tags_(tags)
      {
      }
    rObject condition_;
    rObject clause_;
    rObject on_leave_;
    bool triggered_;
    scheduler::tags_type tags_;
  };

  class AtHandler : public Runner
  {
  public:
    AtHandler(const Runner&);
    virtual ~AtHandler();
    virtual void work();
    virtual bool frozen() const;
    virtual bool blocked() const;
    void add_job(AtJob*);
  private:
    void complete_tags(const AtJob&);
    void rebuild_tags();
    std::list<AtJob*> jobs_;
    bool yielding;
    scheduler::tags_type tags_;
  };

  // There is only one at job handler. It will be created if it doesn't exist
  // and destroyed when it has no more jobs to handle.
  static AtHandler* at_job_handler;

  AtHandler::AtHandler(const Runner& model) :
    Runner(model, 0),
    yielding(false)
  {
  }

  AtHandler::~AtHandler()
  {
  }

  void
  AtHandler::work()
  {
    bool check_for_blocked = true;
    bool tags_need_rebuilding;
    side_effect_free_set(true);

    while (true)
    {
      non_interruptible_set(true);
      yielding = false;
      tags_need_rebuilding = false;

      // We have been woken up, either because we may have something to do
      // or because some tag conditions have changed.

      std::list<AtJob*> pending;
      swap(jobs_, pending);

      foreach (AtJob* job, pending)
      {
	bool blocked = false;
	bool frozen = false;
	foreach (const scheduler::rTag& tag, job->tags_)
	{
	  // Checking for a blocked state may be costly, so skip it when we
	  // know for sure that none of our at jobs can be blocked.
	  if (check_for_blocked)
	    blocked |= tag->blocked();
	  frozen  |= tag->frozen();
	}

	// If job has been blocked or stopped, we will not keep it any longer.
	if (blocked)
	{
	  tags_need_rebuilding = true;
	  delete job;
	  continue;
	}

	// If job has been frozen, we will not consider it for the moment.
	if (frozen)
	{
	  jobs_.push_back(job);
	  continue;
	}

	// Check the job condition and continue if it has not changed.
	bool new_state;
	try
	{
	  new_state =
	    object::is_true(urbi_call(*this, job->condition_, SYMBOL(eval)));
	}
	catch (const kernel::exception& ke)
	{
	  std::cerr << "at condition triggered an exception: " << ke.what()
		    << std::endl;
	  tags_need_rebuilding = true;
	  delete job;
	  continue;
	}
	catch (...)
	{
	  std::cerr << "at condition triggered an exception\n";
	  delete job;
	  continue;
	}
	if (new_state == job->triggered_)
	{
	  jobs_.push_back(job);
	  continue;
	}

	// There has been a change in the condition, act accordingly depending
	// on whether we have seen a rising or a falling edge and save the
	// condition evaluation result.
	rObject to_launch = new_state ? job->clause_ : job->on_leave_;
	if (to_launch != object::nil_class)
	{
	  // Temporarily install the needed tags as the current tags.
	  tags_set(job->tags_);
	  // We do not need to check for an exception here as "detach",
	  // which is the function being called, will not throw and any
	  // exception thrown in the detached runner will not be caught
	  // here anyway.
	  urbi_call(*this, to_launch, SYMBOL(eval));
	}
	job->triggered_ = new_state;
	jobs_.push_back(job);
      }

      // If we have no more jobs, we can destroy ourselves.
      if (jobs_.empty())
      {
	at_job_handler = 0;
	terminate_now();
      }

      non_interruptible_set(false);
      check_for_blocked = false;
      yielding = true;

      // Rebuild tags if our list of jobs has changed.
      if (tags_need_rebuilding)
	rebuild_tags();

      // Go to sleep
      try
      {
	// We want to appear blocked only when explicitly yielding and
	// catching the exception. If, by mistake, a condition evaluation
	// yields and is blocked, we do not want it to get the bogus
        // exception.
	yield_until_things_changed();
      }
      catch (const scheduler::BlockedException& e)
      {
	// We have at least one "at" job which needs to be blocked.
	check_for_blocked = true;
      }
      catch (const kernel::exception& e)
      {
	std::cerr << "at job handler exited with exception " << e.what()
		  << std::endl;
	throw;
      }
      catch (...)
      {
	std::cerr << "at job handler exited with unknown exception\n";
	throw;
      }
    }
  }

  bool
  AtHandler::frozen() const
  {
    return false;
  }

  bool
  AtHandler::blocked() const
  {
    if (!yielding)
      return false;
    foreach (const scheduler::rTag& t, tags_)
      if (t->blocked())
	return true;
    return false;
  }

  void
  AtHandler::add_job(AtJob* job)
  {
    jobs_.push_back(job);
    complete_tags(*job);
  }

  void
  AtHandler::rebuild_tags()
  {
    tags_.clear();
    foreach (const AtJob* job, jobs_)
      complete_tags(*job);
  }

  void
  AtHandler::complete_tags(const AtJob& job)
  {
    foreach (scheduler::rTag t, job.tags_)
    {
      foreach (const scheduler::rTag& u, tags_)
	if (t == u)
	  goto already_found;
      tags_.push_back(t);
    already_found:
      ;
    }
  }

  void
  register_at_job(const runner::Runner& starter,
		  rObject condition,
		  rObject clause,
		  rObject on_leave)
  {
    if (!at_job_handler)
    {
      at_job_handler = new AtHandler(starter);
      at_job_handler->start_job();
    }
    AtJob* job = new AtJob(condition,
			   clause,
			   on_leave,
			   starter.tags_get());
    at_job_handler->add_job(job);
  }

} // namespace runner
<|endoftext|>


<commit_before>#ifdef COMPILING_HALIDE_RUNTIME
#include "HalideRuntime.h"
#define HALIDE_BUFFER_HELPER_ATTRS __attribute__((always_inline, weak))
#else
#define HALIDE_BUFFER_HELPER_ATTRS inline
#endif

// Structs are annoying to deal with from within Halide Stmts. These
// utility functions are for dealing with buffer_t in that
// context. They are not intended for use outside of Halide code, and
// not exposed in HalideRuntime.h. The symbols are private to the
// module and should be inlined and then stripped. This blob of code
// also gets copy-pasted into C outputs.

extern "C" {

HALIDE_BUFFER_HELPER_ATTRS
uint8_t *_halide_buffer_get_host(const halide_buffer_t *buf) {
    return buf->host;
}

HALIDE_BUFFER_HELPER_ATTRS
uint64_t _halide_buffer_get_device(const halide_buffer_t *buf) {
    return buf->device;
}

HALIDE_BUFFER_HELPER_ATTRS
const struct halide_device_interface_t *_halide_buffer_get_device_interface(const halide_buffer_t *buf) {
    return buf->device_interface;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_min(const halide_buffer_t *buf, int d) {
    return buf->dim[d].min;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_max(const halide_buffer_t *buf, int d) {
    return buf->dim[d].min + buf->dim[d].extent - 1;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_extent(const halide_buffer_t *buf, int d) {
    return buf->dim[d].extent;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_stride(const halide_buffer_t *buf, int d) {
    return buf->dim[d].stride;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_set_host_dirty(halide_buffer_t *buf, bool val) {
    buf->set_host_dirty(val);
    return 0;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_set_device_dirty(halide_buffer_t *buf, bool val) {
    buf->set_device_dirty(val);
    return 0;
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_get_host_dirty(const halide_buffer_t *buf) {
    return buf->host_dirty();
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_get_device_dirty(const halide_buffer_t *buf) {
    return buf->device_dirty();
}

HALIDE_BUFFER_HELPER_ATTRS
halide_dimension_t *_halide_buffer_get_shape(halide_buffer_t *buf) {
    return buf->dim;
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_is_bounds_query(const halide_buffer_t *buf) {
    return buf->host == NULL && buf->device == 0;
}

HALIDE_BUFFER_HELPER_ATTRS
uint8_t _halide_buffer_get_type_code(const halide_buffer_t *buf) {
    return buf->type.code;
}

HALIDE_BUFFER_HELPER_ATTRS
uint8_t _halide_buffer_get_type_bits(const halide_buffer_t *buf) {
    return buf->type.bits;
}

HALIDE_BUFFER_HELPER_ATTRS
uint16_t _halide_buffer_get_type_lanes(const halide_buffer_t *buf) {
    return buf->type.lanes;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_init(halide_buffer_t *dst,
                                     halide_dimension_t *dst_shape,
                                     void *host,
                                     uint64_t device, halide_device_interface_t *device_interface,
                                     int type_code, int type_bits,
                                     int dimensions,
                                     halide_dimension_t *shape,
                                     uint64_t flags) {
    dst->host = (uint8_t *)host;
    dst->device = device;
    dst->device_interface = device_interface;
    dst->type.code = (halide_type_code_t)type_code;
    dst->type.bits = (uint8_t)type_bits;
    dst->type.lanes = 1;
    dst->dimensions = dimensions;
    dst->dim = dst_shape;
    if (shape != dst->dim) {
        for (int i = 0; i < dimensions; i++) {
            dst->dim[i] = shape[i];
        }
    }
    dst->flags = flags;
    return dst;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_init_from_buffer(halide_buffer_t *dst,
                                                 halide_dimension_t *dst_shape,
                                                 const halide_buffer_t *src) {
    dst->host = src->host;
    dst->device = src->device;
    dst->device_interface = src->device_interface;
    dst->type = src->type;
    dst->dimensions = src->dimensions;
    dst->dim = dst_shape;
    dst->flags = src->flags;
    for (int i = 0; i < dst->dimensions; i++) {
        dst->dim[i] = src->dim[i];
    }
    return dst;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_crop(void *user_context,
                                     halide_buffer_t *dst,
                                     halide_dimension_t *dst_shape,
                                     const halide_buffer_t *src,
                                     const int *min, const int *extent) {
    *dst = *src;
    dst->dim = dst_shape;
    int64_t offset = 0;
    for (int i = 0; i < dst->dimensions; i++) {
        dst->dim[i] = src->dim[i];
        dst->dim[i].min = min[i];
        dst->dim[i].extent = extent[i];
        offset += (min[i] - src->dim[i].min) * src->dim[i].stride;
    }
    offset *= src->type.bytes();
    dst->host += offset;
    dst->device_interface = 0;
    dst->device = 0;
    if (src->device_interface) {
        src->device_interface->device_crop(user_context, src, dst);
    }
    return dst;
}

// Called on return from an extern stage where the output buffer was a
// crop of some other larger buffer. This happens for extern stages
// with distinct store_at/compute_at levels. Each call to the stage
// only fills in part of the buffer.
HALIDE_BUFFER_HELPER_ATTRS
void _halide_buffer_retire_crop_after_extern_stage(void *user_context,
                                                   void *obj) {
    halide_buffer_t **buffers = (halide_buffer_t **)obj;
    halide_buffer_t *crop = buffers[0];
    halide_buffer_t *parent = buffers[1];

    if (crop->device) {
        if (!parent->device) {
            // We have been given a device allocation by the extern
            // stage. It only represents the cropped region, so we
            // can't just give it to the parent.
            if (crop->device_dirty()) {
                crop->device_interface->copy_to_host(user_context, crop);
            }
            crop->device_interface->device_free(user_context, crop);
        } else {
            // We are a crop of an existing device allocation.
            if (crop->device_dirty()) {
                parent->set_device_dirty();
            }
            crop->device_interface->device_release_crop(user_context, crop);
        }
    }
    if (crop->host_dirty()) {
        parent->set_host_dirty();
    }

}

HALIDE_BUFFER_HELPER_ATTRS
void _halide_buffer_retire_crops_after_extern_stage(void *user_context,
                                                    void *obj) {
    halide_buffer_t **buffers = (halide_buffer_t **)obj;
    while (*buffers) {
        _halide_buffer_retire_crop(user_context, buffers);
        buffers += 2;
    }
}

}

#undef HALIDE_BUFFER_HELPER_ATTRS
<commit_msg>Missed one renaming<commit_after>#ifdef COMPILING_HALIDE_RUNTIME
#include "HalideRuntime.h"
#define HALIDE_BUFFER_HELPER_ATTRS __attribute__((always_inline, weak))
#else
#define HALIDE_BUFFER_HELPER_ATTRS inline
#endif

// Structs are annoying to deal with from within Halide Stmts. These
// utility functions are for dealing with buffer_t in that
// context. They are not intended for use outside of Halide code, and
// not exposed in HalideRuntime.h. The symbols are private to the
// module and should be inlined and then stripped. This blob of code
// also gets copy-pasted into C outputs.

extern "C" {

HALIDE_BUFFER_HELPER_ATTRS
uint8_t *_halide_buffer_get_host(const halide_buffer_t *buf) {
    return buf->host;
}

HALIDE_BUFFER_HELPER_ATTRS
uint64_t _halide_buffer_get_device(const halide_buffer_t *buf) {
    return buf->device;
}

HALIDE_BUFFER_HELPER_ATTRS
const struct halide_device_interface_t *_halide_buffer_get_device_interface(const halide_buffer_t *buf) {
    return buf->device_interface;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_min(const halide_buffer_t *buf, int d) {
    return buf->dim[d].min;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_max(const halide_buffer_t *buf, int d) {
    return buf->dim[d].min + buf->dim[d].extent - 1;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_extent(const halide_buffer_t *buf, int d) {
    return buf->dim[d].extent;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_get_stride(const halide_buffer_t *buf, int d) {
    return buf->dim[d].stride;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_set_host_dirty(halide_buffer_t *buf, bool val) {
    buf->set_host_dirty(val);
    return 0;
}

HALIDE_BUFFER_HELPER_ATTRS
int _halide_buffer_set_device_dirty(halide_buffer_t *buf, bool val) {
    buf->set_device_dirty(val);
    return 0;
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_get_host_dirty(const halide_buffer_t *buf) {
    return buf->host_dirty();
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_get_device_dirty(const halide_buffer_t *buf) {
    return buf->device_dirty();
}

HALIDE_BUFFER_HELPER_ATTRS
halide_dimension_t *_halide_buffer_get_shape(halide_buffer_t *buf) {
    return buf->dim;
}

HALIDE_BUFFER_HELPER_ATTRS
bool _halide_buffer_is_bounds_query(const halide_buffer_t *buf) {
    return buf->host == NULL && buf->device == 0;
}

HALIDE_BUFFER_HELPER_ATTRS
uint8_t _halide_buffer_get_type_code(const halide_buffer_t *buf) {
    return buf->type.code;
}

HALIDE_BUFFER_HELPER_ATTRS
uint8_t _halide_buffer_get_type_bits(const halide_buffer_t *buf) {
    return buf->type.bits;
}

HALIDE_BUFFER_HELPER_ATTRS
uint16_t _halide_buffer_get_type_lanes(const halide_buffer_t *buf) {
    return buf->type.lanes;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_init(halide_buffer_t *dst,
                                     halide_dimension_t *dst_shape,
                                     void *host,
                                     uint64_t device, halide_device_interface_t *device_interface,
                                     int type_code, int type_bits,
                                     int dimensions,
                                     halide_dimension_t *shape,
                                     uint64_t flags) {
    dst->host = (uint8_t *)host;
    dst->device = device;
    dst->device_interface = device_interface;
    dst->type.code = (halide_type_code_t)type_code;
    dst->type.bits = (uint8_t)type_bits;
    dst->type.lanes = 1;
    dst->dimensions = dimensions;
    dst->dim = dst_shape;
    if (shape != dst->dim) {
        for (int i = 0; i < dimensions; i++) {
            dst->dim[i] = shape[i];
        }
    }
    dst->flags = flags;
    return dst;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_init_from_buffer(halide_buffer_t *dst,
                                                 halide_dimension_t *dst_shape,
                                                 const halide_buffer_t *src) {
    dst->host = src->host;
    dst->device = src->device;
    dst->device_interface = src->device_interface;
    dst->type = src->type;
    dst->dimensions = src->dimensions;
    dst->dim = dst_shape;
    dst->flags = src->flags;
    for (int i = 0; i < dst->dimensions; i++) {
        dst->dim[i] = src->dim[i];
    }
    return dst;
}

HALIDE_BUFFER_HELPER_ATTRS
halide_buffer_t *_halide_buffer_crop(void *user_context,
                                     halide_buffer_t *dst,
                                     halide_dimension_t *dst_shape,
                                     const halide_buffer_t *src,
                                     const int *min, const int *extent) {
    *dst = *src;
    dst->dim = dst_shape;
    int64_t offset = 0;
    for (int i = 0; i < dst->dimensions; i++) {
        dst->dim[i] = src->dim[i];
        dst->dim[i].min = min[i];
        dst->dim[i].extent = extent[i];
        offset += (min[i] - src->dim[i].min) * src->dim[i].stride;
    }
    offset *= src->type.bytes();
    dst->host += offset;
    dst->device_interface = 0;
    dst->device = 0;
    if (src->device_interface) {
        src->device_interface->device_crop(user_context, src, dst);
    }
    return dst;
}

// Called on return from an extern stage where the output buffer was a
// crop of some other larger buffer. This happens for extern stages
// with distinct store_at/compute_at levels. Each call to the stage
// only fills in part of the buffer.
HALIDE_BUFFER_HELPER_ATTRS
void _halide_buffer_retire_crop_after_extern_stage(void *user_context,
                                                   void *obj) {
    halide_buffer_t **buffers = (halide_buffer_t **)obj;
    halide_buffer_t *crop = buffers[0];
    halide_buffer_t *parent = buffers[1];

    if (crop->device) {
        if (!parent->device) {
            // We have been given a device allocation by the extern
            // stage. It only represents the cropped region, so we
            // can't just give it to the parent.
            if (crop->device_dirty()) {
                crop->device_interface->copy_to_host(user_context, crop);
            }
            crop->device_interface->device_free(user_context, crop);
        } else {
            // We are a crop of an existing device allocation.
            if (crop->device_dirty()) {
                parent->set_device_dirty();
            }
            crop->device_interface->device_release_crop(user_context, crop);
        }
    }
    if (crop->host_dirty()) {
        parent->set_host_dirty();
    }

}

HALIDE_BUFFER_HELPER_ATTRS
void _halide_buffer_retire_crops_after_extern_stage(void *user_context,
                                                    void *obj) {
    halide_buffer_t **buffers = (halide_buffer_t **)obj;
    while (*buffers) {
        _halide_buffer_retire_crop_after_extern_stage(user_context, buffers);
        buffers += 2;
    }
}

}

#undef HALIDE_BUFFER_HELPER_ATTRS
<|endoftext|>