<commit_before>// @(#)alimdc:$Name$:$Id$
// Author: Fons Rademakers  26/11/99

/**************************************************************************
 * Copyright(c) 1998-2003, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// AliRawDB                                                             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <errno.h>
#include <Riostream.h>

#if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,0)
#include <TBufferFile.h>
#else
#include <TBuffer.h>
#endif

#include <TSystem.h>
#include <TKey.h>

#include <TObjString.h>

#include "AliESD.h"
#include "AliRawEvent.h"
#include "AliRawDataArray.h"
#include "AliRawEventHeaderBase.h"
#include "AliRawEquipment.h"
#include "AliRawEquipmentHeader.h"
#include "AliStats.h"

#include "AliRawDB.h"

ClassImp(AliRawDB)

const char *AliRawDB::fgkAliRootTag = "$Name$";

//______________________________________________________________________________
AliRawDB::AliRawDB(AliRawEvent *event,
		   AliESD *esd,
		   Int_t compress,
                   const char* fileName) :
  fRawDB(NULL),
  fTree(NULL),
  fEvent(event),
  fESDTree(NULL),
  fESD(esd),
  fCompress(compress),
  fMaxSize(-1),
  fFS1(""),
  fFS2(""),
  fDeleteFiles(kFALSE),
  fStop(kFALSE),
  fGuidFileFolder(NULL)
{
   // Create a new raw DB

  for (Int_t iDet = 0; iDet < AliDAQ::kNDetectors; iDet++)
    fDetRawData[iDet] = new AliRawDataArray(AliDAQ::NumberOfDdls(iDet));

  fDetRawData[AliDAQ::kNDetectors] = new AliRawDataArray(100);

   if (fileName) {
      if (!Create(fileName))
         MakeZombie();
   }
}

//______________________________________________________________________________
AliRawDB::~AliRawDB() {
  // Destructor

  if(Close()==-1) Error("~AliRawDB", "cannot close output file!");

  for (Int_t iDet = 0; iDet < (AliDAQ::kNDetectors + 1); iDet++)
    delete fDetRawData[iDet];
}

//______________________________________________________________________________
Bool_t AliRawDB::FSHasSpace(const char *fs) const
{
   // Check for at least fMaxSize bytes of free space on the file system.
   // If the space is not available return kFALSE, kTRUE otherwise.

   Long_t id, bsize, blocks, bfree;

   if (gSystem->GetFsInfo(fs, &id, &bsize, &blocks, &bfree) == 1) {
      Error("FSHasSpace", "could not stat file system %s", fs);
      return kFALSE;
   }

   // Leave 5 percent of diskspace free
   Double_t avail = Double_t(bfree) * 0.95;
   if (avail*bsize > fMaxSize)
      return kTRUE;

   Warning("FSHasSpace", "no space on file system %s", fs);
   return kFALSE;
}

//______________________________________________________________________________
const char *AliRawDB::GetFileName() const
{
   // Return filename based on hostname and date and time. This will make
   // each file unique. Also makes sure (via FSHasSpace()) that there is
   // enough space on the file system to store the file. Returns 0 in
   // case of error or interrupt signal.

   static TString fname;
   static Bool_t  fstoggle = kFALSE;

   TString fs = fstoggle ? fFS2 : fFS1;
   TDatime dt;

   TString hostname = gSystem->HostName();
   Int_t pos;
   if ((pos = hostname.Index(".")) != kNPOS)
      hostname.Remove(pos);

   if (!FSHasSpace(fs)) {
      while (1) {
         fstoggle = !fstoggle;
         fs = fstoggle ? fFS2 : fFS1;
         if (FSHasSpace(fs)) break;
         Info("GetFileName", "sleeping 30 seconds before retrying...");
         gSystem->Sleep(30000);   // sleep for 30 seconds
         if (fStop) return 0;
      }
   }

   fname = fs + "/" + hostname + "_";
   fname += dt.GetDate();
   fname += "_";
   fname += dt.GetTime();
   fname += ".root";

   fstoggle = !fstoggle;

   return fname;
}

//______________________________________________________________________________
void AliRawDB::SetFS(const char* fs1, const char* fs2)
{
// set the file system location

  fFS1 = fs1;
  if (fs1 && !fFS1.Contains(":")) {
    gSystem->ResetErrno();
    gSystem->MakeDirectory(fs1);
    if (gSystem->GetErrno() && gSystem->GetErrno() != EEXIST) {
      SysError("SetFS", "mkdir %s", fs1);
    }
  }

  fFS2 = fs2;
  if (fs2) {
    gSystem->ResetErrno();
    gSystem->MakeDirectory(fs2);
    if (gSystem->GetErrno() && gSystem->GetErrno() != EEXIST) {
      SysError("SetFS", "mkdir %s", fs2);
    }
  }
}

//______________________________________________________________________________
Bool_t AliRawDB::Create(const char* fileName)
{
   // Create a new raw DB.

   const Int_t kMaxRetry = 1;
   const Int_t kMaxSleep = 1;      // seconds
   const Int_t kMaxSleepLong = 10; // seconds
   Int_t retry = 0;

again:
   if (fStop) return kFALSE;

   const char *fname = fileName;
   if (!fname) fname = GetFileName();
   if (!fname) {
      Error("Create", "error getting raw DB file name");
      return kFALSE;
   }

   retry++;

   fRawDB = TFile::Open(fname, GetOpenOption(),
			Form("ALICE raw-data file (%s)", GetAliRootTag()), fCompress,
			GetNetopt());
   if (!fRawDB) {
      if (retry < kMaxRetry) {
         Warning("Create", "failure to open file, sleeping %d %s before retrying...",
                 kMaxSleep, kMaxSleep==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleep*1000);
         goto again;
      }
      Error("Create", "failure to open file %s after %d tries", fname, kMaxRetry);
      return kFALSE;
   }
   if (retry > 1)
      Warning("Create", "succeeded to open file after %d retries", retry);

   if (fRawDB->IsZombie()) {
      if (fRawDB->GetErrno() == ENOSPC ||
          fRawDB->GetErrno() == 1018   ||   // SECOMERR
          fRawDB->GetErrno() == 1027) {     // SESYSERR
         fRawDB->ResetErrno();
         delete fRawDB;
         Warning("Create", "file is a zombie (no space), sleeping %d %s before retrying...",
                 kMaxSleepLong, kMaxSleepLong==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleepLong*1000);   // sleep 10 seconds before retrying
         goto again;
      }
      Error("Create", "file %s is zombie", fname);
      fRawDB->ResetErrno();
      delete fRawDB;
      fRawDB = 0;
      if (retry < kMaxRetry) {
         Warning("Create", "file is a zombie, sleeping %d %s before retrying...",
                 kMaxSleep, kMaxSleep==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleep*1000);
         goto again;
      }
      Error("Create", "failure to open file %s after %d tries", fname, kMaxRetry);
      return kFALSE;
   }

   // Create raw data TTree
   MakeTree();

   return kTRUE;
}

//______________________________________________________________________________
void AliRawDB::MakeTree()
{
   // Create ROOT Tree object container.

   fTree = new TTree("RAW", Form("ALICE raw-data tree (%s)", GetAliRootTag()));
   fTree->SetAutoSave(2000000000);  // autosave when 2 Gbyte written

   fTree->BranchRef();

   Int_t bufsize = 256000;
   // splitting 29.6 MB/s, no splitting 35.3 MB/s on P4 2GHz 15k SCSI
   //Int_t split   = 1;
   Int_t split   = 0;
   fTree->Branch("rawevent", "AliRawEvent", &fEvent, bufsize, split);

   // Make brach for each sub-detector
   for (Int_t iDet = 0; iDet < AliDAQ::kNDetectors; iDet++) {
     fTree->Branch(AliDAQ::DetectorName(iDet),"AliRawDataArray",
		   &fDetRawData[iDet],bufsize,split);
   }
   // Make special branch for unrecognized raw-data payloads
   fTree->Branch("Common","AliRawDataArray",
		   &fDetRawData[AliDAQ::kNDetectors],bufsize,split);

   // Create tree which will contain the HLT ESD information

   if (fESD) {
     fESDTree = new TTree("esdTree", Form("ALICE HLT ESD tree (%s)", GetAliRootTag()));
     fESDTree->SetAutoSave(2000000000);  // autosave when 2 Gbyte written
     split   = 0;
     fESDTree->Branch("ESD", "AliESD", &fESD, bufsize, split);
   }

}

//______________________________________________________________________________
Int_t AliRawDB::Close()
{
   // Close raw DB.
   if (!fRawDB) return 0;

   if (!fRawDB->IsOpen()) return 0;

   fRawDB->cd();

   // Write the tree.
   Bool_t error = kFALSE;
   if (fTree->Write() == 0)
     error = kTRUE;
   if (fESDTree)
     if (fESDTree->Write() == 0)
       error = kTRUE;

   // Close DB, this also deletes the fTree
   fRawDB->Close();

   Int_t filesize = fRawDB->GetEND();

   if (fDeleteFiles) {
      gSystem->Unlink(fRawDB->GetName());
      delete fRawDB;
      fRawDB = 0;
      if(!error)
	return filesize;
      else
	return -1;
   }

   // Write a text file with file GUID
   // in the specified folder
   WriteGuidFile();

   delete fRawDB;
   fRawDB = 0;
   if(!error)
     return filesize;
   else
     return -1;
}

//______________________________________________________________________________
Int_t AliRawDB::Fill()
{
   // Fill the trees and return the number of written bytes

  for (Int_t iDet = 0; iDet < (AliDAQ::kNDetectors + 1); iDet++)
    fDetRawData[iDet]->ClearData();

   // Move the raw-data payloads to the corresponding branches
  for(Int_t iSubEvent = 0; iSubEvent < fEvent->GetNSubEvents(); iSubEvent++) {
    AliRawEvent *subEvent = fEvent->GetSubEvent(iSubEvent);
    for(Int_t iEquipment = 0; iEquipment < subEvent->GetNEquipments(); iEquipment++) {
      AliRawEquipment *equipment = subEvent->GetEquipment(iEquipment);
      UInt_t eqId = equipment->GetEquipmentHeader()->GetId();
      Int_t ddlIndex;
      Int_t iDet = AliDAQ::DetectorIDFromDdlID(eqId,ddlIndex);
      if (iDet < 0 || iDet > AliDAQ::kNDetectors)
	iDet = AliDAQ::kNDetectors;
      equipment->SetRawDataRef(fDetRawData[iDet]);
    }
  }

   Double_t bytes = fRawDB->GetBytesWritten();
   Bool_t error = kFALSE;
   if (fTree->Fill() == -1)
     error = kTRUE;
   if (fESDTree)
     if (fESDTree->Fill() == -1)
       error = kTRUE;
   if(!error)
     return Int_t(fRawDB->GetBytesWritten() - bytes);
   else
     return -1;
}

//______________________________________________________________________________
Int_t AliRawDB::GetTotalSize()
{
   // Return the total size of the trees
  Int_t total = 0;

  {
    Int_t skey = 0;
    TDirectory *dir = fTree->GetDirectory();
    if (dir) {
      TKey *key = dir->GetKey(fTree->GetName());
      if (key) skey = key->GetKeylen();
    }
    total += skey;
    if (fTree->GetZipBytes() > 0) total += fTree->GetTotBytes();
#if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,0)
    TBufferFile b(TBuffer::kWrite,10000);
#else
    TBuffer b(TBuffer::kWrite,10000);
#endif
    TTree::Class()->WriteBuffer(b,fTree);
    total += b.Length();
  }

  if(fESDTree)
    {
      Int_t skey = 0;
      TDirectory *dir = fESDTree->GetDirectory();
      if (dir) {
	TKey *key = dir->GetKey(fESDTree->GetName());
	if (key) skey = key->GetKeylen();
      }
      total += skey;
      if (fESDTree->GetZipBytes() > 0) total += fESDTree->GetTotBytes();
#if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,0)
      TBufferFile b(TBuffer::kWrite,10000);
#else
      TBuffer b(TBuffer::kWrite,10000);
#endif
      TTree::Class()->WriteBuffer(b,fESDTree);
      total += b.Length();
    }

  return total;
}

//______________________________________________________________________________
void AliRawDB::WriteStats(AliStats* stats)
{
   // Write stats to raw DB, local run DB and global MySQL DB.

   AliRawEventHeaderBase &header = *GetEvent()->GetHeader();

   // Write stats into RawDB
   TDirectory *ds = gDirectory;
   GetDB()->cd();
   stats->SetEvents(GetEvents());
   stats->SetLastId(header.GetP("Id")[0]);
   stats->SetFileSize(GetBytesWritten());
   stats->SetCompressionFactor(GetCompressionFactor());
   stats->SetEndTime();
   stats->Write("stats");
   ds->cd();
}

//______________________________________________________________________________
Bool_t AliRawDB::NextFile(const char* fileName)
{
   // Close te current file and open a new one.
   // Returns kFALSE in case opening failed.

   Close();

   if (!Create(fileName)) return kFALSE;
   return kTRUE;
}

//______________________________________________________________________________
Float_t AliRawDB::GetCompressionFactor() const
{
   // Return compression factor.

   if (fTree->GetZipBytes() == 0.)
      return 1.0;
   else
      return fTree->GetTotBytes()/fTree->GetZipBytes();
}

//______________________________________________________________________________
const char *AliRawDB::GetAliRootTag()
{
  // Return the aliroot tag (version)
  // used to generate the raw data file.
  // Stored in the raw-data file title.

  TString version = fgkAliRootTag;
  version.Remove(TString::kBoth,'$');
  version.ReplaceAll("Name","AliRoot version");

  return version.Data();
}

//______________________________________________________________________________
void AliRawDB::WriteGuidFile()
{
  // Write the guid file
  // in the specified folder

   TString guidFileName;
   if (fGuidFileFolder) {
     guidFileName = fGuidFileFolder;

     TString pathStr = fRawDB->GetName();
     TObjArray *pathArr = pathStr.Tokenize('/');
     guidFileName.Append("/");
     guidFileName.Append(((TObjString *)pathArr->Last())->String());
     pathArr->Delete();
     delete pathArr;
   }
   else
     guidFileName = fRawDB->GetName();

   guidFileName += ".guid";

   ofstream fguid(guidFileName.Data());
   TString guid = fRawDB->GetUUID().AsString();
   fguid << "guid: \t" << guid.Data();
   fguid.close();
}
<commit_msg>Can not afford WriteBuffer in order to get a precise estimate on the intermediate size of the output file.<commit_after>// @(#)alimdc:$Name$:$Id$
// Author: Fons Rademakers  26/11/99

/**************************************************************************
 * Copyright(c) 1998-2003, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// AliRawDB                                                             //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <errno.h>
#include <Riostream.h>

#if ROOT_VERSION_CODE >= ROOT_VERSION(5,15,0)
#include <TBufferFile.h>
#else
#include <TBuffer.h>
#endif

#include <TSystem.h>
#include <TKey.h>

#include <TObjString.h>

#include "AliESD.h"
#include "AliRawEvent.h"
#include "AliRawDataArray.h"
#include "AliRawEventHeaderBase.h"
#include "AliRawEquipment.h"
#include "AliRawEquipmentHeader.h"
#include "AliStats.h"

#include "AliRawDB.h"

ClassImp(AliRawDB)

const char *AliRawDB::fgkAliRootTag = "$Name$";

//______________________________________________________________________________
AliRawDB::AliRawDB(AliRawEvent *event,
		   AliESD *esd,
		   Int_t compress,
                   const char* fileName) :
  fRawDB(NULL),
  fTree(NULL),
  fEvent(event),
  fESDTree(NULL),
  fESD(esd),
  fCompress(compress),
  fMaxSize(-1),
  fFS1(""),
  fFS2(""),
  fDeleteFiles(kFALSE),
  fStop(kFALSE),
  fGuidFileFolder(NULL)
{
   // Create a new raw DB

  for (Int_t iDet = 0; iDet < AliDAQ::kNDetectors; iDet++)
    fDetRawData[iDet] = new AliRawDataArray(AliDAQ::NumberOfDdls(iDet));

  fDetRawData[AliDAQ::kNDetectors] = new AliRawDataArray(100);

   if (fileName) {
      if (!Create(fileName))
         MakeZombie();
   }
}

//______________________________________________________________________________
AliRawDB::~AliRawDB() {
  // Destructor

  if(Close()==-1) Error("~AliRawDB", "cannot close output file!");

  for (Int_t iDet = 0; iDet < (AliDAQ::kNDetectors + 1); iDet++)
    delete fDetRawData[iDet];
}

//______________________________________________________________________________
Bool_t AliRawDB::FSHasSpace(const char *fs) const
{
   // Check for at least fMaxSize bytes of free space on the file system.
   // If the space is not available return kFALSE, kTRUE otherwise.

   Long_t id, bsize, blocks, bfree;

   if (gSystem->GetFsInfo(fs, &id, &bsize, &blocks, &bfree) == 1) {
      Error("FSHasSpace", "could not stat file system %s", fs);
      return kFALSE;
   }

   // Leave 5 percent of diskspace free
   Double_t avail = Double_t(bfree) * 0.95;
   if (avail*bsize > fMaxSize)
      return kTRUE;

   Warning("FSHasSpace", "no space on file system %s", fs);
   return kFALSE;
}

//______________________________________________________________________________
const char *AliRawDB::GetFileName() const
{
   // Return filename based on hostname and date and time. This will make
   // each file unique. Also makes sure (via FSHasSpace()) that there is
   // enough space on the file system to store the file. Returns 0 in
   // case of error or interrupt signal.

   static TString fname;
   static Bool_t  fstoggle = kFALSE;

   TString fs = fstoggle ? fFS2 : fFS1;
   TDatime dt;

   TString hostname = gSystem->HostName();
   Int_t pos;
   if ((pos = hostname.Index(".")) != kNPOS)
      hostname.Remove(pos);

   if (!FSHasSpace(fs)) {
      while (1) {
         fstoggle = !fstoggle;
         fs = fstoggle ? fFS2 : fFS1;
         if (FSHasSpace(fs)) break;
         Info("GetFileName", "sleeping 30 seconds before retrying...");
         gSystem->Sleep(30000);   // sleep for 30 seconds
         if (fStop) return 0;
      }
   }

   fname = fs + "/" + hostname + "_";
   fname += dt.GetDate();
   fname += "_";
   fname += dt.GetTime();
   fname += ".root";

   fstoggle = !fstoggle;

   return fname;
}

//______________________________________________________________________________
void AliRawDB::SetFS(const char* fs1, const char* fs2)
{
// set the file system location

  fFS1 = fs1;
  if (fs1 && !fFS1.Contains(":")) {
    gSystem->ResetErrno();
    gSystem->MakeDirectory(fs1);
    if (gSystem->GetErrno() && gSystem->GetErrno() != EEXIST) {
      SysError("SetFS", "mkdir %s", fs1);
    }
  }

  fFS2 = fs2;
  if (fs2) {
    gSystem->ResetErrno();
    gSystem->MakeDirectory(fs2);
    if (gSystem->GetErrno() && gSystem->GetErrno() != EEXIST) {
      SysError("SetFS", "mkdir %s", fs2);
    }
  }
}

//______________________________________________________________________________
Bool_t AliRawDB::Create(const char* fileName)
{
   // Create a new raw DB.

   const Int_t kMaxRetry = 1;
   const Int_t kMaxSleep = 1;      // seconds
   const Int_t kMaxSleepLong = 10; // seconds
   Int_t retry = 0;

again:
   if (fStop) return kFALSE;

   const char *fname = fileName;
   if (!fname) fname = GetFileName();
   if (!fname) {
      Error("Create", "error getting raw DB file name");
      return kFALSE;
   }

   retry++;

   fRawDB = TFile::Open(fname, GetOpenOption(),
			Form("ALICE raw-data file (%s)", GetAliRootTag()), fCompress,
			GetNetopt());
   if (!fRawDB) {
      if (retry < kMaxRetry) {
         Warning("Create", "failure to open file, sleeping %d %s before retrying...",
                 kMaxSleep, kMaxSleep==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleep*1000);
         goto again;
      }
      Error("Create", "failure to open file %s after %d tries", fname, kMaxRetry);
      return kFALSE;
   }
   if (retry > 1)
      Warning("Create", "succeeded to open file after %d retries", retry);

   if (fRawDB->IsZombie()) {
      if (fRawDB->GetErrno() == ENOSPC ||
          fRawDB->GetErrno() == 1018   ||   // SECOMERR
          fRawDB->GetErrno() == 1027) {     // SESYSERR
         fRawDB->ResetErrno();
         delete fRawDB;
         Warning("Create", "file is a zombie (no space), sleeping %d %s before retrying...",
                 kMaxSleepLong, kMaxSleepLong==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleepLong*1000);   // sleep 10 seconds before retrying
         goto again;
      }
      Error("Create", "file %s is zombie", fname);
      fRawDB->ResetErrno();
      delete fRawDB;
      fRawDB = 0;
      if (retry < kMaxRetry) {
         Warning("Create", "file is a zombie, sleeping %d %s before retrying...",
                 kMaxSleep, kMaxSleep==1 ? "second" : "seconds");
         gSystem->Sleep(kMaxSleep*1000);
         goto again;
      }
      Error("Create", "failure to open file %s after %d tries", fname, kMaxRetry);
      return kFALSE;
   }

   // Create raw data TTree
   MakeTree();

   return kTRUE;
}

//______________________________________________________________________________
void AliRawDB::MakeTree()
{
   // Create ROOT Tree object container.

   fTree = new TTree("RAW", Form("ALICE raw-data tree (%s)", GetAliRootTag()));
   fTree->SetAutoSave(2000000000);  // autosave when 2 Gbyte written

   fTree->BranchRef();

   Int_t bufsize = 256000;
   // splitting 29.6 MB/s, no splitting 35.3 MB/s on P4 2GHz 15k SCSI
   //Int_t split   = 1;
   Int_t split   = 0;
   fTree->Branch("rawevent", "AliRawEvent", &fEvent, bufsize, split);

   // Make brach for each sub-detector
   for (Int_t iDet = 0; iDet < AliDAQ::kNDetectors; iDet++) {
     fTree->Branch(AliDAQ::DetectorName(iDet),"AliRawDataArray",
		   &fDetRawData[iDet],bufsize,split);
   }
   // Make special branch for unrecognized raw-data payloads
   fTree->Branch("Common","AliRawDataArray",
		   &fDetRawData[AliDAQ::kNDetectors],bufsize,split);

   // Create tree which will contain the HLT ESD information

   if (fESD) {
     fESDTree = new TTree("esdTree", Form("ALICE HLT ESD tree (%s)", GetAliRootTag()));
     fESDTree->SetAutoSave(2000000000);  // autosave when 2 Gbyte written
     split   = 0;
     fESDTree->Branch("ESD", "AliESD", &fESD, bufsize, split);
   }

}

//______________________________________________________________________________
Int_t AliRawDB::Close()
{
   // Close raw DB.
   if (!fRawDB) return 0;

   if (!fRawDB->IsOpen()) return 0;

   fRawDB->cd();

   // Write the tree.
   Bool_t error = kFALSE;
   if (fTree->Write() == 0)
     error = kTRUE;
   if (fESDTree)
     if (fESDTree->Write() == 0)
       error = kTRUE;

   // Close DB, this also deletes the fTree
   fRawDB->Close();

   Int_t filesize = fRawDB->GetEND();

   if (fDeleteFiles) {
      gSystem->Unlink(fRawDB->GetName());
      delete fRawDB;
      fRawDB = 0;
      if(!error)
	return filesize;
      else
	return -1;
   }

   // Write a text file with file GUID
   // in the specified folder
   WriteGuidFile();

   delete fRawDB;
   fRawDB = 0;
   if(!error)
     return filesize;
   else
     return -1;
}

//______________________________________________________________________________
Int_t AliRawDB::Fill()
{
   // Fill the trees and return the number of written bytes

  for (Int_t iDet = 0; iDet < (AliDAQ::kNDetectors + 1); iDet++)
    fDetRawData[iDet]->ClearData();

   // Move the raw-data payloads to the corresponding branches
  for(Int_t iSubEvent = 0; iSubEvent < fEvent->GetNSubEvents(); iSubEvent++) {
    AliRawEvent *subEvent = fEvent->GetSubEvent(iSubEvent);
    for(Int_t iEquipment = 0; iEquipment < subEvent->GetNEquipments(); iEquipment++) {
      AliRawEquipment *equipment = subEvent->GetEquipment(iEquipment);
      UInt_t eqId = equipment->GetEquipmentHeader()->GetId();
      Int_t ddlIndex;
      Int_t iDet = AliDAQ::DetectorIDFromDdlID(eqId,ddlIndex);
      if (iDet < 0 || iDet > AliDAQ::kNDetectors)
	iDet = AliDAQ::kNDetectors;
      equipment->SetRawDataRef(fDetRawData[iDet]);
    }
  }

   Double_t bytes = fRawDB->GetBytesWritten();
   Bool_t error = kFALSE;
   if (fTree->Fill() == -1)
     error = kTRUE;
   if (fESDTree)
     if (fESDTree->Fill() == -1)
       error = kTRUE;
   if(!error)
     return Int_t(fRawDB->GetBytesWritten() - bytes);
   else
     return -1;
}

//______________________________________________________________________________
Int_t AliRawDB::GetTotalSize()
{
   // Return the total size of the trees
  Int_t total = 0;

  {
    Int_t skey = 0;
    TDirectory *dir = fTree->GetDirectory();
    if (dir) {
      TKey *key = dir->GetKey(fTree->GetName());
      if (key) skey = key->GetKeylen();
    }
    total += skey;
    if (fTree->GetZipBytes() > 0) total += fTree->GetTotBytes();
  }

  if(fESDTree)
    {
      Int_t skey = 0;
      TDirectory *dir = fESDTree->GetDirectory();
      if (dir) {
	TKey *key = dir->GetKey(fESDTree->GetName());
	if (key) skey = key->GetKeylen();
      }
      total += skey;
      if (fESDTree->GetZipBytes() > 0) total += fESDTree->GetTotBytes();
    }

  return total;
}

//______________________________________________________________________________
void AliRawDB::WriteStats(AliStats* stats)
{
   // Write stats to raw DB, local run DB and global MySQL DB.

   AliRawEventHeaderBase &header = *GetEvent()->GetHeader();

   // Write stats into RawDB
   TDirectory *ds = gDirectory;
   GetDB()->cd();
   stats->SetEvents(GetEvents());
   stats->SetLastId(header.GetP("Id")[0]);
   stats->SetFileSize(GetBytesWritten());
   stats->SetCompressionFactor(GetCompressionFactor());
   stats->SetEndTime();
   stats->Write("stats");
   ds->cd();
}

//______________________________________________________________________________
Bool_t AliRawDB::NextFile(const char* fileName)
{
   // Close te current file and open a new one.
   // Returns kFALSE in case opening failed.

   Close();

   if (!Create(fileName)) return kFALSE;
   return kTRUE;
}

//______________________________________________________________________________
Float_t AliRawDB::GetCompressionFactor() const
{
   // Return compression factor.

   if (fTree->GetZipBytes() == 0.)
      return 1.0;
   else
      return fTree->GetTotBytes()/fTree->GetZipBytes();
}

//______________________________________________________________________________
const char *AliRawDB::GetAliRootTag()
{
  // Return the aliroot tag (version)
  // used to generate the raw data file.
  // Stored in the raw-data file title.

  TString version = fgkAliRootTag;
  version.Remove(TString::kBoth,'$');
  version.ReplaceAll("Name","AliRoot version");

  return version.Data();
}

//______________________________________________________________________________
void AliRawDB::WriteGuidFile()
{
  // Write the guid file
  // in the specified folder

   TString guidFileName;
   if (fGuidFileFolder) {
     guidFileName = fGuidFileFolder;

     TString pathStr = fRawDB->GetName();
     TObjArray *pathArr = pathStr.Tokenize('/');
     guidFileName.Append("/");
     guidFileName.Append(((TObjString *)pathArr->Last())->String());
     pathArr->Delete();
     delete pathArr;
   }
   else
     guidFileName = fRawDB->GetName();

   guidFileName += ".guid";

   ofstream fguid(guidFileName.Data());
   TString guid = fRawDB->GetUUID().AsString();
   fguid << "guid: \t" << guid.Data();
   fguid.close();
}
<|endoftext|>


<commit_before>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, 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 Willow Garage, 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 "precomp.hpp"

using namespace std;

void pcl::fatal_error(const std::string& message, const std::string& file, int line, const std::string& /*func*/)
{
  cout << "Fatal error: " << message << " in " << file << ":" << line << endl;
  exit(0);
}

