<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkVolumeDataVtkMapper3D.h"
#include "mitkLevelWindow.h"
#include "mitkDataTreeNode.h"
#include "mitkProperties.h"
#include "mitkColorProperty.h"
#include "mitkOpenGLRenderer.h"
#include "mitkLookupTableProperty.h"
#include "mitkTransferFunctionProperty.h"

#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkVolumeRayCastMapper.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkVolumeRayCastCompositeFunction.h>
#include <vtkFiniteDifferenceGradientEstimator.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCallbackCommand.h>
#include <vtkImageShiftScale.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageWriter.h>
#include <vtkVolumeTextureMapper2D.h>
#include <vtkImageData.h>
#include <vtkLODProp3D.h>
#include <vtkImageResample.h>
#include <mitkVtkRenderWindow.h>

#include <itkMultiThreader.h>

const mitk::Image* mitk::VolumeDataVtkMapper3D::GetInput()
{
  return static_cast<const mitk::Image*> ( GetData() );
}

mitk::VolumeDataVtkMapper3D::VolumeDataVtkMapper3D()
{
  //  m_VtkVolumeMapper = vtkVolumeRayCastMapper::New();
	// m_VtkVolumeMapper = vtkVolumeTextureMapper2D::New();

  m_LowResMapper =  vtkVolumeTextureMapper2D::New();
  m_MedResMapper =  vtkVolumeRayCastMapper::New();
  m_HiResMapper = vtkVolumeRayCastMapper::New();

  //m_MedResMapper->AutoAdjustSampleDistancesOff();
  //m_MedResMapper->SetImageSampleDistance(2.5);

  //m_HiResMapper->AutoAdjustSampleDistancesOff();
  //m_HiResMapper->SetImageSampleDistance(0.5);
  m_HiResMapper->SetMaximumImageSampleDistance(10.0);
  m_HiResMapper->SetMinimumImageSampleDistance(1.0);
  m_HiResMapper->IntermixIntersectingGeometryOn();
  m_HiResMapper->SetNumberOfThreads( itk::MultiThreader::GetGlobalDefaultNumberOfThreads() );

  vtkVolumeRayCastCompositeFunction* compositeFunction = vtkVolumeRayCastCompositeFunction::New();
  m_MedResMapper->SetVolumeRayCastFunction(compositeFunction);
  m_HiResMapper->SetVolumeRayCastFunction(compositeFunction);
//  compositeFunction->Delete();
  vtkFiniteDifferenceGradientEstimator* gradientEstimator =
  vtkFiniteDifferenceGradientEstimator::New();
/* if (dynamic_cast<vtkVolumeRayCastMapper*>(m_VtkVolumeMapper)){
  dynamic_cast<vtkVolumeRayCastMapper*>(m_VtkVolumeMapper)->SetGradientEstimator(gradientEstimator);
  } else if (dynamic_cast<vtkVolumeTextureMapper2D*>(m_VtkVolumeMapper)){
  dynamic_cast<vtkVolumeTextureMapper2D*>(m_VtkVolumeMapper)->SetGradientEstimator(gradientEstimator);
  } */
  m_LowResMapper->SetGradientEstimator(gradientEstimator);
  m_MedResMapper->SetGradientEstimator(gradientEstimator);
  m_HiResMapper->SetGradientEstimator(gradientEstimator);

  gradientEstimator->Delete();

  m_VolumeProperty = vtkVolumeProperty::New();
  m_VolumeLOD = vtkLODProp3D::New();

  m_VolumeLOD->AddLOD(m_HiResMapper,m_VolumeProperty,0.0);
  m_VolumeLOD->AddLOD(m_MedResMapper,m_VolumeProperty,0.0);
  m_VolumeLOD->AddLOD(m_LowResMapper,m_VolumeProperty,0.0);

  m_Resampler = vtkImageResample::New();
  m_Resampler->SetAxisMagnificationFactor(0,0.5);
  m_Resampler->SetAxisMagnificationFactor(1,0.5);
  m_Resampler->SetAxisMagnificationFactor(2,0.5);

  //m_Volume = vtkVolume::New();
  // m_Volume->SetMapper( m_VtkVolumeMapper );
  // m_Volume->SetProperty(m_VolumeProperty);
  // m_Prop3D = m_Volume;
  m_Prop3D = m_VolumeLOD;
  m_Prop3D->Register(NULL);

  m_ImageCast = vtkImageShiftScale::New();
  m_ImageCast->SetOutputScalarTypeToUnsignedShort();
  m_ImageCast->ClampOverflowOn();

  m_UnitSpacingImageFilter = vtkImageChangeInformation::New();
  m_UnitSpacingImageFilter->SetInput(m_ImageCast->GetOutput());
  m_UnitSpacingImageFilter->SetOutputSpacing(1.0,1.0,1.0);
}

mitk::VolumeDataVtkMapper3D::~VolumeDataVtkMapper3D()
{
  // m_VtkVolumeMapper->Delete();
  //m_Volume->Delete();
  m_UnitSpacingImageFilter->Delete();
  m_ImageCast->Delete();
  m_LowResMapper->Delete();
  m_MedResMapper->Delete();
  m_HiResMapper->Delete();
  m_Resampler->Delete();
  m_VolumeProperty->Delete();
  m_VolumeLOD->Delete();
}
void mitk::VolumeDataVtkMapper3D::AbortCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata) {
  // std::cout << "abort test called" << std::endl;
  vtkRenderWindow* renWin = dynamic_cast<vtkRenderWindow*>(caller);
  assert(renWin);
   int foo=renWin->GetEventPending();
   if(foo!=0)
   {
     renWin->SetAbortRender(1);
   }
  // FIXME: qApp->hasPendingEvents is always true, renWin->GetEventPending is
  // always false. So aborting the render doesn't work this way.

  // if (
  //   qApp->hasPendingEvents()
  // )
  // {
  // std::cout << "setting abort render" << std::endl;
  //   renWin->SetAbortRender(1);
  // }
}

void mitk::VolumeDataVtkMapper3D::GenerateData(mitk::BaseRenderer* renderer)
{

  if(IsVisible(renderer)==false ||
      GetDataTreeNode() == NULL ||
      dynamic_cast<mitk::BoolProperty*>(GetDataTreeNode()->GetProperty("volumerendering",renderer).GetPointer())==NULL ||
      dynamic_cast<mitk::BoolProperty*>(GetDataTreeNode()->GetProperty("volumerendering",renderer).GetPointer())->GetValue() == false
    )
  {
    //  FIXME: don't understand this
    if (m_Prop3D) {
      //      std::cout << "visibility off" <<std::endl;

      m_Prop3D->VisibilityOff();
    }
    return;
  }
  if (m_Prop3D) {
    m_Prop3D->VisibilityOn();
  }

  mitk::Image* input  = const_cast<mitk::Image *>(this->GetInput());
  if((input==NULL) || (input->IsInitialized()==false))
    return;

  // FIXME: const_cast; maybe GetVtkImageData can be made const by using volatile
  const TimeSlicedGeometry* inputtimegeometry = input->GetTimeSlicedGeometry();
  assert(inputtimegeometry!=NULL);

  const Geometry3D* worldgeometry = renderer->GetCurrentWorldGeometry();

  assert(worldgeometry!=NULL);

  int timestep=0;
  ScalarType time = worldgeometry->GetTimeBounds()[0];
  if(time>-ScalarTypeNumericTraits::max())
    timestep = inputtimegeometry->MSToTimeStep(time);

  if(inputtimegeometry->IsValidTime(timestep)==false)
    return;

  vtkImageData* inputData = input->GetVtkImageData(timestep);
  if(inputData==NULL)
    return;

  m_ImageCast->SetInput( inputData );

//  m_ImageCast->Update();

  //trying to avoid update-problem, when size of input changed. Does not really help much.
  if(m_ImageCast->GetOutput()!=NULL)
  {
    int inputWE[6];
    inputData->GetWholeExtent(inputWE);
    int * outputWE=m_UnitSpacingImageFilter->GetOutput()->GetExtent();
    if(m_ImageCast->GetOutput()!=NULL && memcmp(inputWE, outputWE,sizeof(int)*6) != 0)
    {
//      m_ImageCast->GetOutput()->SetUpdateExtentToWholeExtent();
//      m_UnitSpacingImageFilter->GetOutput()->SetUpdateExtentToWholeExtent();
      m_UnitSpacingImageFilter->GetOutput()->SetUpdateExtent(inputWE);
      m_UnitSpacingImageFilter->UpdateWholeExtent();
    }
  }

  //m_VtkVolumeMapper->SetInput( m_UnitSpacingImageFilter->GetOutput() );
  //m_Volume->SetMapper( m_VtkVolumeMapper );
  m_Resampler->SetInput(m_UnitSpacingImageFilter->GetOutput());

  m_LowResMapper->SetInput(m_Resampler->GetOutput());
  m_MedResMapper->SetInput(m_UnitSpacingImageFilter->GetOutput());
  m_HiResMapper->SetInput(m_UnitSpacingImageFilter->GetOutput());

  vtkPiecewiseFunction *opacityTransferFunction;
  vtkColorTransferFunction* colorTransferFunction;

  opacityTransferFunction  = vtkPiecewiseFunction::New();
  colorTransferFunction    = vtkColorTransferFunction::New();

  m_ImageCast->SetShift(0);

mitk::TransferFunctionProperty::Pointer tranferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty*>(this->GetDataTreeNode()->GetProperty("TransferFunction").GetPointer());
  if (tranferFunctionProp.IsNull()) {
   // create one
    mitk::TransferFunction::Pointer newTF = mitk::TransferFunction::New();
    newTF->InitializeByMitkImage(input);
//    newTF->SetMin(-10);
//    newTF->SetMax(+310);
    this->GetDataTreeNode()->SetProperty("TransferFunction", new TransferFunctionProperty(newTF.GetPointer()));
  }

  mitk::LookupTableProperty::Pointer lookupTableProp;
  lookupTableProp = dynamic_cast<mitk::LookupTableProperty*>(this->GetDataTreeNode()->GetProperty("LookupTable").GetPointer());
 mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty*>(this->GetDataTreeNode()->GetProperty("TransferFunction").GetPointer());
  if (transferFunctionProp.IsNotNull() && transferFunctionProp->GetValue()->GetValid())   {

    opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction();
    colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction();
  } else if (lookupTableProp.IsNotNull() )
  {
    lookupTableProp->GetLookupTable().CreateColorTransferFunction(colorTransferFunction);
    colorTransferFunction->ClampingOn();
    lookupTableProp->GetLookupTable().CreateOpacityTransferFunction(opacityTransferFunction);
    opacityTransferFunction->ClampingOn();
  }
  else
  {
    mitk::LevelWindow levelWindow;
    int lw_min,lw_max;

    bool binary=false;
    GetDataTreeNode()->GetBoolProperty("binary", binary, renderer);
    if(binary)
    {
      lw_min=0; lw_max=2;
    }
    else
    if(GetLevelWindow(levelWindow,renderer,"levelWindow") || GetLevelWindow(levelWindow,renderer))
    {
      lw_min = (int)levelWindow.GetMin();
      lw_max = (int)levelWindow.GetMax();
      //      std::cout << "levwin:" << levelWindow << std::endl;
      if(lw_min<0)
      {
        m_ImageCast->SetShift(-lw_min);
        lw_max+=-lw_min;
        lw_min=0;
      }
    }
    else
    {
      lw_min = 0;
      lw_max = 255;
    }

    opacityTransferFunction->AddPoint( lw_min, 0.0 );
    opacityTransferFunction->AddPoint( lw_max, 0.8 );
    opacityTransferFunction->ClampingOn();

    //colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 1.0 );
    //colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, 1.0, 0.0, 0.0 );
    //colorTransferFunction->AddRGBPoint( lw_max, 0.0, 1.0, 0.0 );

    float rgb[3]={1.0f,1.0f,1.0f};
    // check for color prop and use it for rendering if it exists
    if(GetColor(rgb, renderer))
    {
      colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 0.0 );
      colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, rgb[0], rgb[1], rgb[2] );
      colorTransferFunction->AddRGBPoint( lw_max, rgb[0], rgb[1], rgb[2] );
    }
    else
    {
      colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 0.0 );
      colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, 1, 1, 0.0 );
      colorTransferFunction->AddRGBPoint( lw_max, 0.8, 0.2, 0 );
    }

    colorTransferFunction->ClampingOn();
  }

  m_VolumeProperty->SetColor( colorTransferFunction );
  m_VolumeProperty->SetScalarOpacity( opacityTransferFunction );
  m_VolumeProperty->SetDiffuse(0.2);
  m_VolumeProperty->SetAmbient(0.9);
  m_VolumeProperty->ShadeOn();

  mitk::OpenGLRenderer* openGlRenderer = dynamic_cast<mitk::OpenGLRenderer*>(renderer);
  assert(openGlRenderer);
  vtkRenderWindow* vtkRendWin = dynamic_cast<vtkRenderWindow*>(openGlRenderer->GetVtkRenderWindow());
  if (vtkRendWin) {
//    vtkRendWin->SetDesiredUpdateRate(25.0);
    vtkRenderWindowInteractor* interactor = vtkRendWin->GetInteractor();
    interactor->SetDesiredUpdateRate(50000.0);
    interactor->SetStillUpdateRate(0.0001);

    vtkCallbackCommand* cbc = vtkCallbackCommand::New();
    cbc->SetCallback(mitk::VolumeDataVtkMapper3D::AbortCallback);
    vtkRendWin->AddObserver(vtkCommand::AbortCheckEvent,cbc);

  } else {
    std::cout << "no vtk renderwindow" << std::endl;
  }
  //m_Volume->Update();
  // m_VolumeLOD->Update();
  // m_Prop3D = m_Volume;

  // now add an AbortCheckEvent callback for cancelling the rendering

