<commit_before>////////////////////////////////////////////////////////////////////////
//
// AliTPCTracking.C
//
// date: 22.08.2002
// author: Jiri Chudoba based on code of Jourij Belikov
// version: 1.0
// description:
//      reconstructs of tracks in TPC inthe following steps:
//         TPC cluster finding
//         TPC track finding
// input parameters:
//        Int_t nEvents      ... nr of events to process
//        Int_t firstEventNr ... first event number (starts from 0)
//        Char_t* fileNameHits ... name of file with hits
//        Char_t* fileNameDigits .. name of file with TPC digits
//        Char_t* fileNameClusters .. name of file with TPC clusters (output)
//        Char_t* fileNameTracks .. name of file with TPC tracks (output)
//
//        default file names correspond to pp production (2002-04)
//
// History:
//
//     03.03.2003 ... SetFieldFactor moved to AliTracker class and
//                    LoadTPCParam moved to AliTPC class
//                    TString replaced by Char_t*
//
//     20.11.2002 ... Changes due to a changed interface of AliTPCtracker.
//                    Use Riostream.h instead of iostream.h
//
//     22.08.2002 ... first version
//
////////////////////////////////////////////////////////////////////////

#if !defined(__CINT__) || defined(__MAKECINT__)
#include "Riostream.h"
#include "TTree.h"
#include "TSystem.h"
#include "TArrayF.h"
#include "TPC/alles.h"
#include "TPC/AliTPCtracker.h"
#include "TPC/AliTPCclusterer.h"
#include "TPC/AliTPC.h"
#include "STEER/AliRun.h"
#include "STEER/AliHeader.h"
#include "STEER/AliGenEventHeader.h"
#include "STEER/AliMagF.h"
#include "STEER/AliTracker.h"

#endif

Int_t gDEBUG = 2;

Int_t TPCFindClusters(Int_t nEvents=1, Int_t firstEvent=0,
		      Char_t* fileNameDigits="rfio:galiceSDR.root",
		      Char_t* fileNameClusters="tpc.clusters.root");
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      TFile* fileDigits, TFile* fileClusters,
		      AliTPCParam* paramTPC=0);
Int_t TPCFindTracks(Int_t nEvents=1, Int_t firstEvent=0,
		    Char_t* fileNameClusters="tpc.clusters.root",
		    Char_t* fileNameTracks="tpc.tracks.root");
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    TFile* fileClusters, TFile* fileTracks,
		    AliTPCParam* paramTPC=0);

void FindVertex(Int_t iEvent, Double_t *vertex);
void PrintVertex(TArrayF &primaryVertex);

Int_t AliTPCTracking(Int_t nEvents=1, Int_t firstEvent=0,
		     Char_t* fileNameHits="rfio:galice.root",
		     Char_t* fileNameDigits="rfio:galiceSDR.root",
		     Char_t* fileNameClusters="tpc.clusters.root",
		     Char_t* fileNameTracks="tpc.tracks.root");

////////////////////////////////////////////////////////////////////////
Int_t AliTPCTracking( Int_t nEvents, Int_t firstEvent,
		      Char_t* fileNameHits,
		      Char_t* fileNameDigits,
		      Char_t* fileNameClusters,
		      Char_t* fileNameTracks) {

  AliTracker::SetFieldFactor(fileNameHits,kFALSE);

// ********** Find TPC clusters *********** //
  if (TPCFindClusters(nEvents,firstEvent,fileNameDigits,fileNameClusters)) {
    cerr<<"Failed to get TPC clusters: !\n";
    return 1;
  }

// ********** Find TPC tracks *********** //
  if (TPCFindTracks(nEvents,firstEvent,fileNameClusters,fileNameTracks)) {
    cerr<<"Failed to get TPC tracks !\n";
    return 2;
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      Char_t* fileNameDigits, Char_t* fileNameClusters) {

  Int_t rc;
  const Char_t *name="TPCFindClusters";
  if (gDEBUG>1) cout<<name<<" starts...\n";
  if (gDEBUG>1) gBenchmark->Start(name);
  TFile *fileClusters = TFile::Open(fileNameClusters,"recreate");
  TFile *fileDigits = TFile::Open(fileNameDigits);
  if (!fileDigits->IsOpen()) {
    cerr<<"Cannnot open "<<fileNameDigits<<" !\n";
    return 1;
  }
  if (!fileClusters->IsOpen()) {
    cerr<<"Cannnot open "<<fileNameClusters<<" !\n";
    return 1;
  }

  rc = TPCFindClusters(nEvents,firstEvent,fileDigits,fileClusters);

  fileDigits->Close();
  fileClusters->Close();
  delete fileDigits;
  delete fileClusters;
  if (gDEBUG>1) gBenchmark->Stop(name);
  if (gDEBUG>1) gBenchmark->Show(name);

  return rc;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      TFile* fileDigits, TFile* fileClusters,
		      AliTPCParam* paramTPC) {

  fileDigits->cd();
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileDigits);
  if (!paramTPC) return 1;

  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindClusters: event "<<iEvent<<endl;
    AliTPCclusterer::Digits2Clusters(paramTPC, fileClusters, iEvent);
  }
  return 0;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    Char_t* fileNameClusters, Char_t* fileNameTracks) {

  Int_t rc = 0;
  const Char_t *name="TPCFindTracks";
  if (gDEBUG>1) cout<<name<<" starts"<<endl;
  if (gDEBUG>1) gBenchmark->Start(name);
  TFile *fileTracks = TFile::Open(fileNameTracks,"recreate");
  TFile *fileClusters =TFile::Open(fileNameClusters);

  rc = TPCFindTracks(nEvents, firstEvent, fileClusters, fileTracks);

  fileClusters->Close();
  fileTracks->Close();
  delete fileClusters;
  delete fileTracks;
  if (gDEBUG>1) gBenchmark->Stop(name);
  if (gDEBUG>1) gBenchmark->Show(name);
  return rc;

}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    TFile *fileClusters, TFile * fileTracks,
		    AliTPCParam* paramTPC) {

  Int_t rc = 0;
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileClusters);
  if (!paramTPC) return 1;

  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindTracks: event "<<iEvent<<endl;
    AliTPCtracker *tracker = new AliTPCtracker(paramTPC);
    tracker->SetEventNumber(iEvent);
    Double_t vertex[3];
    FindVertex(iEvent,vertex);
    tracker->SetVertex(vertex);
    fileClusters->cd();
    rc = tracker->Clusters2Tracks(0,fileTracks);
    delete tracker;
  }
  return rc;
}

////////////////////////////////////////////////////////////////////////
void FindVertex(Int_t eventNr, Double_t *vertex) {

  vertex[0] = vertex[1] = vertex[2] = 0.;
  if (!gAlice) {
    cerr<<"gAlice was not found! Using vertex position (0,0,0).\n";
    return;
  }

  gAlice->GetEvent(eventNr);
  AliHeader *header = gAlice->GetHeader();
  if (!header) {
    cerr<<"header was not found!\n";
    return;
  }
  AliGenEventHeader* genEventHeader = header->GenEventHeader();
  if (!genEventHeader) {
    cerr<<"AliGenEventHeader was not found!\n";
    return;
  }

  TArrayF primaryVertex(3);
  genEventHeader->PrimaryVertex(primaryVertex);
  PrintVertex(primaryVertex);
  vertex[0] = static_cast<Double_t>(primaryVertex[0]);
  vertex[1] = static_cast<Double_t>(primaryVertex[1]);
  vertex[2] = static_cast<Double_t>(primaryVertex[2]);
//  delete header;
  delete genEventHeader;
  return;

}
////////////////////////////////////////////////////////////////////////
void PrintVertex(TArrayF &primaryVertex)
{
  cout <<"Vertex: "
       <<primaryVertex[0]<<" "
       <<primaryVertex[1]<<" "
       <<primaryVertex[2]<<" "<<endl;
  exit;
}
////////////////////////////////////////////////////////////////////////
<commit_msg>Option to use the new (V3) tracking (T.Kuhr)<commit_after>////////////////////////////////////////////////////////////////////////
//
// AliTPCTracking.C
//
// date: 22.08.2002
// author: Jiri Chudoba based on code of Jourij Belikov
// version: 1.0
// description:
//      reconstructs of tracks in TPC inthe following steps:
//         TPC cluster finding
//         TPC track finding
// input parameters:
//        Int_t nEvents      ... nr of events to process
//        Int_t firstEventNr ... first event number (starts from 0)
//        const char* fileNameHits ... name of file with hits
//        const char* fileNameDigits .. name of file with TPC digits
//        const char* fileNameClusters .. name of file with TPC clusters (output)
//        const char* fileNameTracks .. name of file with TPC tracks (output)
//
//        default file names correspond to pp production (2002-04)
//
// History:
//
//     18.03.2003 ... Char_t* replaced by const char*
//
//     03.03.2003 ... SetFieldFactor moved to AliTracker class and
//                    LoadTPCParam moved to AliTPC class
//                    TString replaced by Char_t*
//
//     20.11.2002 ... Changes due to a changed interface of AliTPCtracker.
//                    Use Riostream.h instead of iostream.h
//
//     22.08.2002 ... first version
//
////////////////////////////////////////////////////////////////////////

#if !defined(__CINT__) || defined(__MAKECINT__)
#include "Riostream.h"
#include "TFile.h"
#include "TTree.h"
#include "TBenchmark.h"
#include "AliTPCtracker.h"
#include "AliTPCtrackerMI.h"
#include "AliTPCclusterer.h"
#include "AliTPCclustererMI.h"
#include "AliTPC.h"
#include "AliRun.h"
#include "AliHeader.h"
#include "AliGenEventHeader.h"
#include "AliTracker.h"

#endif

Int_t gDEBUG = 2;

Int_t AliTPCTracking(Int_t nEvents=1, Int_t firstEvent=0,
		     const char* fileNameHits="galice.root",
		     const char* fileNameDigits="tpc.digits.root",
		     const char* fileNameClusters="tpc.clusters.root",
		     const char* fileNameTracks="tpc.tracks.root",
		     Bool_t versionMI = kFALSE);

Int_t TPCFindClusters(Int_t nEvents=1, Int_t firstEvent=0,
		      const char* fileNameDigits="tpc.digits.root",
		      const char* fileNameClusters="tpc.clusters.root",
		      Bool_t versionMI = kFALSE);
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      TFile* fileDigits, TFile* fileClusters,
		      AliTPCParam* paramTPC=0);