void pcl::scaleCloud1(const CloudSet& input_cloud, CloudSet& output_cloud)
{
  //gets smart pointer for "typed" data
  const Cloud<Point3f> cloud = input_cloud.get<Point3f>();

  //gets smart pointer CloudData for "named" "untyped" channel and reinterprets it as "typed" cloud
  const Cloud<float> fpfh33f(input_cloud.get("fpfh33"));

  PCL_Assert( fpfh33f.cols() == 33 );
  size_t features_count = fpfh33f.rows();

  //allocates "typed" channel and gets smart pointer to it
  Cloud<Point3f> output = output_cloud.create<Point3f>(1, 10);

  //allocates "named" "untyped" channel and gets smart pointer to it
  CloudData output_featues = output_cloud.create(features_count, 33 * sizeof(float), "fpfh33");

  //rewrites "named" channel with new data, old buffer will be released
  output_cloud.set(fpfh33f, "fpfh33");
}

void pcl::scaleCloud2(const In& input_cloud, Out& output_cloud)
{
  //gets named data
  const CloudData cloud_data = input_cloud.get("fpfh33");

  //gets named data and reinterprets as typed cloud
  const Cloud<float> cloud = input_cloud.get<float>("fpfh33");

  //checks that data is the same
  PCL_Assert(cloud.ptr() == cloud_data.ptr<float>());

  //gets data, works only if 'input_cloud' proxy class was constructed with Cloud or CloudData
  const CloudData cloud_data1 = input_cloud.get();

  //gets "typed" data, worsk for CloudSet, Cloud<Normal3f>, std::vector<Normal3f>
  //returns empty pointer of input doesn't contain such channel
  const Cloud<Normal3f> cloud1 = input_cloud.get<Normal3f>();

  //allocates "typed" channel, works for CloudSet, Cloud<Point3f>, vector<Point3f>
  Cloud<Point3f> c1 = output_cloud.create<Point3f>(10, 10);

  //allocates "named" channel, works only for CloudSet
  CloudData c2 = output_cloud.create(10, 10 * sizeof(Point3f), "named");

  // allocates array, works only for Cloud<T>, CloudData, vector<T>
  // PCL_Assert(10 * sizeof(Point3f) % sizeof(T) == 0)
  // won't work for CloudSet since "unnamed" and "untyped"
  CloudData c3 = output_cloud.create(10, 10 * sizeof(Point3f));

}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::ChannelKind

