<commit_before>//===- DCE.cpp - Code to perform dead code elimination --------------------===//
//
// This file implements dead code elimination and basic block merging.
//
// Specifically, this:
//   * removes definitions with no uses (including unused constants)
//   * removes basic blocks with no predecessors
//   * merges a basic block into its predecessor if there is only one and the
//     predecessor only has one successor.
//   * Eliminates PHI nodes for basic blocks with a single predecessor
//   * Eliminates a basic block that only contains an unconditional branch
//
// TODO: This should REALLY be worklist driven instead of iterative.  Right now,
// we scan linearly through values, removing unused ones as we go.  The problem
// is that this may cause other earlier values to become unused.  To make sure
// that we get them all, we iterate until things stop changing.  Instead, when
// removing a value, recheck all of its operands to see if they are now unused.
// Piece of cake, and more efficient as well.
//
// Note, this is not trivial, because we have to worry about invalidating
// iterators.  :(
//
//===----------------------------------------------------------------------===//

#include "llvm/Optimizations/DCE.h"
#include "llvm/Support/STLExtras.h"
#include "llvm/Module.h"
#include "llvm/Method.h"
#include "llvm/BasicBlock.h"
#include "llvm/iTerminators.h"
#include "llvm/iOther.h"
#include "llvm/Assembly/Writer.h"
#include "llvm/CFG.h"
#include <algorithm>

using namespace cfg;

struct ConstPoolDCE {
  enum { EndOffs = 0 };
  static bool isDCEable(const ConstPoolVal *CPV) {
    // TODO: The bytecode writer requires that all used types are in the
    // constant pool for the current method.  This is messy and is really
    // irritating. FIXME
    return CPV->getType() != Type::TypeTy;  // Don't DCE Type plane constants!
  }
};

struct BasicBlockDCE {
  enum { EndOffs = 1 };
  static bool isDCEable(const Instruction *I) {
    return !I->hasSideEffects();
  }
};

template<class Container, class DCEController>
static bool RemoveUnusedDefs(Container &Vals, DCEController DCEControl) {
  bool Changed = false;
  int Offset = DCEController::EndOffs;

  for (typename Container::iterator DI = Vals.begin();
       DI != Vals.end()-Offset; ) {
    // Look for un"used" definitions...
    if ((*DI)->use_empty() && DCEController::isDCEable(*DI)) {
      // Bye bye
      //cerr << "Removing: " << *DI;
      delete Vals.remove(DI);
      Changed = true;
    } else {
      ++DI;
    }
  }
  return Changed;
}

// RemoveSingularPHIs - This removes PHI nodes from basic blocks that have only
// a single predecessor.  This means that the PHI node must only have a single
// RHS value and can be eliminated.
//
// This routine is very simple because we know that PHI nodes must be the first
// things in a basic block, if they are present.
//
static bool RemoveSingularPHIs(BasicBlock *BB) {
  pred_iterator PI(pred_begin(BB));
  if (PI == pred_end(BB) || ++PI != pred_end(BB))
    return false;   // More than one predecessor...

  Instruction *I = BB->front();
  if (!I->isPHINode()) return false;  // No PHI nodes

  //cerr << "Killing PHIs from " << BB;
  //cerr << "Pred #0 = " << *pred_begin(BB);

  //cerr << "Method == " << BB->getParent();

  do {
    PHINode *PN = (PHINode*)I;
    assert(PN->getNumOperands() == 2 && "PHI node should only have one value!");
    Value *V = PN->getOperand(0);

    PN->replaceAllUsesWith(V);      // Replace PHI node with its single value.
    delete BB->getInstList().remove(BB->begin());

    I = BB->front();
  } while (I->isPHINode());

  return true;  // Yes, we nuked at least one phi node
}

bool opt::DoRemoveUnusedConstants(SymTabValue *S) {
  bool Changed = false;
  ConstantPool &CP = S->getConstantPool();
  for (ConstantPool::plane_iterator PI = CP.begin(); PI != CP.end(); ++PI)
    Changed |= RemoveUnusedDefs(**PI, ConstPoolDCE());
  return Changed;
}

static void ReplaceUsesWithConstant(Instruction *I) {
  // Get the method level constant pool
  ConstantPool &CP = I->getParent()->getParent()->getConstantPool();

  ConstPoolVal *CPV = 0;
  ConstantPool::PlaneType *P;
  if (!CP.getPlane(I->getType(), P)) {  // Does plane exist?
    // Yes, is it empty?
    if (!P->empty()) CPV = P->front();
  }

  if (CPV == 0) { // We don't have an existing constant to reuse.  Just add one.
    CPV = ConstPoolVal::getNullConstant(I->getType());  // Create a new constant

    // Add the new value to the constant pool...
    CP.insert(CPV);
  }

  // Make all users of this instruction reference the constant instead
  I->replaceAllUsesWith(CPV);
}

// PropogatePredecessors - This gets "Succ" ready to have the predecessors from
// "BB".  This is a little tricky because "Succ" has PHI nodes, which need to
// have extra slots added to them to hold the merge edges from BB's
// predecessors.
//
// Assumption: BB is the single predecessor of Succ.
//
static void PropogatePredecessorsForPHIs(BasicBlock *BB, BasicBlock *Succ) {
  assert(Succ->front()->isPHINode() && "Only works on PHId BBs!");

  // If there is more than one predecessor, and there are PHI nodes in
  // the successor, then we need to add incoming edges for the PHI nodes
  //
  const vector<BasicBlock*> BBPreds(pred_begin(BB), pred_end(BB));

  BasicBlock::iterator I = Succ->begin();
  do {                     // Loop over all of the PHI nodes in the successor BB
    PHINode *PN = (PHINode*)*I;
    Value *OldVal = PN->removeIncomingValue(BB);
    assert(OldVal && "No entry in PHI for Pred BB!");

    for (vector<BasicBlock*>::const_iterator PredI = BBPreds.begin(),
	   End = BBPreds.end(); PredI != End; ++PredI) {
      // Add an incoming value for each of the new incoming values...
      PN->addIncoming(OldVal, *PredI);
    }

    ++I;
  } while ((*I)->isPHINode());
}

// SimplifyCFG - This function is used to do simplification of a CFG.  For
// example, it adjusts branches to branches to eliminate the extra hop, it
// eliminates unreachable basic blocks, and does other "peephole" optimization
// of the CFG.  It returns true if a modification was made, and returns an
// iterator that designates the first element remaining after the block that
// was deleted.
//
// WARNING:  The entry node of a method may not be simplified.
//
bool opt::SimplifyCFG(Method::iterator &BBIt) {
  assert(*BBIt && (*BBIt)->getParent() && "Block not embedded in method!");
  BasicBlock *BB = *BBIt;
  Method *M = BB->getParent();
  assert(BB->getTerminator() && "Degenerate basic block encountered!");
  assert(BB->getParent()->front() != BB && "Can't Simplify entry block!");

  // Remove basic blocks that have no predecessors... which are unreachable.
  if (pred_begin(BB) == pred_end(BB) &&
      !BB->hasConstantPoolReferences()) {
    //cerr << "Removing BB: \n" << BB;

    // Loop through all of our successors and make sure they know that one
    // of their predecessors is going away.
    for_each(succ_begin(BB), succ_end(BB),
	     std::bind2nd(std::mem_fun(&BasicBlock::removePredecessor), BB));

    while (!BB->empty()) {
      Instruction *I = BB->back();
      // If this instruction is used, replace uses with an arbitrary
      // constant value.  Because control flow can't get here, we don't care
      // what we replace the value with.  Note that since this block is
      // unreachable, and all values contained within it must dominate their
      // uses, that all uses will eventually be removed.
      if (!I->use_empty()) ReplaceUsesWithConstant(I);

      // Remove the instruction from the basic block
      delete BB->getInstList().pop_back();
    }
    delete M->getBasicBlocks().remove(BBIt);
    return true;
  }

  // Check to see if this block has no instructions and only a single
  // successor.  If so, replace block references with successor.
  succ_iterator SI(succ_begin(BB));
  if (SI != succ_end(BB) && ++SI == succ_end(BB)) {  // One succ?
    Instruction *I = BB->front();
    if (I->isTerminator()) {   // Terminator is the only instruction!
      BasicBlock *Succ = *succ_begin(BB); // There is exactly one successor
      //cerr << "Killing Trivial BB: \n" << BB;

      if (Succ != BB) {   // Arg, don't hurt infinite loops!
	if (Succ->front()->isPHINode()) {
	  // If our successor has PHI nodes, then we need to update them to
	  // include entries for BB's predecessors, not for BB itself.
	  //
	  PropogatePredecessorsForPHIs(BB, Succ);
	}

	BB->replaceAllUsesWith(Succ);
	BB = M->getBasicBlocks().remove(BBIt);

	if (BB->hasName() && !Succ->hasName())  // Transfer name if we can
	  Succ->setName(BB->getName());
	delete BB;                              // Delete basic block

	//cerr << "Method after removal: \n" << M;
	return true;
      }
    }
  }

  // Merge basic blocks into their predecessor if there is only one pred,
  // and if there is only one successor of the predecessor.
  pred_iterator PI(pred_begin(BB));
  if (PI != pred_end(BB) && *PI != BB &&    // Not empty?  Not same BB?
      ++PI == pred_end(BB) && !BB->hasConstantPoolReferences()) {
    BasicBlock *Pred = *pred_begin(BB);
    TerminatorInst *Term = Pred->getTerminator();
    assert(Term != 0 && "malformed basic block without terminator!");

    // Does the predecessor block only have a single successor?
    succ_iterator SI(succ_begin(Pred));
    if (++SI == succ_end(Pred)) {
      //cerr << "Merging: " << BB << "into: " << Pred;

      // Delete the unconditianal branch from the predecessor...
      BasicBlock::iterator DI = Pred->end();
      assert(Pred->getTerminator() &&
	     "Degenerate basic block encountered!");  // Empty bb???
      delete Pred->getInstList().remove(--DI);        // Destroy uncond branch

      // Move all definitions in the succecessor to the predecessor...
      while (!BB->empty()) {
	DI = BB->begin();
	Instruction *Def = BB->getInstList().remove(DI); // Remove from front
	Pred->getInstList().push_back(Def);              // Add to end...
      }

      // Remove basic block from the method... and advance iterator to the
      // next valid block...
      BB = M->getBasicBlocks().remove(BBIt);

      // Make all PHI nodes that refered to BB now refer to Pred as their
      // source...
      BB->replaceAllUsesWith(Pred);

      // Inherit predecessors name if it exists...
      if (BB->hasName() && !Pred->hasName()) Pred->setName(BB->getName());

      delete BB; // You ARE the weakest link... goodbye
      return true;
    }
  }

  return false;
}

