<commit_before>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2014--2015
*/

/*
  Alternate POST / setup and loop / main for non-OpenTRV code running on OpenTRV h/w platform.

  Also for rapid prototyping without dead-weight of OpenTRV intricate timing, etc!
 */

#include "V0p2_Main.h"

#include "V0p2_Generic_Config.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.

// Arduino libraries.
//#include <Wire.h>
#ifdef ALLOW_CC1_SUPPORT
#include <OTProtocolCC.h>
#endif

#include "Control.h"
#include "FHT8V_Wireless_Rad_Valve.h"
#include "Power_Management.h"
#include "RFM22_Radio.h"
#include "Security.h"
#include "Serial_IO.h"
#include "UI_Minimal.h"

// Link in support for alternate Power On Self-Test and main loop if required.
#if defined(ALT_MAIN_LOOP)

// Mask for Port B input change interrupts.
#define MASK_PB_BASIC 0b00000000 // Nothing.
#ifdef PIN_RFM_NIRQ
  #if (PIN_RFM_NIRQ < 8) || (PIN_RFM_NIRQ > 15)
    #error PIN_RFM_NIRQ expected to be on port B
  #endif
  #define RFM23B_INT_MASK (1 << (PIN_RFM_NIRQ&7))
  #define MASK_PB (MASK_PB_BASIC | RFM23B_INT_MASK)
#else
  #define MASK_PB MASK_PB_BASIC
#endif

//// Mask for Port D input change interrupts.
//#define MASK_PD_BASIC 0b00000001 // Just RX.
//#if defined(ENABLE_VOICE_SENSOR)
//#if VOICE_NIRQ > 7
//#error voice interrupt on wrong port
//#endif
//#define VOICE_INT_MASK (1 << (VOICE_NIRQ&7))
//#define MASK_PD (MASK_PD_BASIC | VOICE_INT_MASK)
//#else
//#define MASK_PD MASK_PD_BASIC // Just RX.
//#endif

// Called from startup() after some initial setup has been done.
// Can abort with panic() if need be.
void POSTalt()
  {
#if defined(USE_MODULE_RFM22RADIOSIMPLE)
  // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
  static const OTRadioLink::OTRadioChannelConfig RFMConfig(FHT8V_RFM22_Reg_Values, true, true, true);
  RFM23B.preinit(NULL);
  // Check that the radio is correctly connected; panic if not...
  if(!RFM23B.configure(1, &RFMConfig) || !RFM23B.begin()) { panic(); }
#endif

  // Force initialisation into low-power state.
  const int heat = TemperatureC16.read();
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
  DEBUG_SERIAL_PRINT(heat);
  DEBUG_SERIAL_PRINTLN();
#endif
//  const int light = AmbLight.read();
//#if 0 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
//  DEBUG_SERIAL_PRINT(light);
//  DEBUG_SERIAL_PRINTLN();
//#endif

  // Trailing setup for the run
  // --------------------------

  // Set up async edge interrupts.
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
    //PCMSK0 = PB; PCINT  0--7    (LEARN1 and Radio)
    //PCMSK1 = PC; PCINT  8--15
    //PCMSK2 = PD; PCINT 16--24   (LEARN2 and MODE, RX)

    PCICR =
#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
        1 | // 0x1 enables PB/PCMSK0.
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
        2 | // 0x2 enables PC/PCMSK1.
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
        4 | // 0x4 enables PD/PCMSK2.
#endif
        0;

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
    PCMSK0 = MASK_PB;
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
    PCMSK1 = MASK_PC;
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
    PCMSK2 = MASK_PD;
#endif
    }

  RFM23B.listen(true);
  }

#if defined(ALT_MAIN_LOOP)

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
//// Interrupt count.  Marked volatile so safe to read without a lock as is a single byte.
//static volatile uint8_t intCountPB;
// Previous state of port B pins to help detect changes.
static volatile uint8_t prevStatePB;
// Interrupt service routine for PB I/O port transition changes.
ISR(PCINT0_vect)
  {
//  ++intCountPB;
  const uint8_t pins = PINB;
  const uint8_t changes = pins ^ prevStatePB;
  prevStatePB = pins;

#if defined(RFM23B_INT_MASK)
  // RFM23B nIRQ falling edge is of interest.
  // Handler routine not required/expected to 'clear' this interrupt.
  // TODO: try to ensure that OTRFM23BLink.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
  if((changes & RFM23B_INT_MASK) && !(pins & RFM23B_INT_MASK))
    { RFM23B.handleInterruptSimple(); }
#endif

  }
#endif

#if defined(MASK_PC) && (MASK_PC != 0) // If PB interrupts required.
// Previous state of port C pins to help detect changes.
static volatile uint8_t prevStatePC;
// Interrupt service routine for PC I/O port transition changes.
ISR(PCINT1_vect)
  {
//  const uint8_t pins = PINC;
//  const uint8_t changes = pins ^ prevStatePC;
//  prevStatePC = pins;
//
// ...
  }
#endif

#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
// Previous state of port D pins to help detect changes.
static volatile uint8_t prevStatePD;
// Interrupt service routine for PD I/O port transition changes (including RX).
ISR(PCINT2_vect)
  {
//  const uint8_t pins = PIND;
//  const uint8_t changes = pins ^ prevStatePD;
//  prevStatePD = pins;
//
// ....
  }
#endif

#endif // ALT_MAIN

// Called from loop().
void loopAlt()
  {
  // Sleep in low-power mode (waiting for interrupts) until seconds roll.
  // NOTE: sleep at the top of the loop to minimise timing jitter/delay from Arduino background activity after loop() returns.
  // DHD20130425: waking up from sleep and getting to start processing below this block may take >10ms.
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*E"); // End-of-cycle sleep.
#endif

#if !defined(MIN_ENERGY_BOOT)
  OTV0P2BASE::powerDownSerial(); // Ensure that serial I/O is off.
  // Power down most stuff (except radio for hub RX).
  minimisePowerWithoutSleep();
#endif
  static uint_fast8_t TIME_LSD; // Controller's notion of seconds within major cycle.
  uint_fast8_t newTLSD;
  while(TIME_LSD == (newTLSD = OTV0P2BASE::getSecondsLT()))
    {
    // Poll I/O and process message incrementally (in this otherwise idle time)
    // before sleep and on wakeup in case some IO needs further processing now,
    // eg work was accrued during the previous major slow/outer loop
    // or the in a previous orbit of this loop sleep or nap was terminated by an I/O interrupt.
    // Come back and have another go if work was done, until the next tick at most.
    if(handleQueuedMessages(&Serial, true, &RFM23B)) { continue; }

// If missing h/w interrupts for anything that needs rapid response
// then AVOID the lowest-power long sleep.
#if CONFIG_IMPLIES_MAY_NEED_CONTINUOUS_RX && !defined(PIN_RFM_NIRQ)
#define MUST_POLL_FREQUENTLY true
#else
#define MUST_POLL_FREQUENTLY false
#endif
    if(MUST_POLL_FREQUENTLY /** && in hub mode */ )
      {
      // No h/w interrupt wakeup on receipt of frame,
      // so can only sleep for a short time between explicit poll()s,
      // though allow wake on interrupt anyway to minimise loop timing jitter.
      OTV0P2BASE::nap(WDTO_15MS, true);
      }
    else
      {
      // Normal long minimal-power sleep until wake-up interrupt.
      // Rely on interrupt to force fall through to I/O poll() below.
      OTV0P2BASE::sleepUntilInt();
      }
//    DEBUG_SERIAL_PRINTLN_FLASHSTRING("w"); // Wakeup.

//    idle15AndPoll(); // Attempt to crash the board!

    }
  TIME_LSD = newTLSD;

#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*S"); // Start-of-cycle wake.
#endif

  // START LOOP BODY
  // ===============

//  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*");

//  // Power up serial for the loop body.
//  // May just want to turn it on in POSTalt() and leave it on...
//  const bool neededWaking = powerUpSerialIfDisabled();

//#if defined(USE_MODULE_FHT8VSIMPLE)
//  // Try for double TX for more robust conversation with valve?
//  const bool doubleTXForFTH8V = false;
//  // FHT8V is highest priority and runs first.
//  // ---------- HALF SECOND #0 -----------
//  bool useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_First(doubleTXForFTH8V); // Time for extra TX before UI.
////  if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@0"); }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE)
//  if(useExtraFHT8VTXSlots)
//    {
//    // Time for extra TX before other actions, but don't bother if minimising power in frost mode.
//    // ---------- HALF SECOND #1 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@1"); }
//    }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE) && defined(V0P2BASE_TWO_S_TICK_RTC_SUPPORT)
//  if(useExtraFHT8VTXSlots)
//    {
//    // ---------- HALF SECOND #2 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@2"); }
//    }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE) && defined(V0P2BASE_TWO_S_TICK_RTC_SUPPORT)
//  if(useExtraFHT8VTXSlots)
//    {
//    // ---------- HALF SECOND #3 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@3"); }
//    }
//#endif

#ifdef HAS_DORM1_VALVE_DRIVE
  // Yank valve to random target every minute to try to upset it!
  if(0 == TIME_LSD) { ValveDirect.set(OTV0P2BASE::randRNG8() % 101); }

  // Simulate human doing the right thing after fitting valve.
  if(ValveDirect.isWaitingForValveToBeFitted()) { ValveDirect.signalValveFitted(); }

  // Provide regular poll to motor driver.
  // May take significant time to run
  // so don't call when timing is critical or not much left,
  // eg around critical TXes.
  ValveDirect.read();
#endif

//  // Reading shaft encoder.
//  // Measure motor count against (fixed) internal reference.
//  power_intermittent_peripherals_enable(true);
//  const uint16_t mc = analogueNoiseReducedRead(MOTOR_DRIVE_MC_AIN, INTERNAL);
//  void power_intermittent_peripherals_disable();
//  DEBUG_SERIAL_PRINT_FLASHSTRING("Count input: ");
//  DEBUG_SERIAL_PRINT(mc);
//  DEBUG_SERIAL_PRINTLN();

//  // Command-Line Interface (CLI) polling.
//  // If a reasonable chunk of the minor cycle remains after all other work is done
//  // AND the CLI is / should be active OR a status line has just been output
//  // then poll/prompt the user for input
//  // using a timeout which should safely avoid overrun, ie missing the next basic tick,
//  // and which should also allow some energy-saving sleep.
//#if 1 // && defined(SUPPORT_CLI)
//  if(true)
//    {
//    const uint8_t sct = getSubCycleTime();
//    const uint8_t listenTime = max(GSCT_MAX/16, CLI_POLL_MIN_SCT);
//    if(sct < (GSCT_MAX - 2*listenTime))
//      // Don't listen beyond the last 16th of the cycle,
//      // or a minimal time if only prodding for interaction with automated front-end,
//      // as listening for UART RX uses lots of power.
//      { pollCLI(OTV0P2BASE::randRNG8NextBoolean() ? (GSCT_MAX-listenTime) : (sct+CLI_POLL_MIN_SCT), 0 == TIME_LSD); }
//    }
//#endif

//  // Force any pending output before return / possible UART power-down.
//  flushSerialSCTSensitive();
//  if(neededWaking) { OTV0P2BASE::powerDownSerial(); }
  }