size_t pcl::ChannelKind::size_of(int type)
{
  size_t sizes[] =
  {
    0, //None,
    sizeof(unsigned char), //Byte,

    sizeof(Point3f), sizeof(Point3fa), sizeof(Point3d),    // points
    sizeof(Normal3f), sizeof(Normal3fa), sizeof(Normal3d), // normals

    sizeof(RGB), sizeof(RGBA), //colors

    sizeof(Vector3f), sizeof(Vector3d), sizeof(Vector4f), sizeof(Vector4d) // Eigen types
  };

  const size_t sizes_size = sizeof(sizes)/sizeof(sizes[0]);

  PCL_Assert(0U <= type && type < sizes_size);
  return sizes[type];
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::In

pcl::In::In() : kind_(Kind::None), type_(channel_traits<void>::type), obj_(0) {}
template<> pcl::In::In(const CloudData& cloud_data) : kind_(Kind::Cloud), type_(channel_traits<unsigned char>::type), obj_((void*)&cloud_data) {}
template<> pcl::In::In(const CloudSet& cloud_set)   : kind_(Kind::CloudSet),  type_(channel_traits<void>::type), obj_((void*)&cloud_set) {}

int pcl::In::type() const { return type_; }

const pcl::CloudData pcl::In::get(const std::string& name) const
{
  PCL_Assert(kind_ == Kind::CloudSet);
  return reinterpret_cast<const CloudSet*>(obj_)->get(name);;
}

const pcl::CloudData pcl::In::get() const
{
  if (kind_ == Kind::CloudSet)
    PCL_Assert(!"Can't retrieve untyped and unnamed CloudData from CloudSet");
  if (kind_ == Kind::Cloud)
    return *reinterpret_cast<const CloudData*>(obj_);
  else /* kind_ == KInd::StdVector */
    return from_vector();

  return CloudData();
};

namespace
{
  template<typename T> inline const void* get_vecdata(const void *obj, size_t& sizeBytes, bool allocate)
  {
     if (allocate)
     {
       PCL_Assert( sizeBytes % sizeof(T) == 0);
       std::vector<T>& v = *(std::vector<T>*)obj;
       v.resize(sizeBytes/sizeof(T));
     }

     const std::vector<T>& v = *reinterpret_cast<const std::vector<T>*>(obj);
     sizeBytes = v.size() * sizeof(T);
     return &v[0];
  }
}

pcl::CloudData pcl::In::from_vector(size_t size_bytes, bool allocate) const
{
  typedef const void* (*func_t)(const void *, size_t&, bool);
  using namespace pcl;

  func_t funcs[] =
  {
    0, // None
    get_vecdata<unsigned char>, //Byte
    get_vecdata<Point3f>, get_vecdata<Point3fa>, get_vecdata<Point3d>, //points
    get_vecdata<Normal3f>, get_vecdata<Normal3fa>, get_vecdata<Normal3d>, //normals

    get_vecdata<RGB>, get_vecdata<RGBA>, // colors

    get_vecdata<Vector3f>, get_vecdata<Vector3d>, get_vecdata<Vector4f>, get_vecdata<Vector4d>, // Eigen types
  };

  size_t funcs_size = sizeof(funcs)/sizeof(funcs[0]);
  if (type_ == 0 || type_ >= (int)funcs_size)
      PCL_FatalError("Unsupported type");

  const void *data = funcs[type_](obj_, size_bytes, allocate);
  return CloudData(1, size_bytes, (void*)data);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::Out

template<> pcl::Out::Out(const CloudData& cloud_data)  : In(cloud_data) {}
template<> pcl::Out::Out(const CloudSet& cloud_set) : In(cloud_set) {}

pcl::CloudData pcl::Out::create(size_type rows, size_type colsBytes, const std::string& name)
{
  PCL_Assert(kind_ == Kind::CloudSet);
  CloudSet& cloud_set = *reinterpret_cast<CloudSet*>(obj_);
  return cloud_set.create(rows, colsBytes, name);
}

pcl::CloudData pcl::Out::create(size_type rows, size_type colsBytes)
{
  if (kind_ == Kind::CloudSet)
    PCL_FatalError("Can't allocate unnamed and untyped data in CloudSet");

  size_t type_size = ChannelKind::size_of(type_);
  PCL_Assert(colsBytes % type_size == 0);

  if (kind_ == Kind::Cloud)
  {
    CloudData cloud_data = *reinterpret_cast<CloudData*>(obj_);
    cloud_data.create(rows, colsBytes);
    return cloud_data;
  }
  else /* Kind::StdVector */
    return from_vector(colsBytes * rows, true);
}

<commit_msg>comments gramma<commit_after>/*
 * Software License Agreement (BSD License)
 *
 *  Copyright (c) 2011, Willow Garage, 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 Willow Garage, 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 "precomp.hpp"

using namespace std;

void pcl::fatal_error(const std::string& message, const std::string& file, int line, const std::string& /*func*/)
{
  cout << "Fatal error: " << message << " in " << file << ":" << line << endl;
  exit(0);
}

void pcl::scaleCloud1(const CloudSet& input_cloud, CloudSet& output_cloud)
{
  //gets smart pointer for "typed" data
  const Cloud<Point3f> cloud = input_cloud.get<Point3f>();

  //gets smart pointer CloudData for "named" "untyped" channel and reinterprets it as "typed" cloud
  const Cloud<float> fpfh33f(input_cloud.get("fpfh33"));

  PCL_Assert( fpfh33f.cols() == 33 );
  size_t features_count = fpfh33f.rows();

  //allocates "typed" channel and gets smart pointer to it
  Cloud<Point3f> output = output_cloud.create<Point3f>(1, 10);

  //allocates "named" "untyped" channel and gets smart pointer to it
  CloudData output_featues = output_cloud.create(features_count, 33 * sizeof(float), "fpfh33");

  //rewrites "named" channel with new data, old buffer will be released
  output_cloud.set(fpfh33f, "fpfh33");
}

void pcl::scaleCloud2(const In& input_cloud, Out& output_cloud)
{
  //gets named data
  const CloudData cloud_data = input_cloud.get("fpfh33");

  //gets named data and reinterprets as typed cloud
  const Cloud<float> cloud = input_cloud.get<float>("fpfh33");

  //checks that data is the same
  PCL_Assert(cloud.ptr() == cloud_data.ptr<float>());

  //gets data, works only if 'input_cloud' proxy class was constructed with Cloud or CloudData
  const CloudData cloud_data1 = input_cloud.get();

  //gets "typed" data, works for CloudSet, Cloud<Normal3f>, std::vector<Normal3f>
  //returns empty pointer if input doesn't contain such channel
  const Cloud<Normal3f> cloud1 = input_cloud.get<Normal3f>();

  size_t rows = 10;
  size_t cols = 10;
  size_t colsBytes = cols * sizeof(Point3f);

  //allocates "typed" channel, works for CloudSet, Cloud<Point3f>, vector<Point3f>
  Cloud<Point3f> c1 = output_cloud.create<Point3f>(rows, cols);

  //allocates "named" channel, works only for CloudSet
  CloudData c2 = output_cloud.create(rows, colsBytes, "named");

  // allocates array, works only for Cloud<T>, CloudData, vector<T>
  // won't work for CloudSet since "unnamed" and "untyped"
  // PCL_Assert(colsBytes % sizeof(T) == 0), where T is a value of template parameter of object from which output_cloud was constructed
  // That means if passed vector<Point3f>, then can't allocate colsBytes == 2.
  CloudData c3 = output_cloud.create(rows, colsBytes);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::ChannelKind

size_t pcl::ChannelKind::size_of(int type)
{
  size_t sizes[] =
  {
    0, //None,
    sizeof(unsigned char), //Byte,

    sizeof(Point3f), sizeof(Point3fa), sizeof(Point3d),    // points
    sizeof(Normal3f), sizeof(Normal3fa), sizeof(Normal3d), // normals

    sizeof(RGB), sizeof(RGBA), //colors

    sizeof(Vector3f), sizeof(Vector3d), sizeof(Vector4f), sizeof(Vector4d) // Eigen types
  };

  const size_t sizes_size = sizeof(sizes)/sizeof(sizes[0]);

  PCL_Assert(0U <= type && type < sizes_size);
  return sizes[type];
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::In

pcl::In::In() : kind_(Kind::None), type_(channel_traits<void>::type), obj_(0) {}
template<> pcl::In::In(const CloudData& cloud_data) : kind_(Kind::Cloud), type_(channel_traits<unsigned char>::type), obj_((void*)&cloud_data) {}
template<> pcl::In::In(const CloudSet& cloud_set)   : kind_(Kind::CloudSet),  type_(channel_traits<void>::type), obj_((void*)&cloud_set) {}

int pcl::In::type() const { return type_; }

const pcl::CloudData pcl::In::get(const std::string& name) const
{
  PCL_Assert(kind_ == Kind::CloudSet);
  return reinterpret_cast<const CloudSet*>(obj_)->get(name);;
}

const pcl::CloudData pcl::In::get() const
{
  if (kind_ == Kind::CloudSet)
    PCL_Assert(!"Can't retrieve untyped and unnamed CloudData from CloudSet");
  if (kind_ == Kind::Cloud)
    return *reinterpret_cast<const CloudData*>(obj_);
  else /* kind_ == KInd::StdVector */
    return from_vector();

  return CloudData();
};

namespace
{
  template<typename T> inline const void* get_vecdata(const void *obj, size_t& sizeBytes, bool allocate)
  {
     if (allocate)
     {
       PCL_Assert( sizeBytes % sizeof(T) == 0);
       std::vector<T>& v = *(std::vector<T>*)obj;
       v.resize(sizeBytes/sizeof(T));
     }

     const std::vector<T>& v = *reinterpret_cast<const std::vector<T>*>(obj);
     sizeBytes = v.size() * sizeof(T);
     return &v[0];
  }
}

pcl::CloudData pcl::In::from_vector(size_t size_bytes, bool allocate) const
{
  typedef const void* (*func_t)(const void *, size_t&, bool);
  using namespace pcl;

  func_t funcs[] =
  {
    0, // None
    get_vecdata<unsigned char>, //Byte
    get_vecdata<Point3f>, get_vecdata<Point3fa>, get_vecdata<Point3d>, //points
    get_vecdata<Normal3f>, get_vecdata<Normal3fa>, get_vecdata<Normal3d>, //normals

    get_vecdata<RGB>, get_vecdata<RGBA>, // colors

    get_vecdata<Vector3f>, get_vecdata<Vector3d>, get_vecdata<Vector4f>, get_vecdata<Vector4d>, // Eigen types
  };

  size_t funcs_size = sizeof(funcs)/sizeof(funcs[0]);
  if (type_ == 0 || type_ >= (int)funcs_size)
      PCL_FatalError("Unsupported type");

  const void *data = funcs[type_](obj_, size_bytes, allocate);
  return CloudData(1, size_bytes, (void*)data);
}

/////////////////////////////////////////////////////////////////////////////////////////////////
// pc::Out

template<> pcl::Out::Out(const CloudData& cloud_data)  : In(cloud_data) {}
template<> pcl::Out::Out(const CloudSet& cloud_set) : In(cloud_set) {}

pcl::CloudData pcl::Out::create(size_type rows, size_type colsBytes, const std::string& name)
{
  PCL_Assert(kind_ == Kind::CloudSet);
  CloudSet& cloud_set = *reinterpret_cast<CloudSet*>(obj_);
  return cloud_set.create(rows, colsBytes, name);
}

pcl::CloudData pcl::Out::create(size_type rows, size_type colsBytes)
{
  if (kind_ == Kind::CloudSet)
    PCL_FatalError("Can't allocate unnamed and untyped data in CloudSet");

  size_t type_size = ChannelKind::size_of(type_);
  PCL_Assert(colsBytes % type_size == 0);

  if (kind_ == Kind::Cloud)
  {
    CloudData cloud_data = *reinterpret_cast<CloudData*>(obj_);
    cloud_data.create(rows, colsBytes);
    return cloud_data;
  }
  else /* Kind::StdVector */
    return from_vector(colsBytes * rows, true);
}

<|endoftext|>


<commit_before>//
// Created by Yury Rakhuba on 11/05/15.
//

#include "TriangleScene.hpp"
#include "DummyController.hpp"

#include "ogl/GLUtils.hpp"

#include "base/Platform.hpp"
#include "base/assert.hpp"
#include "base/math.hpp"

#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

TriangleScene::TriangleScene()
  : m_vertexBuffer(Buffer::Type::VertexBuffer)
{
  float vertices[] =
  {
    1.0, 1.0, 1.0,
    2.0, 3.0, 1.0,
    3.0, 1.0, 1.0
  };

  m_vertexBuffer.Create();
  m_vertexBuffer.Bind();
  m_vertexBuffer.Allocate(sizeof(float) * std::distance(std::begin(vertices), std::end(vertices)), vertices);

  m_prg.reset(new ShaderProgram(Platform::GetReader("simple.vsh"),
                                Platform::GetReader("solid.fsh")));

  GLCHECK(glClearColor(0.65f, 0.65f, 0.65f, 1.0f));
}

TriangleScene::~TriangleScene()
{
  m_vertexBuffer.Destroy();
}

void TriangleScene::Update(double elapsedSeconds)
{
  static float s_rotationAngle = 0.0f;
  s_rotationAngle += math::pi2 * elapsedSeconds;

  glm::mat4 shiftM = glm::translate(glm::mat4(1.0), glm::vec3(2.0f, 2.0f, 0.0f));
  glm::mat4 backShiftM = glm::translate(glm::mat4(1.0), glm::vec3(-2.0f, -2.0f, 0.0f));
  glm::mat4 rotateM = glm::rotate(glm::mat4(1.0), s_rotationAngle, glm::vec3(0.0, 1.0, 0.0));
  glm::vec4 eye(2.0f, 2.0f, -5.0f, 1.0f);

  eye = backShiftM * rotateM * shiftM * eye;

  m_modelView = glm::lookAt(eye.xyz(),
                            glm::vec3(2.0f, 2.0f, 0.0f),
                            glm::vec3(0.0f, 1.0f, 0.0f));
  m_modelViewChanged = true;
}

void TriangleScene::Render()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  m_prg->Bind();

  if (m_projectionChanged)
  {
    GLint projectionLoc = m_prg->GetUniform("u_projection");
    ASSERT(projectionLoc != -1, "");
    GLCHECK(glUniformMatrix4fv(projectionLoc, 1, GL_TRUE, glm::value_ptr(m_projection)));
    m_projectionChanged = false;
  }

  if (m_modelViewChanged)
  {
    GLint modelViewLoc = m_prg->GetUniform("u_modelView");
    ASSERT(modelViewLoc != -1, "");
    GLCHECK(glUniformMatrix4fv(modelViewLoc, 1, GL_TRUE, glm::value_ptr(m_modelView)));
    m_modelViewChanged = false;
  }

  GLint attribLocation = m_prg->GetAttribute("a_position");
  ASSERT(attribLocation != -1, "");
  GLCHECK(glEnableVertexAttribArray(attribLocation));
  GLCHECK(glVertexAttribPointer(attribLocation, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(float), nullptr));

  GLCHECK(glDrawArrays(GL_TRIANGLES, 0, 3));
}

IController & TriangleScene::GetController()
{
  static DummyController controller;
  return controller;
}

void TriangleScene::OnSize(int w, int h)
{
  float fW = w;
  float fH = h;
  if (fW < fH)
    std::swap(fW, fH);

  m_projection = glm::perspective(static_cast<float>(math::pi2), fW / fH, 1.0f, 100.0f);
  m_projectionChanged = true;
}
<commit_msg>camera rotation fix<commit_after>//
// Created by Yury Rakhuba on 11/05/15.
//

#include "TriangleScene.hpp"
#include "DummyController.hpp"

#include "ogl/GLUtils.hpp"

#include "base/Platform.hpp"
#include "base/assert.hpp"
#include "base/math.hpp"

#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"

TriangleScene::TriangleScene()
  : m_vertexBuffer(Buffer::Type::VertexBuffer)
{
  float vertices[] =
  {
    1.0, 1.0, 1.0,
    2.0, 3.0, 1.0,
    3.0, 1.0, 1.0
  };

  m_vertexBuffer.Create();
  m_vertexBuffer.Bind();
  m_vertexBuffer.Allocate(sizeof(float) * std::distance(std::begin(vertices), std::end(vertices)), vertices);

  m_prg.reset(new ShaderProgram(Platform::GetReader("simple.vsh"),
                                Platform::GetReader("solid.fsh")));

  GLCHECK(glClearColor(0.65f, 0.65f, 0.65f, 1.0f));
}

TriangleScene::~TriangleScene()
{
  m_vertexBuffer.Destroy();
}

void TriangleScene::Update(double elapsedSeconds)
{
  static float s_rotationAngle = 0.0f;
  s_rotationAngle += math::pi2 * elapsedSeconds;

  glm::mat4 shiftM = glm::translate(glm::mat4(1.0), glm::vec3(2.0f, 2.0f, 0.0f));
  glm::mat4 rotateM = glm::rotate(shiftM, s_rotationAngle, glm::vec3(0.0, 1.0, 0.0));
  glm::mat4 backShiftM = glm::translate(rotateM, glm::vec3(-2.0f, -2.0f, 0.0f));
  glm::vec4 eye(2.0f, 2.0f, -5.0f, 1.0f);

  eye = backShiftM * eye;

  m_modelView = glm::lookAt(eye.xyz(),
                            glm::vec3(2.0f, 2.0f, 0.0f),
                            glm::vec3(0.0f, 1.0f, 0.0f));
  m_modelViewChanged = true;
}

void TriangleScene::Render()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  m_prg->Bind();

  if (m_projectionChanged)
  {
    GLint projectionLoc = m_prg->GetUniform("u_projection");
    ASSERT(projectionLoc != -1, "");
    GLCHECK(glUniformMatrix4fv(projectionLoc, 1, GL_TRUE, glm::value_ptr(m_projection)));
    m_projectionChanged = false;
  }

  if (m_modelViewChanged)
  {
    GLint modelViewLoc = m_prg->GetUniform("u_modelView");
    ASSERT(modelViewLoc != -1, "");
    GLCHECK(glUniformMatrix4fv(modelViewLoc, 1, GL_TRUE, glm::value_ptr(m_modelView)));
    m_modelViewChanged = false;
  }

  GLint attribLocation = m_prg->GetAttribute("a_position");
  ASSERT(attribLocation != -1, "");
  GLCHECK(glEnableVertexAttribArray(attribLocation));
  GLCHECK(glVertexAttribPointer(attribLocation, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(float), nullptr));

  GLCHECK(glDrawArrays(GL_TRIANGLES, 0, 3));
}

IController & TriangleScene::GetController()
{
  static DummyController controller;
  return controller;
}

void TriangleScene::OnSize(int w, int h)
{
  float fW = w;
  float fH = h;
  if (fW < fH)
    std::swap(fW, fH);

  m_projection = glm::perspective(static_cast<float>(math::pi2), fW / fH, 1.0f, 100.0f);
  m_projectionChanged = true;
}
<|endoftext|>


<commit_before>#include "Private/CloseButton.h"
#include <QSvgWidget>
#include <QHBoxLayout>

namespace WRibbon {
namespace Private {

///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION                                                            //
///////////////////////////////////////////////////////////////////////////////

CloseButton::CloseButton(QWidget* parent) :
  QPushButton(parent) {
  setFlat(true);
  setAutoFillBackground(true);
  setSizePolicy(QSizePolicy());
}

} // namespace Private
} // namespace WRibbon

<commit_msg>Closed #3: CloseButton closes the application.<commit_after>#include "Private/CloseButton.h"
#include <QCoreApplication>
#include <QSvgWidget>
#include <QHBoxLayout>

namespace WRibbon {
namespace Private {

///////////////////////////////////////////////////////////////////////////////
// PUBLIC SECTION                                                            //
///////////////////////////////////////////////////////////////////////////////

CloseButton::CloseButton(QWidget* parent) :
  QPushButton(parent) {
  setFlat(true);
  setAutoFillBackground(true);
  setSizePolicy(QSizePolicy());
  connect(this, &CloseButton::clicked, [=](){QCoreApplication::quit();});
}

} // namespace Private
} // namespace WRibbon

<|endoftext|>


<commit_before>/** @file

    Plugin to perform background fetches of certain content that would
    otherwise not be cached. For example, Range: requests / responses.

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

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

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

////////////////////////////////////////////////////////////////////////////////
// collapsed_forwarding::
//
// ATS plugin to allow collapsed forwarding of concurrent requests for the same
// object. This plugin is based on open_write_fail_action feature, which detects
// cache open write failure on a cache miss and returns a 502 error along with a
// special @-header indicating the reason for 502 error. The plugin acts on the
// error by using an internal redirect follow back to itself, essentially blocking
// the request until a response arrives, at which point, relies on read-while-writer
// feature to start downloading the object to all waiting clients. The following
// config parameters are assumed to be set for this plugin to work:
////////////////////////////////////////////////////////////////////////////////////
// proxy.config.http.cache.open_write_fail_action        1 /////////////////////////
// proxy.config.cache.enable_read_while_writer           1 /////////////////////////
// proxy.config.http.redirection_enabled                 1 /////////////////////////
// proxy.config.http.number_of_redirections             10 /////////////////////////
// proxy.config.http.redirect_use_orig_cache_key         1 /////////////////////////
// proxy.config.http.background_fill_active_timeout      0 /////////////////////////
// proxy.config.http.background_fill_completed_threshold 0 /////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// This plugin currently supports only per-remap mode activation.
////////////////////////////////////////////////////////////////////////////////////

#include <sys/time.h>
#include <ts/ts.h>
#include <ts/remap.h>
#include <set>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <getopt.h>
#include <netdb.h>
#include <map>

static const char *DEBUG_TAG = (char *)"collapsed_forwarding";

static const char *LOCATION_HEADER = "Location";
static const char *REDIRECT_REASON = "See Other";
static const char *ATS_INTERNAL_MESSAGE = "@Ats-Internal";

static int OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = 5;
static int OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = 500;

typedef struct _RequestData {
  TSHttpTxn txnp;
  int wl_retry; // write lock failure retry count
  std::string req_url;
} RequestData;

static int
add_redirect_header(TSMBuffer &bufp, TSMLoc &hdr_loc, const std::string &location)
{
  // This is needed in case the response already contains a Location header
  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER));

  if (field_loc == TS_NULL_MLOC) {
    TSMimeHdrFieldCreateNamed(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER), &field_loc);
  }

  if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, location.c_str(), location.size())) {
    TSDebug(DEBUG_TAG, "Adding Location header %s", LOCATION_HEADER);
    TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
  }

  TSHandleMLocRelease(bufp, hdr_loc, field_loc);

  TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_SEE_OTHER);
  TSHttpHdrReasonSet(bufp, hdr_loc, REDIRECT_REASON, strlen(REDIRECT_REASON));
  return TS_SUCCESS;
}

static bool
check_internal_message_hdr(TSHttpTxn &txnp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  bool found = false;

  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("check_internal_message_hdr: couldn't retrieve client response header");
    return false;
  }

  TSMLoc header_loc = TSMimeHdrFieldFind(bufp, hdr_loc, ATS_INTERNAL_MESSAGE, strlen(ATS_INTERNAL_MESSAGE));
  if (header_loc) {
    found = true;
    // found the header, remove it now..
    TSMimeHdrFieldDestroy(bufp, hdr_loc, header_loc);
    TSHandleMLocRelease(bufp, hdr_loc, header_loc);
  }

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);

  return found;
}

static int
on_OS_DNS(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "OS_DNS request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_request_header(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "Send_Req request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_read_response_header(TSHttpTxn &txnp)
{
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_immediate(RequestData *req, TSCont &contp)
{
  if (!req) {
    TSError("%s: invalid req_data", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSDebug(DEBUG_TAG, "continuation delayed, scheduling now..for url: %s", req->req_url.c_str());

  // add retry_done header to prevent looping
  std::string value;
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(req->txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header for url %s", DEBUG_TAG,
            req->req_url.c_str());
    TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  add_redirect_header(bufp, hdr_loc, req->req_url);
  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_response_header(RequestData *req, TSHttpTxn &txnp, TSCont &contp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSHttpStatus status = TSHttpHdrStatusGet(bufp, hdr_loc);
  TSDebug(DEBUG_TAG, "Response code: %d", status);

  if ((status == TS_HTTP_STATUS_BAD_GATEWAY) || (status == TS_HTTP_STATUS_SEE_OTHER)) {
    bool is_internal_message_hdr = check_internal_message_hdr(txnp);
    bool delay_request =
      is_internal_message_hdr || ((req->wl_retry > 0) && (req->wl_retry < OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES));

    if (delay_request) {
      req->wl_retry++;
      TSDebug(DEBUG_TAG, "delaying request, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(), req->wl_retry);
      TSContSchedule(contp, OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT, TS_THREAD_POOL_TASK);
      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
      return TS_SUCCESS;
    }
  }

  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "request delayed, but unsuccessful, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(),
            req->wl_retry);
    req->wl_retry = 0;
  }

  // done..cleanup
  TSfree(req);
  TSContDestroy(contp);

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
collapsed_cont(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
  RequestData *my_req = static_cast<RequestData *>(TSContDataGet(contp));

  switch (event) {
  case TS_EVENT_HTTP_OS_DNS: {
    return on_OS_DNS(my_req, txnp);
  }

  case TS_EVENT_HTTP_SEND_REQUEST_HDR: {
    return on_send_request_header(my_req, txnp);
  }

  case TS_EVENT_HTTP_READ_RESPONSE_HDR: {
    return on_read_response_header(txnp);
  }
  case TSEvent::TS_EVENT_IMMEDIATE:
  case TSEvent::TS_EVENT_TIMEOUT: {
    return on_immediate(my_req, contp);
  }
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR: {
    return on_send_response_header(my_req, txnp, contp);
  }
  default: {
    TSDebug(DEBUG_TAG, "Unexpected event: %d", event);
    break;
  }
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

TSReturnCode
TSRemapInit(TSRemapInterface * /* api_info */, char * /* errbuf */, int /* errbuf_size */)
{
  TSDebug(DEBUG_TAG, "plugin is succesfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void ** /* ih */, char * /* errbuf */, int /* errbuf_size */)
{
  // basic argv processing..
  for (int i = 2; i < argc; ++i) {
    if (strncmp(argv[i], "--delay=", 8) == 0) {
      OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = atoi((char *)(argv[i] + 8));
    } else if (strncmp(argv[i], "--retries=", 10) == 0) {
      OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = atoi((char *)(argv[i] + 10));
    }
  }

  return TS_SUCCESS;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
  TSCont cont = TSContCreate(collapsed_cont, TSMutexCreate());

  RequestData *req_data;
  req_data = static_cast<RequestData *>(TSmalloc(sizeof(RequestData)));
  memset(req_data, 0, sizeof(RequestData));

  req_data->txnp = rh;
  req_data->wl_retry = 0;

  int url_len = 0;
  char *url = TSHttpTxnEffectiveUrlStringGet(rh, &url_len);
  req_data->req_url = std::string(url, url_len);

  TSfree(url);
  TSContDataSet(cont, req_data);

  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_REQUEST_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_OS_DNS_HOOK, cont);

  return TSREMAP_DID_REMAP;
}
<commit_msg>Remove incorrect description.<commit_after>/** @file

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

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

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

////////////////////////////////////////////////////////////////////////////////
// collapsed_forwarding::
//
// ATS plugin to allow collapsed forwarding of concurrent requests for the same
// object. This plugin is based on open_write_fail_action feature, which detects
// cache open write failure on a cache miss and returns a 502 error along with a
// special @-header indicating the reason for 502 error. The plugin acts on the
// error by using an internal redirect follow back to itself, essentially blocking
// the request until a response arrives, at which point, relies on read-while-writer
// feature to start downloading the object to all waiting clients. The following
// config parameters are assumed to be set for this plugin to work:
////////////////////////////////////////////////////////////////////////////////////
// proxy.config.http.cache.open_write_fail_action        1 /////////////////////////
// proxy.config.cache.enable_read_while_writer           1 /////////////////////////
// proxy.config.http.redirection_enabled                 1 /////////////////////////
// proxy.config.http.number_of_redirections             10 /////////////////////////
// proxy.config.http.redirect_use_orig_cache_key         1 /////////////////////////
// proxy.config.http.background_fill_active_timeout      0 /////////////////////////
// proxy.config.http.background_fill_completed_threshold 0 /////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// This plugin currently supports only per-remap mode activation.
////////////////////////////////////////////////////////////////////////////////////

#include <sys/time.h>
#include <ts/ts.h>
#include <ts/remap.h>
#include <set>
#include <string>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <getopt.h>
#include <netdb.h>
#include <map>

static const char *DEBUG_TAG = (char *)"collapsed_forwarding";

static const char *LOCATION_HEADER = "Location";
static const char *REDIRECT_REASON = "See Other";
static const char *ATS_INTERNAL_MESSAGE = "@Ats-Internal";

static int OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = 5;
static int OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = 500;

typedef struct _RequestData {
  TSHttpTxn txnp;
  int wl_retry; // write lock failure retry count
  std::string req_url;
} RequestData;

static int
add_redirect_header(TSMBuffer &bufp, TSMLoc &hdr_loc, const std::string &location)
{
  // This is needed in case the response already contains a Location header
  TSMLoc field_loc = TSMimeHdrFieldFind(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER));

  if (field_loc == TS_NULL_MLOC) {
    TSMimeHdrFieldCreateNamed(bufp, hdr_loc, LOCATION_HEADER, strlen(LOCATION_HEADER), &field_loc);
  }

  if (TS_SUCCESS == TSMimeHdrFieldValueStringSet(bufp, hdr_loc, field_loc, -1, location.c_str(), location.size())) {
    TSDebug(DEBUG_TAG, "Adding Location header %s", LOCATION_HEADER);
    TSMimeHdrFieldAppend(bufp, hdr_loc, field_loc);
  }

  TSHandleMLocRelease(bufp, hdr_loc, field_loc);

  TSHttpHdrStatusSet(bufp, hdr_loc, TS_HTTP_STATUS_SEE_OTHER);
  TSHttpHdrReasonSet(bufp, hdr_loc, REDIRECT_REASON, strlen(REDIRECT_REASON));
  return TS_SUCCESS;
}

static bool
check_internal_message_hdr(TSHttpTxn &txnp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  bool found = false;

  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("check_internal_message_hdr: couldn't retrieve client response header");
    return false;
  }

  TSMLoc header_loc = TSMimeHdrFieldFind(bufp, hdr_loc, ATS_INTERNAL_MESSAGE, strlen(ATS_INTERNAL_MESSAGE));
  if (header_loc) {
    found = true;
    // found the header, remove it now..
    TSMimeHdrFieldDestroy(bufp, hdr_loc, header_loc);
    TSHandleMLocRelease(bufp, hdr_loc, header_loc);
  }

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);

  return found;
}

static int
on_OS_DNS(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "OS_DNS request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_request_header(const RequestData *req, TSHttpTxn &txnp)
{
  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "Send_Req request delayed %d times, block origin req for url: %s", req->wl_retry, req->req_url.c_str());
    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_read_response_header(TSHttpTxn &txnp)
{
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_immediate(RequestData *req, TSCont &contp)
{
  if (!req) {
    TSError("%s: invalid req_data", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSDebug(DEBUG_TAG, "continuation delayed, scheduling now..for url: %s", req->req_url.c_str());

  // add retry_done header to prevent looping
  std::string value;
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(req->txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header for url %s", DEBUG_TAG,
            req->req_url.c_str());
    TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_ERROR);
    return TS_SUCCESS;
  }

  add_redirect_header(bufp, hdr_loc, req->req_url);
  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(req->txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
on_send_response_header(RequestData *req, TSHttpTxn &txnp, TSCont &contp)
{
  TSMBuffer bufp;
  TSMLoc hdr_loc;
  if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
    TSError("plugin=%s, level=error, error_code=could_not_retrieve_client_response_header", DEBUG_TAG);
    return TS_SUCCESS;
  }

  TSHttpStatus status = TSHttpHdrStatusGet(bufp, hdr_loc);
  TSDebug(DEBUG_TAG, "Response code: %d", status);

  if ((status == TS_HTTP_STATUS_BAD_GATEWAY) || (status == TS_HTTP_STATUS_SEE_OTHER)) {
    bool is_internal_message_hdr = check_internal_message_hdr(txnp);
    bool delay_request =
      is_internal_message_hdr || ((req->wl_retry > 0) && (req->wl_retry < OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES));

    if (delay_request) {
      req->wl_retry++;
      TSDebug(DEBUG_TAG, "delaying request, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(), req->wl_retry);
      TSContSchedule(contp, OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT, TS_THREAD_POOL_TASK);
      TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
      return TS_SUCCESS;
    }
  }

  if (req->wl_retry > 0) {
    TSDebug(DEBUG_TAG, "request delayed, but unsuccessful, url@%p: {{%s}} on retry: %d time", txnp, req->req_url.c_str(),
            req->wl_retry);
    req->wl_retry = 0;
  }

  // done..cleanup
  TSfree(req);
  TSContDestroy(contp);

  TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

static int
collapsed_cont(TSCont contp, TSEvent event, void *edata)
{
  TSHttpTxn txnp = static_cast<TSHttpTxn>(edata);
  RequestData *my_req = static_cast<RequestData *>(TSContDataGet(contp));

  switch (event) {
  case TS_EVENT_HTTP_OS_DNS: {
    return on_OS_DNS(my_req, txnp);
  }

  case TS_EVENT_HTTP_SEND_REQUEST_HDR: {
    return on_send_request_header(my_req, txnp);
  }

  case TS_EVENT_HTTP_READ_RESPONSE_HDR: {
    return on_read_response_header(txnp);
  }
  case TSEvent::TS_EVENT_IMMEDIATE:
  case TSEvent::TS_EVENT_TIMEOUT: {
    return on_immediate(my_req, contp);
  }
  case TS_EVENT_HTTP_SEND_RESPONSE_HDR: {
    return on_send_response_header(my_req, txnp, contp);
  }
  default: {
    TSDebug(DEBUG_TAG, "Unexpected event: %d", event);
    break;
  }
  }

  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
  return TS_SUCCESS;
}

TSReturnCode
TSRemapInit(TSRemapInterface * /* api_info */, char * /* errbuf */, int /* errbuf_size */)
{
  TSDebug(DEBUG_TAG, "plugin is succesfully initialized");
  return TS_SUCCESS;
}