static bool DoDCEPass(Method *M) {
  Method::iterator BBIt, BBEnd = M->end();
  if (M->begin() == BBEnd) return false;  // Nothing to do
  bool Changed = false;

  // Loop through now and remove instructions that have no uses...
  for (BBIt = M->begin(); BBIt != BBEnd; ++BBIt) {
    Changed |= RemoveUnusedDefs((*BBIt)->getInstList(), BasicBlockDCE());
    Changed |= RemoveSingularPHIs(*BBIt);
  }

  // Loop over all of the basic blocks (except the first one) and remove them
  // if they are unneeded...
  //
  for (BBIt = M->begin(), ++BBIt; BBIt != M->end(); ) {
    if (opt::SimplifyCFG(BBIt)) {
      Changed = true;
    } else {
      ++BBIt;
    }
  }

  // Remove unused constants
  return Changed | opt::DoRemoveUnusedConstants(M);
}

// It is possible that we may require multiple passes over the code to fully
// eliminate dead code.  Iterate until we are done.
//
bool opt::DoDeadCodeElimination(Method *M) {
  bool Changed = false;
  while (DoDCEPass(M)) Changed = true;
  return Changed;
}

bool opt::DoDeadCodeElimination(Module *C) {
  bool Val = C->reduceApply(DoDeadCodeElimination);

  while (DoRemoveUnusedConstants(C)) Val = true;
  return Val;
}
<commit_msg>Enable the elimination of method prototypes that are not referenced<commit_after>//===- DCE.cpp - Code to perform dead code elimination --------------------===//
//
// This file implements dead code elimination and basic block merging.
//
// Specifically, this:
//   * removes definitions with no uses (including unused constants)
//   * removes basic blocks with no predecessors
//   * merges a basic block into its predecessor if there is only one and the
//     predecessor only has one successor.
//   * Eliminates PHI nodes for basic blocks with a single predecessor
//   * Eliminates a basic block that only contains an unconditional branch
//   * Eliminates method prototypes that are not referenced
//
// TODO: This should REALLY be worklist driven instead of iterative.  Right now,
// we scan linearly through values, removing unused ones as we go.  The problem
// is that this may cause other earlier values to become unused.  To make sure
// that we get them all, we iterate until things stop changing.  Instead, when
// removing a value, recheck all of its operands to see if they are now unused.
// Piece of cake, and more efficient as well.
//
// Note, this is not trivial, because we have to worry about invalidating
// iterators.  :(
//
//===----------------------------------------------------------------------===//

#include "llvm/Optimizations/DCE.h"
#include "llvm/Support/STLExtras.h"
#include "llvm/Module.h"
#include "llvm/Method.h"
#include "llvm/BasicBlock.h"
#include "llvm/iTerminators.h"
#include "llvm/iOther.h"
#include "llvm/Assembly/Writer.h"
#include "llvm/CFG.h"
#include <algorithm>

using namespace cfg;

struct ConstPoolDCE {
  enum { EndOffs = 0 };
  static bool isDCEable(const ConstPoolVal *CPV) {
    // TODO: The bytecode writer requires that all used types are in the
    // constant pool for the current method.  This is messy and is really
    // irritating. FIXME
    return CPV->getType() != Type::TypeTy;  // Don't DCE Type plane constants!
  }
};

struct BasicBlockDCE {
  enum { EndOffs = 1 };
  static bool isDCEable(const Instruction *I) {
    return !I->hasSideEffects();
  }
};

template<class Container, class DCEController>
static bool RemoveUnusedDefs(Container &Vals, DCEController DCEControl) {
  bool Changed = false;
  int Offset = DCEController::EndOffs;

  for (typename Container::iterator DI = Vals.begin();
       DI != Vals.end()-Offset; ) {
    // Look for un"used" definitions...
    if ((*DI)->use_empty() && DCEController::isDCEable(*DI)) {
      // Bye bye
      //cerr << "Removing: " << *DI;
      delete Vals.remove(DI);
      Changed = true;
    } else {
      ++DI;
    }
  }
  return Changed;
}

// RemoveSingularPHIs - This removes PHI nodes from basic blocks that have only
// a single predecessor.  This means that the PHI node must only have a single
// RHS value and can be eliminated.
//
// This routine is very simple because we know that PHI nodes must be the first
// things in a basic block, if they are present.
//
static bool RemoveSingularPHIs(BasicBlock *BB) {
  pred_iterator PI(pred_begin(BB));
  if (PI == pred_end(BB) || ++PI != pred_end(BB))
    return false;   // More than one predecessor...

  Instruction *I = BB->front();
  if (!I->isPHINode()) return false;  // No PHI nodes

  //cerr << "Killing PHIs from " << BB;
  //cerr << "Pred #0 = " << *pred_begin(BB);

  //cerr << "Method == " << BB->getParent();

  do {
    PHINode *PN = (PHINode*)I;
    assert(PN->getNumOperands() == 2 && "PHI node should only have one value!");
    Value *V = PN->getOperand(0);

    PN->replaceAllUsesWith(V);      // Replace PHI node with its single value.
    delete BB->getInstList().remove(BB->begin());

    I = BB->front();
  } while (I->isPHINode());

  return true;  // Yes, we nuked at least one phi node
}

bool opt::DoRemoveUnusedConstants(SymTabValue *S) {
  bool Changed = false;
  ConstantPool &CP = S->getConstantPool();
  for (ConstantPool::plane_iterator PI = CP.begin(); PI != CP.end(); ++PI)
    Changed |= RemoveUnusedDefs(**PI, ConstPoolDCE());
  return Changed;
}

static void ReplaceUsesWithConstant(Instruction *I) {
  // Get the method level constant pool
  ConstantPool &CP = I->getParent()->getParent()->getConstantPool();

  ConstPoolVal *CPV = 0;
  ConstantPool::PlaneType *P;
  if (!CP.getPlane(I->getType(), P)) {  // Does plane exist?
    // Yes, is it empty?
    if (!P->empty()) CPV = P->front();
  }

  if (CPV == 0) { // We don't have an existing constant to reuse.  Just add one.
    CPV = ConstPoolVal::getNullConstant(I->getType());  // Create a new constant

    // Add the new value to the constant pool...
    CP.insert(CPV);
  }

  // Make all users of this instruction reference the constant instead
  I->replaceAllUsesWith(CPV);
}