Int_t TPCFindClustersMI(Int_t nEvents, Int_t firstEvent,
			TFile* fileDigits, TFile* fileClusters,
			AliTPCParam* paramTPC=0);
Int_t TPCFindTracks(Int_t nEvents=1, Int_t firstEvent=0,
		    const char* fileNameClusters="tpc.clusters.root",
		    const char* fileNameTracks="tpc.tracks.root",
		    Bool_t versionMI = kFALSE);
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    TFile* fileClusters, TFile* fileTracks,
		    AliTPCParam* paramTPC=0);
Int_t TPCFindTracksMI(Int_t nEvents, Int_t firstEvent,
		      TFile* fileClusters, TFile* fileTracks,
		      AliTPCParam* paramTPC=0);

void FindVertex(Int_t iEvent, Double_t *vertex);
void PrintVertex(TArrayF &primaryVertex);

////////////////////////////////////////////////////////////////////////
Int_t AliTPCTracking( Int_t nEvents, Int_t firstEvent,
		      const char* fileNameHits,
		      const char* fileNameDigits,
		      const char* fileNameClusters,
		      const char* fileNameTracks,
		      Bool_t versionMI) {

  AliTracker::SetFieldFactor(fileNameHits,kFALSE);

// ********** Find TPC clusters *********** //
  if (fileNameDigits && fileNameClusters) {
    if (TPCFindClusters(nEvents,firstEvent,fileNameDigits,fileNameClusters,versionMI)) {
      cerr<<"Failed to get TPC clusters: !\n";
      return 1;
    }
  }

// ********** Find TPC tracks *********** //
  if (fileNameClusters && fileNameTracks) {
    if (TPCFindTracks(nEvents,firstEvent,fileNameClusters,fileNameTracks,versionMI)) {
      cerr<<"Failed to get TPC tracks !\n";
      return 2;
    }
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      const char* fileNameDigits, const char* fileNameClusters,
		      Bool_t versionMI) {

  Int_t rc;
  const Char_t *name="TPCFindClusters";
  if (gDEBUG>1) cout<<name<<" starts...\n";
  if (gDEBUG>1) gBenchmark->Start(name);
  TFile *fileClusters = TFile::Open(fileNameClusters,"recreate");
  TFile *fileDigits = TFile::Open(fileNameDigits);
  if (!fileDigits->IsOpen()) {
    cerr<<"Cannnot open "<<fileNameDigits<<" !\n";
    return 1;
  }
  if (!fileClusters->IsOpen()) {
    cerr<<"Cannnot open "<<fileNameClusters<<" !\n";
    return 1;
  }

  if (versionMI) {
    rc = TPCFindClustersMI(nEvents,firstEvent,fileDigits,fileClusters);
  } else {
    rc = TPCFindClusters(nEvents,firstEvent,fileDigits,fileClusters);
  }

  fileDigits->Close();
  fileClusters->Close();
  delete fileDigits;
  delete fileClusters;
  if (gDEBUG>1) gBenchmark->Stop(name);
  if (gDEBUG>1) gBenchmark->Show(name);

  return rc;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindClusters(Int_t nEvents, Int_t firstEvent,
		      TFile* fileDigits, TFile* fileClusters,
		      AliTPCParam* paramTPC) {

  fileDigits->cd();
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileDigits);
  if (!paramTPC) return 1;

  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindClusters: event "<<iEvent<<endl;
    AliTPCclusterer::Digits2Clusters(paramTPC, fileClusters, iEvent);
  }
  return 0;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindClustersMI(Int_t nEvents, Int_t firstEvent,
			TFile* fileDigits, TFile* fileClusters,
			AliTPCParam* paramTPC) {

  fileDigits->cd();
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileDigits);
  if (!paramTPC) return 1;

  AliTPCclustererMI clusterer;
  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindClustersMI: event "<<iEvent<<endl;
    char treeName[100];
    sprintf(treeName, "TreeD_75x40_100x60_150x60_%d", iEvent);
    TTree* input = (TTree*) fileDigits->Get(treeName);
    fileClusters->cd();
    sprintf(treeName, "TreeC_TPC_%d", iEvent);
    TTree* output = new TTree(treeName, treeName);
    clusterer.SetInput(input);
    clusterer.SetOutput(output);
    clusterer.Digits2Clusters(paramTPC, iEvent);
  }

  return 0;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    const char* fileNameClusters, const char* fileNameTracks,
		    Bool_t versionMI) {

  Int_t rc = 0;
  const Char_t *name="TPCFindTracks";
  if (gDEBUG>1) cout<<name<<" starts"<<endl;
  if (gDEBUG>1) gBenchmark->Start(name);
  TFile *fileTracks = TFile::Open(fileNameTracks,"recreate");
  TFile *fileClusters =TFile::Open(fileNameClusters);

  if (versionMI) {
    rc = TPCFindTracksMI(nEvents, firstEvent, fileClusters, fileTracks);
  } else {
    rc = TPCFindTracks(nEvents, firstEvent, fileClusters, fileTracks);
  }

  fileClusters->Close();
  fileTracks->Close();
  delete fileClusters;
  delete fileTracks;
  if (gDEBUG>1) gBenchmark->Stop(name);
  if (gDEBUG>1) gBenchmark->Show(name);
  return rc;

}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindTracks(Int_t nEvents, Int_t firstEvent,
		    TFile *fileClusters, TFile * fileTracks,
		    AliTPCParam* paramTPC) {

  Int_t rc = 0;
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileClusters);
  if (!paramTPC) return 1;

  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindTracks: event "<<iEvent<<endl;
    AliTPCtracker *tracker = new AliTPCtracker(paramTPC);
    tracker->SetEventNumber(iEvent);
    Double_t vertex[3];
    FindVertex(iEvent,vertex);
    tracker->SetVertex(vertex);
    fileClusters->cd();
    rc = tracker->Clusters2Tracks(0,fileTracks);
    delete tracker;
  }
  return rc;
}
////////////////////////////////////////////////////////////////////////
Int_t TPCFindTracksMI(Int_t nEvents, Int_t firstEvent,
		      TFile *fileClusters, TFile * fileTracks,
		      AliTPCParam* paramTPC) {

  Int_t rc = 0;
  if (!paramTPC) paramTPC = AliTPC::LoadTPCParam(fileClusters);
  if (!paramTPC) return 1;

  for (Int_t iEvent = firstEvent; iEvent < firstEvent+nEvents; iEvent++){
    if (gDEBUG > 2) cout<<"TPCFindTracksMI: event "<<iEvent<<endl;
    AliTPCtrackerMI tracker(paramTPC, iEvent);
    rc = tracker.Clusters2Tracks(0, fileTracks);
  }
  return rc;
}

////////////////////////////////////////////////////////////////////////
void FindVertex(Int_t eventNr, Double_t *vertex) {

  vertex[0] = vertex[1] = vertex[2] = 0.;
  if (!gAlice) {
    cerr<<"gAlice was not found! Using vertex position (0,0,0).\n";
    return;
  }

  gAlice->GetEvent(eventNr);
  AliHeader *header = gAlice->GetHeader();
  if (!header) {
    cerr<<"header was not found!\n";
    return;
  }
  AliGenEventHeader* genEventHeader = header->GenEventHeader();
  if (!genEventHeader) {
    cerr<<"AliGenEventHeader was not found!\n";
    return;
  }

  TArrayF primaryVertex(3);
  genEventHeader->PrimaryVertex(primaryVertex);
  PrintVertex(primaryVertex);
  vertex[0] = static_cast<Double_t>(primaryVertex[0]);
  vertex[1] = static_cast<Double_t>(primaryVertex[1]);
  vertex[2] = static_cast<Double_t>(primaryVertex[2]);
//  delete header;
  delete genEventHeader;
  return;

}
////////////////////////////////////////////////////////////////////////
void PrintVertex(TArrayF &primaryVertex)
{
  cout <<"Vertex: "
       <<primaryVertex[0]<<" "
       <<primaryVertex[1]<<" "
       <<primaryVertex[2]<<" "<<endl;
}
////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>/*
 * Unit tests for the Raft library.
 *
 * Copyright (C) 2017 Nextworks
 * Author: Vincenzo Maffione <v.maffione@gmail.com>
 *
 * This file is part of rlite.
 *
 * 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 Street, Fifth Floor, Boston, MA  02110-1301 USA
 */

#include <iostream>
#include <list>
#include <string>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <functional>
#include <queue>
#include <random>
#include <ctime>
#include <memory>

#include "rlite/cpputils.hpp"
#include "rlite/raft.hpp"

using namespace std;

static string
logfile(const string &replica)
{
    return string("/tmp/raft_test_") + replica + "_log";
}

static constexpr int kFirstCommand = 18;

class TestReplica : public RaftSM {
    uint32_t output_counter = kFirstCommand;
    bool failed             = false;
    list<string> peers;

public:
    TestReplica() = default;
    RL_NONCOPIABLE(TestReplica);
    TestReplica(const std::string &smname, const ReplicaId &myname,
                std::string logname, const list<string> &others)
        : RaftSM(smname, myname, logname,
                 /*cmd_size=*/sizeof(uint32_t), std::cerr, std::cout),
          peers(others)
    {
    }
    ~TestReplica() { shutdown(); }

    /* Apply a command to the replicated state machine. */
    virtual int apply(const char *const serbuf) override
    {
        uint32_t cmd = *(reinterpret_cast<const uint32_t *>(serbuf));
        if (++output_counter != cmd) {
            cout << "Mismatch: expected " << output_counter << ", got " << cmd
                 << endl;
            return -1;
        }
        return 0;
    }

    /* Called to emulate failure of a replica. The replica won't receive
     * messages until respawn. */
    void fail() { failed = true; }

    /* Is this replica alive? */
    bool up() const { return !failed; }

    /* Called to emulate a replica trying to recover after failure. */
    int respawn(RaftSMOutput *out)
    {
        failed = false;
        return init(peers, out);
    };
};

enum class TestEventType {
    RaftTimer = 0,
    ClientRequest,
    SMFailure,
    SMRespawn,
};

struct TestEvent {
    TestEventType event_type;
    unsigned int abstime = 0;
    TestReplica *sm      = nullptr;
    RaftTimerType ttype  = RaftTimerType::Invalid;

    bool operator<(const TestEvent &o) const { return abstime < o.abstime; }
    bool operator>=(const TestEvent &o) const { return !(*this < o); }

    static uint32_t get_next_command() { return ++input_counter; }