TSReturnCode
TSRemapNewInstance(int argc, char *argv[], void ** /* ih */, char * /* errbuf */, int /* errbuf_size */)
{
  // basic argv processing..
  for (int i = 2; i < argc; ++i) {
    if (strncmp(argv[i], "--delay=", 8) == 0) {
      OPEN_WRITE_FAIL_REQ_DELAY_TIMEOUT = atoi((char *)(argv[i] + 8));
    } else if (strncmp(argv[i], "--retries=", 10) == 0) {
      OPEN_WRITE_FAIL_MAX_REQ_DELAY_RETRIES = atoi((char *)(argv[i] + 10));
    }
  }

  return TS_SUCCESS;
}

TSRemapStatus
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
{
  TSCont cont = TSContCreate(collapsed_cont, TSMutexCreate());

  RequestData *req_data;
  req_data = static_cast<RequestData *>(TSmalloc(sizeof(RequestData)));
  memset(req_data, 0, sizeof(RequestData));

  req_data->txnp = rh;
  req_data->wl_retry = 0;

  int url_len = 0;
  char *url = TSHttpTxnEffectiveUrlStringGet(rh, &url_len);
  req_data->req_url = std::string(url, url_len);

  TSfree(url);
  TSContDataSet(cont, req_data);

  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_REQUEST_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, cont);
  TSHttpTxnHookAdd(rh, TS_HTTP_OS_DNS_HOOK, cont);

  return TSREMAP_DID_REMAP;
}
<|endoftext|>