// PropogatePredecessors - This gets "Succ" ready to have the predecessors from
// "BB".  This is a little tricky because "Succ" has PHI nodes, which need to
// have extra slots added to them to hold the merge edges from BB's
// predecessors.
//
// Assumption: BB is the single predecessor of Succ.
//
static void PropogatePredecessorsForPHIs(BasicBlock *BB, BasicBlock *Succ) {
  assert(Succ->front()->isPHINode() && "Only works on PHId BBs!");

  // If there is more than one predecessor, and there are PHI nodes in
  // the successor, then we need to add incoming edges for the PHI nodes
  //
  const vector<BasicBlock*> BBPreds(pred_begin(BB), pred_end(BB));

  BasicBlock::iterator I = Succ->begin();
  do {                     // Loop over all of the PHI nodes in the successor BB
    PHINode *PN = (PHINode*)*I;
    Value *OldVal = PN->removeIncomingValue(BB);
    assert(OldVal && "No entry in PHI for Pred BB!");

    for (vector<BasicBlock*>::const_iterator PredI = BBPreds.begin(),
	   End = BBPreds.end(); PredI != End; ++PredI) {
      // Add an incoming value for each of the new incoming values...
      PN->addIncoming(OldVal, *PredI);
    }

    ++I;
  } while ((*I)->isPHINode());
}

// SimplifyCFG - This function is used to do simplification of a CFG.  For
// example, it adjusts branches to branches to eliminate the extra hop, it
// eliminates unreachable basic blocks, and does other "peephole" optimization
// of the CFG.  It returns true if a modification was made, and returns an
// iterator that designates the first element remaining after the block that
// was deleted.
//
// WARNING:  The entry node of a method may not be simplified.
//
bool opt::SimplifyCFG(Method::iterator &BBIt) {
  assert(*BBIt && (*BBIt)->getParent() && "Block not embedded in method!");
  BasicBlock *BB = *BBIt;
  Method *M = BB->getParent();
  assert(BB->getTerminator() && "Degenerate basic block encountered!");
  assert(BB->getParent()->front() != BB && "Can't Simplify entry block!");

  // Remove basic blocks that have no predecessors... which are unreachable.
  if (pred_begin(BB) == pred_end(BB) &&
      !BB->hasConstantPoolReferences()) {
    //cerr << "Removing BB: \n" << BB;

    // Loop through all of our successors and make sure they know that one
    // of their predecessors is going away.
    for_each(succ_begin(BB), succ_end(BB),
	     std::bind2nd(std::mem_fun(&BasicBlock::removePredecessor), BB));

    while (!BB->empty()) {
      Instruction *I = BB->back();
      // If this instruction is used, replace uses with an arbitrary
      // constant value.  Because control flow can't get here, we don't care
      // what we replace the value with.  Note that since this block is
      // unreachable, and all values contained within it must dominate their
      // uses, that all uses will eventually be removed.
      if (!I->use_empty()) ReplaceUsesWithConstant(I);

      // Remove the instruction from the basic block
      delete BB->getInstList().pop_back();
    }
    delete M->getBasicBlocks().remove(BBIt);
    return true;
  }

  // Check to see if this block has no instructions and only a single
  // successor.  If so, replace block references with successor.
  succ_iterator SI(succ_begin(BB));
  if (SI != succ_end(BB) && ++SI == succ_end(BB)) {  // One succ?
    Instruction *I = BB->front();
    if (I->isTerminator()) {   // Terminator is the only instruction!
      BasicBlock *Succ = *succ_begin(BB); // There is exactly one successor
      //cerr << "Killing Trivial BB: \n" << BB;

      if (Succ != BB) {   // Arg, don't hurt infinite loops!
	if (Succ->front()->isPHINode()) {
	  // If our successor has PHI nodes, then we need to update them to
	  // include entries for BB's predecessors, not for BB itself.
	  //
	  PropogatePredecessorsForPHIs(BB, Succ);
	}

	BB->replaceAllUsesWith(Succ);
	BB = M->getBasicBlocks().remove(BBIt);

	if (BB->hasName() && !Succ->hasName())  // Transfer name if we can
	  Succ->setName(BB->getName());
	delete BB;                              // Delete basic block

	//cerr << "Method after removal: \n" << M;
	return true;
      }
    }
  }

  // Merge basic blocks into their predecessor if there is only one pred,
  // and if there is only one successor of the predecessor.
  pred_iterator PI(pred_begin(BB));
  if (PI != pred_end(BB) && *PI != BB &&    // Not empty?  Not same BB?
      ++PI == pred_end(BB) && !BB->hasConstantPoolReferences()) {
    BasicBlock *Pred = *pred_begin(BB);
    TerminatorInst *Term = Pred->getTerminator();
    assert(Term != 0 && "malformed basic block without terminator!");

    // Does the predecessor block only have a single successor?
    succ_iterator SI(succ_begin(Pred));
    if (++SI == succ_end(Pred)) {
      //cerr << "Merging: " << BB << "into: " << Pred;

      // Delete the unconditianal branch from the predecessor...
      BasicBlock::iterator DI = Pred->end();
      assert(Pred->getTerminator() &&
	     "Degenerate basic block encountered!");  // Empty bb???
      delete Pred->getInstList().remove(--DI);        // Destroy uncond branch

      // Move all definitions in the succecessor to the predecessor...
      while (!BB->empty()) {
	DI = BB->begin();
	Instruction *Def = BB->getInstList().remove(DI); // Remove from front
	Pred->getInstList().push_back(Def);              // Add to end...
      }

      // Remove basic block from the method... and advance iterator to the
      // next valid block...
      BB = M->getBasicBlocks().remove(BBIt);

      // Make all PHI nodes that refered to BB now refer to Pred as their
      // source...
      BB->replaceAllUsesWith(Pred);

      // Inherit predecessors name if it exists...
      if (BB->hasName() && !Pred->hasName()) Pred->setName(BB->getName());

      delete BB; // You ARE the weakest link... goodbye
      return true;
    }
  }

  return false;
}

static bool DoDCEPass(Method *M) {
  Method::iterator BBIt, BBEnd = M->end();
  if (M->begin() == BBEnd) return false;  // Nothing to do
  bool Changed = false;

  // Loop through now and remove instructions that have no uses...
  for (BBIt = M->begin(); BBIt != BBEnd; ++BBIt) {
    Changed |= RemoveUnusedDefs((*BBIt)->getInstList(), BasicBlockDCE());
    Changed |= RemoveSingularPHIs(*BBIt);
  }

  // Loop over all of the basic blocks (except the first one) and remove them
  // if they are unneeded...
  //
  for (BBIt = M->begin(), ++BBIt; BBIt != M->end(); ) {
    if (opt::SimplifyCFG(BBIt)) {
      Changed = true;
    } else {
      ++BBIt;
    }
  }

  // Remove unused constants
  return Changed | opt::DoRemoveUnusedConstants(M);
}

// It is possible that we may require multiple passes over the code to fully
// eliminate dead code.  Iterate until we are done.
//
bool opt::DoDeadCodeElimination(Method *M) {
  bool Changed = false;
  while (DoDCEPass(M)) Changed = true;
  return Changed;
}

bool opt::DoDeadCodeElimination(Module *Mod) {
  bool Changed = false;

  for (Module::iterator MI = Mod->begin(); MI != Mod->end(); ) {
    Method *Meth = *MI;
    if (!Meth->isExternal()) {                 // DCE normal methods
      Changed |= DoDeadCodeElimination(Meth);
      ++MI;                                    // Next method please
    } else if (Meth->use_size() == 0) {        // No references to prototype?
      //cerr << "Removing method proto: " << Meth->getName() << endl;
      delete Mod->getMethodList().remove(MI);  // Remove prototype
      // Remove moves iterator to point to the next one automatically
    } else {
      ++MI;                                    // Skip prototype in use.
    }
  }

  while (DoRemoveUnusedConstants(Mod)) Changed = true;
  return Changed;
}
<|endoftext|>


<commit_before>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <SDL_opengl.h>
#include <SDL.h>

#include <algorithm>
#include <cmath>

#include "game.hxx"
#include "player.hxx"
#include "globals.hxx"
#include "music_player.hxx"

#define ACCEL -0.015f
#define MIN_CONV 0.5f
#define MAX_CONV 2.5f

using namespace std;