    static TestEvent CreateTimerEvent(unsigned int t, TestReplica *_sm,
                                      RaftTimerType ty)
    {
        TestEvent e;
        e.event_type = TestEventType::RaftTimer;
        e.abstime    = t;
        e.sm         = _sm;
        e.ttype      = ty;
        return e;
    }

    static TestEvent CreateRequestEvent(unsigned int t)
    {
        TestEvent e;
        e.event_type = TestEventType::ClientRequest;
        e.abstime    = t;
        return e;
    }

    static TestEvent CreateFailureEvent(unsigned int t, TestReplica *_sm)
    {
        TestEvent e;
        e.event_type = TestEventType::SMFailure;
        e.abstime    = t;
        e.sm         = _sm;
        return e;
    }

    static TestEvent CreateRespawnEvent(unsigned int t, TestReplica *_sm)
    {
        TestEvent e;
        e.event_type = TestEventType::SMRespawn;
        e.abstime    = t;
        e.sm         = _sm;
        return e;
    }

private:
    static uint32_t input_counter;
};

uint32_t TestEvent::input_counter = kFirstCommand;

int
main()
{
    list<string> names = {"r1", "r2", "r3", "r4", "r5"};
    map<string, std::unique_ptr<TestReplica>> replicas;
    list<TestEvent> events;
    unsigned int t           = 0; /* time */
    const unsigned int t_max = 800;
    RaftSMOutput output;

    srand(time(0));

    /* Clean up leftover logfiles, if any. */
    for (const auto &local : names) {
        remove(logfile(local).c_str());
    }

    /* Create and initialize all the replicas. */
    for (const auto &local : names) {
        string logfilename = logfile(local);
        list<string> peers;
        std::unique_ptr<TestReplica> sm;

        for (const auto &peer : names) {
            if (peer != local) {
                peers.push_back(peer);
            }
        }

        sm = make_unique<TestReplica>(
            /*smname=*/local + "-sm", /*myname=*/local, logfilename, peers);
        if (sm->respawn(&output)) {
            return -1;
        }
        replicas[local] = std::move(sm);
    }

    /* Push some client submission, failures and respawn events. */
    events.push_back(TestEvent::CreateRequestEvent(350));
    events.push_back(TestEvent::CreateRequestEvent(360));
    events.push_back(TestEvent::CreateFailureEvent(365, replicas["r3"].get()));
    events.push_back(TestEvent::CreateRequestEvent(370));
    events.push_back(TestEvent::CreateRequestEvent(450));
    events.push_back(TestEvent::CreateFailureEvent(450, replicas["r4"].get()));
    events.push_back(TestEvent::CreateRequestEvent(454));
    events.push_back(TestEvent::CreateRequestEvent(455));
    // events.push_back(TestEvent::CreateRespawnEvent(500,
    // replicas["r3"].get()));
    events.push_back(TestEvent::CreateRequestEvent(550));
    events.push_back(TestEvent::CreateRequestEvent(560));
    events.sort();

    /* Stop the simulation when we are over-time or when we run out
     * of messages and events (except for the heartbeat timeouts). */
    auto should_stop = [t, t_max, &output, &events]() -> bool {
        auto only_timeouts = [&events]() -> bool {
            for (const auto &e : events) {
                if (e.event_type != TestEventType::RaftTimer) {
                    return false;
                }
            }
            return true;
        };
        auto only_heartbeat_commands = [&output]() -> bool {
            for (const auto &c : output.timer_commands) {
                if (c.type != RaftTimerType::HeartBeat) {
                    return false;
                }
            }
            return true;
        };
        return t > t_max || (output.output_messages.empty() &&
                             only_heartbeat_commands() && only_timeouts());
    };

    while (!should_stop()) {
        list<TestEvent> postponed;
        unsigned int t_next = t + 1;
        RaftSMOutput output_next;

        cout << "| t = " << t << " |" << endl;

        /* Process current output messages. */
        for (const auto &p : output.output_messages) {
            auto *rv  = dynamic_cast<RaftRequestVote *>(p.second.get());
            auto *rvr = dynamic_cast<RaftRequestVoteResp *>(p.second.get());
            auto *ae  = dynamic_cast<RaftAppendEntries *>(p.second.get());
            auto *aer = dynamic_cast<RaftAppendEntriesResp *>(p.second.get());
            int r     = 0;

            assert(replicas.count(p.first));
            if (!replicas[p.first]->up()) {
                /* Replica is currently down, we just drop this message. */
            } else if (rv) {
                r = replicas[p.first]->request_vote_input(*rv, &output_next);
            } else if (rvr) {
                r = replicas[p.first]->request_vote_resp_input(*rvr,
                                                               &output_next);
            } else if (ae) {
                r = replicas[p.first]->append_entries_input(*ae, &output_next);
            } else if (aer) {
                r = replicas[p.first]->append_entries_resp_input(*aer,
                                                                 &output_next);
            } else {
                assert(false);
            }

            if (r) {
                return r;
            }
        }

        /* Process current timer commands. */
        for (const RaftTimerCmd &cmd : output.timer_commands) {
            for (auto it = events.begin(); it != events.end(); it++) {
                if (it->sm == cmd.sm && it->ttype == cmd.type) {
                    events.erase(it);
                    break;
                }
            }
            if (cmd.action == RaftTimerAction::Restart) {
                events.push_back(TestEvent::CreateTimerEvent(
                    t + cmd.milliseconds, dynamic_cast<TestReplica *>(cmd.sm),
                    cmd.type));
            } else {
                assert(cmd.action == RaftTimerAction::Stop);
            }
        }

        /* Process all the timers expired so far, updating the associated
         * Raft state machine. */
        events.sort();
        while (!events.empty()) {
            const TestEvent &next = events.front();
            if (t < next.abstime) {
                if (output_next.output_messages.empty() &&
                    output_next.timer_commands.empty() && postponed.empty()) {
                    /* No need to go step by step, we can jump to the next
                     * event. */
                    t_next = next.abstime;
                }
                break;
            }
            switch (next.event_type) {
            case TestEventType::RaftTimer: {
                /* This event is a timer firing. */
                if (next.sm->up() &&
                    next.sm->timer_expired(next.ttype, &output_next)) {
                    return -1;
                }
                break;
            }

            case TestEventType::ClientRequest: {
                /* This event is a client submission. */
                bool submitted = false;
                for (const auto &kv : replicas) {
                    if (kv.second->leader() && kv.second->up()) {
                        uint32_t cmd = TestEvent::get_next_command();
                        LogIndex request_id;

                        if (kv.second->submit(reinterpret_cast<char *>(&cmd),
                                              &request_id, &output_next)) {
                            return -1;
                        }
                        submitted = true;
                        cout << "Command " << cmd << " submitted" << endl;
                        break;
                    }
                }
                if (!submitted) {
                    /* This can happen because no leader is currently elected
                     * or because the current leader is down. */
                    postponed.push_back(TestEvent::CreateRequestEvent(t + 100));
                    cout << "Client request postponed (no leader)" << endl;
                }
                break;
            }

            case TestEventType::SMFailure: {
                next.sm->fail();
                cout << "Replica " << next.sm->local_name() << " failed"
                     << endl;
                break;
            }

            case TestEventType::SMRespawn: {
                next.sm->respawn(&output_next);
                cout << "Replica " << next.sm->local_name() << " respawn"
                     << endl;
                break;
            }
            }
            events.pop_front();
        }
        /* Append the 'postponed' list at the end of the events list. */
        events.splice(events.end(), postponed);

        /* Update time and Raft state machine output. */
        t      = t_next;
        output = std::move(output_next);
    }

    return 0;
}
<commit_msg>lib: raft: test: add final check<commit_after>/*
 * Unit tests for the Raft library.
 *
 * Copyright (C) 2017 Nextworks
 * Author: Vincenzo Maffione <v.maffione@gmail.com>
 *
 * This file is part of rlite.
 *
 * 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 Street, Fifth Floor, Boston, MA  02110-1301 USA
 */

#include <iostream>
#include <list>
#include <string>
#include <cstdint>
#include <cassert>
#include <cstring>
#include <functional>
#include <queue>
#include <random>
#include <ctime>
#include <memory>

#include "rlite/cpputils.hpp"
#include "rlite/raft.hpp"

using namespace std;

static string
logfile(const string &replica)
{
    return string("/tmp/raft_test_") + replica + "_log";
}

class TestReplica : public RaftSM {
    list<uint32_t> committed_commands;
    list<string> peers;
    bool failed = false;

public:
    TestReplica() = default;
    RL_NONCOPIABLE(TestReplica);
    TestReplica(const std::string &smname, const ReplicaId &myname,
                std::string logname, const list<string> &others)
        : RaftSM(smname, myname, logname,
                 /*cmd_size=*/sizeof(uint32_t), std::cerr, std::cout),
          peers(others)
    {
    }
    ~TestReplica() { shutdown(); }

    /* Apply a command to the replicated state machine. */
    virtual int apply(const char *const serbuf) override
    {
        uint32_t cmd = *(reinterpret_cast<const uint32_t *>(serbuf));
        committed_commands.push_back(cmd);
        return 0;
    }

    /* Called to emulate failure of a replica. The replica won't receive
     * messages until respawn. */
    void fail() { failed = true; }

    /* Is this replica alive? */
    bool up() const { return !failed; }

    /* Called to emulate a replica trying to recover after failure. */
    int respawn(RaftSMOutput *out)
    {
        failed = false;
        return init(peers, out);
    };

    bool check(uint32_t num_commands) const
    {
        uint32_t expected = 1;

        if (committed_commands.size() != num_commands) {
            return false;
        }
        for (auto cmd : committed_commands) {
            if (cmd != expected++) {
                return false;
            }
        }

        return true;
    }
};

enum class TestEventType {
    RaftTimer = 0,
    ClientRequest,
    SMFailure,
    SMRespawn,
};

struct TestEvent {
    TestEventType event_type;
    unsigned int abstime = 0;
    TestReplica *sm      = nullptr;
    RaftTimerType ttype  = RaftTimerType::Invalid;

    bool operator<(const TestEvent &o) const { return abstime < o.abstime; }
    bool operator>=(const TestEvent &o) const { return !(*this < o); }

    static TestEvent CreateTimerEvent(unsigned int t, TestReplica *_sm,
                                      RaftTimerType ty)
    {
        TestEvent e;
        e.event_type = TestEventType::RaftTimer;
        e.abstime    = t;
        e.sm         = _sm;
        e.ttype      = ty;
        return e;
    }