<commit_before>#pragma once

#include <eosio/chain/trace.hpp>
#include <eosio/chain/types.hpp>
#include <eosio/chain/block.hpp>
#include <eosio/chain/block_state.hpp>

namespace eosio { namespace trace_api_plugin {

   struct authorization_trace_v0 {
      chain::name account;
      chain::name permission;
   };

   struct action_trace_v0 {
      uint64_t                            global_sequence = {};
      chain::name                         receiver = {};
      chain::name                         account = {};
      chain::name                         action = {};
      std::vector<authorization_trace_v0> authorization = {};
      chain::bytes                        data = {};
   };

   struct transaction_trace_v0 {
      using status_type = chain::transaction_receipt_header::status_enum;

      chain::transaction_id_type    id = {};
      status_type                   status = status_type::hard_fail;
      std::vector<action_trace_v0>  actions = {};
   };

   struct block_trace_v0 {
      chain::block_id_type               id = {};
      uint64_t                           number = {};
      chain::block_id_type               previous_id = {};
      chain::block_timestamp_type        timestamp = chain::block_timestamp_type(0);
      chain::name                        producer = {};
      std::vector<transaction_trace_v0>  transactions = {};
   };

   action_trace_v0 to_action_trace_v0( const chain::action_trace& at ) {
      action_trace_v0 r;
      r.receiver = at.receiver;
      r.account = at.act.account;
      r.action = at.act.name;
      r.data = at.act.data;
      if( at.receipt ) {
         r.global_sequence = at.receipt->global_sequence;
      }
      r.authorization.reserve( at.act.authorization.size());
      for( const auto& auth : at.act.authorization ) {
         r.authorization.emplace_back( authorization_trace_v0{auth.actor, auth.permission} );
      }
      return r;
   }