//  colorTransferFunction->Delete();
//  opacityTransferFunction->Delete();
}

void mitk::VolumeDataVtkMapper3D::ApplyProperties(vtkActor* actor, mitk::BaseRenderer* renderer)
{

}
<commit_msg>CHG: removed obsolete validity check of transfer function<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkVolumeDataVtkMapper3D.h"
#include "mitkLevelWindow.h"
#include "mitkDataTreeNode.h"
#include "mitkProperties.h"
#include "mitkColorProperty.h"
#include "mitkOpenGLRenderer.h"
#include "mitkLookupTableProperty.h"
#include "mitkTransferFunctionProperty.h"

#include <vtkActor.h>
#include <vtkProperty.h>
#include <vtkVolumeRayCastMapper.h>
#include <vtkVolume.h>
#include <vtkVolumeProperty.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkVolumeRayCastCompositeFunction.h>
#include <vtkFiniteDifferenceGradientEstimator.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCallbackCommand.h>
#include <vtkImageShiftScale.h>
#include <vtkImageChangeInformation.h>
#include <vtkImageWriter.h>
#include <vtkVolumeTextureMapper2D.h>
#include <vtkImageData.h>
#include <vtkLODProp3D.h>
#include <vtkImageResample.h>
#include <mitkVtkRenderWindow.h>

#include <itkMultiThreader.h>

const mitk::Image* mitk::VolumeDataVtkMapper3D::GetInput()
{
  return static_cast<const mitk::Image*> ( GetData() );
}

mitk::VolumeDataVtkMapper3D::VolumeDataVtkMapper3D()
{
  //  m_VtkVolumeMapper = vtkVolumeRayCastMapper::New();
	// m_VtkVolumeMapper = vtkVolumeTextureMapper2D::New();

  m_LowResMapper =  vtkVolumeTextureMapper2D::New();
  m_MedResMapper =  vtkVolumeRayCastMapper::New();
  m_HiResMapper = vtkVolumeRayCastMapper::New();

  //m_MedResMapper->AutoAdjustSampleDistancesOff();
  //m_MedResMapper->SetImageSampleDistance(2.5);

  //m_HiResMapper->AutoAdjustSampleDistancesOff();
  //m_HiResMapper->SetImageSampleDistance(0.5);
  m_HiResMapper->SetMaximumImageSampleDistance(10.0);
  m_HiResMapper->SetMinimumImageSampleDistance(1.0);
  m_HiResMapper->IntermixIntersectingGeometryOn();
  m_HiResMapper->SetNumberOfThreads( itk::MultiThreader::GetGlobalDefaultNumberOfThreads() );

  vtkVolumeRayCastCompositeFunction* compositeFunction = vtkVolumeRayCastCompositeFunction::New();
  m_MedResMapper->SetVolumeRayCastFunction(compositeFunction);
  m_HiResMapper->SetVolumeRayCastFunction(compositeFunction);
//  compositeFunction->Delete();
  vtkFiniteDifferenceGradientEstimator* gradientEstimator =
  vtkFiniteDifferenceGradientEstimator::New();
/* if (dynamic_cast<vtkVolumeRayCastMapper*>(m_VtkVolumeMapper)){
  dynamic_cast<vtkVolumeRayCastMapper*>(m_VtkVolumeMapper)->SetGradientEstimator(gradientEstimator);
  } else if (dynamic_cast<vtkVolumeTextureMapper2D*>(m_VtkVolumeMapper)){
  dynamic_cast<vtkVolumeTextureMapper2D*>(m_VtkVolumeMapper)->SetGradientEstimator(gradientEstimator);
  } */
  m_LowResMapper->SetGradientEstimator(gradientEstimator);
  m_MedResMapper->SetGradientEstimator(gradientEstimator);
  m_HiResMapper->SetGradientEstimator(gradientEstimator);

  gradientEstimator->Delete();

  m_VolumeProperty = vtkVolumeProperty::New();
  m_VolumeLOD = vtkLODProp3D::New();

  m_VolumeLOD->AddLOD(m_HiResMapper,m_VolumeProperty,0.0);
  m_VolumeLOD->AddLOD(m_MedResMapper,m_VolumeProperty,0.0);
  m_VolumeLOD->AddLOD(m_LowResMapper,m_VolumeProperty,0.0);

  m_Resampler = vtkImageResample::New();
  m_Resampler->SetAxisMagnificationFactor(0,0.5);
  m_Resampler->SetAxisMagnificationFactor(1,0.5);
  m_Resampler->SetAxisMagnificationFactor(2,0.5);

  //m_Volume = vtkVolume::New();
  // m_Volume->SetMapper( m_VtkVolumeMapper );
  // m_Volume->SetProperty(m_VolumeProperty);
  // m_Prop3D = m_Volume;
  m_Prop3D = m_VolumeLOD;
  m_Prop3D->Register(NULL);

  m_ImageCast = vtkImageShiftScale::New();
  m_ImageCast->SetOutputScalarTypeToUnsignedShort();
  m_ImageCast->ClampOverflowOn();

  m_UnitSpacingImageFilter = vtkImageChangeInformation::New();
  m_UnitSpacingImageFilter->SetInput(m_ImageCast->GetOutput());
  m_UnitSpacingImageFilter->SetOutputSpacing(1.0,1.0,1.0);
}

mitk::VolumeDataVtkMapper3D::~VolumeDataVtkMapper3D()
{
  // m_VtkVolumeMapper->Delete();
  //m_Volume->Delete();
  m_UnitSpacingImageFilter->Delete();
  m_ImageCast->Delete();
  m_LowResMapper->Delete();
  m_MedResMapper->Delete();
  m_HiResMapper->Delete();
  m_Resampler->Delete();
  m_VolumeProperty->Delete();
  m_VolumeLOD->Delete();
}
void mitk::VolumeDataVtkMapper3D::AbortCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata) {
  // std::cout << "abort test called" << std::endl;
  vtkRenderWindow* renWin = dynamic_cast<vtkRenderWindow*>(caller);
  assert(renWin);
   int foo=renWin->GetEventPending();
   if(foo!=0)
   {
     renWin->SetAbortRender(1);
   }
  // FIXME: qApp->hasPendingEvents is always true, renWin->GetEventPending is
  // always false. So aborting the render doesn't work this way.

  // if (
  //   qApp->hasPendingEvents()
  // )
  // {
  // std::cout << "setting abort render" << std::endl;
  //   renWin->SetAbortRender(1);
  // }
}