    static TestEvent CreateRequestEvent(unsigned int t)
    {
        TestEvent e;
        e.event_type = TestEventType::ClientRequest;
        e.abstime    = t;
        return e;
    }

    static TestEvent CreateFailureEvent(unsigned int t, TestReplica *_sm)
    {
        TestEvent e;
        e.event_type = TestEventType::SMFailure;
        e.abstime    = t;
        e.sm         = _sm;
        return e;
    }

    static TestEvent CreateRespawnEvent(unsigned int t, TestReplica *_sm)
    {
        TestEvent e;
        e.event_type = TestEventType::SMRespawn;
        e.abstime    = t;
        e.sm         = _sm;
        return e;
    }
};

int
main()
{
    list<string> names = {"r1", "r2", "r3", "r4", "r5"};
    map<string, std::unique_ptr<TestReplica>> replicas;
    list<TestEvent> events;
    unsigned int t           = 0; /* time */
    const unsigned int t_max = 800;
    uint32_t input_counter   = 1;
    RaftSMOutput output;

    srand(time(0));

    /* Clean up leftover logfiles, if any. */
    for (const auto &local : names) {
        remove(logfile(local).c_str());
    }

    /* Create and initialize all the replicas. */
    for (const auto &local : names) {
        string logfilename = logfile(local);
        list<string> peers;
        std::unique_ptr<TestReplica> sm;

        for (const auto &peer : names) {
            if (peer != local) {
                peers.push_back(peer);
            }
        }

        sm = make_unique<TestReplica>(
            /*smname=*/local + "-sm", /*myname=*/local, logfilename, peers);
        if (sm->respawn(&output)) {
            return -1;
        }
        replicas[local] = std::move(sm);
    }

    /* Push some client submission, failures and respawn events. */
    events.push_back(TestEvent::CreateRequestEvent(350));
    events.push_back(TestEvent::CreateRequestEvent(360));
    events.push_back(TestEvent::CreateFailureEvent(365, replicas["r3"].get()));
    events.push_back(TestEvent::CreateRequestEvent(370));
    events.push_back(TestEvent::CreateRequestEvent(450));
    events.push_back(TestEvent::CreateFailureEvent(450, replicas["r4"].get()));
    events.push_back(TestEvent::CreateRequestEvent(454));
    events.push_back(TestEvent::CreateRequestEvent(455));
    // events.push_back(TestEvent::CreateRespawnEvent(500,
    // replicas["r3"].get()));
    events.push_back(TestEvent::CreateRequestEvent(550));
    events.push_back(TestEvent::CreateRequestEvent(560));
    events.sort();

    /* Stop the simulation when we are over-time or when we run out
     * of messages and events (except for the heartbeat timeouts). */
    auto should_stop = [t, t_max, &output, &events]() -> bool {
        auto only_timeouts = [&events]() -> bool {
            for (const auto &e : events) {
                if (e.event_type != TestEventType::RaftTimer) {
                    return false;
                }
            }
            return true;
        };
        auto only_heartbeat_commands = [&output]() -> bool {
            for (const auto &c : output.timer_commands) {
                if (c.type != RaftTimerType::HeartBeat) {
                    return false;
                }
            }
            return true;
        };
        return t > t_max || (output.output_messages.empty() &&
                             only_heartbeat_commands() && only_timeouts());
    };

    while (!should_stop()) {
        list<TestEvent> postponed;
        unsigned int t_next = t + 1;
        RaftSMOutput output_next;

        cout << "| t = " << t << " |" << endl;

        /* Process current output messages. */
        for (const auto &p : output.output_messages) {
            auto *rv  = dynamic_cast<RaftRequestVote *>(p.second.get());
            auto *rvr = dynamic_cast<RaftRequestVoteResp *>(p.second.get());
            auto *ae  = dynamic_cast<RaftAppendEntries *>(p.second.get());
            auto *aer = dynamic_cast<RaftAppendEntriesResp *>(p.second.get());
            int r     = 0;

            assert(replicas.count(p.first));
            if (!replicas[p.first]->up()) {
                /* Replica is currently down, we just drop this message. */
            } else if (rv) {
                r = replicas[p.first]->request_vote_input(*rv, &output_next);
            } else if (rvr) {
                r = replicas[p.first]->request_vote_resp_input(*rvr,
                                                               &output_next);
            } else if (ae) {
                r = replicas[p.first]->append_entries_input(*ae, &output_next);
            } else if (aer) {
                r = replicas[p.first]->append_entries_resp_input(*aer,
                                                                 &output_next);
            } else {
                assert(false);
            }

            if (r) {
                return r;
            }
        }

        /* Process current timer commands. */
        for (const RaftTimerCmd &cmd : output.timer_commands) {
            for (auto it = events.begin(); it != events.end(); it++) {
                if (it->sm == cmd.sm && it->ttype == cmd.type) {
                    events.erase(it);
                    break;
                }
            }
            if (cmd.action == RaftTimerAction::Restart) {
                events.push_back(TestEvent::CreateTimerEvent(
                    t + cmd.milliseconds, dynamic_cast<TestReplica *>(cmd.sm),
                    cmd.type));
            } else {
                assert(cmd.action == RaftTimerAction::Stop);
            }
        }

        /* Process all the timers expired so far, updating the associated
         * Raft state machine. */
        events.sort();
        while (!events.empty()) {
            const TestEvent &next = events.front();
            if (t < next.abstime) {
                if (output_next.output_messages.empty() &&
                    output_next.timer_commands.empty() && postponed.empty()) {
                    /* No need to go step by step, we can jump to the next
                     * event. */
                    t_next = next.abstime;
                }
                break;
            }
            switch (next.event_type) {
            case TestEventType::RaftTimer: {
                /* This event is a timer firing. */
                if (next.sm->up() &&
                    next.sm->timer_expired(next.ttype, &output_next)) {
                    return -1;
                }
                break;
            }

            case TestEventType::ClientRequest: {
                /* This event is a client submission. */
                bool submitted = false;
                for (const auto &kv : replicas) {
                    if (kv.second->leader() && kv.second->up()) {
                        uint32_t cmd = input_counter++;
                        LogIndex request_id;

                        if (kv.second->submit(reinterpret_cast<char *>(&cmd),
                                              &request_id, &output_next)) {
                            return -1;
                        }
                        submitted = true;
                        cout << "Command " << cmd << " submitted" << endl;
                        break;
                    }
                }
                if (!submitted) {
                    /* This can happen because no leader is currently elected
                     * or because the current leader is down. */
                    postponed.push_back(TestEvent::CreateRequestEvent(t + 100));
                    cout << "Client request postponed (no leader)" << endl;
                }
                break;
            }

            case TestEventType::SMFailure: {
                next.sm->fail();
                cout << "Replica " << next.sm->local_name() << " failed"
                     << endl;
                break;
            }

            case TestEventType::SMRespawn: {
                next.sm->respawn(&output_next);
                cout << "Replica " << next.sm->local_name() << " respawn"
                     << endl;
                break;
            }
            }
            events.pop_front();
        }
        /* Append the 'postponed' list at the end of the events list. */
        events.splice(events.end(), postponed);

        /* Update time and Raft state machine output. */
        t      = t_next;
        output = std::move(output_next);
    }

    for (const auto &kv : replicas) {
        if (kv.second->up()) {
            cout << "Replica " << kv.first << " up, check " << std::boolalpha
                 << kv.second->check(input_counter - 1) << endl;
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "UIGifButton.h"

namespace DuiLib
{
	CGifButtonUI::CGifButtonUI()
		:m_pGif(NULL)
		,m_nPreUpdateDelay(0)
		,m_isUpdateTime(false)
	{
	}

	CGifButtonUI::~CGifButtonUI()
	{
		m_pManager->KillTimer(this, GIF_TIMER_ID);

		if (m_pGif)
		{
			delete m_pGif;
			m_pGif = NULL;
		}
	}

	void CGifButtonUI::SetVisible( bool bVisible /*= true*/ )
	{
		if(bVisible == false)
			m_pManager->KillTimer(this, GIF_TIMER_ID);

		CButtonUI::SetVisible(bVisible);
	}

	void CGifButtonUI::PaintStatusImage( HDC hDC )
	{
		if(m_pGif)
		{
			TImageInfo* pImageInfo = NULL;
			if (m_isUpdateTime)
			{
				m_isUpdateTime = false;
				pImageInfo = m_pGif->GetNextFrameInfo();
			}
			else
			{
				pImageInfo = m_pGif->GetCurrentFrameInfo();
			}
			if (pImageInfo)
			{
				RECT rcBmpPart = {0};
				RECT rcCorners = {0};
				rcBmpPart.right = pImageInfo->nX;
				rcBmpPart.bottom = pImageInfo->nY;
				CRenderEngine::DrawImage(hDC,pImageInfo->hBitmap,m_rcItem, m_rcPaint,rcBmpPart,rcCorners,pImageInfo->alphaChannel,255);
				if (m_nPreUpdateDelay != pImageInfo->delay)
				{
					m_pManager->KillTimer(this, GIF_TIMER_ID);
					m_pManager->SetTimer(this, GIF_TIMER_ID, pImageInfo->delay);
					m_nPreUpdateDelay = pImageInfo->delay;
				}
			}
		}
		//ûgifͼƬ,ͨťһ
		else
		{
			if( IsFocused() )
				m_uButtonState |= UISTATE_FOCUSED;
			else
				m_uButtonState &= ~ UISTATE_FOCUSED;
			if( !IsEnabled() )
				m_uButtonState |= UISTATE_DISABLED;
			else
				m_uButtonState &= ~ UISTATE_DISABLED;

			if( (m_uButtonState & UISTATE_DISABLED) != 0 )
			{
				//whmiao need finish diable image
// 				if( !m_sDisabledImage.IsEmpty() )
// 				{
// 					if( !DrawImage(hDC, (LPCTSTR)m_sDisabledImage))
// 						m_sDisabledImage.Empty();
// 					else
// 						return;
// 				}
			}
			else if( (m_uButtonState & UISTATE_PUSHED) != 0 )
			{
				//whmiao need finish diable image
// 				if( !m_sPushedImage.IsEmpty() )
// 				{
// 					if( !DrawImage(hDC, (LPCTSTR)m_sPushedImage) )
// 						m_sPushedImage.Empty();
// 					else
// 						return;
// 				}
			}
			else if( (m_uButtonState & UISTATE_HOT) != 0 )
			{
				//whmiao need finish diable image
// 				if( !m_sHotImage.IsEmpty() )
// 				{
// 					if( !DrawImage(hDC, (LPCTSTR)m_sHotImage) )
// 						m_sHotImage.Empty();
// 					else
// 						return;
// 				}
			}
			else if( (m_uButtonState & UISTATE_FOCUSED) != 0 )
			{
// 				if( !m_sFocusedImage.IsEmpty() )
// 				{
// 					if( !DrawImage(hDC, (LPCTSTR)m_sFocusedImage) )
// 						m_sFocusedImage.Empty();
// 					else
// 						return;
// 				}
			}

// 			if( !m_sNormalImage.IsEmpty() )
// 			{
// 				if( !DrawImage(hDC, (LPCTSTR)m_sNormalImage) )
// 					m_sNormalImage.Empty();
// 				else
// 					return;
// 			}
		}
	}

	void CGifButtonUI::DoEvent( TEventUI& event )
	{
		if( event.Type == UIEVENT_TIMER && event.wParam == GIF_TIMER_ID )
		{
			m_isUpdateTime = true;
			Invalidate();
			return;
		}

		CButtonUI::DoEvent(event);
	}

	void CGifButtonUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue )
	{
		if( _tcscmp(pstrName, _T("NormalGifFile")) == 0 )
		{
			SetNormalGifFile(pstrValue);
		}

		CButtonUI::SetAttribute(pstrName,pstrValue);
	}

	void CGifButtonUI::SetNormalGifFile( LPCTSTR pstrName )
	{
		if(pstrName == NULL) return;

		if (m_pGif)
		{
			m_pManager->KillTimer(this, GIF_TIMER_ID);
			m_nPreUpdateDelay = 0;
			delete m_pGif;
			m_pGif = NULL;
		}

		m_pGif = CRenderEngine::LoadGif(STRINGorID(pstrName),0, 0);

		Invalidate();
	}
}<commit_msg>GIfbutton 没有gif图像 做为正常CButton<commit_after>#include "stdafx.h"
#include "UIGifButton.h"

namespace DuiLib
{
	CGifButtonUI::CGifButtonUI()
		:m_pGif(NULL)
		,m_nPreUpdateDelay(0)
		,m_isUpdateTime(false)
	{
	}

	CGifButtonUI::~CGifButtonUI()
	{
		m_pManager->KillTimer(this, GIF_TIMER_ID);

		if (m_pGif)
		{
			delete m_pGif;
			m_pGif = NULL;
		}
	}

	void CGifButtonUI::SetVisible( bool bVisible /*= true*/ )
	{
		if(bVisible == false)
			m_pManager->KillTimer(this, GIF_TIMER_ID);

		CButtonUI::SetVisible(bVisible);
	}

	void CGifButtonUI::PaintStatusImage( HDC hDC )
	{
		if(m_pGif)
		{
			TImageInfo* pImageInfo = NULL;
			if (m_isUpdateTime)
			{
				m_isUpdateTime = false;
				pImageInfo = m_pGif->GetNextFrameInfo();
			}
			else
			{
				pImageInfo = m_pGif->GetCurrentFrameInfo();
			}
			if (pImageInfo)
			{
				RECT rcBmpPart = {0};
				RECT rcCorners = {0};
				rcBmpPart.right = pImageInfo->nX;
				rcBmpPart.bottom = pImageInfo->nY;
				CRenderEngine::DrawImage(hDC,pImageInfo->hBitmap,m_rcItem, m_rcPaint,rcBmpPart,rcCorners,pImageInfo->alphaChannel,255);
				if (m_nPreUpdateDelay != pImageInfo->delay)
				{
					m_pManager->KillTimer(this, GIF_TIMER_ID);
					m_pManager->SetTimer(this, GIF_TIMER_ID, pImageInfo->delay);
					m_nPreUpdateDelay = pImageInfo->delay;
				}
			}
		}
		//ûgifͼƬ,ͨťһ
		else
		{
             CButtonUI::PaintStatusImage(hDC);
		}
	}

	void CGifButtonUI::DoEvent( TEventUI& event )
	{
		if( event.Type == UIEVENT_TIMER && event.wParam == GIF_TIMER_ID )
		{
			m_isUpdateTime = true;
			Invalidate();
			return;
		}

		CButtonUI::DoEvent(event);
	}

	void CGifButtonUI::SetAttribute( LPCTSTR pstrName, LPCTSTR pstrValue )
	{
		if( _tcscmp(pstrName, _T("NormalGifFile")) == 0 )
		{
			SetNormalGifFile(pstrValue);
		} else {
			CButtonUI::SetAttribute(pstrName, pstrValue);
		}
	}

	void CGifButtonUI::SetNormalGifFile( LPCTSTR pstrName )
	{
		if(pstrName == NULL) return;

		if (m_pGif)
		{
			m_pManager->KillTimer(this, GIF_TIMER_ID);
			m_nPreUpdateDelay = 0;
			delete m_pGif;
			m_pGif = NULL;
		}

		m_pGif = CRenderEngine::LoadGif(STRINGorID(pstrName),0, 0);

		Invalidate();
	}
}<|endoftext|>


<commit_before>#include <LibUtilities/BasicUtils/SessionReader.h>
#include <LibUtilities/BasicUtils/MeshPartition.h>
#include <LibUtilities/Communication/CommSerial.h>

#include <iostream>

using namespace std;
using namespace Nektar;
using namespace Nektar::LibUtilities;

class BrokenComm : public CommSerial
{
public:
    LIB_UTILITIES_EXPORT BrokenComm(int argc, char* argv[], int size) : CommSerial(argc, argv)
    {
        m_size = size;
        m_type = "Broken parallel";
    }
    LIB_UTILITIES_EXPORT BrokenComm(int size) : CommSerial(0, NULL)
    {
        m_size = size;
        m_type = "Broken parallel";
    }
    LIB_UTILITIES_EXPORT virtual ~BrokenComm() {}
    void v_SplitComm(int pRows, int pColumns)
    {
        m_commRow    = boost::shared_ptr<BrokenComm>(new BrokenComm(pColumns));
        m_commColumn = boost::shared_ptr<BrokenComm>(new BrokenComm(pRows));
    }
};

int main(int argc, char *argv[])
{
    vector<string> filenames(1);
    filenames[0] = argv[argc-1];

    CommSharedPtr vComm = boost::shared_ptr<BrokenComm>(new BrokenComm(argc, argv, 256));

    LibUtilities::SessionReaderSharedPtr vSession
        = LibUtilities::SessionReader::CreateInstance(argc, argv, filenames, vComm);

    return 0;
}

<commit_msg>Make PartitionCheck utility a little more usable, start rename to PartitionAnalyse<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// File: PartitionCheck.cpp
//
// For more information, please see: http://www.nektar.info
//
// The MIT License
//
// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
// Department of Aeronautics, Imperial College London (UK), and Scientific
// Computing and Imaging Institute, University of Utah (USA).
//
// License for the specific language governing rights and limitations under
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// Description: Small utility to export histogram of partition sizes.
//
///////////////////////////////////////////////////////////////////////////////

#include <LibUtilities/BasicUtils/SessionReader.h>
#include <LibUtilities/BasicUtils/MeshPartition.h>
#include <LibUtilities/Communication/CommSerial.h>

#include <iostream>

using namespace std;
using namespace Nektar;
using namespace Nektar::LibUtilities;

class FauxComm : public CommSerial
{
public:
    LIB_UTILITIES_EXPORT FauxComm(int argc, char* argv[], int size)
        : CommSerial(argc, argv)
    {
        m_size = size;
        m_type = "Faux parallel";
    }
    LIB_UTILITIES_EXPORT FauxComm(int size) : CommSerial(0, NULL)
    {
        m_size = size;
        m_type = "Faux parallel";
    }
    LIB_UTILITIES_EXPORT virtual ~FauxComm() {}
    void v_SplitComm(int pRows, int pColumns)
    {
        m_commRow    = boost::shared_ptr<FauxComm>(new FauxComm(pColumns));
        m_commColumn = boost::shared_ptr<FauxComm>(new FauxComm(pRows));
    }
};

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        cerr << "Usage: PartitionAnalyze <nproc> <xml file1> [xml file 2..n]"
             << endl;
        return 1;
    }

    int nParts = atoi(argv[1]);
    vector<string> filenames(argv + 2, argv + argc);

    CommSharedPtr vComm = boost::shared_ptr<FauxComm>(
        new FauxComm(argc, argv, nParts));

    char **new_argv = new char*[argc];
    new_argv[0] = strdup("PartitionAnalyze");
    new_argv[1] = strdup("--part-info");
    for (int i = 0; i < argc-2; ++i)
    {
        new_argv[i+2] = strdup(filenames[i].c_str());
    }

    LibUtilities::SessionReaderSharedPtr vSession
        = LibUtilities::SessionReader::CreateInstance(
            argc, new_argv, filenames, vComm);

    return 0;
}