   transaction_trace_v0 to_transaction_trace_v0( const chain::transaction_trace& t ) {
      transaction_trace_v0 r;
      r.id = t.id;
      if( t.receipt ) {
         r.status = t.receipt->status;
      } else {
         r.status = eosio::chain::transaction_receipt_header::status_enum::hard_fail; // not really, but better than other values
      }
      r.actions.reserve( t.action_traces.size());
      for( const auto& at : t.action_traces ) {
         r.actions.emplace_back( to_action_trace_v0( at ));
      }
      return r;
   }

   /// @return block_trace_v0 without any transaction_trace_v0
   block_trace_v0 create_block_trace_v0( const chain::block_state_ptr& bsp ) {
      block_trace_v0 r;
      r.id = bsp->id;
      r.number = bsp->block_num;
      r.previous_id = bsp->block->previous;
      r.timestamp = bsp->block->timestamp;
      r.producer = bsp->block->producer;
      return r;
   }

} }

FC_REFLECT(eosio::trace_api_plugin::authorization_trace_v0, (account)(permission))
FC_REFLECT(eosio::trace_api_plugin::action_trace_v0, (global_sequence)(receiver)(account)(action)(authorization)(data))
FC_REFLECT(eosio::trace_api_plugin::transaction_trace_v0, (id)(status)(actions))
FC_REFLECT(eosio::trace_api_plugin::block_trace_v0, (id)(number)(previous_id)(timestamp)(producer)(transactions))
<commit_msg>Use default hard_fail for status<commit_after>#pragma once