#endif
<commit_msg>TODO-370: stress testing<commit_after>/*
The OpenTRV project licenses this file to you
under the Apache Licence, Version 2.0 (the "Licence");
you may not use this file except in compliance
with the Licence. You may obtain a copy of the Licence at

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

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

Author(s) / Copyright (s): Damon Hart-Davis 2014--2015
*/

/*
  Alternate POST / setup and loop / main for non-OpenTRV code running on OpenTRV h/w platform.

  Also for rapid prototyping without dead-weight of OpenTRV intricate timing, etc!
 */

#include "V0p2_Main.h"

#include "V0p2_Generic_Config.h"
#include "V0p2_Board_IO_Config.h" // I/O pin allocation: include ahead of I/O module headers.

// Arduino libraries.
//#include <Wire.h>
#ifdef ALLOW_CC1_SUPPORT
#include <OTProtocolCC.h>
#endif

#include "Control.h"
#include "FHT8V_Wireless_Rad_Valve.h"
#include "Power_Management.h"
#include "RFM22_Radio.h"
#include "Security.h"
#include "Serial_IO.h"
#include "UI_Minimal.h"

// Link in support for alternate Power On Self-Test and main loop if required.
#if defined(ALT_MAIN_LOOP)

// Mask for Port B input change interrupts.
#define MASK_PB_BASIC 0b00000000 // Nothing.
#ifdef PIN_RFM_NIRQ
  #if (PIN_RFM_NIRQ < 8) || (PIN_RFM_NIRQ > 15)
    #error PIN_RFM_NIRQ expected to be on port B
  #endif
  #define RFM23B_INT_MASK (1 << (PIN_RFM_NIRQ&7))
  #define MASK_PB (MASK_PB_BASIC | RFM23B_INT_MASK)
#else
  #define MASK_PB MASK_PB_BASIC
#endif

//// Mask for Port D input change interrupts.
//#define MASK_PD_BASIC 0b00000001 // Just RX.
//#if defined(ENABLE_VOICE_SENSOR)
//#if VOICE_NIRQ > 7
//#error voice interrupt on wrong port
//#endif
//#define VOICE_INT_MASK (1 << (VOICE_NIRQ&7))
//#define MASK_PD (MASK_PD_BASIC | VOICE_INT_MASK)
//#else
//#define MASK_PD MASK_PD_BASIC // Just RX.
//#endif

// Called from startup() after some initial setup has been done.
// Can abort with panic() if need be.
void POSTalt()
  {
#if defined(USE_MODULE_RFM22RADIOSIMPLE)
  // Initialise the radio, if configured, ASAP because it can suck a lot of power until properly initialised.
  static const OTRadioLink::OTRadioChannelConfig RFMConfig(FHT8V_RFM22_Reg_Values, true, true, true);
  RFM23B.preinit(NULL);
  // Check that the radio is correctly connected; panic if not...
  if(!RFM23B.configure(1, &RFMConfig) || !RFM23B.begin()) { panic(); }
#endif

  // Force initialisation into low-power state.
  const int heat = TemperatureC16.read();
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINT_FLASHSTRING("temp: ");
  DEBUG_SERIAL_PRINT(heat);
  DEBUG_SERIAL_PRINTLN();
#endif
//  const int light = AmbLight.read();
//#if 0 && defined(DEBUG)
//  DEBUG_SERIAL_PRINT_FLASHSTRING("light: ");
//  DEBUG_SERIAL_PRINT(light);
//  DEBUG_SERIAL_PRINTLN();
//#endif

  // Trailing setup for the run
  // --------------------------

  // Set up async edge interrupts.
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
    {
    //PCMSK0 = PB; PCINT  0--7    (LEARN1 and Radio)
    //PCMSK1 = PC; PCINT  8--15
    //PCMSK2 = PD; PCINT 16--24   (LEARN2 and MODE, RX)

    PCICR =
#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
        1 | // 0x1 enables PB/PCMSK0.
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
        2 | // 0x2 enables PC/PCMSK1.
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
        4 | // 0x4 enables PD/PCMSK2.
#endif
        0;

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
    PCMSK0 = MASK_PB;
#endif
#if defined(MASK_PC) && (MASK_PC != 0) // If PC interrupts required.
    PCMSK1 = MASK_PC;
#endif
#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
    PCMSK2 = MASK_PD;
#endif
    }

  RFM23B.listen(true);
  }

#if defined(ALT_MAIN_LOOP)

#if defined(MASK_PB) && (MASK_PB != 0) // If PB interrupts required.
//// Interrupt count.  Marked volatile so safe to read without a lock as is a single byte.
//static volatile uint8_t intCountPB;
// Previous state of port B pins to help detect changes.
static volatile uint8_t prevStatePB;
// Interrupt service routine for PB I/O port transition changes.
ISR(PCINT0_vect)
  {
//  ++intCountPB;
  const uint8_t pins = PINB;
  const uint8_t changes = pins ^ prevStatePB;
  prevStatePB = pins;

#if defined(RFM23B_INT_MASK)
  // RFM23B nIRQ falling edge is of interest.
  // Handler routine not required/expected to 'clear' this interrupt.
  // TODO: try to ensure that OTRFM23BLink.handleInterruptSimple() is inlineable to minimise ISR prologue/epilogue time and space.
  if((changes & RFM23B_INT_MASK) && !(pins & RFM23B_INT_MASK))
    { RFM23B.handleInterruptSimple(); }
#endif

  }
#endif

#if defined(MASK_PC) && (MASK_PC != 0) // If PB interrupts required.
// Previous state of port C pins to help detect changes.
static volatile uint8_t prevStatePC;
// Interrupt service routine for PC I/O port transition changes.
ISR(PCINT1_vect)
  {
//  const uint8_t pins = PINC;
//  const uint8_t changes = pins ^ prevStatePC;
//  prevStatePC = pins;
//
// ...
  }
#endif

#if defined(MASK_PD) && (MASK_PD != 0) // If PD interrupts required.
// Previous state of port D pins to help detect changes.
static volatile uint8_t prevStatePD;
// Interrupt service routine for PD I/O port transition changes (including RX).
ISR(PCINT2_vect)
  {
//  const uint8_t pins = PIND;
//  const uint8_t changes = pins ^ prevStatePD;
//  prevStatePD = pins;
//
// ....
  }
#endif

#endif // ALT_MAIN

// Called from loop().
void loopAlt()
  {
  // Sleep in low-power mode (waiting for interrupts) until seconds roll.
  // NOTE: sleep at the top of the loop to minimise timing jitter/delay from Arduino background activity after loop() returns.
  // DHD20130425: waking up from sleep and getting to start processing below this block may take >10ms.
#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*E"); // End-of-cycle sleep.
#endif

#if !defined(MIN_ENERGY_BOOT)
  OTV0P2BASE::powerDownSerial(); // Ensure that serial I/O is off.
  // Power down most stuff (except radio for hub RX).
  minimisePowerWithoutSleep();
#endif
  static uint_fast8_t TIME_LSD; // Controller's notion of seconds within major cycle.
  uint_fast8_t newTLSD;
  while(TIME_LSD == (newTLSD = OTV0P2BASE::getSecondsLT()))
    {
    // Poll I/O and process message incrementally (in this otherwise idle time)
    // before sleep and on wakeup in case some IO needs further processing now,
    // eg work was accrued during the previous major slow/outer loop
    // or the in a previous orbit of this loop sleep or nap was terminated by an I/O interrupt.
    // Come back and have another go if work was done, until the next tick at most.
    if(handleQueuedMessages(&Serial, true, &RFM23B)) { continue; }

// If missing h/w interrupts for anything that needs rapid response
// then AVOID the lowest-power long sleep.
#if CONFIG_IMPLIES_MAY_NEED_CONTINUOUS_RX && !defined(PIN_RFM_NIRQ)
#define MUST_POLL_FREQUENTLY true
#else
#define MUST_POLL_FREQUENTLY false
#endif
    if(MUST_POLL_FREQUENTLY /** && in hub mode */ )
      {
      // No h/w interrupt wakeup on receipt of frame,
      // so can only sleep for a short time between explicit poll()s,
      // though allow wake on interrupt anyway to minimise loop timing jitter.
      OTV0P2BASE::nap(WDTO_15MS, true);
      }
    else
      {
      // Normal long minimal-power sleep until wake-up interrupt.
      // Rely on interrupt to force fall through to I/O poll() below.
      OTV0P2BASE::sleepUntilInt();
      }
//    DEBUG_SERIAL_PRINTLN_FLASHSTRING("w"); // Wakeup.

//    idle15AndPoll(); // Attempt to crash the board!

    }
  TIME_LSD = newTLSD;

#if 0 && defined(DEBUG)
  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*S"); // Start-of-cycle wake.
#endif

  // START LOOP BODY
  // ===============

//  DEBUG_SERIAL_PRINTLN_FLASHSTRING("*");

//  // Power up serial for the loop body.
//  // May just want to turn it on in POSTalt() and leave it on...
//  const bool neededWaking = powerUpSerialIfDisabled();

//#if defined(USE_MODULE_FHT8VSIMPLE)
//  // Try for double TX for more robust conversation with valve?
//  const bool doubleTXForFTH8V = false;
//  // FHT8V is highest priority and runs first.
//  // ---------- HALF SECOND #0 -----------
//  bool useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_First(doubleTXForFTH8V); // Time for extra TX before UI.
////  if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@0"); }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE)
//  if(useExtraFHT8VTXSlots)
//    {
//    // Time for extra TX before other actions, but don't bother if minimising power in frost mode.
//    // ---------- HALF SECOND #1 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@1"); }
//    }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE) && defined(V0P2BASE_TWO_S_TICK_RTC_SUPPORT)
//  if(useExtraFHT8VTXSlots)
//    {
//    // ---------- HALF SECOND #2 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@2"); }
//    }
//#endif

//#if defined(USE_MODULE_FHT8VSIMPLE) && defined(V0P2BASE_TWO_S_TICK_RTC_SUPPORT)
//  if(useExtraFHT8VTXSlots)
//    {
//    // ---------- HALF SECOND #3 -----------
//    useExtraFHT8VTXSlots = localFHT8VTRVEnabled() && FHT8VPollSyncAndTX_Next(doubleTXForFTH8V);
////    if(useExtraFHT8VTXSlots) { DEBUG_SERIAL_PRINTLN_FLASHSTRING("ES@3"); }
//    }
//#endif

#ifdef HAS_DORM1_VALVE_DRIVE
  // Move valve to new target every minute to try to upset it!
  // Targets at key thresholds and random.
  if(0 == TIME_LSD)
    {
    switch(OTV0P2BASE::randRNG8() & 1)
      {
      case 0: ValveDirect.set(DEFAULT_MIN_VALVE_PC_REALLY_OPEN-1); break; // Nominally shut.
      case 1: ValveDirect.set(DEFAULT_VALVE_PC_MODERATELY_OPEN); break; // Nominally open.
      // Random.
//      default: ValveDirect.set(OTV0P2BASE::randRNG8() % 101); break;
      }
    }

  // Simulate human doing the right thing after fitting valve when required.
  if(ValveDirect.isWaitingForValveToBeFitted()) { ValveDirect.signalValveFitted(); }

  // Provide regular poll to motor driver.
  // May take significant time to run
  // so don't call when timing is critical or not much left,
  // eg around critical TXes.
  const uint8_t pc = ValveDirect.read();
  DEBUG_SERIAL_PRINT_FLASHSTRING("Pos%: ");
  DEBUG_SERIAL_PRINT(pc);
  DEBUG_SERIAL_PRINTLN();
#endif

//  // Reading shaft encoder.
//  // Measure motor count against (fixed) internal reference.
//  power_intermittent_peripherals_enable(true);
//  const uint16_t mc = analogueNoiseReducedRead(MOTOR_DRIVE_MC_AIN, INTERNAL);
//  void power_intermittent_peripherals_disable();
//  DEBUG_SERIAL_PRINT_FLASHSTRING("Count input: ");
//  DEBUG_SERIAL_PRINT(mc);
//  DEBUG_SERIAL_PRINTLN();

//  // Command-Line Interface (CLI) polling.
//  // If a reasonable chunk of the minor cycle remains after all other work is done
//  // AND the CLI is / should be active OR a status line has just been output
//  // then poll/prompt the user for input
//  // using a timeout which should safely avoid overrun, ie missing the next basic tick,
//  // and which should also allow some energy-saving sleep.
//#if 1 // && defined(SUPPORT_CLI)
//  if(true)
//    {
//    const uint8_t sct = getSubCycleTime();
//    const uint8_t listenTime = max(GSCT_MAX/16, CLI_POLL_MIN_SCT);
//    if(sct < (GSCT_MAX - 2*listenTime))
//      // Don't listen beyond the last 16th of the cycle,
//      // or a minimal time if only prodding for interaction with automated front-end,
//      // as listening for UART RX uses lots of power.
//      { pollCLI(OTV0P2BASE::randRNG8NextBoolean() ? (GSCT_MAX-listenTime) : (sct+CLI_POLL_MIN_SCT), 0 == TIME_LSD); }
//    }
//#endif

//  // Force any pending output before return / possible UART power-down.
//  flushSerialSCTSensitive();
//  if(neededWaking) { OTV0P2BASE::powerDownSerial(); }
  }