<|endoftext|>


<commit_before><commit_msg>added colors generation<commit_after><|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <grpc/support/alloc.h>
#include <grpc++/impl/call.h>
#include <grpc++/client_context.h>
#include <grpc++/channel_interface.h>

#include "src/cpp/proto/proto_utils.h"

namespace grpc {

void CallOpBuffer::Reset(void* next_return_tag) {
  return_tag_ = next_return_tag;

  send_initial_metadata_ = false;
  initial_metadata_count_ = 0;
  gpr_free(initial_metadata_);

  recv_initial_metadata_ = nullptr;
  gpr_free(recv_initial_metadata_arr_.metadata);
  recv_initial_metadata_arr_ = {0, 0, nullptr};

  send_message_ = nullptr;
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }

  recv_message_ = nullptr;
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }

  client_send_close_ = false;

  recv_trailing_metadata_ = nullptr;
  recv_status_ = nullptr;
  gpr_free(recv_trailing_metadata_arr_.metadata);
  recv_trailing_metadata_arr_ = {0, 0, nullptr};

  status_code_ = GRPC_STATUS_OK;
  gpr_free(status_details_);
  status_details_ = nullptr;
  status_details_capacity_ = 0;

  send_status_ = nullptr;
  trailing_metadata_count_ = 0;
  trailing_metadata_ = nullptr;
}