#include <eosio/chain/trace.hpp>
#include <eosio/chain/types.hpp>
#include <eosio/chain/block.hpp>
#include <eosio/chain/block_state.hpp>

namespace eosio { namespace trace_api_plugin {

   struct authorization_trace_v0 {
      chain::name account;
      chain::name permission;
   };

   struct action_trace_v0 {
      uint64_t                            global_sequence = {};
      chain::name                         receiver = {};
      chain::name                         account = {};
      chain::name                         action = {};
      std::vector<authorization_trace_v0> authorization = {};
      chain::bytes                        data = {};
   };

   struct transaction_trace_v0 {
      using status_type = chain::transaction_receipt_header::status_enum;

      chain::transaction_id_type    id = {};
      status_type                   status = status_type::hard_fail;
      std::vector<action_trace_v0>  actions = {};
   };

   struct block_trace_v0 {
      chain::block_id_type               id = {};
      uint64_t                           number = {};
      chain::block_id_type               previous_id = {};
      chain::block_timestamp_type        timestamp = chain::block_timestamp_type(0);
      chain::name                        producer = {};
      std::vector<transaction_trace_v0>  transactions = {};
   };

   /// Used by to_transaction_trace_v0 for creation of action_trace_v0
   inline action_trace_v0 to_action_trace_v0( const chain::action_trace& at ) {
      action_trace_v0 r;
      r.receiver = at.receiver;
      r.account = at.act.account;
      r.action = at.act.name;
      r.data = at.act.data;
      if( at.receipt ) {
         r.global_sequence = at.receipt->global_sequence;
      }
      r.authorization.reserve( at.act.authorization.size());
      for( const auto& auth : at.act.authorization ) {
         r.authorization.emplace_back( authorization_trace_v0{auth.actor, auth.permission} );
      }
      return r;
   }