void mitk::VolumeDataVtkMapper3D::GenerateData(mitk::BaseRenderer* renderer)
{

  if(IsVisible(renderer)==false ||
      GetDataTreeNode() == NULL ||
      dynamic_cast<mitk::BoolProperty*>(GetDataTreeNode()->GetProperty("volumerendering",renderer).GetPointer())==NULL ||
      dynamic_cast<mitk::BoolProperty*>(GetDataTreeNode()->GetProperty("volumerendering",renderer).GetPointer())->GetValue() == false
    )
  {
    //  FIXME: don't understand this
    if (m_Prop3D) {
      //      std::cout << "visibility off" <<std::endl;

      m_Prop3D->VisibilityOff();
    }
    return;
  }
  if (m_Prop3D) {
    m_Prop3D->VisibilityOn();
  }

  mitk::Image* input  = const_cast<mitk::Image *>(this->GetInput());
  if((input==NULL) || (input->IsInitialized()==false))
    return;

  // FIXME: const_cast; maybe GetVtkImageData can be made const by using volatile
  const TimeSlicedGeometry* inputtimegeometry = input->GetTimeSlicedGeometry();
  assert(inputtimegeometry!=NULL);

  const Geometry3D* worldgeometry = renderer->GetCurrentWorldGeometry();

  assert(worldgeometry!=NULL);

  int timestep=0;
  ScalarType time = worldgeometry->GetTimeBounds()[0];
  if(time>-ScalarTypeNumericTraits::max())
    timestep = inputtimegeometry->MSToTimeStep(time);

  if(inputtimegeometry->IsValidTime(timestep)==false)
    return;

  vtkImageData* inputData = input->GetVtkImageData(timestep);
  if(inputData==NULL)
    return;

  m_ImageCast->SetInput( inputData );

//  m_ImageCast->Update();

  //trying to avoid update-problem, when size of input changed. Does not really help much.
  if(m_ImageCast->GetOutput()!=NULL)
  {
    int inputWE[6];
    inputData->GetWholeExtent(inputWE);
    int * outputWE=m_UnitSpacingImageFilter->GetOutput()->GetExtent();
    if(m_ImageCast->GetOutput()!=NULL && memcmp(inputWE, outputWE,sizeof(int)*6) != 0)
    {
//      m_ImageCast->GetOutput()->SetUpdateExtentToWholeExtent();
//      m_UnitSpacingImageFilter->GetOutput()->SetUpdateExtentToWholeExtent();
      m_UnitSpacingImageFilter->GetOutput()->SetUpdateExtent(inputWE);
      m_UnitSpacingImageFilter->UpdateWholeExtent();
    }
  }

  //m_VtkVolumeMapper->SetInput( m_UnitSpacingImageFilter->GetOutput() );
  //m_Volume->SetMapper( m_VtkVolumeMapper );
  m_Resampler->SetInput(m_UnitSpacingImageFilter->GetOutput());

  m_LowResMapper->SetInput(m_Resampler->GetOutput());
  m_MedResMapper->SetInput(m_UnitSpacingImageFilter->GetOutput());
  m_HiResMapper->SetInput(m_UnitSpacingImageFilter->GetOutput());

  vtkPiecewiseFunction *opacityTransferFunction;
  vtkColorTransferFunction* colorTransferFunction;

  opacityTransferFunction  = vtkPiecewiseFunction::New();
  colorTransferFunction    = vtkColorTransferFunction::New();

  m_ImageCast->SetShift(0);

mitk::TransferFunctionProperty::Pointer tranferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty*>(this->GetDataTreeNode()->GetProperty("TransferFunction").GetPointer());
  if (tranferFunctionProp.IsNull()) {
   // create one
    mitk::TransferFunction::Pointer newTF = mitk::TransferFunction::New();
    newTF->InitializeByMitkImage(input);
    this->GetDataTreeNode()->SetProperty("TransferFunction", new TransferFunctionProperty(newTF.GetPointer()));
  }

  mitk::LookupTableProperty::Pointer lookupTableProp;
  lookupTableProp = dynamic_cast<mitk::LookupTableProperty*>(this->GetDataTreeNode()->GetProperty("LookupTable").GetPointer());
  mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast<mitk::TransferFunctionProperty*>(this->GetDataTreeNode()->GetProperty("TransferFunction").GetPointer());
  if ( transferFunctionProp.IsNotNull() )   {

    opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction();
    colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction();
  } else if (lookupTableProp.IsNotNull() )
  {
    lookupTableProp->GetLookupTable().CreateColorTransferFunction(colorTransferFunction);
    colorTransferFunction->ClampingOn();
    lookupTableProp->GetLookupTable().CreateOpacityTransferFunction(opacityTransferFunction);
    opacityTransferFunction->ClampingOn();
  }
  else
  {
    mitk::LevelWindow levelWindow;
    int lw_min,lw_max;

    bool binary=false;
    GetDataTreeNode()->GetBoolProperty("binary", binary, renderer);
    if(binary)
    {
      lw_min=0; lw_max=2;
    }
    else
    if(GetLevelWindow(levelWindow,renderer,"levelWindow") || GetLevelWindow(levelWindow,renderer))
    {
      lw_min = (int)levelWindow.GetMin();
      lw_max = (int)levelWindow.GetMax();
      //      std::cout << "levwin:" << levelWindow << std::endl;
      if(lw_min<0)
      {
        m_ImageCast->SetShift(-lw_min);
        lw_max+=-lw_min;
        lw_min=0;
      }
    }
    else
    {
      lw_min = 0;
      lw_max = 255;
    }

    opacityTransferFunction->AddPoint( lw_min, 0.0 );
    opacityTransferFunction->AddPoint( lw_max, 0.8 );
    opacityTransferFunction->ClampingOn();

    //colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 1.0 );
    //colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, 1.0, 0.0, 0.0 );
    //colorTransferFunction->AddRGBPoint( lw_max, 0.0, 1.0, 0.0 );

    float rgb[3]={1.0f,1.0f,1.0f};
    // check for color prop and use it for rendering if it exists
    if(GetColor(rgb, renderer))
    {
      colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 0.0 );
      colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, rgb[0], rgb[1], rgb[2] );
      colorTransferFunction->AddRGBPoint( lw_max, rgb[0], rgb[1], rgb[2] );
    }
    else
    {
      colorTransferFunction->AddRGBPoint( lw_min, 0.0, 0.0, 0.0 );
      colorTransferFunction->AddRGBPoint( (lw_min+lw_max)/2, 1, 1, 0.0 );
      colorTransferFunction->AddRGBPoint( lw_max, 0.8, 0.2, 0 );
    }

    colorTransferFunction->ClampingOn();
  }
  m_VolumeProperty->SetColor( colorTransferFunction );
  m_VolumeProperty->SetScalarOpacity( opacityTransferFunction );
  m_VolumeProperty->SetDiffuse(0.2);
  m_VolumeProperty->SetAmbient(0.9);
  m_VolumeProperty->ShadeOn();

  mitk::OpenGLRenderer* openGlRenderer = dynamic_cast<mitk::OpenGLRenderer*>(renderer);
  assert(openGlRenderer);
  vtkRenderWindow* vtkRendWin = dynamic_cast<vtkRenderWindow*>(openGlRenderer->GetVtkRenderWindow());
  if (vtkRendWin) {
//    vtkRendWin->SetDesiredUpdateRate(25.0);
    vtkRenderWindowInteractor* interactor = vtkRendWin->GetInteractor();
    interactor->SetDesiredUpdateRate(50000.0);
    interactor->SetStillUpdateRate(0.0001);

    vtkCallbackCommand* cbc = vtkCallbackCommand::New();
    cbc->SetCallback(mitk::VolumeDataVtkMapper3D::AbortCallback);
    vtkRendWin->AddObserver(vtkCommand::AbortCheckEvent,cbc);

  } else {
    std::cout << "no vtk renderwindow" << std::endl;
  }
  //m_Volume->Update();
  // m_VolumeLOD->Update();
  // m_Prop3D = m_Volume;

  // now add an AbortCheckEvent callback for cancelling the rendering

//  colorTransferFunction->Delete();
//  opacityTransferFunction->Delete();
}

void mitk::VolumeDataVtkMapper3D::ApplyProperties(vtkActor* actor, mitk::BaseRenderer* renderer)
{

}
<|endoftext|>


<commit_before>#include "mex.h"
#include <iostream>
#include "drakeUtil.h"
#include "RigidBodyManipulator.h"
#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>
#include "drake/ExponentialPlusPiecewisePolynomial.h"
#include "math.h"
#include <vector>

using namespace Eigen;
using namespace std;

MatrixXd expm(const MatrixXd& A) {
  MatrixXd F;
  MatrixExponential<MatrixXd>(A).compute(F);
  return F;
}