namespace {
// TODO(yangg) if the map is changed before we send, the pointers will be a
// mess. Make sure it does not happen.
grpc_metadata* FillMetadataArray(
    std::multimap<grpc::string, grpc::string>* metadata) {
  if (metadata->empty()) { return nullptr; }
  grpc_metadata* metadata_array = (grpc_metadata*)gpr_malloc(
      metadata->size()* sizeof(grpc_metadata));
  size_t i = 0;
  for (auto iter = metadata->cbegin();
       iter != metadata->cend();
       ++iter, ++i) {
    metadata_array[i].key = iter->first.c_str();
    metadata_array[i].value = iter->second.c_str();
    metadata_array[i].value_length = iter->second.size();
  }
  return metadata_array;
}

void FillMetadataMap(grpc_metadata_array* arr,
                     std::multimap<grpc::string, grpc::string>* metadata) {
  for (size_t i = 0; i < arr->count; i++) {
    // TODO(yangg) handle duplicates?
    metadata->insert(std::pair<grpc::string, grpc::string>(
        arr->metadata[i].key, {arr->metadata[i].value, arr->metadata[i].value_length}));
  }
  grpc_metadata_array_destroy(arr);
  grpc_metadata_array_init(arr);
}
}  // namespace

void CallOpBuffer::AddSendInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  send_initial_metadata_ = true;
  initial_metadata_count_ = metadata->size();
  initial_metadata_ = FillMetadataArray(metadata);
}

void CallOpBuffer::AddSendInitialMetadata(ClientContext *ctx) {
  AddSendInitialMetadata(&ctx->metadata_);
}

void CallOpBuffer::AddSendMessage(const google::protobuf::Message& message) {
  send_message_ = &message;
}

void CallOpBuffer::AddRecvMessage(google::protobuf::Message *message) {
  recv_message_ = message;
}

void CallOpBuffer::AddClientSendClose() {
  client_send_close_ = true;
}

void CallOpBuffer::AddClientRecvStatus(
    std::multimap<grpc::string, grpc::string>* metadata, Status *status) {
  recv_trailing_metadata_ = metadata;
  recv_status_ = status;
}

void CallOpBuffer::AddServerSendStatus(
    std::multimap<grpc::string, grpc::string>* metadata, const Status& status) {
  trailing_metadata_count_ = metadata->size();
  trailing_metadata_ = FillMetadataArray(metadata);
  send_status_ = &status;
}

void CallOpBuffer::FillOps(grpc_op *ops, size_t *nops) {
  *nops = 0;
  if (send_initial_metadata_) {
    ops[*nops].op = GRPC_OP_SEND_INITIAL_METADATA;
    ops[*nops].data.send_initial_metadata.count = initial_metadata_count_;
    ops[*nops].data.send_initial_metadata.metadata = initial_metadata_;
    (*nops)++;
  }
  if (recv_initial_metadata_) {
    ops[*nops].op = GRPC_OP_RECV_INITIAL_METADATA;
    ops[*nops].data.recv_initial_metadata = &recv_initial_metadata_arr_;
    (*nops)++;
  }
  if (send_message_) {
    bool success = SerializeProto(*send_message_, &send_message_buf_);
    if (!success) {
      // TODO handle parse failure
    }
    ops[*nops].op = GRPC_OP_SEND_MESSAGE;
    ops[*nops].data.send_message = send_message_buf_;
    (*nops)++;
  }
  if (recv_message_) {
    ops[*nops].op = GRPC_OP_RECV_MESSAGE;
    ops[*nops].data.recv_message = &recv_message_buf_;
    (*nops)++;
  }
  if (client_send_close_) {
    ops[*nops].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
    (*nops)++;
  }
  if (recv_status_) {
    ops[*nops].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
    ops[*nops].data.recv_status_on_client.trailing_metadata =
        &recv_trailing_metadata_arr_;
    ops[*nops].data.recv_status_on_client.status = &status_code_;
    ops[*nops].data.recv_status_on_client.status_details = &status_details_;
    ops[*nops].data.recv_status_on_client.status_details_capacity =
        &status_details_capacity_;
    (*nops)++;
  }
  if (send_status_) {
    ops[*nops].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
    ops[*nops].data.send_status_from_server.trailing_metadata_count =
        trailing_metadata_count_;
    ops[*nops].data.send_status_from_server.trailing_metadata =
        trailing_metadata_;
    ops[*nops].data.send_status_from_server.status =
        static_cast<grpc_status_code>(send_status_->code());
    ops[*nops].data.send_status_from_server.status_details =
        send_status_->details().c_str();
    (*nops)++;
  }
}

void CallOpBuffer::FinalizeResult(void **tag, bool *status) {
  // Release send buffers.
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }
  if (initial_metadata_) {
    gpr_free(initial_metadata_);
    initial_metadata_ = nullptr;
  }
  if (trailing_metadata_count_) {
    gpr_free(trailing_metadata_);
    trailing_metadata_ = nullptr;
  }
  // Set user-facing tag.
  *tag = return_tag_;
  // Process received initial metadata
  if (recv_initial_metadata_) {
    FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
  }
  // Parse received message if any.
  if (recv_message_ && recv_message_buf_) {
    *status = DeserializeProto(recv_message_buf_, recv_message_);
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }
  // Parse received status.
  if (recv_status_) {
    FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
    *recv_status_ = Status(
        static_cast<StatusCode>(status_code_),
        status_details_ ?  grpc::string(status_details_, status_details_capacity_)
                        :  grpc::string());
  }
}

void CCallDeleter::operator()(grpc_call* c) {
  grpc_call_destroy(c);
}

Call::Call(grpc_call* call, CallHook *call_hook, CompletionQueue* cq)
    : call_hook_(call_hook), cq_(cq), call_(call) {}

void Call::PerformOps(CallOpBuffer* buffer) {
  call_hook_->PerformOpsOnCall(buffer, this);
}

}  // namespace grpc
<commit_msg>Tweak metadata sending<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <grpc/support/alloc.h>
#include <grpc++/impl/call.h>
#include <grpc++/client_context.h>
#include <grpc++/channel_interface.h>

#include "src/cpp/proto/proto_utils.h"

namespace grpc {

void CallOpBuffer::Reset(void* next_return_tag) {
  return_tag_ = next_return_tag;

  send_initial_metadata_ = false;
  initial_metadata_count_ = 0;
  gpr_free(initial_metadata_);

  recv_initial_metadata_ = nullptr;
  gpr_free(recv_initial_metadata_arr_.metadata);
  recv_initial_metadata_arr_ = {0, 0, nullptr};

  send_message_ = nullptr;
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }

  recv_message_ = nullptr;
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }

  client_send_close_ = false;

  recv_trailing_metadata_ = nullptr;
  recv_status_ = nullptr;
  gpr_free(recv_trailing_metadata_arr_.metadata);
  recv_trailing_metadata_arr_ = {0, 0, nullptr};

  status_code_ = GRPC_STATUS_OK;
  gpr_free(status_details_);
  status_details_ = nullptr;
  status_details_capacity_ = 0;

  send_status_ = nullptr;
  trailing_metadata_count_ = 0;
  trailing_metadata_ = nullptr;
}

namespace {
// TODO(yangg) if the map is changed before we send, the pointers will be a
// mess. Make sure it does not happen.
grpc_metadata* FillMetadataArray(
    std::multimap<grpc::string, grpc::string>* metadata) {
  if (metadata->empty()) { return nullptr; }
  grpc_metadata* metadata_array = (grpc_metadata*)gpr_malloc(
      metadata->size()* sizeof(grpc_metadata));
  size_t i = 0;
  for (auto iter = metadata->cbegin();
       iter != metadata->cend();
       ++iter, ++i) {
    metadata_array[i].key = iter->first.c_str();
    metadata_array[i].value = iter->second.c_str();
    metadata_array[i].value_length = iter->second.size();
  }
  return metadata_array;
}

void FillMetadataMap(grpc_metadata_array* arr,
                     std::multimap<grpc::string, grpc::string>* metadata) {
  for (size_t i = 0; i < arr->count; i++) {
    // TODO(yangg) handle duplicates?
    metadata->insert(std::pair<grpc::string, grpc::string>(
        arr->metadata[i].key, {arr->metadata[i].value, arr->metadata[i].value_length}));
  }
  grpc_metadata_array_destroy(arr);
  grpc_metadata_array_init(arr);
}
}  // namespace

void CallOpBuffer::AddSendInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  send_initial_metadata_ = true;
  initial_metadata_count_ = metadata->size();
  initial_metadata_ = FillMetadataArray(metadata);
}

void CallOpBuffer::AddSendInitialMetadata(ClientContext *ctx) {
  AddSendInitialMetadata(&ctx->metadata_);
}

void CallOpBuffer::AddSendMessage(const google::protobuf::Message& message) {
  send_message_ = &message;
}

void CallOpBuffer::AddRecvMessage(google::protobuf::Message *message) {
  recv_message_ = message;
}

void CallOpBuffer::AddClientSendClose() {
  client_send_close_ = true;
}

void CallOpBuffer::AddClientRecvStatus(
    std::multimap<grpc::string, grpc::string>* metadata, Status *status) {
  recv_trailing_metadata_ = metadata;
  recv_status_ = status;
}

void CallOpBuffer::AddServerSendStatus(
    std::multimap<grpc::string, grpc::string>* metadata, const Status& status) {
  if (metadata != NULL) {
    trailing_metadata_count_ = metadata->size();
    trailing_metadata_ = FillMetadataArray(metadata);
  } else {
    trailing_metadata_count_ = 0;
  }
  send_status_ = &status;
}

void CallOpBuffer::FillOps(grpc_op *ops, size_t *nops) {
  *nops = 0;
  if (send_initial_metadata_) {
    ops[*nops].op = GRPC_OP_SEND_INITIAL_METADATA;
    ops[*nops].data.send_initial_metadata.count = initial_metadata_count_;
    ops[*nops].data.send_initial_metadata.metadata = initial_metadata_;
    (*nops)++;
  }
  if (recv_initial_metadata_) {
    ops[*nops].op = GRPC_OP_RECV_INITIAL_METADATA;
    ops[*nops].data.recv_initial_metadata = &recv_initial_metadata_arr_;
    (*nops)++;
  }
  if (send_message_) {
    bool success = SerializeProto(*send_message_, &send_message_buf_);
    if (!success) {
      abort();
      // TODO handle parse failure
    }
    ops[*nops].op = GRPC_OP_SEND_MESSAGE;
    ops[*nops].data.send_message = send_message_buf_;
    (*nops)++;
  }
  if (recv_message_) {
    ops[*nops].op = GRPC_OP_RECV_MESSAGE;
    ops[*nops].data.recv_message = &recv_message_buf_;
    (*nops)++;
  }
  if (client_send_close_) {
    ops[*nops].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
    (*nops)++;
  }
  if (recv_status_) {
    ops[*nops].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
    ops[*nops].data.recv_status_on_client.trailing_metadata =
        &recv_trailing_metadata_arr_;
    ops[*nops].data.recv_status_on_client.status = &status_code_;
    ops[*nops].data.recv_status_on_client.status_details = &status_details_;
    ops[*nops].data.recv_status_on_client.status_details_capacity =
        &status_details_capacity_;
    (*nops)++;
  }
  if (send_status_) {
    ops[*nops].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
    ops[*nops].data.send_status_from_server.trailing_metadata_count =
        trailing_metadata_count_;
    ops[*nops].data.send_status_from_server.trailing_metadata =
        trailing_metadata_;
    ops[*nops].data.send_status_from_server.status =
        static_cast<grpc_status_code>(send_status_->code());
    ops[*nops].data.send_status_from_server.status_details =
        send_status_->details().c_str();
    (*nops)++;
  }
}