   /// @return transaction_trace_v0 with populated action_trace_v0
   inline transaction_trace_v0 to_transaction_trace_v0( const chain::transaction_trace& t ) {
      transaction_trace_v0 r;
      r.id = t.id;
      if( t.receipt ) { // if no receipt leave as default hard_fail
         r.status = t.receipt->status;
      }
      r.actions.reserve( t.action_traces.size());
      for( const auto& at : t.action_traces ) {
         r.actions.emplace_back( to_action_trace_v0( at ));
      }
      return r;
   }

   /// @return block_trace_v0 without any transaction_trace_v0
   inline block_trace_v0 create_block_trace_v0( const chain::block_state_ptr& bsp ) {
      block_trace_v0 r;
      r.id = bsp->id;
      r.number = bsp->block_num;
      r.previous_id = bsp->block->previous;
      r.timestamp = bsp->block->timestamp;
      r.producer = bsp->block->producer;
      return r;
   }

} }

FC_REFLECT(eosio::trace_api_plugin::authorization_trace_v0, (account)(permission))
FC_REFLECT(eosio::trace_api_plugin::action_trace_v0, (global_sequence)(receiver)(account)(action)(authorization)(data))
FC_REFLECT(eosio::trace_api_plugin::transaction_trace_v0, (id)(status)(actions))
FC_REFLECT(eosio::trace_api_plugin::block_trace_v0, (id)(number)(previous_id)(timestamp)(producer)(transactions))
<|endoftext|>


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

#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_

#include <rmw/rmw.h>

#include <functional>
#include <memory>
#include <string>
#include <utility>

#include "rcl/error_handling.h"

#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"

namespace rclcpp
{
namespace experimental
{

template<
  typename MessageT,
  typename Alloc = std::allocator<void>,
  typename Deleter = std::default_delete<MessageT>,
  typename CallbackMessageT = MessageT>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)

  using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
  using MessageAlloc = typename MessageAllocTraits::allocator_type;
  using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
  using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;

  using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
    MessageT,
    Alloc,
    Deleter
    >::UniquePtr;

  SubscriptionIntraProcess(
    AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
    std::shared_ptr<Alloc> allocator,
    rclcpp::Context::SharedPtr context,
    const std::string & topic_name,
    rmw_qos_profile_t qos_profile,
    rclcpp::IntraProcessBufferType buffer_type)
  : SubscriptionIntraProcessBase(topic_name, qos_profile),
    any_callback_(callback)
  {
    if (!std::is_same<MessageT, CallbackMessageT>::value) {
      throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
    }

    // Create the intra-process buffer.
    buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
      buffer_type,
      qos_profile,
      allocator);

    // Create the guard condition.
    rcl_guard_condition_options_t guard_condition_options =
      rcl_guard_condition_get_default_options();

    gc_ = rcl_get_zero_initialized_guard_condition();
    rcl_ret_t ret = rcl_guard_condition_init(
      &gc_, context->get_rcl_context().get(), guard_condition_options);

    if (RCL_RET_OK != ret) {
      throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
    }

    TRACEPOINT(
      rclcpp_subscription_callback_added,
      static_cast<const void *>(this),
      static_cast<const void *>(&any_callback_));
    // The callback object gets copied, so if registration is done too early/before this point
    // (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
    // in subsequent tracepoints.
#ifndef TRACETOOLS_DISABLED
    any_callback_.register_callback_for_tracing();
#endif
  }

  bool
  is_ready(rcl_wait_set_t * wait_set)
  {
    (void)wait_set;
    return buffer_->has_data();
  }

  void execute()
  {
    execute_impl<CallbackMessageT>();
  }

  void
  provide_intra_process_message(ConstMessageSharedPtr message)
  {
    buffer_->add_shared(std::move(message));
    trigger_guard_condition();
  }

  void
  provide_intra_process_message(MessageUniquePtr message)
  {
    buffer_->add_unique(std::move(message));
    trigger_guard_condition();
  }

  bool
  use_take_shared_method() const
  {
    return buffer_->use_take_shared_method();
  }

private:
  void
  trigger_guard_condition()
  {
    rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
    (void)ret;
  }

  template<typename T>
  typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
  execute_impl()
  {
    throw std::runtime_error("Subscription intra-process can't handle serialized messages");
  }

  template<class T>
  typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
  execute_impl()
  {
    rmw_message_info_t msg_info;
    msg_info.publisher_gid = {0, {0}};
    msg_info.from_intra_process = true;

    if (any_callback_.use_take_shared_method()) {
      ConstMessageSharedPtr msg = buffer_->consume_shared();
      any_callback_.dispatch_intra_process(msg, msg_info);
    } else {
      MessageUniquePtr msg = buffer_->consume_unique();
      any_callback_.dispatch_intra_process(std::move(msg), msg_info);
    }
  }

  AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
  BufferUniquePtr buffer_;
};

}  // namespace experimental
}  // namespace rclcpp

#endif  // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
<commit_msg>Not finalize guard condition while destructing SubscriptionIntraProcess (#1307)<commit_after>// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_

#include <rmw/rmw.h>

#include <functional>
#include <memory>
#include <string>
#include <utility>

#include "rcl/error_handling.h"

#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"

namespace rclcpp
{
namespace experimental
{

template<
  typename MessageT,
  typename Alloc = std::allocator<void>,
  typename Deleter = std::default_delete<MessageT>,
  typename CallbackMessageT = MessageT>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)

  using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
  using MessageAlloc = typename MessageAllocTraits::allocator_type;
  using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
  using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;

  using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
    MessageT,
    Alloc,
    Deleter
    >::UniquePtr;

  SubscriptionIntraProcess(
    AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
    std::shared_ptr<Alloc> allocator,
    rclcpp::Context::SharedPtr context,
    const std::string & topic_name,
    rmw_qos_profile_t qos_profile,
    rclcpp::IntraProcessBufferType buffer_type)
  : SubscriptionIntraProcessBase(topic_name, qos_profile),
    any_callback_(callback)
  {
    if (!std::is_same<MessageT, CallbackMessageT>::value) {
      throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
    }

    // Create the intra-process buffer.
    buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
      buffer_type,
      qos_profile,
      allocator);

    // Create the guard condition.
    rcl_guard_condition_options_t guard_condition_options =
      rcl_guard_condition_get_default_options();

    gc_ = rcl_get_zero_initialized_guard_condition();
    rcl_ret_t ret = rcl_guard_condition_init(
      &gc_, context->get_rcl_context().get(), guard_condition_options);

    if (RCL_RET_OK != ret) {
      throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
    }

    TRACEPOINT(
      rclcpp_subscription_callback_added,
      static_cast<const void *>(this),
      static_cast<const void *>(&any_callback_));
    // The callback object gets copied, so if registration is done too early/before this point
    // (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
    // in subsequent tracepoints.
#ifndef TRACETOOLS_DISABLED
    any_callback_.register_callback_for_tracing();
#endif
  }

  ~SubscriptionIntraProcess()
  {
    if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
      RCUTILS_LOG_ERROR_NAMED(
        "rclcpp",
        "Failed to destroy guard condition: %s",
        rcutils_get_error_string().str);
    }
  }

  bool
  is_ready(rcl_wait_set_t * wait_set)
  {
    (void)wait_set;
    return buffer_->has_data();
  }

  void execute()
  {
    execute_impl<CallbackMessageT>();
  }

  void
  provide_intra_process_message(ConstMessageSharedPtr message)
  {
    buffer_->add_shared(std::move(message));
    trigger_guard_condition();
  }

  void
  provide_intra_process_message(MessageUniquePtr message)
  {
    buffer_->add_unique(std::move(message));
    trigger_guard_condition();
  }

  bool
  use_take_shared_method() const
  {
    return buffer_->use_take_shared_method();
  }

private:
  void
  trigger_guard_condition()
  {
    rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
    (void)ret;
  }

  template<typename T>
  typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
  execute_impl()
  {
    throw std::runtime_error("Subscription intra-process can't handle serialized messages");
  }

  template<class T>
  typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
  execute_impl()
  {
    rmw_message_info_t msg_info;
    msg_info.publisher_gid = {0, {0}};
    msg_info.from_intra_process = true;

    if (any_callback_.use_take_shared_method()) {
      ConstMessageSharedPtr msg = buffer_->consume_shared();
      any_callback_.dispatch_intra_process(msg, msg_info);
    } else {
      MessageUniquePtr msg = buffer_->consume_unique();
      any_callback_.dispatch_intra_process(std::move(msg), msg_info);
    }
  }

  AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
  BufferUniquePtr buffer_;
};

}  // namespace experimental
}  // namespace rclcpp

#endif  // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
<|endoftext|>