Game::Game()
: currentConvulsion(0), targetConvulsion(1),
  playerAmplitude(0),
  enemyFactory(*this),
  speed(-2.0f)
{
  player = new Player(&field, &distortion, getNearClippingPlane(),
                      &playerDeath, this);
  field.add(player);
}

Game::~Game() {}

void Game::update(float et) {
  float distortionDiff = targetConvulsion - currentConvulsion;
  distortionDiff *= pow(0.1f, et);
  currentConvulsion = targetConvulsion - distortionDiff;
  distortion.setConvulsionMult(currentConvulsion);

  if (player)
    player->setAmplitude(playerAmplitude);

  enemyFactory.update(et, -speed);
  tunnel.update(et);
  field.update(et);

  float oldNear = getNearClippingPlane();
  speed += ACCEL*et;
  float nearDelta = getNearClippingPlane() - oldNear;
  float shift = speed*et;
  if (player) {
    player->shotSpeed = -speed;

    float oldz = player->getZ();
    player->advance(shift, nearDelta);
    if (player)
      shift = player->getZ() - oldz + nearDelta;
  }

  field.translateZ(shift);
  tunnel.translateZ(shift);
  distortion.translateZ(shift, -speed);
}

void Game::configureGL() {
  glEnable(GL_FOG);
  glFogi(GL_FOG_MODE, GL_LINEAR);
  glFogf(GL_FOG_DENSITY, 0.25f);
  glFogf(GL_FOG_START, max(1.0f, getNearClippingPlane()));
  glFogf(GL_FOG_END, max(1.1f, 1.1f*getSpawnDistance()));

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-1, 1, -vheight, vheight, getNearClippingPlane(), 128.0f);
  glScalef(2.0f, 2.0f, 1);
  glTranslatef(-0.5f, -vheight/2.0f, 0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void Game::draw() {
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  tunnel.draw(distortion);
  field.draw();
}

void Game::motion(float x,float) {
  if (player)
    player->move(x);
}

void Game::button(unsigned button) {
  if (player) {
    if (button == SDL_BUTTON_LEFT)
      player->jump();
  }
}

bool Game::running() const {
  return !!player;
}

void Game::playerDeath(void* that) {
  ((Game*)that)->player = NULL;
}

float Game::getNearClippingPlane() const {
  return -1.5f/speed;
}

float Game::getSpawnDistance() const {
  return min(8*-speed, Tunnel::gridLength*Tunnel::gsqlen/2);
}

float Game::getPlayerX() const {
  return player? player->getX() : 0.5f;
}

static void amplitudeCallback(void* thisVoid, float amp) {
  ((Game*)thisVoid)->amplitude(amp);
}

void Game::amplitude(float amp) {
  // XXX Minor threading issues here; we could see a partial floating-point
  // update. Oh well.
  targetConvulsion = amp*(MAX_CONV - MIN_CONV) + MIN_CONV;
  playerAmplitude = amp;
}

void Game::startMusic(const char*const* list, unsigned len) {
  mixer.play(new MusicPlayer(list, len, amplitudeCallback, this), 0x7FFF);
}
<commit_msg>Use a perspective which more closely follows the player.<commit_after>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <SDL_opengl.h>
#include <SDL.h>

#include <algorithm>
#include <cmath>

#include "game.hxx"
#include "player.hxx"
#include "globals.hxx"
#include "music_player.hxx"

#define ACCEL -0.015f
#define MIN_CONV 0.5f
#define MAX_CONV 2.5f

using namespace std;

Game::Game()
: currentConvulsion(0), targetConvulsion(1),
  playerAmplitude(0),
  enemyFactory(*this),
  speed(-2.0f)
{
  player = new Player(&field, &distortion, getNearClippingPlane(),
                      &playerDeath, this);
  field.add(player);
}

Game::~Game() {}

void Game::update(float et) {
  float distortionDiff = targetConvulsion - currentConvulsion;
  distortionDiff *= pow(0.1f, et);
  currentConvulsion = targetConvulsion - distortionDiff;
  distortion.setConvulsionMult(currentConvulsion);

  if (player)
    player->setAmplitude(playerAmplitude);

  enemyFactory.update(et, -speed);
  tunnel.update(et);
  field.update(et);

  float oldNear = getNearClippingPlane();
  speed += ACCEL*et;
  float nearDelta = getNearClippingPlane() - oldNear;
  float shift = speed*et;
  if (player) {
    player->shotSpeed = -speed;

    float oldz = player->getZ();
    player->advance(shift, nearDelta);
    if (player)
      shift = player->getZ() - oldz + nearDelta;
  }

  field.translateZ(shift);
  tunnel.translateZ(shift);
  distortion.translateZ(shift, -speed);
}

void Game::configureGL() {
  glEnable(GL_FOG);
  glFogi(GL_FOG_MODE, GL_LINEAR);
  glFogf(GL_FOG_DENSITY, 0.25f);
  glFogf(GL_FOG_START, max(1.0f, getNearClippingPlane()));
  glFogf(GL_FOG_END, max(1.1f, 1.1f*getSpawnDistance()));

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-1/10.0f, 1/10.0f, -vheight/10.0f, vheight/10.0f,
            getNearClippingPlane(), 128.0f);
  glScalef(2.0f, 2.0f, 1);
  float tx, ty;
  if (player) {
    tx = - (player->x - 0.5f)*0.9f - 0.5f;
    ty = -vheight/3.0f - player->y;
  } else {
    tx = -0.5f;
    ty = -vheight/3.0f;
  }

  glTranslatef(tx, ty, -0.5f);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void Game::draw() {
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
  tunnel.draw(distortion);
  field.draw();
}

void Game::motion(float x,float) {
  if (player)
    player->move(x);
}

void Game::button(unsigned button) {
  if (player) {
    if (button == SDL_BUTTON_LEFT)
      player->jump();
  }
}

bool Game::running() const {
  return !!player;
}

void Game::playerDeath(void* that) {
  ((Game*)that)->player = NULL;
}

float Game::getNearClippingPlane() const {
  return -1.5f/speed/10.0f;
}

float Game::getSpawnDistance() const {
  return min(8*-speed, Tunnel::gridLength*Tunnel::gsqlen/2);
}

float Game::getPlayerX() const {
  return player? player->getX() : 0.5f;
}

static void amplitudeCallback(void* thisVoid, float amp) {
  ((Game*)thisVoid)->amplitude(amp);
}

void Game::amplitude(float amp) {
  // XXX Minor threading issues here; we could see a partial floating-point
  // update. Oh well.
  targetConvulsion = amp*(MAX_CONV - MIN_CONV) + MIN_CONV;
  playerAmplitude = amp;
}

void Game::startMusic(const char*const* list, unsigned len) {
  mixer.play(new MusicPlayer(list, len, amplitudeCallback, this), 0x7FFF);
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "rpcnetwork.h"
#include "rpcservicepool.h"
#include "rpcsendv2.h"
#include "rpctargetpool.h"
#include "rpcnetworkparams.h"
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/scheduler.h>
#include <vespa/fnet/transport.h>
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/errorcode.h>
#include <vespa/messagebus/iprotocol.h>
#include <vespa/messagebus/routing/routingnode.h>
#include <vespa/slobrok/sbmirror.h>
#include <vespa/slobrok/sbregister.h>
#include <vespa/vespalib/component/vtag.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/size_literals.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/fastos/thread.h>
#include <thread>

#include <vespa/log/log.h>
LOG_SETUP(".rpcnetwork");

using vespalib::make_string;
using namespace std::chrono_literals;

namespace mbus {

namespace {

/**
 * Implements a helper class for {@link RPCNetwork#sync()}. It provides a
 * blocking method {@link #await()} that will wait until the internal state
 * of this object is set to 'done'. By scheduling this task in the network
 * thread and then calling this method, we achieve handshaking with the network
 * thread.
 */
class SyncTask : public FNET_Task {
private:
    vespalib::Gate _gate;

public:
    SyncTask(FNET_Scheduler &s) :
        FNET_Task(&s),
        _gate() {
        ScheduleNow();
    }
    ~SyncTask() override {
        Kill();
    }

    void await() {
        _gate.await();
    }

    void PerformTask() override {
        _gate.countDown();
    }
};

struct TargetPoolTask : public FNET_Task {
    RPCTargetPool &_pool;

    TargetPoolTask(FNET_Scheduler &scheduler, RPCTargetPool &pool)
        : FNET_Task(&scheduler),
        _pool(pool)
    {
        ScheduleNow();
    }
    ~TargetPoolTask() override {
        Kill();
    }
    void PerformTask() override {
        _pool.flushTargets(false);
        Schedule(1.0);
    }
};

fnet::TransportConfig
toFNETConfig(const RPCNetworkParams & params) {
    return fnet::TransportConfig(params.getNumNetworkThreads())
              .maxInputBufferSize(params.getMaxInputBufferSize())
              .maxOutputBufferSize(params.getMaxOutputBufferSize())
              .tcpNoDelay(params.getTcpNoDelay())
              .events_before_wakeup(params.events_before_wakeup());
}

}

RPCNetwork::SendContext::SendContext(RPCNetwork &net, const Message &msg,
                                     const std::vector<RoutingNode*> &recipients)
    : _net(net),
      _msg(msg),
      _traceLevel(msg.getTrace().getLevel()),
      _recipients(recipients),
      _hasError(false),
      _pending(_recipients.size()),
      _version(_net.getVersion())
{ }

void
RPCNetwork::SendContext::handleVersion(const vespalib::Version *version)
{
    bool shouldSend = false;
    {
        std::lock_guard guard(_lock);
        if (version == nullptr) {
            _hasError = true;
        } else if (*version < _version) {
            _version = *version;
        }
        if (--_pending == 0) {
            shouldSend = true;
        }
    }
    if (shouldSend) {
        _net.send(*this);
        delete this;
    }
}

RPCNetwork::RPCNetwork(const RPCNetworkParams &params) :
    _owner(nullptr),
    _ident(params.getIdentity()),
    _threadPool(std::make_unique<FastOS_ThreadPool>(128_Ki, 0)),
    _transport(std::make_unique<FNET_Transport>(toFNETConfig(params))),
    _orb(std::make_unique<FRT_Supervisor>(_transport.get())),
    _scheduler(*_transport->GetScheduler()),
    _slobrokCfgFactory(std::make_unique<slobrok::ConfiguratorFactory>(params.getSlobrokConfig())),
    _mirror(std::make_unique<slobrok::api::MirrorAPI>(*_orb, *_slobrokCfgFactory)),
    _regAPI(std::make_unique<slobrok::api::RegisterAPI>(*_orb, *_slobrokCfgFactory)),
    _requestedPort(params.getListenPort()),
    _targetPool(std::make_unique<RPCTargetPool>(params.getConnectionExpireSecs(), params.getNumRpcTargets())),
    _targetPoolTask(std::make_unique<TargetPoolTask>(_scheduler, *_targetPool)),
    _servicePool(std::make_unique<RPCServicePool>(*_mirror, 4_Ki)),
    _sendV2(std::make_unique<RPCSendV2>()),
    _sendAdapters(),
    _compressionConfig(params.getCompressionConfig())
{
}

RPCNetwork::~RPCNetwork()
{
    shutdown();
}

FRT_RPCRequest *
RPCNetwork::allocRequest()
{
    return _orb->AllocRPCRequest();
}

RPCTarget::SP
RPCNetwork::getTarget(const RPCServiceAddress &address)
{
    return _targetPool->getTarget(*_orb, address);
}

void
RPCNetwork::replyError(const SendContext &ctx, uint32_t errCode, const string &errMsg)
{
    for (RoutingNode * rnode : ctx._recipients) {
        Reply::UP reply(new EmptyReply());
        reply->setTrace(Trace(ctx._traceLevel));
        reply->addError(Error(errCode, errMsg));
        _owner->deliverReply(std::move(reply), *rnode);
    }
}

int RPCNetwork::getPort() const { return _orb->GetListenPort(); }

void
RPCNetwork::flushTargetPool()
{
    _targetPool->flushTargets(true);
}

const vespalib::Version &
RPCNetwork::getVersion() const
{
    return vespalib::Vtag::currentVersion;
}

void
RPCNetwork::attach(INetworkOwner &owner)
{
    LOG_ASSERT(_owner == nullptr);
    _owner = &owner;

    _sendV2->attach(*this);
    _sendAdapters[vespalib::Version(6, 149)] = _sendV2.get();

    FRT_ReflectionBuilder builder(_orb.get());
    builder.DefineMethod("mbus.getVersion", "", "s", FRT_METHOD(RPCNetwork::invoke), this);
    builder.MethodDesc("Retrieves the message bus version.");
    builder.ReturnDesc("version", "The message bus version.");
}

void
RPCNetwork::invoke(FRT_RPCRequest *req)
{
    req->GetReturn()->AddString(getVersion().toString().c_str());
}

const string
RPCNetwork::getConnectionSpec() const
{
    return make_string("tcp/%s:%d", _ident.getHostname().c_str(), _orb->GetListenPort());
}

RPCSendAdapter *
RPCNetwork::getSendAdapter(const vespalib::Version &version)
{
    if (version < _sendAdapters.begin()->first) {
        return nullptr;
    }
    return (--_sendAdapters.upper_bound(version))->second;
}

bool
RPCNetwork::start()
{
    if (!_transport->Start(_threadPool.get())) {
        return false;
    }
    if (!_orb->Listen(_requestedPort)) {
        return false;
    }
    return true;
}

bool
RPCNetwork::waitUntilReady(duration timeout) const
{
    slobrok::api::SlobrokList brokerList;
    slobrok::Configurator::UP configurator = _slobrokCfgFactory->create(brokerList);
    bool hasConfig = false;
    for (int64_t i = 0; i < vespalib::count_ms(timeout)/10; ++i) {
        if (configurator->poll()) {
            hasConfig = true;
        }
        if (_mirror->ready()) {
            return true;
        }
        std::this_thread::sleep_for(10ms);
    }
    if (! hasConfig) {
        LOG(error, "failed to get config for slobroks in %2.2f seconds", vespalib::to_s(timeout));
    } else if (! _mirror->ready()) {
        auto brokers = brokerList.logString();
        LOG(error, "mirror (of %s) failed to become ready in %2.2f seconds", brokers.c_str(), vespalib::to_s(timeout));
    }
    return false;
}

void
RPCNetwork::registerSession(const string &session)
{
    if (_ident.getServicePrefix().empty()) {
        LOG(warning, "The session (%s) will not be registered in the Slobrok since this network has no identity.",
            session.c_str());
        return;
    }
    string name = _ident.getServicePrefix();
    name += "/";
    name += session;
    _regAPI->registerName(name);
}

void
RPCNetwork::unregisterSession(const string &session)
{
    if (_ident.getServicePrefix().empty()) {
        return;
    }
    if (getPort() <= 0) {
        return;
    }
    string name = _ident.getServicePrefix();
    name += "/";
    name += session;
    _regAPI->unregisterName(name);
}

bool
RPCNetwork::allocServiceAddress(RoutingNode &recipient)
{
    const Hop &hop = recipient.getRoute().getHop(0);
    string service = hop.getServiceName();
    Error error = resolveServiceAddress(recipient, service);
    if (error.getCode() == ErrorCode::NONE) {
        return true; // service address resolved
    }
    recipient.setError(error);
    return false; // service adddress not resolved
}

Error
RPCNetwork::resolveServiceAddress(RoutingNode &recipient, const string &serviceName)
{
    RPCServiceAddress::UP ret = _servicePool->resolve(serviceName);
    if ( ! ret) {
        return Error(ErrorCode::NO_ADDRESS_FOR_SERVICE,
                     make_string("The address of service '%s' could not be resolved. It is not currently "
                                 "registered with the Vespa name server. "
                                 "The service must be having problems, or the routing configuration is wrong. "
                                 "Address resolution attempted from host '%s'",
                                 serviceName.c_str(), getIdentity().getHostname().c_str()));
    }
    RPCTarget::SP target = _targetPool->getTarget(*_orb, *ret);
    if ( ! target) {
        return Error(ErrorCode::CONNECTION_ERROR,
                     make_string("Failed to connect to service '%s' from host '%s'.",
                                 serviceName.c_str(), getIdentity().getHostname().c_str()));
    }
    ret->setTarget(std::move(target)); // free by freeServiceAddress()
    recipient.setServiceAddress(std::move(ret));
    return Error();
}

void
RPCNetwork::freeServiceAddress(RoutingNode &recipient)
{
    recipient.setServiceAddress(IServiceAddress::UP());
}

void
RPCNetwork::send(const Message &msg, const std::vector<RoutingNode*> &recipients)
{
    SendContext &ctx = *(new SendContext(*this, msg, recipients)); // deletes self
    duration timeout = ctx._msg.getTimeRemainingNow();
    for (uint32_t i = 0, len = ctx._recipients.size(); i < len; ++i) {
        RoutingNode *&recipient = ctx._recipients[i];

        RPCServiceAddress &address = static_cast<RPCServiceAddress&>(recipient->getServiceAddress());
        LOG_ASSERT(address.hasTarget());

        address.getTarget().resolveVersion(timeout, ctx);
    }
}

namespace {

void
emit_recipient_endpoint(vespalib::asciistream& stream, const RoutingNode& recipient) {
    if (recipient.hasServiceAddress()) {
        // At this point the service addresses _should_ be RPCServiceAddress instances,
        // but stay on the safe side of the tracks anyway.
        const auto* rpc_addr = dynamic_cast<const RPCServiceAddress*>(&recipient.getServiceAddress());
        if (rpc_addr) {
            stream << rpc_addr->getServiceName() << " at " << rpc_addr->getConnectionSpec();
        } else {
            stream << "<non-RPC service address>";
        }
    } else {
        stream << "<unknown service address>";
    }
}

}

vespalib::string
RPCNetwork::buildRecipientListString(const SendContext& ctx) {
    vespalib::asciistream s;
    bool first = true;
    for (const auto* recipient : ctx._recipients) {
        if (!first) {
            s << ", ";
        }
        first = false;
        emit_recipient_endpoint(s, *recipient);
    }
    return s.str();
}

void
RPCNetwork::send(RPCNetwork::SendContext &ctx)
{
    if (ctx._hasError) {
        replyError(ctx, ErrorCode::HANDSHAKE_FAILED,
                make_string("An error occurred while resolving version of recipient(s) [%s] from host '%s'.",
                            buildRecipientListString(ctx).c_str(), getIdentity().getHostname().c_str()));
    } else {
        duration timeRemaining = ctx._msg.getTimeRemainingNow();
        Blob payload = _owner->getProtocol(ctx._msg.getProtocol())->encode(ctx._version, ctx._msg);
        RPCSendAdapter *adapter = getSendAdapter(ctx._version);
        if (adapter == nullptr) {
            replyError(ctx, ErrorCode::INCOMPATIBLE_VERSION,
                       make_string("Can not send to version '%s' recipient.", ctx._version.toString().c_str()));
        } else if (timeRemaining == 0ms) {
            replyError(ctx, ErrorCode::TIMEOUT, "Aborting transmission because zero time remains.");
        } else if (payload.size() == 0) {
            replyError(ctx, ErrorCode::ENCODE_ERROR,
                       make_string("Protocol '%s' failed to encode message.", ctx._msg.getProtocol().c_str()));
        } else if (ctx._recipients.size() == 1) {
            adapter->sendByHandover(*ctx._recipients.front(), ctx._version, std::move(payload), timeRemaining);
        } else {
            for (auto & recipient : ctx._recipients) {
                adapter->send(*recipient, ctx._version, payload, timeRemaining);
            }
        }
    }
}

void
RPCNetwork::sync()
{
    SyncTask task(_scheduler);
    task.await();
}

void
RPCNetwork::shutdown()
{
    // Unschedule any pending target pool flush task that may race with shutdown target flushing
    _scheduler.Kill(_targetPoolTask.get());
    _transport->ShutDown(true);
    _threadPool->Close();
}

void
RPCNetwork::postShutdownHook()
{
    _scheduler.CheckTasks();
}

const slobrok::api::IMirrorAPI &
RPCNetwork::getMirror() const
{
    return *_mirror;
}

} // namespace mbus
<commit_msg>Add missing include<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "rpcnetwork.h"
#include "rpcservicepool.h"
#include "rpcsendv2.h"
#include "rpctargetpool.h"
#include "rpcnetworkparams.h"
#include <vespa/fnet/frt/supervisor.h>
#include <vespa/fnet/scheduler.h>
#include <vespa/fnet/transport.h>
#include <vespa/messagebus/emptyreply.h>
#include <vespa/messagebus/errorcode.h>
#include <vespa/messagebus/iprotocol.h>
#include <vespa/messagebus/routing/routingnode.h>
#include <vespa/slobrok/sbmirror.h>
#include <vespa/slobrok/sbregister.h>
#include <vespa/vespalib/component/vtag.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/vespalib/util/size_literals.h>
#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/gate.h>
#include <vespa/fastos/thread.h>
#include <thread>

#include <vespa/log/log.h>
LOG_SETUP(".rpcnetwork");

using vespalib::make_string;
using namespace std::chrono_literals;

namespace mbus {

namespace {

/**
 * Implements a helper class for {@link RPCNetwork#sync()}. It provides a
 * blocking method {@link #await()} that will wait until the internal state
 * of this object is set to 'done'. By scheduling this task in the network
 * thread and then calling this method, we achieve handshaking with the network
 * thread.
 */
class SyncTask : public FNET_Task {
private:
    vespalib::Gate _gate;

public:
    SyncTask(FNET_Scheduler &s) :
        FNET_Task(&s),
        _gate() {
        ScheduleNow();
    }
    ~SyncTask() override {
        Kill();
    }

    void await() {
        _gate.await();
    }

    void PerformTask() override {
        _gate.countDown();
    }
};

struct TargetPoolTask : public FNET_Task {
    RPCTargetPool &_pool;

    TargetPoolTask(FNET_Scheduler &scheduler, RPCTargetPool &pool)
        : FNET_Task(&scheduler),
        _pool(pool)
    {
        ScheduleNow();
    }
    ~TargetPoolTask() override {
        Kill();
    }
    void PerformTask() override {
        _pool.flushTargets(false);
        Schedule(1.0);
    }
};

fnet::TransportConfig
toFNETConfig(const RPCNetworkParams & params) {
    return fnet::TransportConfig(params.getNumNetworkThreads())
              .maxInputBufferSize(params.getMaxInputBufferSize())
              .maxOutputBufferSize(params.getMaxOutputBufferSize())
              .tcpNoDelay(params.getTcpNoDelay())
              .events_before_wakeup(params.events_before_wakeup());
}

}

RPCNetwork::SendContext::SendContext(RPCNetwork &net, const Message &msg,
                                     const std::vector<RoutingNode*> &recipients)
    : _net(net),
      _msg(msg),
      _traceLevel(msg.getTrace().getLevel()),
      _recipients(recipients),
      _hasError(false),
      _pending(_recipients.size()),
      _version(_net.getVersion())
{ }

void
RPCNetwork::SendContext::handleVersion(const vespalib::Version *version)
{
    bool shouldSend = false;
    {
        std::lock_guard guard(_lock);
        if (version == nullptr) {
            _hasError = true;
        } else if (*version < _version) {
            _version = *version;
        }
        if (--_pending == 0) {
            shouldSend = true;
        }
    }
    if (shouldSend) {
        _net.send(*this);
        delete this;
    }
}

RPCNetwork::RPCNetwork(const RPCNetworkParams &params) :
    _owner(nullptr),
    _ident(params.getIdentity()),
    _threadPool(std::make_unique<FastOS_ThreadPool>(128_Ki, 0)),
    _transport(std::make_unique<FNET_Transport>(toFNETConfig(params))),
    _orb(std::make_unique<FRT_Supervisor>(_transport.get())),
    _scheduler(*_transport->GetScheduler()),
    _slobrokCfgFactory(std::make_unique<slobrok::ConfiguratorFactory>(params.getSlobrokConfig())),
    _mirror(std::make_unique<slobrok::api::MirrorAPI>(*_orb, *_slobrokCfgFactory)),
    _regAPI(std::make_unique<slobrok::api::RegisterAPI>(*_orb, *_slobrokCfgFactory)),
    _requestedPort(params.getListenPort()),
    _targetPool(std::make_unique<RPCTargetPool>(params.getConnectionExpireSecs(), params.getNumRpcTargets())),
    _targetPoolTask(std::make_unique<TargetPoolTask>(_scheduler, *_targetPool)),
    _servicePool(std::make_unique<RPCServicePool>(*_mirror, 4_Ki)),
    _sendV2(std::make_unique<RPCSendV2>()),
    _sendAdapters(),
    _compressionConfig(params.getCompressionConfig())
{
}

RPCNetwork::~RPCNetwork()
{
    shutdown();
}

FRT_RPCRequest *
RPCNetwork::allocRequest()
{
    return _orb->AllocRPCRequest();
}

RPCTarget::SP
RPCNetwork::getTarget(const RPCServiceAddress &address)
{
    return _targetPool->getTarget(*_orb, address);
}

void
RPCNetwork::replyError(const SendContext &ctx, uint32_t errCode, const string &errMsg)
{
    for (RoutingNode * rnode : ctx._recipients) {
        Reply::UP reply(new EmptyReply());
        reply->setTrace(Trace(ctx._traceLevel));
        reply->addError(Error(errCode, errMsg));
        _owner->deliverReply(std::move(reply), *rnode);
    }
}

int RPCNetwork::getPort() const { return _orb->GetListenPort(); }

void
RPCNetwork::flushTargetPool()
{
    _targetPool->flushTargets(true);
}

const vespalib::Version &
RPCNetwork::getVersion() const
{
    return vespalib::Vtag::currentVersion;
}

void
RPCNetwork::attach(INetworkOwner &owner)
{
    LOG_ASSERT(_owner == nullptr);
    _owner = &owner;

    _sendV2->attach(*this);
    _sendAdapters[vespalib::Version(6, 149)] = _sendV2.get();

    FRT_ReflectionBuilder builder(_orb.get());
    builder.DefineMethod("mbus.getVersion", "", "s", FRT_METHOD(RPCNetwork::invoke), this);
    builder.MethodDesc("Retrieves the message bus version.");
    builder.ReturnDesc("version", "The message bus version.");
}

void
RPCNetwork::invoke(FRT_RPCRequest *req)
{
    req->GetReturn()->AddString(getVersion().toString().c_str());
}

const string
RPCNetwork::getConnectionSpec() const
{
    return make_string("tcp/%s:%d", _ident.getHostname().c_str(), _orb->GetListenPort());
}

RPCSendAdapter *
RPCNetwork::getSendAdapter(const vespalib::Version &version)
{
    if (version < _sendAdapters.begin()->first) {
        return nullptr;
    }
    return (--_sendAdapters.upper_bound(version))->second;
}

bool
RPCNetwork::start()
{
    if (!_transport->Start(_threadPool.get())) {
        return false;
    }
    if (!_orb->Listen(_requestedPort)) {
        return false;
    }
    return true;
}

bool
RPCNetwork::waitUntilReady(duration timeout) const
{
    slobrok::api::SlobrokList brokerList;
    slobrok::Configurator::UP configurator = _slobrokCfgFactory->create(brokerList);
    bool hasConfig = false;
    for (int64_t i = 0; i < vespalib::count_ms(timeout)/10; ++i) {
        if (configurator->poll()) {
            hasConfig = true;
        }
        if (_mirror->ready()) {
            return true;
        }
        std::this_thread::sleep_for(10ms);
    }
    if (! hasConfig) {
        LOG(error, "failed to get config for slobroks in %2.2f seconds", vespalib::to_s(timeout));
    } else if (! _mirror->ready()) {
        auto brokers = brokerList.logString();
        LOG(error, "mirror (of %s) failed to become ready in %2.2f seconds", brokers.c_str(), vespalib::to_s(timeout));
    }
    return false;
}

void
RPCNetwork::registerSession(const string &session)
{
    if (_ident.getServicePrefix().empty()) {
        LOG(warning, "The session (%s) will not be registered in the Slobrok since this network has no identity.",
            session.c_str());
        return;
    }
    string name = _ident.getServicePrefix();
    name += "/";
    name += session;
    _regAPI->registerName(name);
}

void
RPCNetwork::unregisterSession(const string &session)
{
    if (_ident.getServicePrefix().empty()) {
        return;
    }
    if (getPort() <= 0) {
        return;
    }
    string name = _ident.getServicePrefix();
    name += "/";
    name += session;
    _regAPI->unregisterName(name);
}

bool
RPCNetwork::allocServiceAddress(RoutingNode &recipient)
{
    const Hop &hop = recipient.getRoute().getHop(0);
    string service = hop.getServiceName();
    Error error = resolveServiceAddress(recipient, service);
    if (error.getCode() == ErrorCode::NONE) {
        return true; // service address resolved
    }
    recipient.setError(error);
    return false; // service adddress not resolved
}

Error
RPCNetwork::resolveServiceAddress(RoutingNode &recipient, const string &serviceName)
{
    RPCServiceAddress::UP ret = _servicePool->resolve(serviceName);
    if ( ! ret) {
        return Error(ErrorCode::NO_ADDRESS_FOR_SERVICE,
                     make_string("The address of service '%s' could not be resolved. It is not currently "
                                 "registered with the Vespa name server. "
                                 "The service must be having problems, or the routing configuration is wrong. "
                                 "Address resolution attempted from host '%s'",
                                 serviceName.c_str(), getIdentity().getHostname().c_str()));
    }
    RPCTarget::SP target = _targetPool->getTarget(*_orb, *ret);
    if ( ! target) {
        return Error(ErrorCode::CONNECTION_ERROR,
                     make_string("Failed to connect to service '%s' from host '%s'.",
                                 serviceName.c_str(), getIdentity().getHostname().c_str()));
    }
    ret->setTarget(std::move(target)); // free by freeServiceAddress()
    recipient.setServiceAddress(std::move(ret));
    return Error();
}

void
RPCNetwork::freeServiceAddress(RoutingNode &recipient)
{
    recipient.setServiceAddress(IServiceAddress::UP());
}

void
RPCNetwork::send(const Message &msg, const std::vector<RoutingNode*> &recipients)
{
    SendContext &ctx = *(new SendContext(*this, msg, recipients)); // deletes self
    duration timeout = ctx._msg.getTimeRemainingNow();
    for (uint32_t i = 0, len = ctx._recipients.size(); i < len; ++i) {
        RoutingNode *&recipient = ctx._recipients[i];

        RPCServiceAddress &address = static_cast<RPCServiceAddress&>(recipient->getServiceAddress());
        LOG_ASSERT(address.hasTarget());

        address.getTarget().resolveVersion(timeout, ctx);
    }
}

namespace {

void
emit_recipient_endpoint(vespalib::asciistream& stream, const RoutingNode& recipient) {
    if (recipient.hasServiceAddress()) {
        // At this point the service addresses _should_ be RPCServiceAddress instances,
        // but stay on the safe side of the tracks anyway.
        const auto* rpc_addr = dynamic_cast<const RPCServiceAddress*>(&recipient.getServiceAddress());
        if (rpc_addr) {
            stream << rpc_addr->getServiceName() << " at " << rpc_addr->getConnectionSpec();
        } else {
            stream << "<non-RPC service address>";
        }
    } else {
        stream << "<unknown service address>";
    }
}

}

vespalib::string
RPCNetwork::buildRecipientListString(const SendContext& ctx) {
    vespalib::asciistream s;
    bool first = true;
    for (const auto* recipient : ctx._recipients) {
        if (!first) {
            s << ", ";
        }
        first = false;
        emit_recipient_endpoint(s, *recipient);
    }
    return s.str();
}

void
RPCNetwork::send(RPCNetwork::SendContext &ctx)
{
    if (ctx._hasError) {
        replyError(ctx, ErrorCode::HANDSHAKE_FAILED,
                make_string("An error occurred while resolving version of recipient(s) [%s] from host '%s'.",
                            buildRecipientListString(ctx).c_str(), getIdentity().getHostname().c_str()));
    } else {
        duration timeRemaining = ctx._msg.getTimeRemainingNow();
        Blob payload = _owner->getProtocol(ctx._msg.getProtocol())->encode(ctx._version, ctx._msg);
        RPCSendAdapter *adapter = getSendAdapter(ctx._version);
        if (adapter == nullptr) {
            replyError(ctx, ErrorCode::INCOMPATIBLE_VERSION,
                       make_string("Can not send to version '%s' recipient.", ctx._version.toString().c_str()));
        } else if (timeRemaining == 0ms) {
            replyError(ctx, ErrorCode::TIMEOUT, "Aborting transmission because zero time remains.");
        } else if (payload.size() == 0) {
            replyError(ctx, ErrorCode::ENCODE_ERROR,
                       make_string("Protocol '%s' failed to encode message.", ctx._msg.getProtocol().c_str()));
        } else if (ctx._recipients.size() == 1) {
            adapter->sendByHandover(*ctx._recipients.front(), ctx._version, std::move(payload), timeRemaining);
        } else {
            for (auto & recipient : ctx._recipients) {
                adapter->send(*recipient, ctx._version, payload, timeRemaining);
            }
        }
    }
}

void
RPCNetwork::sync()
{
    SyncTask task(_scheduler);
    task.await();
}

void
RPCNetwork::shutdown()
{
    // Unschedule any pending target pool flush task that may race with shutdown target flushing
    _scheduler.Kill(_targetPoolTask.get());
    _transport->ShutDown(true);
    _threadPool->Close();
}

void
RPCNetwork::postShutdownHook()
{
    _scheduler.CheckTasks();
}

const slobrok::api::IMirrorAPI &
RPCNetwork::getMirror() const
{
    return *_mirror;
}

} // namespace mbus
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <vector>

#include <glog/logging.h>
#include <gtest/gtest.h>

#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/stubs.h>

#include "Entropy.h"
#include "shadowTreeGeneration.h"

namespace facebook {
namespace react {

static void testShadowNodeTreeLifeCycle(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithChildren,
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });
      alterShadowTree(entropy, nextRootNode, &messWithNodeFlattenednessFlags);
      alterShadowTree(entropy, nextRootNode, &messWithChildren);

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

} // namespace react
} // namespace facebook

using namespace facebook::react;

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 1,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableSmallerTreeMoreIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 16,
      /* repeats */ 512,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 256,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 512,
      /* stages */ 32);
}
<commit_msg>Testing mechanism to find new failing Differ test-cases<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#include <vector>