void CallOpBuffer::FinalizeResult(void **tag, bool *status) {
  // Release send buffers.
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }
  if (initial_metadata_) {
    gpr_free(initial_metadata_);
    initial_metadata_ = nullptr;
  }
  if (trailing_metadata_count_) {
    gpr_free(trailing_metadata_);
    trailing_metadata_ = nullptr;
  }
  // Set user-facing tag.
  *tag = return_tag_;
  // Process received initial metadata
  if (recv_initial_metadata_) {
    FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
  }
  // Parse received message if any.
  if (recv_message_ && recv_message_buf_) {
    *status = DeserializeProto(recv_message_buf_, recv_message_);
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }
  // Parse received status.
  if (recv_status_) {
    FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
    *recv_status_ = Status(
        static_cast<StatusCode>(status_code_),
        status_details_ ?  grpc::string(status_details_, status_details_capacity_)
                        :  grpc::string());
  }
}

void CCallDeleter::operator()(grpc_call* c) {
  grpc_call_destroy(c);
}

Call::Call(grpc_call* call, CallHook *call_hook, CompletionQueue* cq)
    : call_hook_(call_hook), cq_(cq), call_(call) {}

void Call::PerformOps(CallOpBuffer* buffer) {
  call_hook_->PerformOpsOnCall(buffer, this);
}

}  // namespace grpc
<|endoftext|>


<commit_before>#include "unlimited.h"

#include "test/test_bitcoin.h"
#include "../consensus/consensus.h"

#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;

// Defined in rpc_tests.cpp not bitcoin-cli.cpp
extern UniValue CallRPC(string strMethod);

BOOST_FIXTURE_TEST_SUITE(excessiveblock_test, TestingSetup)

BOOST_AUTO_TEST_CASE(rpc_excessive)
{
    BOOST_CHECK_NO_THROW(CallRPC("getexcessiveblock"));

    BOOST_CHECK_NO_THROW(CallRPC("getminingmaxblock"));

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock not_uint"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000000 not_uint"), boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000000 -1"), boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock -1 0"), boost::bad_lexical_cast);

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000 1"), runtime_error);
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 1000"));
    BOOST_CHECK_NO_THROW(CallRPC("setexcessiveblock 1000 1"));

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000 0 0"), runtime_error);

    BOOST_CHECK_THROW(CallRPC("setminingmaxblock"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 100000"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock not_uint"),  boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock -1"),  boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 0"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 0 0"), runtime_error);
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 1000"));
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 101"));

}

BOOST_AUTO_TEST_CASE(buip005)
{
    string exceptedEB;
    string exceptedAD;
    excessiveBlockSize = 1000000;
    excessiveAcceptDepth = 9999999;
    exceptedEB = "EB1";
    exceptedAD = "AD9999999";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    BOOST_CHECK_MESSAGE(BUComments.back() == exceptedAD,
                        "AD ought to have been " << exceptedAD << " when excessiveBlockSize = " << excessiveAcceptDepth);
    excessiveBlockSize = 100000;
    excessiveAcceptDepth = 9999999 + 1;
    exceptedEB = "EB0.1";
    exceptedAD = "AD9999999";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    BOOST_CHECK_MESSAGE(BUComments.back() == exceptedAD,
                        "AD ought to have been " << exceptedAD << " when excessiveBlockSize = " << excessiveAcceptDepth);
    excessiveBlockSize = 10000;
    exceptedEB = "EB0";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    excessiveBlockSize = 150000;
    exceptedEB = "EB0.1";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been rounded to " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    excessiveBlockSize = 150000;
    exceptedEB = "EB0.1";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been rounded to " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());

    // set back to defaults
    excessiveBlockSize = 1000000;
    excessiveAcceptDepth = 4;
}

BOOST_AUTO_TEST_CASE(excessiveChecks)
{
  CBlock block;

  excessiveBlockSize = 16000000;  // Ignore excessive block size when checking sigops and block effort

  // Check sigops values

  // Maintain compatibility with the old sigops calculator for blocks <= 1MB
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");

  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS+1,100,100), "improper sigops");

  // Check sigops > 1MB.
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,1000000+1,(blockSigopsPerMb.value*2),100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,1000000+1,(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,(2*1000000),(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,(2*1000000)+1,(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");

  // Check tx size values
  maxTxSize.value = DEFAULT_LARGEST_TRANSACTION;

  // Within a 1 MB block, a 1MB transaction is not excessive
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,1,1,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE), "improper max tx");

  // With a > 1 MB block, use the maxTxSize to determine
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE+1,1,1,maxTxSize.value), "improper max tx");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE+1,1,1,maxTxSize.value+1), "improper max tx");

}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Added unit tests for emergent consesus validation functions.<commit_after>#include "unlimited.h"

#include "test/test_bitcoin.h"
#include "../consensus/consensus.h"

#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;

// Defined in rpc_tests.cpp not bitcoin-cli.cpp
extern UniValue CallRPC(string strMethod);

BOOST_FIXTURE_TEST_SUITE(excessiveblock_test, TestingSetup)

BOOST_AUTO_TEST_CASE(rpc_excessive)
{
    BOOST_CHECK_NO_THROW(CallRPC("getexcessiveblock"));

    BOOST_CHECK_NO_THROW(CallRPC("getminingmaxblock"));

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock not_uint"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000000 not_uint"), boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000000 -1"), boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setexcessiveblock -1 0"), boost::bad_lexical_cast);

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000 1"), runtime_error);
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 1000"));
    BOOST_CHECK_NO_THROW(CallRPC("setexcessiveblock 1000 1"));

    BOOST_CHECK_THROW(CallRPC("setexcessiveblock 1000 0 0"), runtime_error);

    BOOST_CHECK_THROW(CallRPC("setminingmaxblock"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 100000"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock not_uint"),  boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock -1"),  boost::bad_lexical_cast);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 0"), runtime_error);
    BOOST_CHECK_THROW(CallRPC("setminingmaxblock 0 0"), runtime_error);
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 1000"));
    BOOST_CHECK_NO_THROW(CallRPC("setminingmaxblock 101"));

}

BOOST_AUTO_TEST_CASE(buip005)
{
    string exceptedEB;
    string exceptedAD;
    excessiveBlockSize = 1000000;
    excessiveAcceptDepth = 9999999;
    exceptedEB = "EB1";
    exceptedAD = "AD9999999";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    BOOST_CHECK_MESSAGE(BUComments.back() == exceptedAD,
                        "AD ought to have been " << exceptedAD << " when excessiveBlockSize = " << excessiveAcceptDepth);
    excessiveBlockSize = 100000;
    excessiveAcceptDepth = 9999999 + 1;
    exceptedEB = "EB0.1";
    exceptedAD = "AD9999999";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    BOOST_CHECK_MESSAGE(BUComments.back() == exceptedAD,
                        "AD ought to have been " << exceptedAD << " when excessiveBlockSize = " << excessiveAcceptDepth);
    excessiveBlockSize = 10000;
    exceptedEB = "EB0";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    excessiveBlockSize = 150000;
    exceptedEB = "EB0.1";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been rounded to " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());
    excessiveBlockSize = 150000;
    exceptedEB = "EB0.1";
    settingsToUserAgentString();
    BOOST_CHECK_MESSAGE(BUComments.front() == exceptedEB,
                        "EB ought to have been rounded to " << exceptedEB << " when excessiveBlockSize = "
                        << excessiveBlockSize << " but was " << BUComments.front());

    // set back to defaults
    excessiveBlockSize = 1000000;
    excessiveAcceptDepth = 4;
}

BOOST_AUTO_TEST_CASE(excessiveChecks)
{
  CBlock block;

  excessiveBlockSize = 16000000;  // Ignore excessive block size when checking sigops and block effort

  // Check sigops values

  // Maintain compatibility with the old sigops calculator for blocks <= 1MB
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS,100,100), "improper sigops");

  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE-1,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,BLOCKSTREAM_CORE_MAX_BLOCK_SIGOPS+1,100,100), "improper sigops");

  // Check sigops > 1MB.
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,1000000+1,(blockSigopsPerMb.value*2),100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,1000000+1,(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,(2*1000000),(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,(2*1000000)+1,(blockSigopsPerMb.value*2)+1,100,100), "improper sigops");

  // Check tx size values
  maxTxSize.value = DEFAULT_LARGEST_TRANSACTION;

  // Within a 1 MB block, a 1MB transaction is not excessive
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE,1,1,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE), "improper max tx");

  // With a > 1 MB block, use the maxTxSize to determine
  BOOST_CHECK_MESSAGE(false == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE+1,1,1,maxTxSize.value), "improper max tx");
  BOOST_CHECK_MESSAGE(true == CheckExcessive(block,BLOCKSTREAM_CORE_MAX_BLOCK_SIZE+1,1,1,maxTxSize.value+1), "improper max tx");

}

BOOST_AUTO_TEST_CASE(check_validator_rule)
{
    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(1000000, 1000000));
    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(16000000, 1000000));
    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(1000001, 1000000));

    BOOST_CHECK( ! MiningAndExcessiveBlockValidatorRule(1000000, 1000001));
    BOOST_CHECK( ! MiningAndExcessiveBlockValidatorRule(1000000, 16000000));

    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(1357, 1357));
    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(161616, 2222));
    BOOST_CHECK( MiningAndExcessiveBlockValidatorRule(88889, 88888));

    BOOST_CHECK( ! MiningAndExcessiveBlockValidatorRule(929292, 929293));
    BOOST_CHECK( ! MiningAndExcessiveBlockValidatorRule(4, 234245));
}