PiecewisePolynomial<double> matlabPPFormToPiecewisePolynomial(const mxArray* pp)
{
  vector<double> breaks = matlabToStdVector<double>(mxGetFieldSafe(pp, "breaks"));
  size_t num_segments = breaks.size() - 1; // l

  const mxArray* coefs_mex = mxGetFieldSafe(pp, "coefs"); // a d*l x k matrix
  const size_t* coefs_mex_dims = mxGetDimensions(coefs_mex);
  int num_coefs_mex_dims = mxGetNumberOfDimensions(coefs_mex);

  size_t number_of_elements = mxGetNumberOfElements(coefs_mex);

  const mxArray* dim_mex = mxGetFieldSafe(pp, "dim");
  int num_dims_mex = mxGetNumberOfElements(dim_mex);
  if (num_dims_mex == 0 | num_dims_mex > 2)
    throw runtime_error("case not handled"); // because PiecewisePolynomial can't currently handle it
  const int num_dims = 2;
  mwSize dims[num_dims];
  for (int i = 0; i < num_dims_mex; i++) {
    dims[i] = static_cast<mwSize>(mxGetPr(dim_mex)[i]);
  }
  for (int i = num_dims_mex; i < num_dims; i++)
    dims[i] = 1;

  size_t product_of_dimensions = dims[0]; // d
  for (int i = 1; i < num_dims; ++i) {
    product_of_dimensions *= dims[i];
  }

  size_t num_coefficients = number_of_elements / (num_segments * product_of_dimensions); // k

  vector<PiecewisePolynomial<double>::PolynomialMatrix> polynomial_matrices;
  polynomial_matrices.reserve(num_segments);
  for (mwSize segment_index = 0; segment_index < num_segments; segment_index++) {
    PiecewisePolynomial<double>::PolynomialMatrix polynomial_matrix(dims[0], dims[1]);
    for (mwSize i = 0; i < product_of_dimensions; i++) {
      VectorXd coefficients(num_coefficients);
      mwSize row = segment_index * product_of_dimensions + i;
      for (mwSize coefficient_index = 0; coefficient_index < num_coefficients; coefficient_index++) {
        mwSize sub[] = {row, num_coefficients - coefficient_index - 1}; // Matlab's reverse coefficient indexing...
        coefficients[coefficient_index] = *(mxGetPr(coefs_mex) + sub2ind(num_coefs_mex_dims, coefs_mex_dims, sub));
      }
      polynomial_matrix(i) = Polynomial<double>(coefficients);
    }
    polynomial_matrices.push_back(polynomial_matrix);
  }

  return PiecewisePolynomial<double>(polynomial_matrices, breaks);
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{

  PiecewisePolynomial<double> pp; //this needs to come in as input
  pp = matlabPPFormToPiecewisePolynomial(prhs[0]);
  Map<Matrix4d> A(mxGetPrSafe(prhs[1]));
  Map<MatrixXd> B(mxGetPrSafe(prhs[2]), 4, 2);
  Map<MatrixXd> C(mxGetPrSafe(prhs[3]), 2, 4);
  Map<Matrix2d> D(mxGetPrSafe(prhs[4]));
  Map<Matrix2d> Q(mxGetPrSafe(prhs[5]));
  Map<Matrix2d> R(mxGetPrSafe(prhs[6]));
  Map<Matrix4d> Q1(mxGetPrSafe(prhs[7]));
  Map<Matrix2d> R1(mxGetPrSafe(prhs[8]));
  Map<MatrixXd> N(mxGetPrSafe(prhs[9]), 4, 2);
  Map<Matrix4d> S(mxGetPrSafe(prhs[10]));

  size_t n = static_cast<size_t>(pp.getNumberOfSegments());

  int d = pp.getSegmentPolynomialDegree(0);

  for (size_t i = 1; i < n; i++) {
    assert(pp.getSegmentPolynomialDegree(i) == d);
  }

  VectorXd dt(n);

  std::vector<double> breaks = pp.getSegmentTimes();

  for(size_t i = 0; i < n; i++) {
    dt(i) = breaks[i + 1] - breaks[i];
  }

  size_t k = d + 1;

  MatrixXd zmp_tf = pp.value(pp.getEndTime());

  PiecewisePolynomial<double> zbar_pp = pp - zmp_tf;

  Matrix2d R1i = R1.inverse();

  MatrixXd NB = N.transpose() + B.transpose() * S; //2 x 4
  Matrix4d A2 = NB.transpose() * R1i * B.transpose() - A.transpose();
  MatrixXd B2 = 2 * (C.transpose() - NB.transpose() * R1i * D) * Q; //4 x 2

  Matrix4d A2i = A2.inverse();

  MatrixXd alpha = MatrixXd::Zero(4, n);

  vector<MatrixXd> beta;

  for(size_t i = 0; i < n ; i++) {
    beta.push_back(MatrixXd::Zero(4, k));
  }

  VectorXd s1dt;
  for (int j = n - 1; j >= 0; j--) {

    auto poly_mat = zbar_pp.getPolynomialMatrix(j);
    size_t nq = poly_mat.rows();
    MatrixXd poly_coeffs = MatrixXd::Zero(nq, k);

    for (size_t x = 0; x < nq; x++) {
      poly_coeffs.row(x) = poly_mat(x).getCoefficients().transpose();
    }

    beta[j].col(k - 1) = -A2i * B2 * poly_coeffs.col(k - 1); //correct

    for (int i = k - 2; i >= 0; i--) {
      beta[j].col(i) = A2i * ((i+1) * beta[j].col(i + 1) - B2 * poly_coeffs.col(i)); //correct
    }

    if(j == n - 1) {
      s1dt = VectorXd::Zero(4);
    } else {
      s1dt = alpha.col(j+1) + beta[j + 1].col(0);
    }

    VectorXd dtpow(k - 1);
    for(size_t p = 0; p < k; p++) {
      dtpow(p) = pow(dt(j), p);
    }

    alpha.col(j) = expm(A2*dt(j)).inverse() * (s1dt - beta[j]*dtpow);
  }

  cout << alpha << endl;

  return;

  //matlabPPFormToPiecewisePolynomial

  //const mxArray* mex_breaks = mxGetFieldOrPropertySafe(array, "breaks");
  //const mxArray* mex_coefs = mxGetFieldOrPropertySafe(array, "gamma");
  //PiecewisePolynomial<double> piecewise_polynomial_part = matlabCoefsAndBreaksToPiecewisePolynomial(mex_coefs, mex_breaks, false);

  //ExpPlusPPTrajectory(breaks,K,A,alpha,gamma)
  //ExponentialPlusPiecewisePolynomial(Matrix4d::Identity)
  //ExponentialPlusPiecewisePolynomial(const Eigen::MatrixBase<DerivedK>& K, const Eigen::MatrixBase<DerivedA>& A, const Eigen::MatrixBase<DerivedAlpha>& alpha, const PiecewisePolynomial<CoefficientType>& piecewise_polynomial_part)
  //s1traj = ExpPlusPPTrajectory(breaks,eye(4),A2,alpha,beta);
  //build ExponentialPlusPiecewisePolynomial with alpha and beta here....

}
<commit_msg>builds s1traj as an ExponentialPlusPiecewisePolynomial trajectory using alpha and beta<commit_after>#include "mex.h"
#include <iostream>
#include "drakeUtil.h"
#include "RigidBodyManipulator.h"
#include <Eigen/Dense>
#include <unsupported/Eigen/MatrixFunctions>
#include "drake/ExponentialPlusPiecewisePolynomial.h"
#include "math.h"
#include <vector>

using namespace Eigen;
using namespace std;

MatrixXd expm(const MatrixXd& A) {
  MatrixXd F;
  MatrixExponential<MatrixXd>(A).compute(F);
  return F;
}

PiecewisePolynomial<double> matlabPPFormToPiecewisePolynomial(const mxArray* pp)
{
  vector<double> breaks = matlabToStdVector<double>(mxGetFieldSafe(pp, "breaks"));
  size_t num_segments = breaks.size() - 1; // l

  const mxArray* coefs_mex = mxGetFieldSafe(pp, "coefs"); // a d*l x k matrix
  const size_t* coefs_mex_dims = mxGetDimensions(coefs_mex);
  int num_coefs_mex_dims = mxGetNumberOfDimensions(coefs_mex);

  size_t number_of_elements = mxGetNumberOfElements(coefs_mex);

  const mxArray* dim_mex = mxGetFieldSafe(pp, "dim");
  int num_dims_mex = mxGetNumberOfElements(dim_mex);
  if (num_dims_mex == 0 | num_dims_mex > 2)
    throw runtime_error("case not handled"); // because PiecewisePolynomial can't currently handle it
  const int num_dims = 2;
  mwSize dims[num_dims];
  for (int i = 0; i < num_dims_mex; i++) {
    dims[i] = static_cast<mwSize>(mxGetPr(dim_mex)[i]);
  }
  for (int i = num_dims_mex; i < num_dims; i++)
    dims[i] = 1;

  size_t product_of_dimensions = dims[0]; // d
  for (int i = 1; i < num_dims; ++i) {
    product_of_dimensions *= dims[i];
  }

  size_t num_coefficients = number_of_elements / (num_segments * product_of_dimensions); // k

  vector<PiecewisePolynomial<double>::PolynomialMatrix> polynomial_matrices;
  polynomial_matrices.reserve(num_segments);
  for (mwSize segment_index = 0; segment_index < num_segments; segment_index++) {
    PiecewisePolynomial<double>::PolynomialMatrix polynomial_matrix(dims[0], dims[1]);
    for (mwSize i = 0; i < product_of_dimensions; i++) {
      VectorXd coefficients(num_coefficients);
      mwSize row = segment_index * product_of_dimensions + i;
      for (mwSize coefficient_index = 0; coefficient_index < num_coefficients; coefficient_index++) {
        mwSize sub[] = {row, num_coefficients - coefficient_index - 1}; // Matlab's reverse coefficient indexing...
        coefficients[coefficient_index] = *(mxGetPr(coefs_mex) + sub2ind(num_coefs_mex_dims, coefs_mex_dims, sub));
      }
      polynomial_matrix(i) = Polynomial<double>(coefficients);
    }
    polynomial_matrices.push_back(polynomial_matrix);
  }

  return PiecewisePolynomial<double>(polynomial_matrices, breaks);
}

//func sig:
//computeS1Trajmex(dZMP.pp, A, B, C, D, Q, R, Q1, R1, N, S);

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  auto pp = matlabPPFormToPiecewisePolynomial(prhs[0]);
  Map<Matrix4d> A(mxGetPrSafe(prhs[1]));
  Map<MatrixXd> B(mxGetPrSafe(prhs[2]), 4, 2);
  Map<MatrixXd> C(mxGetPrSafe(prhs[3]), 2, 4);
  Map<Matrix2d> D(mxGetPrSafe(prhs[4]));
  Map<Matrix2d> Q(mxGetPrSafe(prhs[5]));
  Map<Matrix2d> R(mxGetPrSafe(prhs[6]));
  Map<Matrix4d> Q1(mxGetPrSafe(prhs[7]));
  Map<Matrix2d> R1(mxGetPrSafe(prhs[8]));
  Map<MatrixXd> N(mxGetPrSafe(prhs[9]), 4, 2);
  Map<Matrix4d> S(mxGetPrSafe(prhs[10]));

  size_t n = static_cast<size_t>(pp.getNumberOfSegments());
  int d = pp.getSegmentPolynomialDegree(0);
  size_t k = d + 1;

  for (size_t i = 1; i < n; i++) {
    assert(pp.getSegmentPolynomialDegree(i) == d);
  }

  VectorXd dt(n);
  std::vector<double> breaks = pp.getSegmentTimes();

  for (size_t i = 0; i < n; i++) {
    dt(i) = breaks[i + 1] - breaks[i];
  }

  MatrixXd zmp_tf = pp.value(pp.getEndTime());
  PiecewisePolynomial<double> zbar_pp = pp - zmp_tf;

  Matrix2d R1i = R1.inverse();
  MatrixXd NB = N.transpose() + B.transpose() * S; //2 x 4
  Matrix4d A2 = NB.transpose() * R1i * B.transpose() - A.transpose();
  MatrixXd B2 = 2 * (C.transpose() - NB.transpose() * R1i * D) * Q; //4 x 2
  Matrix4d A2i = A2.inverse();

  MatrixXd alpha = MatrixXd::Zero(4, n);

  vector<MatrixXd> beta;
  VectorXd s1dt;

  for (size_t i = 0; i < n ; i++) {
    beta.push_back(MatrixXd::Zero(4, k));
  }

  for (int j = n - 1; j >= 0; j--) {

    auto poly_mat = zbar_pp.getPolynomialMatrix(j);
    size_t nq = poly_mat.rows();
    MatrixXd poly_coeffs = MatrixXd::Zero(nq, k);

    for (size_t x = 0; x < nq; x++) {
      poly_coeffs.row(x) = poly_mat(x).getCoefficients().transpose();
    }

    beta[j].col(k - 1) = -A2i * B2 * poly_coeffs.col(k - 1);

    for (int i = k - 2; i >= 0; i--) {
      beta[j].col(i) = A2i * ((i+1) * beta[j].col(i + 1) - B2 * poly_coeffs.col(i));
    }

    if (j == n - 1) {
      s1dt = VectorXd::Zero(4);
    } else {
      s1dt = alpha.col(j+1) + beta[j + 1].col(0);
    }

    VectorXd dtpow(k - 1);
    for (size_t p = 0; p < k; p++) {
      dtpow(p) = pow(dt(j), p);
    }

    alpha.col(j) = expm(A2*dt(j)).inverse() * (s1dt - beta[j]*dtpow);
  }

  vector<PiecewisePolynomial<double>::PolynomialMatrix> polynomial_matrices;
  for (int segment = 0; segment < n ; segment++) {
    PiecewisePolynomial<double>::PolynomialMatrix polynomial_matrix(4, 1);
    for(int row = 0; row < 4; row++) {
      polynomial_matrix(row) = Polynomial<double>(beta[segment].row(row));
    }
    polynomial_matrices.push_back(polynomial_matrix);
  }

  PiecewisePolynomial<double> pp_part = PiecewisePolynomial<double>(polynomial_matrices, breaks);
  auto s1traj = ExponentialPlusPiecewisePolynomial<double>(Matrix4d::Identity(), A2, alpha, pp_part);
  cout << s1traj.value(0) << endl;
  //do stuff with s1traj
}
<|endoftext|>


<commit_before>/*
 * pHashTest.cpp
 *
 *  Created on: 25 Jul 2013
 *      Author: nicholas
 */

#include <gtest/gtest.h>
#include <iostream>
#include "../../include/hash/ImagePHash.hpp"
#include <GraphicsMagick/Magick++.h>

using namespace Magick;

TEST(ImagePHashTest, hashImage) {
	ImagePHash iph;
	long pHash = iph.getLongHash("src/test/hash/testImage.jpg");
	ASSERT_EQ(7655956633439617023,pHash);
}