#include <glog/logging.h>
#include <gtest/gtest.h>

#include <react/renderer/components/root/RootComponentDescriptor.h>
#include <react/renderer/components/view/ViewComponentDescriptor.h>
#include <react/renderer/mounting/Differentiator.h>
#include <react/renderer/mounting/ShadowViewMutation.h>
#include <react/renderer/mounting/stubs.h>

// Uncomment when random test blocks are uncommented below.
// #include <algorithm>
// #include <random>

#include "Entropy.h"
#include "shadowTreeGeneration.h"

namespace facebook {
namespace react {

static void testShadowNodeTreeLifeCycle(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithChildren,
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

static void testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
    uint_fast32_t seed,
    int treeSize,
    int repeats,
    int stages) {
  auto entropy = seed == 0 ? Entropy() : Entropy(seed);

  auto eventDispatcher = EventDispatcher::Shared{};
  auto contextContainer = std::make_shared<ContextContainer>();
  auto componentDescriptorParameters =
      ComponentDescriptorParameters{eventDispatcher, contextContainer, nullptr};
  auto viewComponentDescriptor =
      ViewComponentDescriptor(componentDescriptorParameters);
  auto rootComponentDescriptor =
      RootComponentDescriptor(componentDescriptorParameters);
  auto noopEventEmitter =
      std::make_shared<ViewEventEmitter const>(nullptr, -1, eventDispatcher);

  auto allNodes = std::vector<ShadowNode::Shared>{};

  for (int i = 0; i < repeats; i++) {
    allNodes.clear();

    auto family = rootComponentDescriptor.createFamily(
        {Tag(1), SurfaceId(1), nullptr}, nullptr);

    // Creating an initial root shadow node.
    auto emptyRootNode = std::const_pointer_cast<RootShadowNode>(
        std::static_pointer_cast<RootShadowNode const>(
            rootComponentDescriptor.createShadowNode(
                ShadowNodeFragment{RootShadowNode::defaultSharedProps()},
                family)));

    // Applying size constraints.
    emptyRootNode = emptyRootNode->clone(
        LayoutConstraints{
            Size{512, 0}, Size{512, std::numeric_limits<Float>::infinity()}},
        LayoutContext{});

    // Generation of a random tree.
    auto singleRootChildNode =
        generateShadowNodeTree(entropy, viewComponentDescriptor, treeSize);

    // Injecting a tree into the root node.
    auto currentRootNode = std::static_pointer_cast<RootShadowNode const>(
        emptyRootNode->ShadowNode::clone(ShadowNodeFragment{
            ShadowNodeFragment::propsPlaceholder(),
            std::make_shared<SharedShadowNodeList>(
                SharedShadowNodeList{singleRootChildNode})}));

    // Building an initial view hierarchy.
    auto viewTree = buildStubViewTreeWithoutUsingDifferentiator(*emptyRootNode);
    viewTree.mutate(
        calculateShadowViewMutations(*emptyRootNode, *currentRootNode, true));

    for (int j = 0; j < stages; j++) {
      auto nextRootNode = currentRootNode;

      // Mutating the tree.
      alterShadowTree(
          entropy,
          nextRootNode,
          {
              &messWithYogaStyles,
              &messWithLayoutableOnlyFlag,
          });
      alterShadowTree(entropy, nextRootNode, &messWithNodeFlattenednessFlags);
      alterShadowTree(entropy, nextRootNode, &messWithChildren);

      std::vector<LayoutableShadowNode const *> affectedLayoutableNodes{};
      affectedLayoutableNodes.reserve(1024);

      // Laying out the tree.
      std::const_pointer_cast<RootShadowNode>(nextRootNode)
          ->layoutIfNeeded(&affectedLayoutableNodes);

      nextRootNode->sealRecursive();
      allNodes.push_back(nextRootNode);

      // Calculating mutations.
      auto mutations =
          calculateShadowViewMutations(*currentRootNode, *nextRootNode, true);

      // Make sure that in a single frame, a DELETE for a
      // view is not followed by a CREATE for the same view.
      {
        std::vector<int> deletedTags{};
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Delete) {
            deletedTags.push_back(mutation.oldChildShadowView.tag);
          }
        }
        for (auto const &mutation : mutations) {
          if (mutation.type == ShadowViewMutation::Type::Create) {
            if (std::find(
                    deletedTags.begin(),
                    deletedTags.end(),
                    mutation.newChildShadowView.tag) != deletedTags.end()) {
              LOG(ERROR) << "Deleted tag was recreated in mutations list: ["
                         << mutation.newChildShadowView.tag << "]";
              react_native_assert(false);
            }
          }
        }
      }

      // Mutating the view tree.
      viewTree.mutate(mutations);

      // Building a view tree to compare with.
      auto rebuiltViewTree =
          buildStubViewTreeWithoutUsingDifferentiator(*nextRootNode);

      // Comparing the newly built tree with the updated one.
      if (rebuiltViewTree != viewTree) {
        // Something went wrong.

        LOG(ERROR) << "Entropy seed: " << entropy.getSeed() << "\n";

        // There are some issues getting `getDebugDescription` to compile
        // under test on Android for now.
#ifdef RN_DEBUG_STRING_CONVERTIBLE
        LOG(ERROR) << "Shadow Tree before: \n"
                   << currentRootNode->getDebugDescription();
        LOG(ERROR) << "Shadow Tree after: \n"
                   << nextRootNode->getDebugDescription();

        LOG(ERROR) << "View Tree before: \n"
                   << getDebugDescription(viewTree.getRootStubView(), {});
        LOG(ERROR) << "View Tree after: \n"
                   << getDebugDescription(
                          rebuiltViewTree.getRootStubView(), {});

        LOG(ERROR) << "Mutations:"
                   << "\n"
                   << getDebugDescription(mutations, {});
#endif

        react_native_assert(false);
      }

      currentRootNode = nextRootNode;
    }
  }