BOOST_AUTO_TEST_CASE(check_excessive_validator)
{
    // fudge global variables....
    maxGeneratedBlock = 1000000;
    excessiveBlockSize = 888;

    unsigned int tmpExcessive = 1000000;
    std::string str;

    str = ExcessiveBlockValidator(tmpExcessive, NULL, true);
    BOOST_CHECK(str.empty());

    excessiveBlockSize = 888;
    str = ExcessiveBlockValidator(tmpExcessive, NULL, false);
    BOOST_CHECK(str.empty());

    str = ExcessiveBlockValidator(tmpExcessive, (unsigned int *) 42, true);
    BOOST_CHECK(str.empty());

    tmpExcessive = maxGeneratedBlock + 1;

    str = ExcessiveBlockValidator(tmpExcessive, NULL, true);
    BOOST_CHECK(str.empty());

    excessiveBlockSize = 888;
    str = ExcessiveBlockValidator(tmpExcessive, NULL, false);
    BOOST_CHECK(str.empty());

    str = ExcessiveBlockValidator(tmpExcessive, (unsigned int *) 42, true);
    BOOST_CHECK(str.empty());

    tmpExcessive = maxGeneratedBlock - 1;

    str = ExcessiveBlockValidator(tmpExcessive, NULL, true);
    BOOST_CHECK(! str.empty());

    str = ExcessiveBlockValidator(tmpExcessive, NULL, false);
    BOOST_CHECK(str.empty());

    str = ExcessiveBlockValidator(tmpExcessive, (unsigned int *) 42, true);
    BOOST_CHECK(! str.empty());
}

BOOST_AUTO_TEST_CASE(check_generated_block_validator)
{
    // fudge global variables....
    maxGeneratedBlock = 888;
    excessiveBlockSize = 1000000;

    uint64_t tmpMGB = 1000000;
    std::string str;

    str = MiningBlockSizeValidator(tmpMGB, NULL, true);
    BOOST_CHECK(str.empty());

    maxGeneratedBlock = 8888881;
    str = MiningBlockSizeValidator(tmpMGB, NULL, false);
    BOOST_CHECK(str.empty());

    str = MiningBlockSizeValidator(tmpMGB, (uint64_t *) 42, true);
    BOOST_CHECK(str.empty());

    tmpMGB = excessiveBlockSize - 1;

    str = MiningBlockSizeValidator(tmpMGB, NULL, true);
    BOOST_CHECK(str.empty());

    maxGeneratedBlock = 8888881;
    str = MiningBlockSizeValidator(tmpMGB, NULL, false);
    BOOST_CHECK(str.empty());

    str = MiningBlockSizeValidator(tmpMGB, (uint64_t *) 42, true);
    BOOST_CHECK(str.empty());

    tmpMGB = excessiveBlockSize + 1;

    str = MiningBlockSizeValidator(tmpMGB, NULL, true);
    BOOST_CHECK(! str.empty());

    str = MiningBlockSizeValidator(tmpMGB, NULL, false);
    BOOST_CHECK(str.empty());

    str = MiningBlockSizeValidator(tmpMGB, (uint64_t *) 42, true);
    BOOST_CHECK(! str.empty());
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/lib/ui/painting/single_frame_codec.h"

#include "flutter/lib/ui/painting/frame_info.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/tonic/logging/dart_invoke.h"

namespace flutter {

SingleFrameCodec::SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor)
    : status_(Status::kNew), descriptor_(std::move(descriptor)) {}

SingleFrameCodec::~SingleFrameCodec() = default;

int SingleFrameCodec::frameCount() const {
  return 1;
}

int SingleFrameCodec::repetitionCount() const {
  return 0;
}

Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) {
  if (!Dart_IsClosure(callback_handle)) {
    return tonic::ToDart("Callback must be a function");
  }

  if (status_ == Status::kComplete) {
    tonic::DartInvoke(callback_handle, {tonic::ToDart(cached_frame_)});
    return Dart_Null();
  }

  // This has to be valid because this method is called from Dart.
  auto dart_state = UIDartState::Current();

  pending_callbacks_.emplace_back(dart_state, callback_handle);

  if (status_ == Status::kInProgress) {
    // Another call to getNextFrame is in progress and will invoke the
    // pending callbacks when decoding completes.
    return Dart_Null();
  }

  auto decoder = dart_state->GetImageDecoder();

  if (!decoder) {
    return tonic::ToDart("Image decoder not available.");
  }

  auto raw_codec_wrapper = new DartPersistentValue(
      dart_state, Dart_HandleFromWeakPersistent(dart_wrapper()));

  // We dont want to to put the raw codec in a lambda capture because we have
  // to mutate (i.e destroy) it in the callback. Using MakeCopyable will create
  // a shared pointer for the captures which can be destroyed on any thread. But
  // we have to ensure that the DartPersistentValue is only destroyed on the UI
  // thread.
  decoder->Decode(descriptor_, [raw_codec_wrapper](auto image) {
    std::unique_ptr<DartPersistentValue> codec_wrapper(raw_codec_wrapper);

    auto state = codec_wrapper->dart_state().lock();

    if (!state) {
      // This is probably because the isolate has been terminated before the
      // image could be decoded.

      return;
    }

    tonic::DartState::Scope scope(state.get());

    SingleFrameCodec* codec = tonic::DartConverter<SingleFrameCodec*>::FromDart(
        codec_wrapper->value());

    if (image.get()) {
      auto canvas_image = fml::MakeRefCounted<CanvasImage>();
      canvas_image->set_image(std::move(image));

      codec->cached_frame_ = fml::MakeRefCounted<FrameInfo>(
          std::move(canvas_image), 0 /* duration */);
    }

    // The cached frame is now available and should be returned to any future
    // callers.
    codec->status_ = Status::kComplete;

    // Invoke any callbacks that were provided before the frame was decoded.
    Dart_Handle frame = tonic::ToDart(codec->cached_frame_);
    for (const DartPersistentValue& callback : codec->pending_callbacks_) {
      tonic::DartInvoke(callback.value(), {frame});
    }
    codec->pending_callbacks_.clear();
  });

  // The encoded data is no longer needed now that it has been handed off
  // to the decoder.
  descriptor_.data.reset();

  status_ = Status::kInProgress;

  return Dart_Null();
}

size_t SingleFrameCodec::GetAllocationSize() {
  const auto& data = descriptor_.data;
  const auto data_byte_size = data ? data->size() : 0;
  const auto frame_byte_size = (cached_frame_ && cached_frame_->image())
                                   ? cached_frame_->image()->GetAllocationSize()
                                   : 0;
  return data_byte_size + frame_byte_size + sizeof(this);
}

}  // namespace flutter
<commit_msg>Ensure that the SingleFrameCodec stays alive until the ImageDecoder invokes its callback (#10297)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/lib/ui/painting/single_frame_codec.h"

#include "flutter/lib/ui/painting/frame_info.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "third_party/tonic/logging/dart_invoke.h"

namespace flutter {

SingleFrameCodec::SingleFrameCodec(ImageDecoder::ImageDescriptor descriptor)
    : status_(Status::kNew), descriptor_(std::move(descriptor)) {}

SingleFrameCodec::~SingleFrameCodec() = default;

int SingleFrameCodec::frameCount() const {
  return 1;
}

int SingleFrameCodec::repetitionCount() const {
  return 0;
}

Dart_Handle SingleFrameCodec::getNextFrame(Dart_Handle callback_handle) {
  if (!Dart_IsClosure(callback_handle)) {
    return tonic::ToDart("Callback must be a function");
  }

  if (status_ == Status::kComplete) {
    tonic::DartInvoke(callback_handle, {tonic::ToDart(cached_frame_)});
    return Dart_Null();
  }

  // This has to be valid because this method is called from Dart.
  auto dart_state = UIDartState::Current();

  pending_callbacks_.emplace_back(dart_state, callback_handle);

  if (status_ == Status::kInProgress) {
    // Another call to getNextFrame is in progress and will invoke the
    // pending callbacks when decoding completes.
    return Dart_Null();
  }

  auto decoder = dart_state->GetImageDecoder();

  if (!decoder) {
    return tonic::ToDart("Image decoder not available.");
  }

  // The SingleFrameCodec must be deleted on the UI thread.  Allocate a RefPtr
  // on the heap to ensure that the SingleFrameCodec remains alive until the
  // decoder callback is invoked on the UI thread.  The callback can then
  // drop the reference.
  fml::RefPtr<SingleFrameCodec>* raw_codec_ref =
      new fml::RefPtr<SingleFrameCodec>(this);

  decoder->Decode(descriptor_, [raw_codec_ref](auto image) {
    std::unique_ptr<fml::RefPtr<SingleFrameCodec>> codec_ref(raw_codec_ref);
    fml::RefPtr<SingleFrameCodec> codec(std::move(*codec_ref));

    auto state = codec->pending_callbacks_.front().dart_state().lock();

    if (!state) {
      // This is probably because the isolate has been terminated before the
      // image could be decoded.

      return;
    }

    tonic::DartState::Scope scope(state.get());

    if (image.get()) {
      auto canvas_image = fml::MakeRefCounted<CanvasImage>();
      canvas_image->set_image(std::move(image));

      codec->cached_frame_ = fml::MakeRefCounted<FrameInfo>(
          std::move(canvas_image), 0 /* duration */);
    }

    // The cached frame is now available and should be returned to any future
    // callers.
    codec->status_ = Status::kComplete;

    // Invoke any callbacks that were provided before the frame was decoded.
    Dart_Handle frame = tonic::ToDart(codec->cached_frame_);
    for (const DartPersistentValue& callback : codec->pending_callbacks_) {
      tonic::DartInvoke(callback.value(), {frame});
    }
    codec->pending_callbacks_.clear();
  });

  // The encoded data is no longer needed now that it has been handed off
  // to the decoder.
  descriptor_.data.reset();

  status_ = Status::kInProgress;

  return Dart_Null();
}

size_t SingleFrameCodec::GetAllocationSize() {
  const auto& data = descriptor_.data;
  const auto data_byte_size = data ? data->size() : 0;
  const auto frame_byte_size = (cached_frame_ && cached_frame_->image())
                                   ? cached_frame_->image()->GetAllocationSize()
                                   : 0;
  return data_byte_size + frame_byte_size + sizeof(this);
}

}  // namespace flutter
<|endoftext|>