#endif
<|endoftext|>


<commit_before>// TODO(pbos): Remove this file when it's no longer built in Chromium.
<commit_msg>Remove webrtcvideoengine.cc.<commit_after><|endoftext|>


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

#include "net/instaweb/system/public/handlers.h"

#include "net/instaweb/system/public/system_rewrite_options.h"
#include "net/instaweb/util/public/writer.h"
#include "pagespeed/kernel/base/string.h"

namespace net_instaweb {

extern const char* CSS_console_css;
extern const char* JS_console_js;

// Handler which serves PSOL console.
void ConsoleHandler(SystemRewriteOptions* options, Writer* writer,
                    MessageHandler* handler) {
  bool statistics_enabled = options->statistics_enabled();
  bool logging_enabled = options->statistics_logging_enabled();
  bool log_dir_set = !options->log_dir().empty();
  if (statistics_enabled && logging_enabled && log_dir_set) {
    // TODO(sligocki): Move static content to a data2cc library.
    writer->Write("<!DOCTYPE html>\n"
                  "\n"
                  "  \n"
                  "    <title>Pagespeed Console</title>\n"
                  "    \n"
                  "    \n"
                  "  \n"
                  "  \n"
                  "
\n"
                  "      Pagespeed Console\n"
                  "
\n"
                  "\n"
                  "
\n"
                  "
\n"
                  "        Notable issues:\n"
                  "
\n"
                  "

\n"
                  "
\n"
                  "    \n"
                  "    \n"
                  "    \n"
                  "  \n"
                  "\n", handler);
  } else {
    writer->Write("<!DOCTYPE html>\n"
                  "
\n"
                  "  Failed to load Pagespeed Console because:\n"
                  "
\n"
                  "
\n", handler);
    if (!statistics_enabled) {
      writer->Write("
ModPagespeedStatistics is not enabled.
\n",
                    handler);
    }
    if (!logging_enabled) {
      writer->Write("
ModPagespeedStatisticsLogging is not enabled."
                    "
\n", handler);
    }
    if (!log_dir_set) {
      writer->Write("
ModPagespeedLogFile is not set.
\n", handler);
    }
    writer->Write("
\n"
                  "
\n"
                  "  In order to use the console you must add the following\n"
                  "  configuration:\n"
                  "
\n"
                  "
\n"
                  "  ModPagespeedStatistics on\n"
                  "  ModPagespeedStatisticsLogging on\n"
                  "  ModPagespeedLogFile /var/log/pagespeed\n"
                  "
\n"
                  "
\n"
                  "  See documentation for more details.\n"
                  "
\n", handler);
  }
}

}  // namespace net_instaweb
<commit_msg>Update ConsoleHandler to spit out server agnostic configuration information, depending mostly on the documentation link to explain how to correctly configure.<commit_after>// Copyright 2013 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: sligocki@google.com (Shawn Ligocki)

#include "net/instaweb/system/public/handlers.h"

#include "net/instaweb/system/public/system_rewrite_options.h"
#include "net/instaweb/util/public/writer.h"
#include "pagespeed/kernel/base/string.h"

namespace net_instaweb {

extern const char* CSS_console_css;
extern const char* JS_console_js;

// Handler which serves PSOL console.
void ConsoleHandler(SystemRewriteOptions* options, Writer* writer,
                    MessageHandler* handler) {
  bool statistics_enabled = options->statistics_enabled();
  bool logging_enabled = options->statistics_logging_enabled();
  bool log_dir_set = !options->log_dir().empty();
  if (statistics_enabled && logging_enabled && log_dir_set) {
    // TODO(sligocki): Move static content to a data2cc library.
    writer->Write("<!DOCTYPE html>\n"
                  "\n"
                  "  \n"
                  "    <title>PageSpeed Console</title>\n"
                  "    \n"
                  "    \n"
                  "  \n"
                  "  \n"
                  "
\n"
                  "      PageSpeed Console\n"
                  "
\n"
                  "\n"
                  "
\n"
                  "
\n"
                  "        Notable issues:\n"
                  "
\n"
                  "

\n"
                  "
\n"
                  "    \n"
                  "    \n"
                  "    \n"
                  "  \n"
                  "\n", handler);
  } else {
    writer->Write("<!DOCTYPE html>\n"
                  "
\n"
                  "  Failed to load PageSpeed Console because:\n"
                  "
\n"
                  "
\n", handler);
    if (!statistics_enabled) {
      writer->Write("
Statistics is not enabled.
\n",
                    handler);
    }
    if (!logging_enabled) {
      writer->Write("
StatisticsLogging is not enabled."
                    "
\n", handler);
    }
    if (!log_dir_set) {
      writer->Write("
LogDir is not set.
\n", handler);
    }
    writer->Write("
\n"
                  "
\n"
                  "  In order to use the console you must configure these\n"
                  "  options. See the console documentation\n"
                  "  for more details.\n"
                  "
\n", handler);
  }
}

}  // namespace net_instaweb
<|endoftext|>


<commit_before><commit_msg>Http Cache: Enable some checks to track a low volume crash.<commit_after><|endoftext|>


<commit_before>
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

struct AnimationTexture
{
	Array<Texture> textures;

	Array<int32> delays;

	int32 duration = 0;

	explicit operator bool() const noexcept
	{
		return !textures.isEmpty();
	}

	Size size() const noexcept
	{
		if (!textures)
		{
			return Size(0, 0);
		}

		return textures.front().size();
	}

	size_t frames() const noexcept
	{
		return textures.size();
	}

	size_t getFrameIndex(int32 timeMillisec) const noexcept
	{
		return AnimatedGIFReader::MillisecToIndex(timeMillisec, delays, duration);
	}

	const Texture& getTexture(int32 timeMillisec) const noexcept
	{
		return textures[getFrameIndex(timeMillisec)];
	}
};

void Main()
{
	AnimationTexture animation;
	{
		const AnimatedGIFReader gif(U"example/test.gif");

		if (!gif)
		{
			throw Error(U"Failed to open a gif file");
		}

		Array<Image> images;

		if (gif.read(images, animation.delays, animation.duration))
		{
			animation.textures = images.map([](const Image& i) { return Texture(i); });
		}
		else
		{
			throw Error(U"Failed to load a gif animation");
		}
	}

	Print << U"{}, {} frames ({} ms)"_fmt(animation.size(), animation.frames(), animation.duration);

	const Point pos(10, 90);
	bool showTiles = false;

	while (System::Update())
	{
		const int32 timeMillisec = static_cast<int32>(Scene::Time() * 1000);
		const auto& texture = animation.getTexture(timeMillisec);

		SimpleGUI::CheckBox(showTiles, U"Show tiles", Vec2(10, 40));

		if (showTiles)
		{
			Rect(pos, texture.size()).draw();
			{
				ScopedViewport2D vp(pos, texture.size());
				for (auto p : step(texture.size() / 10 + Size(1, 1)))
				{
					if (IsEven(p.x + p.y))
					{
						Rect(p * 10, 10).draw(ColorF(0.8));
					}
				}
			}
		}

		texture.draw(pos);
	}
}
<commit_msg>v0.4.3 beta 準備 (mac)<commit_after>
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

void Main()
{
	// 背景を水色にする
	Scene::SetBackground(ColorF(0.8, 0.9, 1.0));

	// 大きさ 60 のフォントを用意
	const Font font(60);

	// 猫のテクスチャを用意
	const Texture cat(Emoji(U"🐈"));

	// 猫の座標
	Vec2 catPos(640, 450);

	while (System::Update())
	{
		// テキストを画面の中心に描く
		font(U"Hello, Siv3D!🐣").drawAt(Scene::Center(), Palette::Black);

		// 大きさをアニメーションさせて猫を表示する
		cat.resized(100 + Periodic::Sine0_1(1s) * 20).drawAt(catPos);

		// マウスカーソルに追従する半透明の赤い円を描く
		Circle(Cursor::Pos(), 40).draw(ColorF(1, 0, 0, 0.5));

		// [A] キーが押されたら
		if (KeyA.down())
		{
			// Hello とデバッグ表示する
			Print << U"Hello!";
		}

		// ボタンが押されたら
		if (SimpleGUI::Button(U"Move the cat", Vec2(600, 20)))
		{
			// 猫の座標を画面内のランダムな位置に移動する
			catPos = RandomVec2(Scene::Rect());
		}
	}
}

//
// = アドバイス =
// macOS 10.15 Catalina で、アプリケーションを起動するたびに
// ファイルアクセス許可のダイアログが表示される場合、プロジェクトのフォルダを
// User/アプリケーション に移動させることで通常は表示されなくなります。
// 特別なファイルシステム関数の使用や、Web カメラ、マイク使用時のダイアログは消せません。
//
// = お役立ちリンク =
//
// OpenSiv3D リファレンス
// https://siv3d.github.io/ja-jp/
//
// チュートリアル
// https://siv3d.github.io/ja-jp/tutorial/basic/
//
// よくある間違い
// https://siv3d.github.io/ja-jp/articles/mistakes/
//
// サポートについて
// https://siv3d.github.io/ja-jp/support/support/
//
// Siv3D Slack (ユーザコミュニティ) への参加
// https://siv3d.github.io/ja-jp/community/community/
//
// 新機能の提案やバグの報告
// https://github.com/Siv3D/OpenSiv3D/issues
//
<|endoftext|>


<commit_before>#ifndef CLFFT_HPP_
#define CLFFT_HPP_

#include "helper.h"
#include "fft_abstract.hpp"
#include "fixture_test_suite.hpp"
#include "clfft_helper.hpp"
#include <clFFT.h>

namespace gearshifft
{
namespace ClFFT
{
  namespace traits
  {
    template< typename T_Precision >
    struct Types;

    template<>
    struct Types<float>
    {
        using ComplexType = cl_float2;
        using RealType = cl_float;
    };

    template<>
    struct Types<double>
    {
        using ComplexType = cl_double2;
        using RealType = cl_double;
    };

    template< typename TPrecision=float >
    struct FFTPrecision: std::integral_constant< clfftPrecision, CLFFT_SINGLE >{};
    template<>
    struct FFTPrecision<double>: std::integral_constant< clfftPrecision, CLFFT_DOUBLE >{};

    template< bool IsComplex=true >
    struct FFTLayout {
      static constexpr clfftLayout value = CLFFT_COMPLEX_INTERLEAVED;
      static constexpr clfftLayout value_transformed = CLFFT_COMPLEX_INTERLEAVED;
    };
    template<>
    struct FFTLayout<false> {
      static constexpr clfftLayout value = CLFFT_REAL;
      static constexpr clfftLayout value_transformed = CLFFT_HERMITIAN_INTERLEAVED;
    };

    template< bool T_isInplace=true >
    struct FFTInplace: std::integral_constant< clfftResultLocation, CLFFT_INPLACE >{};
    template<>
    struct FFTInplace<false>: std::integral_constant< clfftResultLocation, CLFFT_OUTOFPLACE >{};
  } // traits

  struct Context {
    cl_platform_id platform = 0;
    cl_device_id device = 0;
    cl_context ctx = 0;
    void create() {
      std::cout<<"Create OpenCL and clFFT context ..."<<std::endl;
      cl_context_properties props[3] = { CL_CONTEXT_PLATFORM, 0, 0 };
      cl_int err = CL_SUCCESS;
      findClDevice(CL_DEVICE_TYPE_GPU, &platform, &device);
      props[1] = (cl_context_properties)platform;
      ctx = clCreateContext( props, 1, &device, nullptr, nullptr, &err );
      CHECK_CL(err);
      clfftSetupData fftSetup;
      CHECK_CL(clfftInitSetupData(&fftSetup));
      CHECK_CL(clfftSetup(&fftSetup));

    }
    void destroy() {
      if(ctx) {
        std::cout << "Destroying clFFT and OpenCL Context ..." << std::endl;
        CHECK_CL( clfftTeardown( ) );
        CHECK_CL(clReleaseContext( ctx ));
        ctx = 0;
      }
    }
    ~Context()
    {
      destroy();
    }
  } context;

  /**
   * Plan Creator depending on FFT transform type.
   */
  template<clfftDim FFTDim, size_t Ndim>
  constexpr void makePlan(clfftPlanHandle& plan, const std::array<unsigned,Ndim>& e){
    size_t clLengths[3] = {e[0], Ndim==2?e[1]:1, Ndim==3?e[2]:1};
    CHECK_CL(clfftCreateDefaultPlan(&plan, context.ctx, FFTDim, clLengths));
  }

  /**
   * ClFFT plan and execution class.
   *
   * This class handles:
   * - {1D, 2D, 3D} x {R2C, C2R, C2C} x {inplace, outplace} x {float, double}.
   */
  template<typename TFFT, // see fft_abstract.hpp (FFT_Inplace_Real, ...)
           typename TPrecision, // double, float
           size_t   NDim // 1..3
           >
  struct ClFFTImpl
  {
    using ComplexType = typename traits::Types<TPrecision>::ComplexType;
    using RealType = typename traits::Types<TPrecision>::RealType;
    using Extent = std::array<unsigned,NDim>;
    static constexpr auto IsInplace = TFFT::IsInplace;
    static constexpr auto IsComplex = TFFT::IsComplex;
    static constexpr auto Padding = IsInplace && IsComplex==false;
    static constexpr clfftDim FFTDim = NDim==1 ? CLFFT_1D : NDim==2 ? CLFFT_2D : CLFFT_3D;
    using RealOrComplexType = typename std::conditional<IsComplex,ComplexType,RealType>::type;

    size_t n_;
    size_t n_padded_;
    Extent extents_;

    cl_command_queue queue_ = 0;
    clfftPlanHandle plan_   = 0;
    cl_mem data_            = 0;
    cl_mem data_transform_  = 0; // intermediate buffer
    size_t data_size_       = 0;
    size_t data_transform_size_ = 0;
    size_t w;
    size_t h;
    size_t pitch;
    size_t region[3];
    size_t offset[3] = {0, 0, 0};
    size_t strides[3] = {1};
    size_t transform_strides[3] = {1};
    size_t dist;
    size_t transform_dist;

    ClFFTImpl(const Extent& cextents)
      : extents_(cextents)
    {
      cl_int err;
      if(context.ctx==0)
        context.create();
      queue_ = clCreateCommandQueue( context.ctx, context.device, 0, &err );
      CHECK_CL(err);

      n_ = std::accumulate(extents_.begin(), extents_.end(), 1, std::multiplies<unsigned>());
      if(Padding){
        n_padded_ = n_ / extents_[0] * (extents_[0]/2 + 1);
        w      = extents_[0] * sizeof(RealType);
        h      = n_ * sizeof(RealType) / w;
        pitch  = (extents_[0]/2+1) * sizeof(ComplexType);
        region[0] = w; // in bytes
        region[1] = h; // in counts (OpenCL1.1 is wrong here saying in bytes)
        region[2] = 1; // in counts (same)
        strides[1] = 2*(extents_[0]/2+1);
        strides[2] = 2 * n_padded_ / extents_[NDim-1];
        transform_strides[1] = extents_[0]/2+1;
        transform_strides[2] = n_padded_ / extents_[NDim-1];
        dist = 2 * n_padded_;
        transform_dist = n_padded_;
      }

      data_size_ = ( Padding ? 2*n_padded_*sizeof(RealType) : n_ * sizeof(RealOrComplexType) );
      data_transform_size_ = IsInplace ? 0 : n_ * sizeof(ComplexType);

    }

    /**
     * Returns allocated memory on device for FFT
     */
    size_t getAllocSize() {
      return data_size_ + data_transform_size_;
    }
    /**
     * Returns estimated allocated memory on device for FFT plan
     */
    size_t getPlanSize() {
      size_t size1 = 0;
      size_t size2 = 0;
      init_forward();
      CHECK_CL(clfftGetTmpBufSize( plan_, &size1 ));
      init_backward();
      CHECK_CL(clfftGetTmpBufSize( plan_, &size2 ));
      CHECK_CL(clfftDestroyPlan( &plan_ ));
      return std::max(size1,size2);
    }

    // --- next methods are benchmarked ---

    void malloc() {
      cl_int err;
      data_ = clCreateBuffer( context.ctx,
                              CL_MEM_READ_WRITE,
                              data_size_,
                              nullptr, // host pointer @todo
                              &err );
      //std::cout << " data " << data_size_ << std::endl;
      if(IsInplace==false){
        data_transform_ = clCreateBuffer( context.ctx,
                                          CL_MEM_READ_WRITE,
                                          data_transform_size_,
                                          nullptr, // host pointer
                                          &err );
        //std::cout << " transform " <<data_transform_size_ << std::endl;
      }
    }

    // create FFT plan handle
    void init_forward() {
      makePlan<FFTDim>(plan_, extents_);
      CHECK_CL(clfftSetPlanPrecision(plan_, traits::FFTPrecision<TPrecision>::value));
      CHECK_CL(clfftSetLayout(plan_,
                                traits::FFTLayout<IsComplex>::value,
                                traits::FFTLayout<IsComplex>::value_transformed));
      CHECK_CL(clfftSetResultLocation(plan_, traits::FFTInplace<IsInplace>::value));
      if(Padding){
        CHECK_CL(clfftSetPlanInStride(plan_, FFTDim, strides));
        CHECK_CL(clfftSetPlanOutStride(plan_, FFTDim, transform_strides));
        CHECK_CL(clfftSetPlanDistance(plan_, dist, transform_dist));
      }
      CHECK_CL(clfftBakePlan(plan_,
                               1, // number of queues
                               &queue_,
                               nullptr, // callback
                               nullptr)); // user data
    }

    // recreates plan if needed
    void init_backward() {
      if(IsComplex==false){
        CHECK_CL(clfftSetLayout(plan_,
                                  traits::FFTLayout<IsComplex>::value_transformed,
                                  traits::FFTLayout<IsComplex>::value));
        if(Padding){
          CHECK_CL(clfftSetPlanOutStride(plan_, FFTDim, strides));
          CHECK_CL(clfftSetPlanInStride(plan_, FFTDim, transform_strides));
          CHECK_CL(clfftSetPlanDistance(plan_, transform_dist, dist));
        }

        CHECK_CL(clfftBakePlan(plan_,
                                 1, // number of queues
                                 &queue_,
                                 0, // callback
                                 0)); // user data
      }
    }

    void execute_forward() {
      CHECK_CL(clfftEnqueueTransform(plan_,
                                       CLFFT_FORWARD,
                                       1, // numQueuesAndEvents
                                       &queue_,
                                       0, // numWaitEvents
                                       0, // waitEvents
                                       0, // outEvents
                                       &data_,  // input
                                       IsInplace ? &data_ : &data_transform_, // output
                                       0)); // tmpBuffer
      CHECK_CL(clFinish(queue_));
    }
    void execute_backward() {
      CHECK_CL(clfftEnqueueTransform(plan_,
                                       CLFFT_BACKWARD,
                                       1, // numQueuesAndEvents
                                       &queue_,
                                       0, // numWaitEvents
                                       nullptr, // waitEvents
                                       nullptr, // outEvents
                                       IsInplace ? &data_ : &data_transform_, // input
                                       IsInplace ? &data_ : &data_, // output
                                       nullptr)); // tmpBuffer
      CHECK_CL(clFinish(queue_));
    }
    template<typename THostData>
    void upload(THostData* input) {
      if(Padding && NDim>1)
      {
        //printf("pitch=%zu w=%zu h=%zu\n", pitch, w, h);
        CHECK_CL(clEnqueueWriteBufferRect( queue_,
                                             data_,
                                             CL_TRUE, // blocking_write
                                             offset, // buffer origin
                                             offset, // host origin
                                             region,
                                             pitch, // buffer row pitch
                                             0, // buffer slice pitch
                                             0, // host row pitch
                                             0, // host slice pitch
                                             input,
                                             0, // num_events_in_wait_list
                                             nullptr, // event_wait_list
                                             nullptr )); // event
      }else{
        CHECK_CL(clEnqueueWriteBuffer( queue_,
                                         data_,
                                         CL_TRUE, // blocking_write
                                         0, // offset
                                         Padding ? n_*sizeof(RealType) : data_size_,
                                         input,
                                         0, // num_events_in_wait_list
                                         nullptr, // event_wait_list
                                         nullptr )); // event
      }
    }
    template<typename THostData>
    void download(THostData* output) {
      if(Padding && NDim>1)
      {
        CHECK_CL(clEnqueueReadBufferRect( queue_,
                                            data_,
                                            CL_TRUE, // blocking_write
                                            offset, // buffer origin
                                            offset, // host origin
                                            region,
                                            pitch, // buffer row pitch
                                            0, // buffer slice pitch
                                            0, // host row pitch
                                            0, // host slice pitch
                                            output,
                                            0, // num_events_in_wait_list
                                            nullptr, // event_wait_list
                                            nullptr )); // event
      }else{
        CHECK_CL(clEnqueueReadBuffer( queue_,
                                        data_,
                                        CL_TRUE, // blocking_write
                                        0, // offset
                                        Padding ? n_*sizeof(RealType) : data_size_,
                                        output,
                                        0, // num_events_in_wait_list
                                        nullptr, // event_wait_list
                                        nullptr )); // event
      }
    }

    void destroy() {
      CHECK_CL( clFinish(queue_) );
      CHECK_CL( clReleaseMemObject( data_ ) );
      if(IsInplace==false)
        CHECK_CL( clReleaseMemObject( data_transform_ ) );

      CHECK_CL(clfftDestroyPlan( &plan_ ));
      CHECK_CL( clReleaseCommandQueue( queue_ ) );
      data_ = 0;
      data_transform_ = 0;
      plan_ = 0;
      queue_ = 0;
    }
  };

  typedef gearshifft::FFT<gearshifft::FFT_Inplace_Real, ClFFTImpl, helper::TimerCPU> Inplace_Real;
  typedef gearshifft::FFT<gearshifft::FFT_Outplace_Real, ClFFTImpl, helper::TimerCPU> Outplace_Real;
  typedef gearshifft::FFT<gearshifft::FFT_Inplace_Complex, ClFFTImpl, helper::TimerCPU> Inplace_Complex;
  typedef gearshifft::FFT<gearshifft::FFT_Outplace_Complex, ClFFTImpl, helper::TimerCPU> Outplace_Complex;

} // namespace ClFFT
} // gearshifft

// -- Execute Benchmarks --
RUN_BENCHMARKS_NORMALIZED_FFT(ClFFT,
                              gearshifft::ClFFT::Inplace_Real,
                              gearshifft::ClFFT::Outplace_Real,
                              gearshifft::ClFFT::Inplace_Complex,
                              gearshifft::ClFFT::Outplace_Complex
                              )

#endif /* CLFFT_HPP_ */
<commit_msg>minor code change.<commit_after>#ifndef CLFFT_HPP_
#define CLFFT_HPP_

#include "helper.h"
#include "fft_abstract.hpp"
#include "fixture_test_suite.hpp"
#include "clfft_helper.hpp"
#include <clFFT.h>

namespace gearshifft
{
namespace ClFFT
{
  namespace traits
  {
    template< typename T_Precision >
    struct Types;

    template<>
    struct Types<float>
    {
        using ComplexType = cl_float2;
        using RealType = cl_float;
    };

    template<>
    struct Types<double>
    {
        using ComplexType = cl_double2;
        using RealType = cl_double;
    };

    template< typename TPrecision=float >
    struct FFTPrecision: std::integral_constant< clfftPrecision, CLFFT_SINGLE >{};
    template<>
    struct FFTPrecision<double>: std::integral_constant< clfftPrecision, CLFFT_DOUBLE >{};

    template< bool IsComplex=true >
    struct FFTLayout {
      static constexpr clfftLayout value = CLFFT_COMPLEX_INTERLEAVED;
      static constexpr clfftLayout value_transformed = CLFFT_COMPLEX_INTERLEAVED;
    };
    template<>
    struct FFTLayout<false> {
      static constexpr clfftLayout value = CLFFT_REAL;
      static constexpr clfftLayout value_transformed = CLFFT_HERMITIAN_INTERLEAVED;
    };

    template< bool T_isInplace=true >
    struct FFTInplace: std::integral_constant< clfftResultLocation, CLFFT_INPLACE >{};
    template<>
    struct FFTInplace<false>: std::integral_constant< clfftResultLocation, CLFFT_OUTOFPLACE >{};
  } // traits

  struct Context {
    cl_platform_id platform = 0;
    cl_device_id device = 0;
    cl_context ctx = 0;
    void create() {
      std::cout<<"Create OpenCL and clFFT context ..."<<std::endl;
      cl_context_properties props[3] = { CL_CONTEXT_PLATFORM, 0, 0 };
      cl_int err = CL_SUCCESS;
      findClDevice(CL_DEVICE_TYPE_GPU, &platform, &device);
      props[1] = (cl_context_properties)platform;
      ctx = clCreateContext( props, 1, &device, nullptr, nullptr, &err );
      CHECK_CL(err);
      clfftSetupData fftSetup;
      CHECK_CL(clfftInitSetupData(&fftSetup));
      CHECK_CL(clfftSetup(&fftSetup));

    }
    void destroy() {
      if(ctx) {
        std::cout << "Destroying clFFT and OpenCL Context ..." << std::endl;
        CHECK_CL( clfftTeardown( ) );
        CHECK_CL(clReleaseContext( ctx ));
        ctx = 0;
      }
    }
    ~Context()
    {
      destroy();
    }
  } context;

  /**
   * Plan Creator depending on FFT transform type.
   */
  template<clfftDim FFTDim, size_t Ndim>
  constexpr void makePlan(clfftPlanHandle& plan, const std::array<unsigned,Ndim>& e){
    size_t clLengths[3] = {e[0], Ndim==2?e[1]:1, Ndim==3?e[2]:1};
    CHECK_CL(clfftCreateDefaultPlan(&plan, context.ctx, FFTDim, clLengths));
  }

  /**
   * ClFFT plan and execution class.
   *
   * This class handles:
   * - {1D, 2D, 3D} x {R2C, C2R, C2C} x {inplace, outplace} x {float, double}.
   */
  template<typename TFFT, // see fft_abstract.hpp (FFT_Inplace_Real, ...)
           typename TPrecision, // double, float
           size_t   NDim // 1..3
           >
  struct ClFFTImpl
  {
    using ComplexType = typename traits::Types<TPrecision>::ComplexType;
    using RealType = typename traits::Types<TPrecision>::RealType;
    using Extent = std::array<unsigned,NDim>;
    static constexpr auto IsInplace = TFFT::IsInplace;
    static constexpr auto IsComplex = TFFT::IsComplex;
    static constexpr auto Padding = IsInplace && IsComplex==false;
    static constexpr clfftDim FFTDim = NDim==1 ? CLFFT_1D : NDim==2 ? CLFFT_2D : CLFFT_3D;
    using RealOrComplexType = typename std::conditional<IsComplex,ComplexType,RealType>::type;

    size_t n_;
    size_t n_padded_;
    Extent extents_;

    cl_command_queue queue_ = 0;
    clfftPlanHandle plan_   = 0;
    cl_mem data_            = 0;
    cl_mem data_transform_  = 0; // intermediate buffer
    size_t data_size_       = 0;
    size_t data_transform_size_ = 0;
    size_t w;
    size_t h;
    size_t pitch;
    size_t region[3];
    size_t offset[3] = {0, 0, 0};
    size_t strides[3] = {1};
    size_t transform_strides[3] = {1};
    size_t dist;
    size_t transform_dist;

    ClFFTImpl(const Extent& cextents)
      : extents_(cextents)
    {
      cl_int err;
      if(context.ctx==0)
        context.create();
      queue_ = clCreateCommandQueue( context.ctx, context.device, 0, &err );
      CHECK_CL(err);

      n_ = std::accumulate(extents_.begin(), extents_.end(), 1, std::multiplies<unsigned>());
      if(Padding){
        n_padded_ = n_ / extents_[0] * (extents_[0]/2 + 1);
        w      = extents_[0] * sizeof(RealType);
        h      = n_ / extents_[0];
        pitch  = (extents_[0]/2+1) * sizeof(ComplexType);
        region[0] = w; // in bytes
        region[1] = h; // in counts (OpenCL1.1 is wrong here saying in bytes)
        region[2] = 1; // in counts (same)
        strides[1] = 2*(extents_[0]/2+1);
        strides[2] = 2 * n_padded_ / extents_[NDim-1];
        transform_strides[1] = extents_[0]/2+1;
        transform_strides[2] = n_padded_ / extents_[NDim-1];
        dist = 2 * n_padded_;
        transform_dist = n_padded_;
      }

      data_size_ = ( Padding ? 2*n_padded_*sizeof(RealType) : n_ * sizeof(RealOrComplexType) );
      data_transform_size_ = IsInplace ? 0 : n_ * sizeof(ComplexType);
    }

    /**
     * Returns allocated memory on device for FFT
     */
    size_t getAllocSize() {
      return data_size_ + data_transform_size_;
    }
    /**
     * Returns estimated allocated memory on device for FFT plan
     */
    size_t getPlanSize() {
      size_t size1 = 0;
      size_t size2 = 0;
      init_forward();
      CHECK_CL(clfftGetTmpBufSize( plan_, &size1 ));
      init_backward();
      CHECK_CL(clfftGetTmpBufSize( plan_, &size2 ));
      CHECK_CL(clfftDestroyPlan( &plan_ ));
      return std::max(size1,size2);
    }

    // --- next methods are benchmarked ---

    void malloc() {
      cl_int err;
      data_ = clCreateBuffer( context.ctx,
                              CL_MEM_READ_WRITE,
                              data_size_,
                              nullptr, // host pointer @todo
                              &err );
      //std::cout << " data " << data_size_ << std::endl;
      if(IsInplace==false){
        data_transform_ = clCreateBuffer( context.ctx,
                                          CL_MEM_READ_WRITE,
                                          data_transform_size_,
                                          nullptr, // host pointer
                                          &err );
        //std::cout << " transform " <<data_transform_size_ << std::endl;
      }
    }

    // create FFT plan handle
    void init_forward() {
      makePlan<FFTDim>(plan_, extents_);
      CHECK_CL(clfftSetPlanPrecision(plan_, traits::FFTPrecision<TPrecision>::value));
      CHECK_CL(clfftSetLayout(plan_,
                                traits::FFTLayout<IsComplex>::value,
                                traits::FFTLayout<IsComplex>::value_transformed));
      CHECK_CL(clfftSetResultLocation(plan_, traits::FFTInplace<IsInplace>::value));
      if(Padding){
        CHECK_CL(clfftSetPlanInStride(plan_, FFTDim, strides));
        CHECK_CL(clfftSetPlanOutStride(plan_, FFTDim, transform_strides));
        CHECK_CL(clfftSetPlanDistance(plan_, dist, transform_dist));
      }
      CHECK_CL(clfftBakePlan(plan_,
                               1, // number of queues
                               &queue_,
                               nullptr, // callback
                               nullptr)); // user data
    }

    // recreates plan if needed
    void init_backward() {
      if(IsComplex==false){
        CHECK_CL(clfftSetLayout(plan_,
                                  traits::FFTLayout<IsComplex>::value_transformed,
                                  traits::FFTLayout<IsComplex>::value));
        if(Padding){
          CHECK_CL(clfftSetPlanOutStride(plan_, FFTDim, strides));
          CHECK_CL(clfftSetPlanInStride(plan_, FFTDim, transform_strides));
          CHECK_CL(clfftSetPlanDistance(plan_, transform_dist, dist));
        }

        CHECK_CL(clfftBakePlan(plan_,
                                 1, // number of queues
                                 &queue_,
                                 0, // callback
                                 0)); // user data
      }
    }

    void execute_forward() {
      CHECK_CL(clfftEnqueueTransform(plan_,
                                       CLFFT_FORWARD,
                                       1, // numQueuesAndEvents
                                       &queue_,
                                       0, // numWaitEvents
                                       0, // waitEvents
                                       0, // outEvents
                                       &data_,  // input
                                       IsInplace ? &data_ : &data_transform_, // output
                                       0)); // tmpBuffer
      CHECK_CL(clFinish(queue_));
    }
    void execute_backward() {
      CHECK_CL(clfftEnqueueTransform(plan_,
                                       CLFFT_BACKWARD,
                                       1, // numQueuesAndEvents
                                       &queue_,
                                       0, // numWaitEvents
                                       nullptr, // waitEvents
                                       nullptr, // outEvents
                                       IsInplace ? &data_ : &data_transform_, // input
                                       IsInplace ? &data_ : &data_, // output
                                       nullptr)); // tmpBuffer
      CHECK_CL(clFinish(queue_));
    }
    template<typename THostData>
    void upload(THostData* input) {
      if(Padding && NDim>1)
      {
        //printf("pitch=%zu w=%zu h=%zu\n", pitch, w, h);
        CHECK_CL(clEnqueueWriteBufferRect( queue_,
                                             data_,
                                             CL_TRUE, // blocking_write
                                             offset, // buffer origin
                                             offset, // host origin
                                             region,
                                             pitch, // buffer row pitch
                                             0, // buffer slice pitch
                                             0, // host row pitch
                                             0, // host slice pitch
                                             input,
                                             0, // num_events_in_wait_list
                                             nullptr, // event_wait_list
                                             nullptr )); // event
      }else{
        CHECK_CL(clEnqueueWriteBuffer( queue_,
                                         data_,
                                         CL_TRUE, // blocking_write
                                         0, // offset
                                         Padding ? n_*sizeof(RealType) : data_size_,
                                         input,
                                         0, // num_events_in_wait_list
                                         nullptr, // event_wait_list
                                         nullptr )); // event
      }
    }
    template<typename THostData>
    void download(THostData* output) {
      if(Padding && NDim>1)
      {
        CHECK_CL(clEnqueueReadBufferRect( queue_,
                                            data_,
                                            CL_TRUE, // blocking_write
                                            offset, // buffer origin
                                            offset, // host origin
                                            region,
                                            pitch, // buffer row pitch
                                            0, // buffer slice pitch
                                            0, // host row pitch
                                            0, // host slice pitch
                                            output,
                                            0, // num_events_in_wait_list
                                            nullptr, // event_wait_list
                                            nullptr )); // event
      }else{
        CHECK_CL(clEnqueueReadBuffer( queue_,
                                        data_,
                                        CL_TRUE, // blocking_write
                                        0, // offset
                                        Padding ? n_*sizeof(RealType) : data_size_,
                                        output,
                                        0, // num_events_in_wait_list
                                        nullptr, // event_wait_list
                                        nullptr )); // event
      }
    }

    void destroy() {
      CHECK_CL( clFinish(queue_) );
      CHECK_CL( clReleaseMemObject( data_ ) );
      if(IsInplace==false)
        CHECK_CL( clReleaseMemObject( data_transform_ ) );

      CHECK_CL(clfftDestroyPlan( &plan_ ));
      CHECK_CL( clReleaseCommandQueue( queue_ ) );
      data_ = 0;
      data_transform_ = 0;
      plan_ = 0;
      queue_ = 0;
    }
  };

  typedef gearshifft::FFT<gearshifft::FFT_Inplace_Real, ClFFTImpl, helper::TimerCPU> Inplace_Real;
  typedef gearshifft::FFT<gearshifft::FFT_Outplace_Real, ClFFTImpl, helper::TimerCPU> Outplace_Real;
  typedef gearshifft::FFT<gearshifft::FFT_Inplace_Complex, ClFFTImpl, helper::TimerCPU> Inplace_Complex;
  typedef gearshifft::FFT<gearshifft::FFT_Outplace_Complex, ClFFTImpl, helper::TimerCPU> Outplace_Complex;

} // namespace ClFFT
} // gearshifft

// -- Execute Benchmarks --
RUN_BENCHMARKS_NORMALIZED_FFT(ClFFT,
                              gearshifft::ClFFT::Inplace_Real,
                              gearshifft::ClFFT::Outplace_Real,
                              gearshifft::ClFFT::Inplace_Complex,
                              gearshifft::ClFFT::Outplace_Complex
                              )

#endif /* CLFFT_HPP_ */
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <string>

#include "native_client/src/include/nacl_scoped_ptr.h"
#include "native_client/src/include/portability_sockets.h"
#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/trusted/debug_stub/platform.h"
#include "native_client/src/trusted/debug_stub/transport.h"
#include "native_client/src/trusted/debug_stub/util.h"
#include "native_client/src/trusted/service_runtime/sel_ldr.h"

using gdb_rsp::stringvec;
using gdb_rsp::StringSplit;

namespace port {

typedef int socklen_t;

class Transport : public ITransport {
 public:
  Transport()
    : buf_(new char[kBufSize]),
      pos_(0),
      size_(0) {
    handle_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#if NACL_WINDOWS
    CreateSocketEvent();
#endif
  }

  explicit Transport(NaClSocketHandle s)
    : buf_(new char[kBufSize]),
      pos_(0),
      size_(0),
      handle_(s) {
#if NACL_WINDOWS
    CreateSocketEvent();
#endif
  }

  ~Transport() {
    if (handle_ != NACL_INVALID_SOCKET) NaClCloseSocket(handle_);
#if NACL_WINDOWS
    if (!WSACloseEvent(socket_event_)) {
      NaClLog(LOG_FATAL,
              "Transport::~Transport: Failed to close socket event\n");
    }
#endif
  }

#if NACL_WINDOWS
  void CreateSocketEvent() {
    socket_event_ = WSACreateEvent();
    if (socket_event_ == WSA_INVALID_EVENT) {
      NaClLog(LOG_FATAL,
              "Transport::CreateSocketEvent: Failed to create socket event\n");
    }
    if (WSAEventSelect(handle_, socket_event_, FD_READ) == SOCKET_ERROR) {
      NaClLog(LOG_FATAL,
              "Transport::CreateSocketEvent: Failed to bind event to socket\n");
    }
  }
#endif

  // Read from this transport, return true on success.
  virtual bool Read(void *ptr, int32_t len);

  // Write to this transport, return true on success.
  virtual bool Write(const void *ptr, int32_t len);

  // Return true if there is data to read.
  virtual bool IsDataAvailable() {
    if (pos_ < size_) {
      return true;
    }
    fd_set fds;

    FD_ZERO(&fds);
    FD_SET(handle_, &fds);

    // We want a "non-blocking" check
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    // Check if this file handle can select on read
    int cnt = select(static_cast<int>(handle_) + 1, &fds, 0, 0, &timeout);

    // If we are ready, or if there is an error.  We return true
    // on error, to let the next IO request fail.
    if (cnt != 0) return true;

    return false;
  }

  virtual void WaitForDebugStubEvent(struct NaClApp *nap,
                                     bool ignore_input_from_gdb);

// On windows, the header that defines this has other definition
// colitions, so we define it outselves just in case
#ifndef SD_BOTH
#define SD_BOTH 2
#endif

  virtual void Disconnect() {
    // Shutdown the conneciton in both diections.  This should
    // always succeed, and nothing we can do if this fails.
    (void) ::shutdown(handle_, SD_BOTH);
  }

 protected:
  // Copy buffered data to *dst up to len bytes and update dst and len.
  void CopyFromBuffer(char **dst, int32_t *len);

  // Read available data from the socket. Return false on EOF or error.
  bool ReadSomeData();

  static const int kBufSize = 4096;
  nacl::scoped_array<char> buf_;
  int32_t pos_;
  int32_t size_;
  NaClSocketHandle handle_;
#if NACL_WINDOWS
  HANDLE socket_event_;
#endif
};

void Transport::CopyFromBuffer(char **dst, int32_t *len) {
  int32_t copy_bytes = std::min(*len, size_ - pos_);
  memcpy(*dst, buf_.get() + pos_, copy_bytes);
  pos_ += copy_bytes;
  *len -= copy_bytes;
  *dst += copy_bytes;
}

bool Transport::ReadSomeData() {
  while (true) {
    int result = ::recv(handle_, buf_.get() + size_, kBufSize - size_, 0);
    if (result > 0) {
      size_ += result;
      return true;
    }
    if (result == 0)
      return false;
#if NACL_WINDOWS
    // WSAEventSelect sets socket to non-blocking mode. This is essential
    // for socket event notification to work, there is no workaround.
    // See remarks section at the page
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
    if (NaClSocketGetLastError() == WSAEWOULDBLOCK) {
      if (WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
        NaClLog(LOG_FATAL,
                "Transport::ReadSomeData: Failed to wait on socket event\n");
      }
      if (!ResetEvent(socket_event_)) {
        NaClLog(LOG_FATAL,
                "Transport::ReadSomeData: Failed to reset socket event\n");
      }
      continue;
    }
#endif
    if (NaClSocketGetLastError() != EINTR)
      return false;
  }
}

bool Transport::Read(void *ptr, int32_t len) {
  char *dst = static_cast<char *>(ptr);
  if (pos_ < size_) {
    CopyFromBuffer(&dst, &len);
  }
  while (len > 0) {
    pos_ = 0;
    size_ = 0;
    if (!ReadSomeData()) {
      return false;
    }
    CopyFromBuffer(&dst, &len);
  }
  return true;
}

bool Transport::Write(const void *ptr, int32_t len) {
  const char *src = static_cast<const char *>(ptr);
  while (len > 0) {
    int result = ::send(handle_, src, len, 0);
    if (result > 0) {
      src += result;
      len -= result;
      continue;
    }
    if (result == 0) {
      return false;
    }
    if (NaClSocketGetLastError() != EINTR) {
      return false;
    }
  }
  return true;
}

void Transport::WaitForDebugStubEvent(struct NaClApp *nap,
                                      bool ignore_input_from_gdb) {
  bool wait = true;
  // If we are told to ignore messages from gdb, we will exit from this
  // function only if new data is sent by gdb.
  if ((pos_ < size_ && !ignore_input_from_gdb) ||
      nap->faulted_thread_count > 0) {
    // Clear faulted thread events to save debug stub loop iterations.
    wait = false;
  }
#if NACL_WINDOWS
  HANDLE handles[2];
  handles[0] = nap->faulted_thread_event;
  handles[1] = socket_event_;
  int count = size_ < kBufSize ? 2 : 1;
  int result = WaitForMultipleObjects(count, handles, FALSE,
                                      wait ? INFINITE : 0);
  if (result == WAIT_OBJECT_0 + 1) {
    if (!ResetEvent(socket_event_)) {
      NaClLog(LOG_FATAL,
              "Transport::WaitForDebugStubEvent: "
              "Failed to reset socket event\n");
    }
    return;
  }
  if (result == WAIT_TIMEOUT || result == WAIT_OBJECT_0)
    return;
  NaClLog(LOG_FATAL,
          "Transport::WaitForDebugStubEvent: Wait for events failed\n");
#else
  fd_set fds;

  FD_ZERO(&fds);
  FD_SET(nap->faulted_thread_fd_read, &fds);
  int max_fd = nap->faulted_thread_fd_read;
  if (size_ < kBufSize) {
    FD_SET(handle_, &fds);
    max_fd = std::max(max_fd, handle_);
  }

  int ret;
  // We don't need sleep-polling on Linux now, so we set either zero or infinite
  // timeout.
  if (wait) {
    ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
  } else {
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
  }
  if (ret < 0) {
    NaClLog(LOG_FATAL,
            "Transport::WaitForDebugStubEvent: Failed to wait for "
            "debug stub event\n");
  }

  if (ret > 0) {
    if (FD_ISSET(nap->faulted_thread_fd_read, &fds)) {
      char buf[16];
      if (read(nap->faulted_thread_fd_read, &buf, sizeof(buf)) < 0) {
        NaClLog(LOG_FATAL,
                "Transport::WaitForDebugStubEvent: Failed to read from "
                "debug stub event pipe fd\n");
      }
    }
    if (FD_ISSET(handle_, &fds))
      ReadSomeData();
  }
#endif
}

// Convert string in the form of [addr][:port] where addr is a
// IPv4 address or host name, and port is a 16b tcp/udp port.
// Both portions are optional, and only the portion of the address
// provided is updated.  Values are provided in network order.
static bool StringToIPv4(const std::string &instr, uint32_t *addr,
                         uint16_t *port) {
  // Make a copy so the are unchanged unless we succeed
  uint32_t outaddr = *addr;
  uint16_t outport = *port;

  // Substrings of the full ADDR:PORT
  std::string addrstr;
  std::string portstr;

  // We should either have one or two tokens in the form of:
  //  IP - IP, NUL
  //  IP: -  IP, NUL
  //  :PORT - NUL, PORT
  //  IP:PORT - IP, PORT

  // Search for the port marker
  size_t portoff = instr.find(':');

  // If we found a ":" before the end, get both substrings
  if ((portoff != std::string::npos) && (portoff + 1 < instr.size())) {
    addrstr = instr.substr(0, portoff);
    portstr = instr.substr(portoff + 1, std::string::npos);
  } else {
    // otherwise the entire string is the addr portion.
    addrstr = instr;
    portstr = "";
  }

  // If the address portion was provided, update it
  if (addrstr.size()) {
    // Special case 0.0.0.0 which means any IPv4 interface
    if (addrstr == "0.0.0.0") {
      outaddr = 0;
    } else {
      struct hostent *host = gethostbyname(addrstr.data());

      // Check that we found an IPv4 host
      if ((NULL == host) || (AF_INET != host->h_addrtype)) return false;

      // Make sure the IP list isn't empty.
      if (0 == host->h_addr_list[0]) return false;

      // Use the first address in the array of address pointers.
      uint32_t **addrarray = reinterpret_cast<uint32_t**>(host->h_addr_list);
      outaddr = *addrarray[0];
    }
  }

  // if the port portion was provided, then update it
  if (portstr.size()) {
    int val = atoi(portstr.data());
    if ((val < 0) || (val > 65535)) return false;
    outport = ntohs(static_cast<uint16_t>(val));
  }

  // We haven't failed, so set the values
  *addr = outaddr;
  *port = outport;
  return true;
}

static bool BuildSockAddr(const char *addr, struct sockaddr_in *sockaddr) {
  std::string addrstr = addr;
  uint32_t *pip = reinterpret_cast<uint32_t*>(&sockaddr->sin_addr.s_addr);
  uint16_t *pport = reinterpret_cast<uint16_t*>(&sockaddr->sin_port);

  sockaddr->sin_family = AF_INET;
  return StringToIPv4(addrstr, pip, pport);
}

SocketBinding::SocketBinding(NaClSocketHandle socket_handle)
    : socket_handle_(socket_handle) {
}

SocketBinding *SocketBinding::Bind(const char *addr) {
  NaClSocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (socket_handle == NACL_INVALID_SOCKET) {
    NaClLog(LOG_ERROR, "Failed to create socket.\n");
    return NULL;
  }
  struct sockaddr_in saddr;
  // Clearing sockaddr_in first appears to be necessary on Mac OS X.
  memset(&saddr, 0, sizeof(saddr));
  socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = htonl(0x7F000001);
  saddr.sin_port = htons(4014);

  // Override portions address that are provided
  if (addr) BuildSockAddr(addr, &saddr);

  // This is necessary to ensure that the TCP port is released
  // promptly when sel_ldr exits.  Without this, the TCP port might
  // only be released after a timeout, and later processes can fail
  // to bind it.
  int reuse_address = 1;
  if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
                 reinterpret_cast<char *>(&reuse_address),
                 sizeof(reuse_address))) {
    NaClLog(LOG_WARNING, "Failed to set SO_REUSEADDR option.\n");
  }
  // Do not delay sending small packets.  This significantly speeds up
  // remote debugging.  Debug stub uses buffering to send outgoing packets so
  // they are not split into more TCP packets than necessary.
  int nodelay = 1;
  if (setsockopt(socket_handle, IPPROTO_TCP, TCP_NODELAY,
                 reinterpret_cast<char *>(&nodelay),
                 sizeof(nodelay))) {
    NaClLog(LOG_WARNING, "Failed to set TCP_NODELAY option.\n");
  }

  struct sockaddr *psaddr = reinterpret_cast<struct sockaddr *>(&saddr);
  if (bind(socket_handle, psaddr, addrlen)) {
    NaClLog(LOG_ERROR, "Failed to bind server.\n");
    return NULL;
  }

  if (listen(socket_handle, 1)) {
    NaClLog(LOG_ERROR, "Failed to listen.\n");
    return NULL;
  }
  return new SocketBinding(socket_handle);
}

ITransport *SocketBinding::AcceptConnection() {
  NaClSocketHandle socket = ::accept(socket_handle_, NULL, 0);
  if (socket != NACL_INVALID_SOCKET)
    return new Transport(socket);
  return NULL;
}

}  // namespace port
<commit_msg>Debug stub: Set NODELAY on NaCl debug TCP socket<commit_after>/*
 * Copyright (c) 2012 The Native Client Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <string>

#include "native_client/src/include/nacl_scoped_ptr.h"
#include "native_client/src/include/portability_sockets.h"
#include "native_client/src/shared/platform/nacl_log.h"
#include "native_client/src/trusted/debug_stub/platform.h"
#include "native_client/src/trusted/debug_stub/transport.h"
#include "native_client/src/trusted/debug_stub/util.h"
#include "native_client/src/trusted/service_runtime/sel_ldr.h"

using gdb_rsp::stringvec;
using gdb_rsp::StringSplit;

namespace port {

typedef int socklen_t;

class Transport : public ITransport {
 public:
  Transport()
    : buf_(new char[kBufSize]),
      pos_(0),
      size_(0) {
    handle_ = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#if NACL_WINDOWS
    CreateSocketEvent();
#endif
  }

  explicit Transport(NaClSocketHandle s)
    : buf_(new char[kBufSize]),
      pos_(0),
      size_(0),
      handle_(s) {
#if NACL_WINDOWS
    CreateSocketEvent();
#endif
  }

  ~Transport() {
    if (handle_ != NACL_INVALID_SOCKET) NaClCloseSocket(handle_);
#if NACL_WINDOWS
    if (!WSACloseEvent(socket_event_)) {
      NaClLog(LOG_FATAL,
              "Transport::~Transport: Failed to close socket event\n");
    }
#endif
  }

#if NACL_WINDOWS
  void CreateSocketEvent() {
    socket_event_ = WSACreateEvent();
    if (socket_event_ == WSA_INVALID_EVENT) {
      NaClLog(LOG_FATAL,
              "Transport::CreateSocketEvent: Failed to create socket event\n");
    }
    if (WSAEventSelect(handle_, socket_event_, FD_READ) == SOCKET_ERROR) {
      NaClLog(LOG_FATAL,
              "Transport::CreateSocketEvent: Failed to bind event to socket\n");
    }
  }
#endif

  // Read from this transport, return true on success.
  virtual bool Read(void *ptr, int32_t len);

  // Write to this transport, return true on success.
  virtual bool Write(const void *ptr, int32_t len);

  // Return true if there is data to read.
  virtual bool IsDataAvailable() {
    if (pos_ < size_) {
      return true;
    }
    fd_set fds;

    FD_ZERO(&fds);
    FD_SET(handle_, &fds);

    // We want a "non-blocking" check
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    // Check if this file handle can select on read
    int cnt = select(static_cast<int>(handle_) + 1, &fds, 0, 0, &timeout);

    // If we are ready, or if there is an error.  We return true
    // on error, to let the next IO request fail.
    if (cnt != 0) return true;

    return false;
  }

  virtual void WaitForDebugStubEvent(struct NaClApp *nap,
                                     bool ignore_input_from_gdb);

// On windows, the header that defines this has other definition
// colitions, so we define it outselves just in case
#ifndef SD_BOTH
#define SD_BOTH 2
#endif

  virtual void Disconnect() {
    // Shutdown the conneciton in both diections.  This should
    // always succeed, and nothing we can do if this fails.
    (void) ::shutdown(handle_, SD_BOTH);
  }

 protected:
  // Copy buffered data to *dst up to len bytes and update dst and len.
  void CopyFromBuffer(char **dst, int32_t *len);

  // Read available data from the socket. Return false on EOF or error.
  bool ReadSomeData();

  static const int kBufSize = 4096;
  nacl::scoped_array<char> buf_;
  int32_t pos_;
  int32_t size_;
  NaClSocketHandle handle_;
#if NACL_WINDOWS
  HANDLE socket_event_;
#endif
};

void Transport::CopyFromBuffer(char **dst, int32_t *len) {
  int32_t copy_bytes = std::min(*len, size_ - pos_);
  memcpy(*dst, buf_.get() + pos_, copy_bytes);
  pos_ += copy_bytes;
  *len -= copy_bytes;
  *dst += copy_bytes;
}

bool Transport::ReadSomeData() {
  while (true) {
    int result = ::recv(handle_, buf_.get() + size_, kBufSize - size_, 0);
    if (result > 0) {
      size_ += result;
      return true;
    }
    if (result == 0)
      return false;
#if NACL_WINDOWS
    // WSAEventSelect sets socket to non-blocking mode. This is essential
    // for socket event notification to work, there is no workaround.
    // See remarks section at the page
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx
    if (NaClSocketGetLastError() == WSAEWOULDBLOCK) {
      if (WaitForSingleObject(socket_event_, INFINITE) == WAIT_FAILED) {
        NaClLog(LOG_FATAL,
                "Transport::ReadSomeData: Failed to wait on socket event\n");
      }
      if (!ResetEvent(socket_event_)) {
        NaClLog(LOG_FATAL,
                "Transport::ReadSomeData: Failed to reset socket event\n");
      }
      continue;
    }
#endif
    if (NaClSocketGetLastError() != EINTR)
      return false;
  }
}

bool Transport::Read(void *ptr, int32_t len) {
  char *dst = static_cast<char *>(ptr);
  if (pos_ < size_) {
    CopyFromBuffer(&dst, &len);
  }
  while (len > 0) {
    pos_ = 0;
    size_ = 0;
    if (!ReadSomeData()) {
      return false;
    }
    CopyFromBuffer(&dst, &len);
  }
  return true;
}

bool Transport::Write(const void *ptr, int32_t len) {
  const char *src = static_cast<const char *>(ptr);
  while (len > 0) {
    int result = ::send(handle_, src, len, 0);
    if (result > 0) {
      src += result;
      len -= result;
      continue;
    }
    if (result == 0) {
      return false;
    }
    if (NaClSocketGetLastError() != EINTR) {
      return false;
    }
  }
  return true;
}

void Transport::WaitForDebugStubEvent(struct NaClApp *nap,
                                      bool ignore_input_from_gdb) {
  bool wait = true;
  // If we are told to ignore messages from gdb, we will exit from this
  // function only if new data is sent by gdb.
  if ((pos_ < size_ && !ignore_input_from_gdb) ||
      nap->faulted_thread_count > 0) {
    // Clear faulted thread events to save debug stub loop iterations.
    wait = false;
  }
#if NACL_WINDOWS
  HANDLE handles[2];
  handles[0] = nap->faulted_thread_event;
  handles[1] = socket_event_;
  int count = size_ < kBufSize ? 2 : 1;
  int result = WaitForMultipleObjects(count, handles, FALSE,
                                      wait ? INFINITE : 0);
  if (result == WAIT_OBJECT_0 + 1) {
    if (!ResetEvent(socket_event_)) {
      NaClLog(LOG_FATAL,
              "Transport::WaitForDebugStubEvent: "
              "Failed to reset socket event\n");
    }
    return;
  }
  if (result == WAIT_TIMEOUT || result == WAIT_OBJECT_0)
    return;
  NaClLog(LOG_FATAL,
          "Transport::WaitForDebugStubEvent: Wait for events failed\n");
#else
  fd_set fds;

  FD_ZERO(&fds);
  FD_SET(nap->faulted_thread_fd_read, &fds);
  int max_fd = nap->faulted_thread_fd_read;
  if (size_ < kBufSize) {
    FD_SET(handle_, &fds);
    max_fd = std::max(max_fd, handle_);
  }

  int ret;
  // We don't need sleep-polling on Linux now, so we set either zero or infinite
  // timeout.
  if (wait) {
    ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
  } else {
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    ret = select(max_fd + 1, &fds, NULL, NULL, &timeout);
  }
  if (ret < 0) {
    NaClLog(LOG_FATAL,
            "Transport::WaitForDebugStubEvent: Failed to wait for "
            "debug stub event\n");
  }

  if (ret > 0) {
    if (FD_ISSET(nap->faulted_thread_fd_read, &fds)) {
      char buf[16];
      if (read(nap->faulted_thread_fd_read, &buf, sizeof(buf)) < 0) {
        NaClLog(LOG_FATAL,
                "Transport::WaitForDebugStubEvent: Failed to read from "
                "debug stub event pipe fd\n");
      }
    }
    if (FD_ISSET(handle_, &fds))
      ReadSomeData();
  }
#endif
}

// Convert string in the form of [addr][:port] where addr is a
// IPv4 address or host name, and port is a 16b tcp/udp port.
// Both portions are optional, and only the portion of the address
// provided is updated.  Values are provided in network order.
static bool StringToIPv4(const std::string &instr, uint32_t *addr,
                         uint16_t *port) {
  // Make a copy so the are unchanged unless we succeed
  uint32_t outaddr = *addr;
  uint16_t outport = *port;

  // Substrings of the full ADDR:PORT
  std::string addrstr;
  std::string portstr;

  // We should either have one or two tokens in the form of:
  //  IP - IP, NUL
  //  IP: -  IP, NUL
  //  :PORT - NUL, PORT
  //  IP:PORT - IP, PORT

  // Search for the port marker
  size_t portoff = instr.find(':');

  // If we found a ":" before the end, get both substrings
  if ((portoff != std::string::npos) && (portoff + 1 < instr.size())) {
    addrstr = instr.substr(0, portoff);
    portstr = instr.substr(portoff + 1, std::string::npos);
  } else {
    // otherwise the entire string is the addr portion.
    addrstr = instr;
    portstr = "";
  }

  // If the address portion was provided, update it
  if (addrstr.size()) {
    // Special case 0.0.0.0 which means any IPv4 interface
    if (addrstr == "0.0.0.0") {
      outaddr = 0;
    } else {
      struct hostent *host = gethostbyname(addrstr.data());

      // Check that we found an IPv4 host
      if ((NULL == host) || (AF_INET != host->h_addrtype)) return false;

      // Make sure the IP list isn't empty.
      if (0 == host->h_addr_list[0]) return false;

      // Use the first address in the array of address pointers.
      uint32_t **addrarray = reinterpret_cast<uint32_t**>(host->h_addr_list);
      outaddr = *addrarray[0];
    }
  }

  // if the port portion was provided, then update it
  if (portstr.size()) {
    int val = atoi(portstr.data());
    if ((val < 0) || (val > 65535)) return false;
    outport = ntohs(static_cast<uint16_t>(val));
  }

  // We haven't failed, so set the values
  *addr = outaddr;
  *port = outport;
  return true;
}

static bool BuildSockAddr(const char *addr, struct sockaddr_in *sockaddr) {
  std::string addrstr = addr;
  uint32_t *pip = reinterpret_cast<uint32_t*>(&sockaddr->sin_addr.s_addr);
  uint16_t *pport = reinterpret_cast<uint16_t*>(&sockaddr->sin_port);

  sockaddr->sin_family = AF_INET;
  return StringToIPv4(addrstr, pip, pport);
}

SocketBinding::SocketBinding(NaClSocketHandle socket_handle)
    : socket_handle_(socket_handle) {
}

SocketBinding *SocketBinding::Bind(const char *addr) {
  NaClSocketHandle socket_handle = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (socket_handle == NACL_INVALID_SOCKET) {
    NaClLog(LOG_ERROR, "Failed to create socket.\n");
    return NULL;
  }
  struct sockaddr_in saddr;
  // Clearing sockaddr_in first appears to be necessary on Mac OS X.
  memset(&saddr, 0, sizeof(saddr));
  socklen_t addrlen = static_cast<socklen_t>(sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = htonl(0x7F000001);
  saddr.sin_port = htons(4014);

  // Override portions address that are provided
  if (addr) BuildSockAddr(addr, &saddr);

  // This is necessary to ensure that the TCP port is released
  // promptly when sel_ldr exits.  Without this, the TCP port might
  // only be released after a timeout, and later processes can fail
  // to bind it.
  int reuse_address = 1;
  if (setsockopt(socket_handle, SOL_SOCKET, SO_REUSEADDR,
                 reinterpret_cast<char *>(&reuse_address),
                 sizeof(reuse_address))) {
    NaClLog(LOG_WARNING, "Failed to set SO_REUSEADDR option.\n");
  }

  struct sockaddr *psaddr = reinterpret_cast<struct sockaddr *>(&saddr);
  if (bind(socket_handle, psaddr, addrlen)) {
    NaClLog(LOG_ERROR, "Failed to bind server.\n");
    return NULL;
  }

  if (listen(socket_handle, 1)) {
    NaClLog(LOG_ERROR, "Failed to listen.\n");
    return NULL;
  }
  return new SocketBinding(socket_handle);
}

ITransport *SocketBinding::AcceptConnection() {
  NaClSocketHandle socket = ::accept(socket_handle_, NULL, 0);
  if (socket != NACL_INVALID_SOCKET) {
    // Do not delay sending small packets.  This significantly speeds up
    // remote debugging.  Debug stub uses buffering to send outgoing packets so
    // they are not split into more TCP packets than necessary.
    int nodelay = 1;
    if (setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
                   reinterpret_cast<char *>(&nodelay),
                   sizeof(nodelay))) {
      NaClLog(LOG_WARNING, "Failed to set TCP_NODELAY option.\n");
    }
    return new Transport(socket);
  }
  return NULL;
}

}  // namespace port
<|endoftext|>