  SUCCEED();
}

} // namespace react
} // namespace facebook

using namespace facebook::react;

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableBiggerTreeFewerIterationsOptimizedMovesFlattener2) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 1,
      /* size */ 512,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    stableSmallerTreeMoreIterationsOptimizedMovesFlattener) {
  testShadowNodeTreeLifeCycle(
      /* seed */ 0,
      /* size */ 16,
      /* repeats */ 512,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableBiggerTreeFewerIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 256,
      /* repeats */ 32,
      /* stages */ 32);
}

TEST(
    ShadowTreeLifecyleTest,
    unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflattening) {
  testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
      /* seed */ 1337,
      /* size */ 32,
      /* repeats */ 512,
      /* stages */ 32);
}

// You may uncomment this - locally only! - to generate failing seeds.
// TEST(
//     ShadowTreeLifecyleTest,
//     unstableSmallerTreeMoreIterationsExtensiveFlatteningUnflatteningManyRandom)
//     {
//   std::random_device device;
//   for (int i = 0; i < 10; i++) {
//     uint_fast32_t seed = device();
//     LOG(ERROR) << "Seed: " << seed;
//     testShadowNodeTreeLifeCycleExtensiveFlatteningUnflattening(
//         /* seed */ seed,
//         /* size */ 32,
//         /* repeats */ 512,
//         /* stages */ 32);
//   }
// }
<|endoftext|>