int main(int argc, char **argv) {
	 BasicConfigurator config;
	 config.configure();
::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

<commit_msg>Updated ImagePHash test<commit_after>/*
 * pHashTest.cpp
 *
 *  Created on: 25 Jul 2013
 *      Author: nicholas
 */

#include <gtest/gtest.h>
#include <iostream>
#include "../../include/hash/ImagePHash.hpp"
#include <GraphicsMagick/Magick++.h>

using namespace Magick;

TEST(ImagePHashTest, hashImage) {
	ImagePHash iph;
	long pHash = iph.getLongHash("src/test/hash/testImage.jpg");
	ASSERT_EQ(1061781947002206,pHash);
}

int main(int argc, char **argv) {
	 BasicConfigurator config;
	 config.configure();
::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

<|endoftext|>


<commit_before>//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef __APPLE__

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

#include "platform/platform.h"
#include "platformPOSIX/platformPOSIX.h"
#include "platform/platformCPUCount.h"

#include "console/console.h"

#include <unistd.h>

Platform::SystemInfo_struct Platform::SystemInfo;

static inline void rtrim(std::string &s)
{
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

static inline void ltrim(std::string &s)
{
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}

void getCPUInformation()
{
    std::string vendorString;
    std::string brandString;

    std::ifstream cpuInfo("/proc/cpuinfo");

    U32 logicalCoreCount = 0;
    U32 physicalCoreCount = 1;

    if (cpuInfo.is_open())
    {
        // Load every line of the CPU Info
        std::string line;

        while (std::getline(cpuInfo, line))
        {
            std::string fieldName = line.substr(0, line.find(":"));
            rtrim(fieldName);

            // Entries are newline separated
            if (fieldName == "")
            {
                ++logicalCoreCount;
                continue;
            }

            std::string fieldValue = line.substr(line.find(":") + 1, line.length());
            ltrim(fieldValue);
            rtrim(fieldValue);

            // Load fields
            if (fieldName == "vendor_id")
            {
                vendorString = fieldValue.c_str();
            }
            else if (fieldName == "model name")
            {
                brandString = fieldValue.c_str();
            }
            else if (fieldName == "cpu cores")
            {
                physicalCoreCount = dAtoui(fieldValue.c_str());
            }
            else if (fieldName == "flags")
            {
                std::vector<std::string> flags;
                std::istringstream flagStream(fieldValue);

                std::string currentFlag;
                while (std::getline(flagStream, currentFlag, ' '))
                {
                    flags.push_back(currentFlag);
                }

                // Set CPU flags
                if (std::find(flags.begin(), flags.end(), "fpu") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_FPU;
                }

                if (std::find(flags.begin(), flags.end(), "sse3") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE3;
                }

                if (std::find(flags.begin(), flags.end(), "avx") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_AVX;
                }

                if (std::find(flags.begin(), flags.end(), "ssse3") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE3ex;
                }

                if (std::find(flags.begin(), flags.end(), "sse") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE;
                }

                if (std::find(flags.begin(), flags.end(), "sse2") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE2;
                }

                if (std::find(flags.begin(), flags.end(), "sse4_1") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE4_1;
                }

                if (std::find(flags.begin(), flags.end(), "sse4_2") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE4_2;
                }

                if (std::find(flags.begin(), flags.end(), "mmx") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_MMX;
                }
            }
        }

        cpuInfo.close();
    }
    else
    {
        logicalCoreCount = 1;
    }

    Platform::SystemInfo.processor.numLogicalProcessors = logicalCoreCount;
    Platform::SystemInfo.processor.numPhysicalProcessors = physicalCoreCount;
    Platform::SystemInfo.processor.isHyperThreaded = logicalCoreCount != physicalCoreCount;
    Platform::SystemInfo.processor.isMultiCore = physicalCoreCount != 1;
    Platform::SystemInfo.processor.numLogicalProcessors = logicalCoreCount;
    Platform::SystemInfo.processor.numPhysicalProcessors = physicalCoreCount;
    if (Platform::SystemInfo.processor.isMultiCore)
    {
        Platform::SystemInfo.processor.properties |= CPU_PROP_MP;
    }

    // Load processor base frequency
    std::ifstream baseFrequencyStream("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency");
    if (baseFrequencyStream.is_open())
    {
        U32 baseFrequencyKHz = 0;
        baseFrequencyStream >> baseFrequencyKHz;

        Platform::SystemInfo.processor.mhz = baseFrequencyKHz / 1000;
        baseFrequencyStream.close();
    }

    SetProcessorInfo(Platform::SystemInfo.processor, vendorString.c_str(), brandString.c_str());
}

void Processor::init()
{
    getCPUInformation();

#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_X32)
    // Set sane default information
    Platform::SystemInfo.processor.properties |= CPU_PROP_C | CPU_PROP_FPU | CPU_PROP_LE ;

#elif defined(TORQUE_CPU_ARM32) || defined(TORQUE_CPU_ARM64)
    Platform::SystemInfo.processor.type = CPU_ArmCompatible;
    Platform::SystemInfo.processor.name = StringTable->insert("Unknown ARM Processor");
    Platform::SystemInfo.processor.properties = CPU_PROP_C;
#else
#warning Unsupported CPU
#endif

    // Set 64bit flag
#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_ARM64)
    Platform::SystemInfo.processor.properties |= CPU_PROP_64bit;
#endif

    // Once CPU information is resolved, produce an output like Windows does
    Con::printf("Processor Init:");
    Con::printf("   Processor: %s", Platform::SystemInfo.processor.name);
    if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
        Con::printf("      MMX detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
        Con::printf("      SSE detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE2)
        Con::printf("      SSE2 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3)
        Con::printf("      SSE3 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_1)
        Con::printf("      SSE4.1 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_2)
        Con::printf("      SSE4.2 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_AVX)
        Con::printf("      AVX detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3ex)
        Con::printf("      SSE3ex detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_MP)
        Con::printf("   MultiCore CPU detected [%i cores, %i logical]", Platform::SystemInfo.processor.numPhysicalProcessors, Platform::SystemInfo.processor.numLogicalProcessors);

    Con::printf(" ");
}

namespace CPUInfo
{
    EConfig CPUCount(U32 &logical, U32 &physical)
    {
        // We don't set logical or physical here because it's already been determined by this point
        if (Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors == 1)
        {
            return CONFIG_SingleCoreHTEnabled;
        }
        else if (!Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors > 1)
        {
            return CONFIG_MultiCoreAndHTNotCapable;
        }
        else if (!Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors == 1)
        {
            return CONFIG_SingleCoreAndHTNotCapable;
        }

        return CONFIG_MultiCoreAndHTEnabled;
    }
}; // namespace CPUInfo

#endif<commit_msg>* Adjustment: Add static keyword to getCPUInformation in POSIXCPUInfo.<commit_after>//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#ifndef __APPLE__

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

#include "platform/platform.h"
#include "platformPOSIX/platformPOSIX.h"
#include "platform/platformCPUCount.h"

#include "console/console.h"

#include <unistd.h>

Platform::SystemInfo_struct Platform::SystemInfo;

static inline void rtrim(std::string &s)
{
    s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

static inline void ltrim(std::string &s)
{
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
}

static void getCPUInformation()
{
    std::string vendorString;
    std::string brandString;

    std::ifstream cpuInfo("/proc/cpuinfo");

    U32 logicalCoreCount = 0;
    U32 physicalCoreCount = 1;

    if (cpuInfo.is_open())
    {
        // Load every line of the CPU Info
        std::string line;

        while (std::getline(cpuInfo, line))
        {
            std::string fieldName = line.substr(0, line.find(":"));
            rtrim(fieldName);

            // Entries are newline separated
            if (fieldName == "")
            {
                ++logicalCoreCount;
                continue;
            }

            std::string fieldValue = line.substr(line.find(":") + 1, line.length());
            ltrim(fieldValue);
            rtrim(fieldValue);

            // Load fields
            if (fieldName == "vendor_id")
            {
                vendorString = fieldValue.c_str();
            }
            else if (fieldName == "model name")
            {
                brandString = fieldValue.c_str();
            }
            else if (fieldName == "cpu cores")
            {
                physicalCoreCount = dAtoui(fieldValue.c_str());
            }
            else if (fieldName == "flags")
            {
                std::vector<std::string> flags;
                std::istringstream flagStream(fieldValue);

                std::string currentFlag;
                while (std::getline(flagStream, currentFlag, ' '))
                {
                    flags.push_back(currentFlag);
                }

                // Set CPU flags
                if (std::find(flags.begin(), flags.end(), "fpu") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_FPU;
                }

                if (std::find(flags.begin(), flags.end(), "sse3") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE3;
                }

                if (std::find(flags.begin(), flags.end(), "avx") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_AVX;
                }

                if (std::find(flags.begin(), flags.end(), "ssse3") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE3ex;
                }

                if (std::find(flags.begin(), flags.end(), "sse") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE;
                }

                if (std::find(flags.begin(), flags.end(), "sse2") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE2;
                }

                if (std::find(flags.begin(), flags.end(), "sse4_1") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE4_1;
                }

                if (std::find(flags.begin(), flags.end(), "sse4_2") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_SSE4_2;
                }

                if (std::find(flags.begin(), flags.end(), "mmx") != flags.end())
                {
                    Platform::SystemInfo.processor.properties |= CPU_PROP_MMX;
                }
            }
        }

        cpuInfo.close();
    }
    else
    {
        logicalCoreCount = 1;
    }

    Platform::SystemInfo.processor.numLogicalProcessors = logicalCoreCount;
    Platform::SystemInfo.processor.numPhysicalProcessors = physicalCoreCount;
    Platform::SystemInfo.processor.isHyperThreaded = logicalCoreCount != physicalCoreCount;
    Platform::SystemInfo.processor.isMultiCore = physicalCoreCount != 1;
    Platform::SystemInfo.processor.numLogicalProcessors = logicalCoreCount;
    Platform::SystemInfo.processor.numPhysicalProcessors = physicalCoreCount;
    if (Platform::SystemInfo.processor.isMultiCore)
    {
        Platform::SystemInfo.processor.properties |= CPU_PROP_MP;
    }

    // Load processor base frequency
    std::ifstream baseFrequencyStream("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency");
    if (baseFrequencyStream.is_open())
    {
        U32 baseFrequencyKHz = 0;
        baseFrequencyStream >> baseFrequencyKHz;

        Platform::SystemInfo.processor.mhz = baseFrequencyKHz / 1000;
        baseFrequencyStream.close();
    }

    SetProcessorInfo(Platform::SystemInfo.processor, vendorString.c_str(), brandString.c_str());
}

void Processor::init()
{
    getCPUInformation();

#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_X32)
    // Set sane default information
    Platform::SystemInfo.processor.properties |= CPU_PROP_C | CPU_PROP_FPU | CPU_PROP_LE ;

#elif defined(TORQUE_CPU_ARM32) || defined(TORQUE_CPU_ARM64)
    Platform::SystemInfo.processor.type = CPU_ArmCompatible;
    Platform::SystemInfo.processor.name = StringTable->insert("Unknown ARM Processor");
    Platform::SystemInfo.processor.properties = CPU_PROP_C;
#else
#warning Unsupported CPU
#endif

    // Set 64bit flag
#if defined(TORQUE_CPU_X64) || defined(TORQUE_CPU_ARM64)
    Platform::SystemInfo.processor.properties |= CPU_PROP_64bit;
#endif

    // Once CPU information is resolved, produce an output like Windows does
    Con::printf("Processor Init:");
    Con::printf("   Processor: %s", Platform::SystemInfo.processor.name);
    if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
        Con::printf("      MMX detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
        Con::printf("      SSE detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE2)
        Con::printf("      SSE2 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3)
        Con::printf("      SSE3 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_1)
        Con::printf("      SSE4.1 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_2)
        Con::printf("      SSE4.2 detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_AVX)
        Con::printf("      AVX detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3ex)
        Con::printf("      SSE3ex detected" );
    if (Platform::SystemInfo.processor.properties & CPU_PROP_MP)
        Con::printf("   MultiCore CPU detected [%i cores, %i logical]", Platform::SystemInfo.processor.numPhysicalProcessors, Platform::SystemInfo.processor.numLogicalProcessors);

    Con::printf(" ");
}

namespace CPUInfo
{
    EConfig CPUCount(U32 &logical, U32 &physical)
    {
        // We don't set logical or physical here because it's already been determined by this point
        if (Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors == 1)
        {
            return CONFIG_SingleCoreHTEnabled;
        }
        else if (!Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors > 1)
        {
            return CONFIG_MultiCoreAndHTNotCapable;
        }
        else if (!Platform::SystemInfo.processor.isHyperThreaded && Platform::SystemInfo.processor.numPhysicalProcessors == 1)
        {
            return CONFIG_SingleCoreAndHTNotCapable;
        }

        return CONFIG_MultiCoreAndHTEnabled;
    }
}; // namespace CPUInfo

#endif<|endoftext|>


<commit_before>/*
Copyright (c) 2013, 2014, Ilja Honkonen
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 copyright holders nor the names of their 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 HOLDER 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.
*/

#ifndef GET_VAR_DATATYPE_HPP
#define GET_VAR_DATATYPE_HPP

#if defined(MPI_VERSION) && (MPI_VERSION >= 2)

#include "boost/function_types/property_tags.hpp"
#include "boost/mpl/vector.hpp"
#include "boost/tti/has_member_function.hpp"
#include "complex"
#include "cstddef"
#include "cstdint"
#include "mpi.h"
#include "tuple"

namespace gensimcell {
namespace detail {

BOOST_TTI_HAS_MEMBER_FUNCTION(get_mpi_datatype)

/*!
Returns the mpi transfer info of given
variable with a get_mpi_datatype function.
*/
template <
	class Variable_T,
	std::size_t Number_Of_Items = 0
> std::tuple<
	void*,
	int,
	MPI_Datatype
> get_var_datatype(const Variable_T& variable)
{
	static_assert(
		has_member_function_get_mpi_datatype<
			Variable_T,
			std::tuple<void*, int, MPI_Datatype>
		>::value
		or
		has_member_function_get_mpi_datatype<
			Variable_T,
			std::tuple<void*, int, MPI_Datatype>,
			boost::mpl::vector<>,
			boost::function_types::const_qualified
		>::value,
		"Given Variable_T does not have a get_mpi_datatype() "
		"member function that must specify the data to be "
		"transferred by MPI, see the Particles class in "
		"gensimcell/examples/particle_propagation/parallel/particle_variables.hpp "
		"for an example."
	);
	return variable.get_mpi_datatype();
}

/*!
Specializations of get_var_datatype for
standard C++ types with an MPI equivalent.
*/
#define GENSIMCELL_GET_VAR_DATATYPE(GIVEN_CPP_TYPE, GIVEN_MPI_TYPE) \
template <> std::tuple< \
	void*, \
	int, \
	MPI_Datatype                                                      \
> get_var_datatype(const GIVEN_CPP_TYPE& variable) \
{ \
	return std::make_tuple((void*) &variable, 1, GIVEN_MPI_TYPE); \
}

GENSIMCELL_GET_VAR_DATATYPE(bool, MPI_CXX_BOOL)
GENSIMCELL_GET_VAR_DATATYPE(char, MPI_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(double, MPI_DOUBLE)
GENSIMCELL_GET_VAR_DATATYPE(float, MPI_FLOAT)
GENSIMCELL_GET_VAR_DATATYPE(long double, MPI_LONG_DOUBLE)
GENSIMCELL_GET_VAR_DATATYPE(signed char, MPI_SIGNED_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(signed int, MPI_INT)
GENSIMCELL_GET_VAR_DATATYPE(signed long int, MPI_LONG)
GENSIMCELL_GET_VAR_DATATYPE(signed long long int, MPI_LONG_LONG_INT)
GENSIMCELL_GET_VAR_DATATYPE(signed short int, MPI_SHORT)
GENSIMCELL_GET_VAR_DATATYPE(std::complex<float>, MPI_CXX_FLOAT_COMPLEX)
GENSIMCELL_GET_VAR_DATATYPE(std::complex<double>, MPI_CXX_DOUBLE_COMPLEX)
GENSIMCELL_GET_VAR_DATATYPE(std::complex<long double>, MPI_CXX_LONG_DOUBLE_COMPLEX)
GENSIMCELL_GET_VAR_DATATYPE(unsigned char, MPI_UNSIGNED_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(unsigned int, MPI_UNSIGNED)
GENSIMCELL_GET_VAR_DATATYPE(unsigned long int, MPI_UNSIGNED_LONG)
GENSIMCELL_GET_VAR_DATATYPE(unsigned long long int, MPI_UNSIGNED_LONG_LONG)
GENSIMCELL_GET_VAR_DATATYPE(unsigned short int, MPI_UNSIGNED_SHORT)
GENSIMCELL_GET_VAR_DATATYPE(wchar_t, MPI_WCHAR)

/*!
Specializations of get_var_datatype for standard
C++ types with an MPI equivalent inside an array.
*/
#define GENSIMCELL_GET_ARRAY_VAR_DATATYPE(GIVEN_CPP_TYPE, GIVEN_MPI_TYPE) \
template < \
	std::size_t Number_Of_Items \
> std::tuple< \
	void*, \
	int, \
	MPI_Datatype \
> get_var_datatype( \
	const std::array< \
		GIVEN_CPP_TYPE, \
		Number_Of_Items \
	>& variable \
) { \
	return std::make_tuple( \
		(void*) variable.data(), \
		Number_Of_Items, \
		GIVEN_MPI_TYPE \
	); \
}

GENSIMCELL_GET_ARRAY_VAR_DATATYPE(bool, MPI_CXX_BOOL)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(char, MPI_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(double, MPI_DOUBLE)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(float, MPI_FLOAT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(long double, MPI_LONG_DOUBLE)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed char, MPI_SIGNED_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed int, MPI_INT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed long int, MPI_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed long long int, MPI_LONG_LONG_INT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed short int, MPI_SHORT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<float>, MPI_CXX_FLOAT_COMPLEX)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<double>, MPI_CXX_DOUBLE_COMPLEX)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<long double>, MPI_CXX_LONG_DOUBLE_COMPLEX)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned char, MPI_UNSIGNED_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned int, MPI_UNSIGNED)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned long int, MPI_UNSIGNED_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned long long int, MPI_UNSIGNED_LONG_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned short int, MPI_UNSIGNED_SHORT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(wchar_t, MPI_WCHAR)

} // namespace detail
} // namespace gensimcell

#endif // ifdef MPI_VERSION

#endif // ifndef  GET_VAR_DATATYPE_HPP
<commit_msg>Put C++ types behind ifdefs in get_var_datatype().<commit_after>/*
Functions that return the MPI transfer information of some C++ types.

Copyright (c) 2013, 2014, Ilja Honkonen
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 copyright holders nor the names of their 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 HOLDER 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.
*/

#ifndef GET_VAR_DATATYPE_HPP
#define GET_VAR_DATATYPE_HPP

#if defined(MPI_VERSION) && (MPI_VERSION >= 2)

#include "boost/function_types/property_tags.hpp"
#include "boost/mpl/vector.hpp"
#include "boost/tti/has_member_function.hpp"
#include "complex"
#include "cstddef"
#include "cstdint"
#include "mpi.h"
#include "tuple"

namespace gensimcell {
namespace detail {

BOOST_TTI_HAS_MEMBER_FUNCTION(get_mpi_datatype)

/*!
Returns the mpi transfer info of given
variable with a get_mpi_datatype function.
*/
template <
	class Variable_T,
	std::size_t Number_Of_Items = 0
> std::tuple<
	void*,
	int,
	MPI_Datatype
> get_var_datatype(const Variable_T& variable)
{
	static_assert(
		has_member_function_get_mpi_datatype<
			Variable_T,
			std::tuple<void*, int, MPI_Datatype>
		>::value
		or
		has_member_function_get_mpi_datatype<
			Variable_T,
			std::tuple<void*, int, MPI_Datatype>,
			boost::mpl::vector<>,
			boost::function_types::const_qualified
		>::value,
		"Given Variable_T does not have a get_mpi_datatype() "
		"member function that must specify the data to be "
		"transferred by MPI, see the Particles class in "
		"gensimcell/examples/particle_propagation/parallel/particle_variables.hpp "
		"for an example."
	);
	return variable.get_mpi_datatype();
}

/*!
Specializations of get_var_datatype for
standard C++ types with an MPI equivalent.
*/
#define GENSIMCELL_GET_VAR_DATATYPE(GIVEN_CPP_TYPE, GIVEN_MPI_TYPE) \
template <> std::tuple< \
	void*, \
	int, \
	MPI_Datatype                                                      \
> get_var_datatype(const GIVEN_CPP_TYPE& variable) \
{ \
	return std::make_tuple((void*) &variable, 1, GIVEN_MPI_TYPE); \
}

GENSIMCELL_GET_VAR_DATATYPE(char, MPI_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(double, MPI_DOUBLE)
GENSIMCELL_GET_VAR_DATATYPE(float, MPI_FLOAT)
GENSIMCELL_GET_VAR_DATATYPE(long double, MPI_LONG_DOUBLE)
GENSIMCELL_GET_VAR_DATATYPE(signed char, MPI_SIGNED_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(signed int, MPI_INT)
GENSIMCELL_GET_VAR_DATATYPE(signed long int, MPI_LONG)
GENSIMCELL_GET_VAR_DATATYPE(signed long long int, MPI_LONG_LONG_INT)
GENSIMCELL_GET_VAR_DATATYPE(signed short int, MPI_SHORT)
GENSIMCELL_GET_VAR_DATATYPE(unsigned char, MPI_UNSIGNED_CHAR)
GENSIMCELL_GET_VAR_DATATYPE(unsigned int, MPI_UNSIGNED)
GENSIMCELL_GET_VAR_DATATYPE(unsigned long int, MPI_UNSIGNED_LONG)
GENSIMCELL_GET_VAR_DATATYPE(unsigned long long int, MPI_UNSIGNED_LONG_LONG)
GENSIMCELL_GET_VAR_DATATYPE(unsigned short int, MPI_UNSIGNED_SHORT)
GENSIMCELL_GET_VAR_DATATYPE(wchar_t, MPI_WCHAR)

#ifdef MPI_CXX_BOOL
GENSIMCELL_GET_VAR_DATATYPE(bool, MPI_CXX_BOOL)
#endif

#ifdef MPI_CXX_FLOAT_COMPLEX
GENSIMCELL_GET_VAR_DATATYPE(std::complex<float>, MPI_CXX_FLOAT_COMPLEX)
#endif

#ifdef MPI_CXX_DOUBLE_COMPLEX
GENSIMCELL_GET_VAR_DATATYPE(std::complex<double>, MPI_CXX_DOUBLE_COMPLEX)
#endif

#ifdef MPI_CXX_LONG_DOUBLE_COMPLEX
GENSIMCELL_GET_VAR_DATATYPE(std::complex<long double>, MPI_CXX_LONG_DOUBLE_COMPLEX)
#endif

/*!
Specializations of get_var_datatype for standard
C++ types with an MPI equivalent inside an array.
*/
#define GENSIMCELL_GET_ARRAY_VAR_DATATYPE(GIVEN_CPP_TYPE, GIVEN_MPI_TYPE) \
template < \
	std::size_t Number_Of_Items \
> std::tuple< \
	void*, \
	int, \
	MPI_Datatype \
> get_var_datatype( \
	const std::array< \
		GIVEN_CPP_TYPE, \
		Number_Of_Items \
	>& variable \
) { \
	return std::make_tuple( \
		(void*) variable.data(), \
		Number_Of_Items, \
		GIVEN_MPI_TYPE \
	); \
}

GENSIMCELL_GET_ARRAY_VAR_DATATYPE(char, MPI_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(double, MPI_DOUBLE)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(float, MPI_FLOAT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(long double, MPI_LONG_DOUBLE)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed char, MPI_SIGNED_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed int, MPI_INT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed long int, MPI_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed long long int, MPI_LONG_LONG_INT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(signed short int, MPI_SHORT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned char, MPI_UNSIGNED_CHAR)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned int, MPI_UNSIGNED)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned long int, MPI_UNSIGNED_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned long long int, MPI_UNSIGNED_LONG_LONG)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(unsigned short int, MPI_UNSIGNED_SHORT)
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(wchar_t, MPI_WCHAR)

#ifdef MPI_CXX_BOOL
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(bool, MPI_CXX_BOOL)
#endif

#ifdef MPI_CXX_FLOAT_COMPLEX
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<float>, MPI_CXX_FLOAT_COMPLEX)
#endif

#ifdef MPI_CXX_DOUBLE_COMPLEX
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<double>, MPI_CXX_DOUBLE_COMPLEX)
#endif

#ifdef MPI_CXX_LONG_DOUBLE_COMPLEX
GENSIMCELL_GET_ARRAY_VAR_DATATYPE(std::complex<long double>, MPI_CXX_LONG_DOUBLE_COMPLEX)
#endif

} // namespace detail
} // namespace gensimcell

#endif // ifdef MPI_VERSION

#endif // ifndef  GET_VAR_DATATYPE_HPP
<|endoftext|>


<commit_before>// 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 __PROCESS_LOOP_HPP__
#define __PROCESS_LOOP_HPP__

#include <mutex>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/owned.hpp>
#include <process/pid.hpp>
#include <process/process.hpp>

namespace process {

// Provides an asynchronous "loop" abstraction. This abstraction is
// helpful for code that would have synchronously been written as a
// loop but asynchronously ends up being a recursive set of functions
// which depending on the compiler may result in a stack overflow
// (i.e., a compiler that can't do sufficient tail call optimization
// may add stack frames for each recursive call).
//
// The loop abstraction takes an optional PID `pid` and uses it as the
// execution context to run the loop. The implementation does a
// `defer` on this `pid` to "pop" the stack when it needs to
// asynchronously recurse. This also lets callers synchronize
// execution with other code dispatching and deferring using `pid`. If
// `None` is passed for `pid` then no `defer` is done and the stack
// will still "pop" but be restarted from the execution context
// wherever the blocked future is completed. This is usually very safe
// when that blocked future will be completed by the IO thread, but
// should not be used if it's completed by another process (because
// you'll block that process until the next time the loop blocks).
//
// The two functions passed to the loop represent the loop "iterate"
// step and the loop "body" step respectively. Each invocation of
// "iterate" returns the next value and the "body" returns whether or
// not to continue looping (as well as any other processing necessary
// of course). You can think of this synchronously as:
//
//     bool condition = true;
//     do {
//       condition = body(iterate());
//     } while (condition);
//
// Asynchronously using recursion this might look like:
//
//     Future<Nothing> loop()
//     {
//       return iterate()
//         .then([](T t) {
//            return body(t)
//             .then([](bool condition) {
//               if (condition) {
//                 return loop();
//               } else {
//                 return Nothing();
//               }
//             });
//         });
//     }
//
// And asynchronously using `pid` as the execution context:
//
//     Future<Nothing> loop()
//     {
//       return iterate()
//         .then(defer(pid, [](T t) {
//            return body(t)
//             .then(defer(pid, [](bool condition) {
//               if (condition) {
//                 return loop();
//               } else {
//                 return Nothing();
//               }
//             }));
//         }));
//     }
//
// And now what this looks like using `loop`:
//
//     loop(pid,
//          []() {
//            return iterate();
//          },
//          [](T t) {
//            return body(t);
//          });
//
// One difference between the `loop` version of the "body" versus the
// other non-loop examples above is the return value is not `bool` or
// `Future<bool>` but rather `ControlFlow<V>` or
// `Future<ControlFlow<V>>`. This enables you to return values out of
// the loop via a `Break(...)`, for example:
//
//     loop(pid,
//          []() {
//            return iterate();
//          },
//          [](T t) {
//            if (finished(t)) {
//              return Break(SomeValue());
//            }
//            return Continue();
//          });
template <typename Iterate,
          typename Body,
          typename T = typename internal::unwrap<typename result_of<Iterate()>::type>::type, // NOLINT(whitespace/line_length)
          typename CF = typename internal::unwrap<typename result_of<Body(T)>::type>::type, // NOLINT(whitespace/line_length)
          typename V = typename CF::ValueType>
Future<V> loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body);

// A helper for `loop` which creates a Process for us to provide an
// execution context for running the loop.
template <typename Iterate,
          typename Body,
          typename T = typename internal::unwrap<typename result_of<Iterate()>::type>::type, // NOLINT(whitespace/line_length)
          typename CF = typename internal::unwrap<typename result_of<Body(T)>::type>::type, // NOLINT(whitespace/line_length)
          typename V = typename CF::ValueType>
Future<V> loop(Iterate&& iterate, Body&& body)
{
  ProcessBase* process = new ProcessBase();
  return loop<Iterate, Body, T, CF, V>(
      spawn(process, true), // Have libprocess free `process`.
      std::forward<Iterate>(iterate),
      std::forward<Body>(body))
    .onAny([=]() {
      terminate(process);
      // NOTE: no need to `wait` or `delete` since the `spawn` above
      // put libprocess in control of garbage collection.
    });
}

// Generic "control flow" construct that is leveraged by
// implementations such as `loop`. At a high-level a `ControlFlow`
// represents some control flow statement such as `continue` or
// `break`, however, these statements can both have values or be
// value-less (i.e., these are meant to be composed "functionally" so
// the representation of `break` captures a value that "exits the
// current function" but the representation of `continue` does not).
//
// The pattern here is to define the type/representation of control
// flow statements within the `ControlFlow` class (e.g.,
// `ControlFlow::Continue` and `ControlFlow::Break`) but also provide
// "syntactic sugar" to make it easier to use at the call site (e.g.,
// the functions `Continue()` and `Break(...)`).
template <typename T>
class ControlFlow
{
public:
  using ValueType = T;

  enum class Statement
  {
    CONTINUE,
    BREAK
  };

  class Continue
  {
  public:
    Continue() = default;

    template <typename U>
    operator ControlFlow<U>() const
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::CONTINUE, None());
    }
  };

  class Break
  {
  public:
    Break(T t) : t(std::move(t)) {}

    template <typename U>
    operator ControlFlow<U>() const &
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::BREAK, t);
    }

    template <typename U>
    operator ControlFlow<U>() &&
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::BREAK, std::move(t));
    }

  private:
    T t;
  };

  ControlFlow(Statement s, Option<T> t) : s(s), t(std::move(t)) {}

  Statement statement() const { return s; }

  T& value() & { return t.get(); }
  const T& value() const & { return t.get(); }
  T&& value() && { return t.get(); }
  const T&& value() const && { return t.get(); }

private:
  Statement s;
  Option<T> t;
};

// Provides "syntactic sugar" for creating a `ControlFlow::Continue`.
struct Continue
{
  Continue() = default;

  template <typename T>
  operator ControlFlow<T>() const
  {
    return typename ControlFlow<T>::Continue();
  }
};

// Provides "syntactic sugar" for creating a `ControlFlow::Break`.
template <typename T>
typename ControlFlow<typename std::decay<T>::type>::Break Break(T&& t)
{
  return typename ControlFlow<typename std::decay<T>::type>::Break(
      std::forward<T>(t));
}

inline ControlFlow<Nothing>::Break Break()
{
  return ControlFlow<Nothing>::Break(Nothing());
}

namespace internal {

template <typename Iterate, typename Body, typename T, typename R>
class Loop : public std::enable_shared_from_this<Loop<Iterate, Body, T, R>>
{
public:
  Loop(const Option<UPID>& pid, const Iterate& iterate, const Body& body)
    : pid(pid), iterate(iterate), body(body) {}

  Loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body)
    : pid(pid), iterate(std::move(iterate)), body(std::move(body)) {}

  std::shared_ptr<Loop> shared()
  {
    // Must fully specify `shared_from_this` because we're templated.
    return std::enable_shared_from_this<Loop>::shared_from_this();
  }

  std::weak_ptr<Loop> weak()
  {
    return std::weak_ptr<Loop>(shared());
  }

  Future<R> start()
  {
    auto self = shared();
    auto weak_self = weak();

    // Propagating discards:
    //
    // When the caller does a discard we need to propagate it to
    // either the future returned from `iterate` or the future
    // returned from `body`. One easy way to do this would be to add
    // an `onAny` callback for every future returned from `iterate`
    // and `body`, but that would be a slow memory leak that would
    // grow over time, especially if the loop was actually
    // infinite. Instead, we capture the current future that needs to
    // be discarded within a `discard` function that we'll invoke when
    // we get a discard. Because there is a race setting the `discard`
    // function and reading it out to invoke we have to synchronize
    // access using a mutex. An alternative strategy would be to use
    // something like `atomic_load` and `atomic_store` with
    // `shared_ptr` so that we can swap the current future(s)
    // atomically.
    promise.future().onDiscard([weak_self]() {
      auto self = weak_self.lock();
      if (self) {
        // We need to make a copy of the current `discard` function so
        // that we can invoke it outside of the `synchronized` block
        // in the event that discarding invokes causes the `onAny`
        // callbacks that we have added in `run` to execute which may
        // deadlock attempting to re-acquire `mutex`!
        std::function<void()> f = []() {};
        synchronized (self->mutex) {
          f = self->discard;
        }
        f();
      }
    });

    if (pid.isSome()) {
      // Start the loop using `pid` as the execution context.
      dispatch(pid.get(), [self]() {
        self->run(self->iterate());
      });
    } else {
      run(iterate());
    }

    return promise.future();
  }

  void run(Future<T> next)
  {
    auto self = shared();

    // Reset `discard` so that we're not delaying cleanup of any
    // captured futures longer than necessary.
    //
    // TODO(benh): Use `WeakFuture` in `discard` functions instead.
    synchronized (mutex) {
      discard = []() {};
    }

    while (next.isReady()) {
      Future<ControlFlow<R>> flow = body(next.get());
      if (flow.isReady()) {
        switch (flow->statement()) {
          case ControlFlow<R>::Statement::CONTINUE: {
            next = iterate();
            continue;
          }
          case ControlFlow<R>::Statement::BREAK: {
            promise.set(flow->value());
            return;
          }
        }
      } else {
        auto continuation = [self](const Future<ControlFlow<R>>& flow) {
          if (flow.isReady()) {
            switch (flow->statement()) {
              case ControlFlow<R>::Statement::CONTINUE: {
                self->run(self->iterate());
                break;
              }
              case ControlFlow<R>::Statement::BREAK: {
                self->promise.set(flow->value());
                break;
              }
            }
          } else if (flow.isFailed()) {
            self->promise.fail(flow.failure());
          } else if (flow.isDiscarded()) {
            self->promise.discard();
          }
        };

        if (pid.isSome()) {
          flow.onAny(defer(pid.get(), continuation));
        } else {
          flow.onAny(continuation);
        }

        if (!promise.future().hasDiscard()) {
          synchronized (mutex) {
            self->discard = [=]() mutable { flow.discard(); };
          }
        }

        // There's a race between when a discard occurs and the
        // `discard` function gets invoked and therefore we must
        // explicitly always do a discard. In addition, after a
        // discard occurs we'll need to explicitly do discards for
        // each new future that blocks.
        if (promise.future().hasDiscard()) {
          flow.discard();
        }

        return;
      }
    }

    auto continuation = [self](const Future<T>& next) {
      if (next.isReady()) {
        self->run(next);
      } else if (next.isFailed()) {
        self->promise.fail(next.failure());
      } else if (next.isDiscarded()) {
        self->promise.discard();
      }
    };

    if (pid.isSome()) {
      next.onAny(defer(pid.get(), continuation));
    } else {
      next.onAny(continuation);
    }

    if (!promise.future().hasDiscard()) {
      synchronized (mutex) {
        discard = [=]() mutable { next.discard(); };
      }
    }

    // See comment above as to why we need to explicitly discard
    // regardless of the path the if statement took above.
    if (promise.future().hasDiscard()) {
      next.discard();
    }
  }

private:
  const Option<UPID> pid;
  Iterate iterate;
  Body body;
  Promise<R> promise;

  // In order to discard the loop safely we capture the future that
  // needs to be discarded within the `discard` function and reading
  // and writing that function with a mutex.
  std::mutex mutex;
  std::function<void()> discard = []() {};
};

} // namespace internal {

template <typename Iterate, typename Body, typename T, typename CF, typename V>
Future<V> loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body)
{
  using Loop = internal::Loop<
    typename std::decay<Iterate>::type,
    typename std::decay<Body>::type,
    T,
    V>;

  std::shared_ptr<Loop> loop(
      new Loop(pid, std::forward<Iterate>(iterate), std::forward<Body>(body)));

  return loop->start();
}

} // namespace process {

#endif // __PROCESS_LOOP_HPP__
<commit_msg>Fixed unsafe usage of process pointer in loop.hpp.<commit_after>// 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 __PROCESS_LOOP_HPP__
#define __PROCESS_LOOP_HPP__

#include <mutex>

#include <process/defer.hpp>
#include <process/dispatch.hpp>
#include <process/future.hpp>
#include <process/owned.hpp>
#include <process/pid.hpp>
#include <process/process.hpp>

namespace process {

// Provides an asynchronous "loop" abstraction. This abstraction is
// helpful for code that would have synchronously been written as a
// loop but asynchronously ends up being a recursive set of functions
// which depending on the compiler may result in a stack overflow
// (i.e., a compiler that can't do sufficient tail call optimization
// may add stack frames for each recursive call).
//
// The loop abstraction takes an optional PID `pid` and uses it as the
// execution context to run the loop. The implementation does a
// `defer` on this `pid` to "pop" the stack when it needs to
// asynchronously recurse. This also lets callers synchronize
// execution with other code dispatching and deferring using `pid`. If
// `None` is passed for `pid` then no `defer` is done and the stack
// will still "pop" but be restarted from the execution context
// wherever the blocked future is completed. This is usually very safe
// when that blocked future will be completed by the IO thread, but
// should not be used if it's completed by another process (because
// you'll block that process until the next time the loop blocks).
//
// The two functions passed to the loop represent the loop "iterate"
// step and the loop "body" step respectively. Each invocation of
// "iterate" returns the next value and the "body" returns whether or
// not to continue looping (as well as any other processing necessary
// of course). You can think of this synchronously as:
//
//     bool condition = true;
//     do {
//       condition = body(iterate());
//     } while (condition);
//
// Asynchronously using recursion this might look like:
//
//     Future<Nothing> loop()
//     {
//       return iterate()
//         .then([](T t) {
//            return body(t)
//             .then([](bool condition) {
//               if (condition) {
//                 return loop();
//               } else {
//                 return Nothing();
//               }
//             });
//         });
//     }
//
// And asynchronously using `pid` as the execution context:
//
//     Future<Nothing> loop()
//     {
//       return iterate()
//         .then(defer(pid, [](T t) {
//            return body(t)
//             .then(defer(pid, [](bool condition) {
//               if (condition) {
//                 return loop();
//               } else {
//                 return Nothing();
//               }
//             }));
//         }));
//     }
//
// And now what this looks like using `loop`:
//
//     loop(pid,
//          []() {
//            return iterate();
//          },
//          [](T t) {
//            return body(t);
//          });
//
// One difference between the `loop` version of the "body" versus the
// other non-loop examples above is the return value is not `bool` or
// `Future<bool>` but rather `ControlFlow<V>` or
// `Future<ControlFlow<V>>`. This enables you to return values out of
// the loop via a `Break(...)`, for example:
//
//     loop(pid,
//          []() {
//            return iterate();
//          },
//          [](T t) {
//            if (finished(t)) {
//              return Break(SomeValue());
//            }
//            return Continue();
//          });
template <typename Iterate,
          typename Body,
          typename T = typename internal::unwrap<typename result_of<Iterate()>::type>::type, // NOLINT(whitespace/line_length)
          typename CF = typename internal::unwrap<typename result_of<Body(T)>::type>::type, // NOLINT(whitespace/line_length)
          typename V = typename CF::ValueType>
Future<V> loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body);

// A helper for `loop` which creates a Process for us to provide an
// execution context for running the loop.
template <typename Iterate,
          typename Body,
          typename T = typename internal::unwrap<typename result_of<Iterate()>::type>::type, // NOLINT(whitespace/line_length)
          typename CF = typename internal::unwrap<typename result_of<Body(T)>::type>::type, // NOLINT(whitespace/line_length)
          typename V = typename CF::ValueType>
Future<V> loop(Iterate&& iterate, Body&& body)
{
  // Have libprocess own and free the new `ProcessBase`.
  UPID process = spawn(new ProcessBase(), true);

  return loop<Iterate, Body, T, CF, V>(
      process,
      std::forward<Iterate>(iterate),
      std::forward<Body>(body))
    .onAny([=]() {
      terminate(process);
      // NOTE: no need to `wait` or `delete` since the `spawn` above
      // put libprocess in control of garbage collection.
    });
}

// Generic "control flow" construct that is leveraged by
// implementations such as `loop`. At a high-level a `ControlFlow`
// represents some control flow statement such as `continue` or
// `break`, however, these statements can both have values or be
// value-less (i.e., these are meant to be composed "functionally" so
// the representation of `break` captures a value that "exits the
// current function" but the representation of `continue` does not).
//
// The pattern here is to define the type/representation of control
// flow statements within the `ControlFlow` class (e.g.,
// `ControlFlow::Continue` and `ControlFlow::Break`) but also provide
// "syntactic sugar" to make it easier to use at the call site (e.g.,
// the functions `Continue()` and `Break(...)`).
template <typename T>
class ControlFlow
{
public:
  using ValueType = T;

  enum class Statement
  {
    CONTINUE,
    BREAK
  };

  class Continue
  {
  public:
    Continue() = default;

    template <typename U>
    operator ControlFlow<U>() const
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::CONTINUE, None());
    }
  };

  class Break
  {
  public:
    Break(T t) : t(std::move(t)) {}

    template <typename U>
    operator ControlFlow<U>() const &
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::BREAK, t);
    }

    template <typename U>
    operator ControlFlow<U>() &&
    {
      return ControlFlow<U>(ControlFlow<U>::Statement::BREAK, std::move(t));
    }

  private:
    T t;
  };

  ControlFlow(Statement s, Option<T> t) : s(s), t(std::move(t)) {}

  Statement statement() const { return s; }

  T& value() & { return t.get(); }
  const T& value() const & { return t.get(); }
  T&& value() && { return t.get(); }
  const T&& value() const && { return t.get(); }

private:
  Statement s;
  Option<T> t;
};

// Provides "syntactic sugar" for creating a `ControlFlow::Continue`.
struct Continue
{
  Continue() = default;

  template <typename T>
  operator ControlFlow<T>() const
  {
    return typename ControlFlow<T>::Continue();
  }
};

// Provides "syntactic sugar" for creating a `ControlFlow::Break`.
template <typename T>
typename ControlFlow<typename std::decay<T>::type>::Break Break(T&& t)
{
  return typename ControlFlow<typename std::decay<T>::type>::Break(
      std::forward<T>(t));
}

inline ControlFlow<Nothing>::Break Break()
{
  return ControlFlow<Nothing>::Break(Nothing());
}

namespace internal {

template <typename Iterate, typename Body, typename T, typename R>
class Loop : public std::enable_shared_from_this<Loop<Iterate, Body, T, R>>
{
public:
  Loop(const Option<UPID>& pid, const Iterate& iterate, const Body& body)
    : pid(pid), iterate(iterate), body(body) {}

  Loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body)
    : pid(pid), iterate(std::move(iterate)), body(std::move(body)) {}

  std::shared_ptr<Loop> shared()
  {
    // Must fully specify `shared_from_this` because we're templated.
    return std::enable_shared_from_this<Loop>::shared_from_this();
  }

  std::weak_ptr<Loop> weak()
  {
    return std::weak_ptr<Loop>(shared());
  }

  Future<R> start()
  {
    auto self = shared();
    auto weak_self = weak();

    // Propagating discards:
    //
    // When the caller does a discard we need to propagate it to
    // either the future returned from `iterate` or the future
    // returned from `body`. One easy way to do this would be to add
    // an `onAny` callback for every future returned from `iterate`
    // and `body`, but that would be a slow memory leak that would
    // grow over time, especially if the loop was actually
    // infinite. Instead, we capture the current future that needs to
    // be discarded within a `discard` function that we'll invoke when
    // we get a discard. Because there is a race setting the `discard`
    // function and reading it out to invoke we have to synchronize
    // access using a mutex. An alternative strategy would be to use
    // something like `atomic_load` and `atomic_store` with
    // `shared_ptr` so that we can swap the current future(s)
    // atomically.
    promise.future().onDiscard([weak_self]() {
      auto self = weak_self.lock();
      if (self) {
        // We need to make a copy of the current `discard` function so
        // that we can invoke it outside of the `synchronized` block
        // in the event that discarding invokes causes the `onAny`
        // callbacks that we have added in `run` to execute which may
        // deadlock attempting to re-acquire `mutex`!
        std::function<void()> f = []() {};
        synchronized (self->mutex) {
          f = self->discard;
        }
        f();
      }
    });

    if (pid.isSome()) {
      // Start the loop using `pid` as the execution context.
      dispatch(pid.get(), [self]() {
        self->run(self->iterate());
      });
    } else {
      run(iterate());
    }

    return promise.future();
  }

  void run(Future<T> next)
  {
    auto self = shared();

    // Reset `discard` so that we're not delaying cleanup of any
    // captured futures longer than necessary.
    //
    // TODO(benh): Use `WeakFuture` in `discard` functions instead.
    synchronized (mutex) {
      discard = []() {};
    }

    while (next.isReady()) {
      Future<ControlFlow<R>> flow = body(next.get());
      if (flow.isReady()) {
        switch (flow->statement()) {
          case ControlFlow<R>::Statement::CONTINUE: {
            next = iterate();
            continue;
          }
          case ControlFlow<R>::Statement::BREAK: {
            promise.set(flow->value());
            return;
          }
        }
      } else {
        auto continuation = [self](const Future<ControlFlow<R>>& flow) {
          if (flow.isReady()) {
            switch (flow->statement()) {
              case ControlFlow<R>::Statement::CONTINUE: {
                self->run(self->iterate());
                break;
              }
              case ControlFlow<R>::Statement::BREAK: {
                self->promise.set(flow->value());
                break;
              }
            }
          } else if (flow.isFailed()) {
            self->promise.fail(flow.failure());
          } else if (flow.isDiscarded()) {
            self->promise.discard();
          }
        };

        if (pid.isSome()) {
          flow.onAny(defer(pid.get(), continuation));
        } else {
          flow.onAny(continuation);
        }

        if (!promise.future().hasDiscard()) {
          synchronized (mutex) {
            self->discard = [=]() mutable { flow.discard(); };
          }
        }

        // There's a race between when a discard occurs and the
        // `discard` function gets invoked and therefore we must
        // explicitly always do a discard. In addition, after a
        // discard occurs we'll need to explicitly do discards for
        // each new future that blocks.
        if (promise.future().hasDiscard()) {
          flow.discard();
        }

        return;
      }
    }

    auto continuation = [self](const Future<T>& next) {
      if (next.isReady()) {
        self->run(next);
      } else if (next.isFailed()) {
        self->promise.fail(next.failure());
      } else if (next.isDiscarded()) {
        self->promise.discard();
      }
    };

    if (pid.isSome()) {
      next.onAny(defer(pid.get(), continuation));
    } else {
      next.onAny(continuation);
    }

    if (!promise.future().hasDiscard()) {
      synchronized (mutex) {
        discard = [=]() mutable { next.discard(); };
      }
    }

    // See comment above as to why we need to explicitly discard
    // regardless of the path the if statement took above.
    if (promise.future().hasDiscard()) {
      next.discard();
    }
  }

private:
  const Option<UPID> pid;
  Iterate iterate;
  Body body;
  Promise<R> promise;

  // In order to discard the loop safely we capture the future that
  // needs to be discarded within the `discard` function and reading
  // and writing that function with a mutex.
  std::mutex mutex;
  std::function<void()> discard = []() {};
};

} // namespace internal {

template <typename Iterate, typename Body, typename T, typename CF, typename V>
Future<V> loop(const Option<UPID>& pid, Iterate&& iterate, Body&& body)
{
  using Loop = internal::Loop<
    typename std::decay<Iterate>::type,
    typename std::decay<Body>::type,
    T,
    V>;

  std::shared_ptr<Loop> loop(
      new Loop(pid, std::forward<Iterate>(iterate), std::forward<Body>(body)));

  return loop->start();
}

} // namespace process {

#endif // __PROCESS_LOOP_HPP__
<|endoftext|>