<commit_before>// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// 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.

#include <intrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <windows.h>
#include <winternl.h>

#include <map>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"

// ntstatus.h conflicts with windows.h so define this locally.
#ifndef STATUS_NO_SUCH_FILE
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
#endif

namespace crashpad {

size_t* g_extra_memory_pointer;
size_t* g_extra_memory_not_saved;

namespace {

CRITICAL_SECTION g_test_critical_section;

unsigned char g_test_memory[] = {
  99, 98, 97, 96, 95, 94, 93, 92, 91, 90,
  89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
};

ULONG RtlNtStatusToDosError(NTSTATUS status) {
  static const auto rtl_nt_status_to_dos_error =
      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlNtStatusToDosError);
  return rtl_nt_status_to_dos_error(status);
}

void AllocateMemoryOfVariousProtections() {
  SYSTEM_INFO system_info;
  GetSystemInfo(&system_info);

  const size_t kPageSize = system_info.dwPageSize;

  static constexpr uint32_t kPageTypes[] = {
      PAGE_NOACCESS,
      PAGE_READONLY,
      PAGE_READWRITE,
      PAGE_EXECUTE,
      PAGE_EXECUTE_READ,
      PAGE_EXECUTE_READWRITE,

      // PAGE_NOACCESS is invalid with PAGE_GUARD.
      PAGE_READONLY | PAGE_GUARD,
      PAGE_READWRITE | PAGE_GUARD,
      PAGE_EXECUTE | PAGE_GUARD,
      PAGE_EXECUTE_READ | PAGE_GUARD,
      PAGE_EXECUTE_READWRITE | PAGE_GUARD,
  };

  // All of these allocations are leaked, we want to view them in windbg via
  // !vprot.
  void* reserve = VirtualAlloc(
      nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
  PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
  uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);

  for (size_t i = 0; i < arraysize(kPageTypes); ++i) {
    void* result =
        VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
                     kPageSize,
                     MEM_COMMIT,
                     kPageTypes[i]);
    PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i;
  }
}

DWORD WINAPI NullThreadProc(void* param) {
  return 0;
}

// Creates a suspended background thread, and sets EDI/RDI to point at
// g_test_memory so we can confirm it's available in the minidump.
bool CreateThreadWithRegisterPointingToTestMemory() {
  HANDLE thread = CreateThread(
      nullptr, 0, &NullThreadProc, nullptr, CREATE_SUSPENDED, nullptr);
  if (!thread) {
    PLOG(ERROR) << "CreateThread";
    return false;
  }

  CONTEXT context = {0};
  context.ContextFlags = CONTEXT_INTEGER;
  if (!GetThreadContext(thread, &context)) {
    PLOG(ERROR) << "GetThreadContext";
    return false;
  }
#if defined(ARCH_CPU_X86_64)
  context.Rdi = reinterpret_cast<DWORD64>(g_test_memory);
#elif defined(ARCH_CPU_X86)
  context.Edi = reinterpret_cast<DWORD>(g_test_memory);
#endif
  if (!SetThreadContext(thread, &context)) {
    PLOG(ERROR) << "SetThreadContext";
    return false;
  }

  return true;
}

void SomeCrashyFunction() {
  // SetLastError and NTSTATUS so that we have something to view in !gle in
  // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the
  // LastStatusError of the TEB as a side-effect, and we'll be setting
  // ERROR_FILE_NOT_FOUND for GetLastError().
  SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE));

  volatile int* foo = reinterpret_cast<volatile int*>(7);
  *foo = 42;
}

void AllocateExtraMemoryToBeSaved(
    crashpad::SimpleAddressRangeBag* extra_ranges) {
  constexpr size_t kNumVals = 2000;
  size_t* extra_memory = new size_t[kNumVals];
  g_extra_memory_pointer = extra_memory;
  for (size_t i = 0; i < kNumVals; ++i)
    extra_memory[i] = i * 13 + 2;
  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);
  extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer));
}

void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) {
  // Allocate some extra memory, and then Insert() but also Remove() it so we
  // can confirm it doesn't get saved.
  constexpr size_t kNumVals = 2000;
  size_t* extra_memory = new size_t[kNumVals];
  g_extra_memory_not_saved = extra_memory;
  for (size_t i = 0; i < kNumVals; ++i)
    extra_memory[i] = i * 17 + 7;
  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);
  extra_ranges->Insert(&g_extra_memory_not_saved,
                       sizeof(g_extra_memory_not_saved));

  // We keep the pointer's memory, but remove the pointed-to memory.
  extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumVals);
}

int CrashyMain(int argc, wchar_t* argv[]) {
  CrashpadClient client;

  if (argc == 2) {
    if (!client.SetHandlerIPCPipe(argv[1])) {
      LOG(ERROR) << "SetHandler";
      return EXIT_FAILURE;
    }
  } else if (argc == 3) {
    if (!client.StartHandler(base::FilePath(argv[1]),
                             base::FilePath(argv[2]),
                             base::FilePath(),
                             std::string(),
                             std::map<std::string, std::string>(),
                             std::vector<std::string>(),
                             false,
                             true)) {
      LOG(ERROR) << "StartHandler";
      return EXIT_FAILURE;
    }
  } else {
    fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]);
    fprintf(stderr, "       %ls <handler_path> <database_path>\n", argv[0]);
    return EXIT_FAILURE;
  }

  crashpad::SimpleAddressRangeBag* extra_ranges =
      new crashpad::SimpleAddressRangeBag();
  crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
      extra_ranges);
  AllocateExtraMemoryToBeSaved(extra_ranges);
  AllocateExtraUnsavedMemory(extra_ranges);

  // Load and unload some uncommonly used modules so we can see them in the list
  // reported by `lm`. At least two so that we confirm we got the element size
  // advancement of RTL_UNLOAD_EVENT_TRACE correct.
  CHECK(GetModuleHandle(L"lz32.dll") == nullptr);
  CHECK(GetModuleHandle(L"wmerror.dll") == nullptr);
  HMODULE lz32 = LoadLibrary(L"lz32.dll");
  HMODULE wmerror = LoadLibrary(L"wmerror.dll");
  FreeLibrary(lz32);
  FreeLibrary(wmerror);

  // Make sure data pointed to by the stack is captured.
  constexpr int kDataSize = 512;
  int* pointed_to_data = new int[kDataSize];
  for (int i = 0; i < kDataSize; ++i)
    pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);
  int* offset_pointer = &pointed_to_data[128];
  // Encourage the compiler to keep this variable around.
  printf("%p, %p\n", offset_pointer, &offset_pointer);

  crashpad::CrashpadInfo::GetCrashpadInfo()
      ->set_gather_indirectly_referenced_memory(
          TriState::kEnabled, std::numeric_limits<uint32_t>::max());

  std::vector<uint8_t> data_stream1(128, 'x');
  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
      222222,
      reinterpret_cast<const void*>(data_stream1.data()),
      data_stream1.size());

  std::vector<uint8_t> data_stream2(4096, 'z');
  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
      333333,
      reinterpret_cast<const void*>(data_stream2.data()),
      data_stream2.size());

  AllocateMemoryOfVariousProtections();

  if (InitializeCriticalSectionWithDebugInfoIfPossible(
          &g_test_critical_section)) {
    EnterCriticalSection(&g_test_critical_section);
  }

  if (!CreateThreadWithRegisterPointingToTestMemory())
    return EXIT_FAILURE;

  SomeCrashyFunction();

  return EXIT_SUCCESS;
}

}  // namespace
}  // namespace crashpad

int wmain(int argc, wchar_t* argv[]) {
  return crashpad::CrashyMain(argc, argv);
}
<commit_msg>win: fix crashy test program<commit_after>// Copyright 2015 The Crashpad Authors. All rights reserved.
//
// 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.

#include <intrin.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <windows.h>
#include <winternl.h>

#include <map>
#include <string>
#include <type_traits>
#include <vector>

#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "client/crashpad_client.h"
#include "client/crashpad_info.h"
#include "util/win/critical_section_with_debug_info.h"
#include "util/win/get_function.h"

// ntstatus.h conflicts with windows.h so define this locally.
#ifndef STATUS_NO_SUCH_FILE
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
#endif

namespace crashpad {

uint32_t* g_extra_memory_pointer;
uint32_t* g_extra_memory_not_saved;

namespace {

CRITICAL_SECTION g_test_critical_section;

unsigned char g_test_memory[] = {
  99, 98, 97, 96, 95, 94, 93, 92, 91, 90,
  89, 88, 87, 86, 85, 84, 83, 82, 81, 80,
};

ULONG RtlNtStatusToDosError(NTSTATUS status) {
  static const auto rtl_nt_status_to_dos_error =
      GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlNtStatusToDosError);
  return rtl_nt_status_to_dos_error(status);
}

void AllocateMemoryOfVariousProtections() {
  SYSTEM_INFO system_info;
  GetSystemInfo(&system_info);

  const size_t kPageSize = system_info.dwPageSize;

  static constexpr uint32_t kPageTypes[] = {
      PAGE_NOACCESS,
      PAGE_READONLY,
      PAGE_READWRITE,
      PAGE_EXECUTE,
      PAGE_EXECUTE_READ,
      PAGE_EXECUTE_READWRITE,

      // PAGE_NOACCESS is invalid with PAGE_GUARD.
      PAGE_READONLY | PAGE_GUARD,
      PAGE_READWRITE | PAGE_GUARD,
      PAGE_EXECUTE | PAGE_GUARD,
      PAGE_EXECUTE_READ | PAGE_GUARD,
      PAGE_EXECUTE_READWRITE | PAGE_GUARD,
  };

  // All of these allocations are leaked, we want to view them in windbg via
  // !vprot.
  void* reserve = VirtualAlloc(
      nullptr, arraysize(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE);
  PCHECK(reserve) << "VirtualAlloc MEM_RESERVE";
  uintptr_t reserve_as_int = reinterpret_cast<uintptr_t>(reserve);

  for (size_t i = 0; i < arraysize(kPageTypes); ++i) {
    void* result =
        VirtualAlloc(reinterpret_cast<void*>(reserve_as_int + (kPageSize * i)),
                     kPageSize,
                     MEM_COMMIT,
                     kPageTypes[i]);
    PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i;
  }
}

DWORD WINAPI NullThreadProc(void* param) {
  return 0;
}

// Creates a suspended background thread, and sets EDI/RDI to point at
// g_test_memory so we can confirm it's available in the minidump.
bool CreateThreadWithRegisterPointingToTestMemory() {
  HANDLE thread = CreateThread(
      nullptr, 0, &NullThreadProc, nullptr, CREATE_SUSPENDED, nullptr);
  if (!thread) {
    PLOG(ERROR) << "CreateThread";
    return false;
  }

  CONTEXT context = {0};
  context.ContextFlags = CONTEXT_INTEGER;
  if (!GetThreadContext(thread, &context)) {
    PLOG(ERROR) << "GetThreadContext";
    return false;
  }
#if defined(ARCH_CPU_X86_64)
  context.Rdi = reinterpret_cast<DWORD64>(g_test_memory);
#elif defined(ARCH_CPU_X86)
  context.Edi = reinterpret_cast<DWORD>(g_test_memory);
#endif
  if (!SetThreadContext(thread, &context)) {
    PLOG(ERROR) << "SetThreadContext";
    return false;
  }

  return true;
}

void SomeCrashyFunction() {
  // SetLastError and NTSTATUS so that we have something to view in !gle in
  // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the
  // LastStatusError of the TEB as a side-effect, and we'll be setting
  // ERROR_FILE_NOT_FOUND for GetLastError().
  SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE));

  volatile int* foo = reinterpret_cast<volatile int*>(7);
  *foo = 42;
}

void AllocateExtraMemoryToBeSaved(
    crashpad::SimpleAddressRangeBag* extra_ranges) {
  constexpr size_t kNumVals = 2000;
  auto extra_memory = new uint32_t[kNumVals];
  g_extra_memory_pointer = extra_memory;
  for (size_t i = 0; i < kNumVals; ++i)
    extra_memory[i] =
        static_cast<std::remove_reference<decltype(extra_memory[0])>::type>(
            i * 13 + 2);
  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);
  extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer));
}

void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) {
  // Allocate some extra memory, and then Insert() but also Remove() it so we
  // can confirm it doesn't get saved.
  constexpr size_t kNumVals = 2000;
  auto extra_memory = new uint32_t[kNumVals];
  g_extra_memory_not_saved = extra_memory;
  for (size_t i = 0; i < kNumVals; ++i)
    extra_memory[i] =
        static_cast<std::remove_reference<decltype(extra_memory[0])>::type>(
            i * 17 + 7);
  extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals);
  extra_ranges->Insert(&g_extra_memory_not_saved,
                       sizeof(g_extra_memory_not_saved));

  // We keep the pointer's memory, but remove the pointed-to memory.
  extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumVals);
}

int CrashyMain(int argc, wchar_t* argv[]) {
  CrashpadClient client;

  if (argc == 2) {
    if (!client.SetHandlerIPCPipe(argv[1])) {
      LOG(ERROR) << "SetHandler";
      return EXIT_FAILURE;
    }
  } else if (argc == 3) {
    if (!client.StartHandler(base::FilePath(argv[1]),
                             base::FilePath(argv[2]),
                             base::FilePath(),
                             std::string(),
                             std::map<std::string, std::string>(),
                             std::vector<std::string>(),
                             false,
                             true)) {
      LOG(ERROR) << "StartHandler";
      return EXIT_FAILURE;
    }
  } else {
    fprintf(stderr, "Usage: %ls <server_pipe_name>\n", argv[0]);
    fprintf(stderr, "       %ls <handler_path> <database_path>\n", argv[0]);
    return EXIT_FAILURE;
  }

  crashpad::SimpleAddressRangeBag* extra_ranges =
      new crashpad::SimpleAddressRangeBag();
  crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges(
      extra_ranges);
  AllocateExtraMemoryToBeSaved(extra_ranges);
  AllocateExtraUnsavedMemory(extra_ranges);

  // Load and unload some uncommonly used modules so we can see them in the list
  // reported by `lm`. At least two so that we confirm we got the element size
  // advancement of RTL_UNLOAD_EVENT_TRACE correct.
  CHECK(GetModuleHandle(L"lz32.dll") == nullptr);
  CHECK(GetModuleHandle(L"wmerror.dll") == nullptr);
  HMODULE lz32 = LoadLibrary(L"lz32.dll");
  HMODULE wmerror = LoadLibrary(L"wmerror.dll");
  FreeLibrary(lz32);
  FreeLibrary(wmerror);

  // Make sure data pointed to by the stack is captured.
  constexpr int kDataSize = 512;
  int* pointed_to_data = new int[kDataSize];
  for (int i = 0; i < kDataSize; ++i)
    pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0);
  int* offset_pointer = &pointed_to_data[128];
  // Encourage the compiler to keep this variable around.
  printf("%p, %p\n", offset_pointer, &offset_pointer);

  crashpad::CrashpadInfo::GetCrashpadInfo()
      ->set_gather_indirectly_referenced_memory(
          TriState::kEnabled, std::numeric_limits<uint32_t>::max());

  std::vector<uint8_t> data_stream1(128, 'x');
  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
      222222,
      reinterpret_cast<const void*>(data_stream1.data()),
      data_stream1.size());

  std::vector<uint8_t> data_stream2(4096, 'z');
  crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream(
      333333,
      reinterpret_cast<const void*>(data_stream2.data()),
      data_stream2.size());

  AllocateMemoryOfVariousProtections();

  if (InitializeCriticalSectionWithDebugInfoIfPossible(
          &g_test_critical_section)) {
    EnterCriticalSection(&g_test_critical_section);
  }

  if (!CreateThreadWithRegisterPointingToTestMemory())
    return EXIT_FAILURE;

  SomeCrashyFunction();

  return EXIT_SUCCESS;
}

}  // namespace
}  // namespace crashpad

int wmain(int argc, wchar_t* argv[]) {
  return crashpad::CrashyMain(argc, argv);
}
<|endoftext|>


<commit_before>// BSD 2-Clause License, see github.com/ma16/rpio

#include "Error.h"
#include "Signaling.h"

using namespace Protocol::OneWire::Bang ;

// defect:
// https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=192908
#define DEFECT_D1 1
// don't disable interrupts anymore since the problem does no longer
// occur.

bool Signaling::init()
{
    //  t0 t1 t2 t3 t4 t5 t6 t7
    // ---+     +-----+     +----...
    //    |     |     |     |
    //    +-----+     +-----+

    // Reset-Pulse
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    // ...assumes the configured output level is Low
    // ...note, errors must not be thrown as long as Out or Events enabled
    this->master->io.sleep(this->master->timing.resetPulse_min) ;
#if DEFECT_D1
    auto v = this->master->intr.status() ;
    this->master->intr.disable(v) ;
#endif
    this->master->io.detect(this->master->pin,Rpi::Gpio::Event::Fall) ;
    // [defect] The event status flag gets immediately* raised regardless
    // whether Fall or AsyncFall is used.
    // (*) mostly in the immediate query, or the query thereafter;
    // however not always (not exactly reproducible). Still, with
    // AsyncFall mostly in the immediate query (and not later).
    this->master->io.events(this->master->mask) ;
    auto t2 = this->master->io.recent() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    auto t3 = this->master->io.time() ;
    // ...if we got suspended, t3 and following time-stamps may lay
    // even behind the end of the Presence-Pulse (if there was any)
    auto isPresent = 0 !=
	this->master->io.waitForEvent(t3,
				      this->master->timing.presenceIdle_max,
				      this->master->mask) ;
    auto t4 = this->master->io.recent() ;
    auto t5 = this->master->io.time() ;
    this->master->io.detect(this->master->pin,Rpi::Gpio::Event::Fall,false) ;
    this->master->io.events(this->master->mask) ; // reset late events
#if DEFECT_D1
    this->master->intr.enable(v) ;
#endif
    if (isPresent)
    {
	if (t4 == t3)
	    throw Error(Error::Type::Retry,__LINE__) ;
	if (t5 - t2 < this->master->timing.presenceIdle_min)
	    throw Error(Error::Type::Timing,__LINE__) ;

	// rx: wait for LH-edge (end of Presence-Pulse)
	this->master->io.waitForLevel(t5,
				      this->master->timing.presencePulse_max,
				      this->master->mask,
				      /*High*/this->master->mask) ;
	auto t7 = this->master->io.recent() ;
	if (t7 - t4 < this->master->timing.presencePulse_min)
	    throw Error(Error::Type::Timing,__LINE__) ;
    }

    // end of init. sequence
    this->master->io.wait(t3,this->master->timing.presenceFrame_max) ;

    return isPresent ;
}

bool Signaling::read(bool busy)
{
    //  t0 t1 t2 t3 t4 t5
    // ---+     +-----+---
    //    |     |     |
    //    +-----+-----+

    // initiate Read-Time-Slot
    auto t0 = this->master->io.time() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    auto t1 = this->master->io.time() ;
    this->master->io.wait(t1,this->master->timing.init_min) ;
    // the device keeps holding the wire low in order to "send" a
    // 0-bit; there is no LH-HL gap as long as the pulse to initiate
    // the Read-Time-Slot is long enough
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    this->master->io.sleep(this->master->timing.rc_max) ;
    auto sample_t3 = 0 != (this->master->mask & this->master->io.levels()) ;
    auto t3 = this->master->io.time() ;
    // ...if we got suspended, t3 and following time-stamps may lay
    // far behind the end of the 0-bit Pulse (if there was any)
    if (busy && t3-t0 > this->master->timing.init_max)
	throw Error(Error::Type::Reset,__LINE__) ;
    if (t3 - t0 > this->master->timing.rdv_min)
	throw Error(Error::Type::Retry,__LINE__) ;

    // wait for LH edge (depending on the time we got a 1-bit or 0-bit)
    this->master->io.waitForLevel(t1,
				  this->master->timing.slot_max,
				  this->master->mask,
				  /*High*/this->master->mask) ;
    auto t5 = this->master->io.recent() ;
    // minimum idle time after LH edge
    this->master->io.wait(t5,this->master->timing.rec_min) ;
    auto timeout = t5 - t1 >= this->master->timing.slot_max ;
    if (timeout)
	throw Error(Error::Type::Retry,__LINE__) ;
    auto sample_rdv = (t5 - t1) <= this->master->timing.rdv_min ;
    if (sample_t3 != sample_rdv)
	throw Error(Error::Type::Retry,__LINE__) ;
    // wait for end of time-slot period
    this->master->io.wait(t1,this->master->timing.slot_min) ;
    return sample_t3 ;
}

void Signaling::write(bool bit)
{
    //  t0 t1 t2 t3
    // ---+     +---
    //    |     |
    //    +-----+

    // tx: short or long Bit Pulse
    auto t0 = this->master->io.time() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    auto t1 = this->master->io.time() ;
    auto min = bit
	? this->master->timing.write_1_min
	: this->master->timing.write_0_min ;
    this->master->io.wait(t1,min) ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    auto t3 = this->master->io.time() ;
    auto max = bit
	? this->master->timing.write_1_max
	: this->master->timing.write_0_max ;
    if (t3 - t0 > max)
	throw Error(Error::Type::Retry,__LINE__) ;
    // wait for end of time-slot period
    this->master->io.wait(t1,this->master->timing.slot_min) ;
}
<commit_msg>make a note for improvements<commit_after>// BSD 2-Clause License, see github.com/ma16/rpio

#include "Error.h"
#include "Signaling.h"

using namespace Protocol::OneWire::Bang ;

// defect:
// https://www.raspberrypi.org/forums/viewtopic.php?f=66&t=192908
#define DEFECT_D1 1
// don't disable interrupts anymore since the problem does no longer
// occur.

bool Signaling::init()
{
    //  t0 t1 t2 t3 t4 t5 t6 t7
    // ---+     +-----+     +----...
    //    |     |     |     |
    //    +-----+     +-----+

    // Reset-Pulse
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    // ...assumes the configured output level is Low
    // ...note, errors must not be thrown as long as Out or Events enabled
    this->master->io.sleep(this->master->timing.resetPulse_min) ;
    // ...[todo] this may be a good time to voluntary suspend the thread
#if DEFECT_D1
    auto v = this->master->intr.status() ;
    this->master->intr.disable(v) ;
#endif
    this->master->io.detect(this->master->pin,Rpi::Gpio::Event::Fall) ;
    // [defect] The event status flag gets immediately* raised regardless
    // whether Fall or AsyncFall is used.
    // (*) mostly in the immediate query, or the query thereafter;
    // however not always (not exactly reproducible). Still, with
    // AsyncFall mostly in the immediate query (and not later).
    this->master->io.events(this->master->mask) ;
    auto t2 = this->master->io.recent() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    auto t3 = this->master->io.time() ;
    // ...if we got suspended, t3 and following time-stamps may lay
    // even behind the end of the Presence-Pulse (if there was any)
    auto isPresent = 0 !=
	this->master->io.waitForEvent(t3,
				      this->master->timing.presenceIdle_max,
				      this->master->mask) ;
    auto t4 = this->master->io.recent() ;
    auto t5 = this->master->io.time() ;
    this->master->io.detect(this->master->pin,Rpi::Gpio::Event::Fall,false) ;
    this->master->io.events(this->master->mask) ; // reset late events
#if DEFECT_D1
    this->master->intr.enable(v) ;
#endif
    if (isPresent)
    {
	if (t4 == t3)
	    throw Error(Error::Type::Retry,__LINE__) ;
	if (t5 - t2 < this->master->timing.presenceIdle_min)
	    throw Error(Error::Type::Timing,__LINE__) ;

	// rx: wait for LH-edge (end of Presence-Pulse)
	this->master->io.waitForLevel(t5,
				      this->master->timing.presencePulse_max,
				      this->master->mask,
				      /*High*/this->master->mask) ;
	auto t7 = this->master->io.recent() ;
	if (t7 - t4 < this->master->timing.presencePulse_min)
	    throw Error(Error::Type::Timing,__LINE__) ;
    }

    // end of init. sequence
    this->master->io.wait(t3,this->master->timing.presenceFrame_max) ;

    return isPresent ;
}

bool Signaling::read(bool busy)
{
    //  t0 t1 t2 t3 t4 t5
    // ---+     +-----+---
    //    |     |     |
    //    +-----+-----+

    // initiate Read-Time-Slot
    auto t0 = this->master->io.time() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    auto t1 = this->master->io.time() ;
    this->master->io.wait(t1,this->master->timing.init_min) ;
    // the device keeps holding the wire low in order to "send" a
    // 0-bit; there is no LH-HL gap as long as the pulse to initiate
    // the Read-Time-Slot is long enough
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    this->master->io.sleep(this->master->timing.rc_max) ;
    auto sample_t3 = 0 != (this->master->mask & this->master->io.levels()) ;
    auto t3 = this->master->io.time() ;
    // ...if we got suspended, t3 and following time-stamps may lay
    // far behind the end of the 0-bit Pulse (if there was any)
    if (busy && t3-t0 > this->master->timing.init_max)
	throw Error(Error::Type::Reset,__LINE__) ;
    if (t3 - t0 > this->master->timing.rdv_min)
	throw Error(Error::Type::Retry,__LINE__) ;

    // wait for LH edge (depending on the time we got a 1-bit or 0-bit)
    this->master->io.waitForLevel(t1,
				  this->master->timing.slot_max,
				  this->master->mask,
				  /*High*/this->master->mask) ;
    auto t5 = this->master->io.recent() ;
    // minimum idle time after LH edge
    this->master->io.wait(t5,this->master->timing.rec_min) ;
    auto timeout = t5 - t1 >= this->master->timing.slot_max ;
    if (timeout)
	throw Error(Error::Type::Retry,__LINE__) ;
    auto sample_rdv = (t5 - t1) <= this->master->timing.rdv_min ;
    if (sample_t3 != sample_rdv)
	throw Error(Error::Type::Retry,__LINE__) ;
    // wait for end of time-slot period
    this->master->io.wait(t1,this->master->timing.slot_min) ;
    return sample_t3 ;
}

void Signaling::write(bool bit)
{
    //  t0 t1 t2 t3
    // ---+     +---
    //    |     |
    //    +-----+

    // tx: short or long Bit Pulse
    auto t0 = this->master->io.time() ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::Out) ;
    auto t1 = this->master->io.time() ;
    auto min = bit
	? this->master->timing.write_1_min
	: this->master->timing.write_0_min ;
    this->master->io.wait(t1,min) ;
    this->master->io.mode(this->master->pin,Rpi::Gpio::Mode::In) ;
    auto t3 = this->master->io.time() ;
    auto max = bit
	? this->master->timing.write_1_max
	: this->master->timing.write_0_max ;
    if (t3 - t0 > max)
	throw Error(Error::Type::Retry,__LINE__) ;
    // wait for end of time-slot period
    this->master->io.wait(t1,this->master->timing.slot_min) ;
}
<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/

#define EIGEN_USE_THREADS

#include <deque>
#include <utility>

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/core/framework/resource_mgr.h"
#include "tensorflow/core/framework/variant.h"
#include "tensorflow/core/framework/variant_encode_decode.h"
#include "tensorflow/core/kernels/ops_util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/types.h"

namespace tensorflow {

namespace {

class Mutex : public ResourceBase {
 public:
  explicit Mutex(OpKernelContext* c, const string& name)
      : locked_(false),
        thread_pool_(new thread::ThreadPool(
            c->env(), ThreadOptions(),
            strings::StrCat("mutex_lock_thread_", SanitizeThreadSuffix(name)),
            1 /* num_threads */, false /* low_latency_hint */)),
        name_(name) {
    VLOG(2) << "Creating mutex with name " << name << ": " << this;
  }

  string DebugString() override { return strings::StrCat("Mutex ", name_); }

  class LockReleaser {
   public:
    explicit LockReleaser(Mutex* mutex) : mutex_(mutex) {}

    LockReleaser(const LockReleaser&) = delete;
    LockReleaser& operator=(const LockReleaser&) = delete;

    virtual ~LockReleaser() {
      VLOG(3) << "Destroying LockReleaser " << this << " for mutex: " << mutex_;
      if (mutex_) {
        mutex_lock lock(mutex_->mu_);
        mutex_->locked_ = false;
        mutex_->cv_.notify_all();
        VLOG(3) << "Destroying LockReleaser " << this
                << ": sent notifications.";
      }
    }

   private:
    Mutex* mutex_;
  };

  struct SharedLockReleaser {
    std::shared_ptr<LockReleaser> shared_lock;

    explicit SharedLockReleaser(std::shared_ptr<LockReleaser>&& lock)
        : shared_lock(std::forward<decltype(lock)>(lock)) {
      VLOG(3) << "Creating shared_ptr of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    SharedLockReleaser(SharedLockReleaser&& rhs)
        : shared_lock(std::move(rhs.shared_lock)) {
      VLOG(3) << "Moving SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    SharedLockReleaser(const SharedLockReleaser& rhs)
        : shared_lock(rhs.shared_lock) {
      VLOG(3) << "Copying SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    ~SharedLockReleaser() {
      VLOG(3) << "Destroying SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    void Encode(VariantTensorData*) const {
      // Not supported.
    }

    bool Decode(const VariantTensorData&) {
      return false;  // Not supported.
    }
  };

  void AcquireAsync(
      OpKernelContext* c,
      std::function<void(const Status& s, SharedLockReleaser lock)> fn) {
    CancellationManager* cm = c->cancellation_manager();
    CancellationToken token{};
    bool* cancelled = nullptr;
    if (cm) {
      cancelled = new bool(false);  // GUARDED_BY(mu_);
      token = cm->get_cancellation_token();
      const bool already_cancelled =
          !cm->RegisterCallback(token, [this, cancelled]() {
            mutex_lock lock(mu_);
            *cancelled = true;
            cv_.notify_all();
          });
      if (already_cancelled) {
        delete cancelled;
        fn(errors::Cancelled("Lock acquisition cancelled."),
           SharedLockReleaser{nullptr});
        return;
      }
    }
    thread_pool_->Schedule(std::bind(
        [this, c, cm, cancelled,
         token](std::function<void(const Status& s, SharedLockReleaser&& lock)>
                    fn_) {
          bool local_locked;
          {
            mutex_lock lock(mu_);
            while (locked_ && !(cancelled && *cancelled)) {
              cv_.wait(lock);
            }
            local_locked = locked_ = !(cancelled && *cancelled);
          }
          if (cm) {
            cm->DeregisterCallback(token);
            delete cancelled;
          }
          if (local_locked) {  // Not cancelled.
            fn_(Status::OK(),
                SharedLockReleaser{std::make_shared<LockReleaser>(this)});
          } else {
            fn_(errors::Cancelled("Lock acqusition cancelled."),
                SharedLockReleaser{nullptr});
          }
        },
        std::move(fn)));
  }

 private:
  mutex mu_;
  condition_variable cv_ GUARDED_BY(mu_);
  bool locked_ GUARDED_BY(mu_);
  std::unique_ptr<thread::ThreadPool> thread_pool_;
  string name_;
};

}  // namespace

class MutexLockOp : public AsyncOpKernel {
 public:
  explicit MutexLockOp(OpKernelConstruction* c) : AsyncOpKernel(c) {}

 public:
  void ComputeAsync(OpKernelContext* c, DoneCallback done) override {
    Mutex* mutex = nullptr;
    OP_REQUIRES_OK_ASYNC(
        c,
        LookupOrCreateResource<Mutex>(c, HandleFromInput(c, 0), &mutex,
                                      [this, c](Mutex** ptr) {
                                        *ptr = new Mutex(
                                            c, HandleFromInput(c, 0).name());
                                        return Status::OK();
                                      }),
        done);

    Tensor* variant;
    OP_REQUIRES_OK_ASYNC(c, c->allocate_output(0, TensorShape({}), &variant),
                         done);

    mutex->AcquireAsync(
        c, std::bind(
               [this, c, variant, mutex](DoneCallback done_,
                                         // End of bound arguments.
                                         const Status& s,
                                         Mutex::SharedLockReleaser&& lock) {
                 core::ScopedUnref unref(mutex);
                 VLOG(2) << "Finished locking mutex " << mutex
                         << " with lock: " << lock.shared_lock.get()
                         << " status: " << s.ToString();
                 if (s.ok()) {
                   variant->scalar<Variant>()() = std::move(lock);
                 } else {
                   c->SetStatus(s);
                 }
                 done_();
               },
               std::move(done), std::placeholders::_1, std::placeholders::_2));
  }
};

class ConsumeMutexLockOp : public OpKernel {
 public:
  explicit ConsumeMutexLockOp(OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(OpKernelContext* c) override {
    VLOG(2) << "Executing ConsumeMutexLockOp";
    const Tensor& lock_t = c->input(0);
    OP_REQUIRES(
        c, lock_t.dims() == 0,
        errors::InvalidArgument("Expected input to be a scalar, saw shape: ",
                                lock_t.shape().DebugString()));
    OP_REQUIRES(
        c, lock_t.dtype() == DT_VARIANT,
        errors::InvalidArgument("Expected input to be a variant, saw type: ",
                                DataTypeString(lock_t.dtype())));
    const auto* lock =
        lock_t.scalar<Variant>()().get<Mutex::SharedLockReleaser>();
    OP_REQUIRES(c, lock,
                errors::InvalidArgument(
                    "Expected input to contain a SharedLockReleaser "
                    "object, but saw variant: '",
                    lock_t.scalar<Variant>()().DebugString(), "'"));
    const int use_count = lock->shared_lock.use_count();
    OP_REQUIRES(
        c, use_count == 1,
        errors::InvalidArgument("Expected use count of lock to be 1, but saw: ",
                                use_count));
  }

  bool IsExpensive() override { return false; }
};

REGISTER_KERNEL_BUILDER(Name("MutexLock").Device(DEVICE_CPU), MutexLockOp);

REGISTER_KERNEL_BUILDER(Name("MutexV2").Device(DEVICE_CPU),
                        ResourceHandleOp<Mutex>);

REGISTER_KERNEL_BUILDER(Name("ConsumeMutexLock").Device(DEVICE_CPU),
                        ConsumeMutexLockOp);

}  // namespace tensorflow
<commit_msg>[TF CriticalSection] Bugfix: deref the Mutex before calling done_()<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/

#define EIGEN_USE_THREADS

#include <deque>
#include <utility>

#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/core/framework/resource_mgr.h"
#include "tensorflow/core/framework/variant.h"
#include "tensorflow/core/framework/variant_encode_decode.h"
#include "tensorflow/core/kernels/ops_util.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/types.h"

namespace tensorflow {

namespace {

class Mutex : public ResourceBase {
 public:
  explicit Mutex(OpKernelContext* c, const string& name)
      : locked_(false),
        thread_pool_(new thread::ThreadPool(
            c->env(), ThreadOptions(),
            strings::StrCat("mutex_lock_thread_", SanitizeThreadSuffix(name)),
            1 /* num_threads */, false /* low_latency_hint */)),
        name_(name) {
    VLOG(2) << "Creating mutex with name " << name << ": " << this;
  }

  string DebugString() override { return strings::StrCat("Mutex ", name_); }

  class LockReleaser {
   public:
    explicit LockReleaser(Mutex* mutex) : mutex_(mutex) {}

    LockReleaser(const LockReleaser&) = delete;
    LockReleaser& operator=(const LockReleaser&) = delete;

    virtual ~LockReleaser() {
      VLOG(3) << "Destroying LockReleaser " << this << " for mutex: " << mutex_;
      if (mutex_) {
        mutex_lock lock(mutex_->mu_);
        mutex_->locked_ = false;
        mutex_->cv_.notify_all();
        VLOG(3) << "Destroying LockReleaser " << this
                << ": sent notifications.";
      }
    }

   private:
    Mutex* mutex_;
  };

  struct SharedLockReleaser {
    std::shared_ptr<LockReleaser> shared_lock;

    explicit SharedLockReleaser(std::shared_ptr<LockReleaser>&& lock)
        : shared_lock(std::forward<decltype(lock)>(lock)) {
      VLOG(3) << "Creating shared_ptr of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    SharedLockReleaser(SharedLockReleaser&& rhs)
        : shared_lock(std::move(rhs.shared_lock)) {
      VLOG(3) << "Moving SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    SharedLockReleaser(const SharedLockReleaser& rhs)
        : shared_lock(rhs.shared_lock) {
      VLOG(3) << "Copying SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    ~SharedLockReleaser() {
      VLOG(3) << "Destroying SharedLockReleaser of " << shared_lock.get()
              << " count is: " << shared_lock.use_count();
    }

    void Encode(VariantTensorData*) const {
      // Not supported.
    }

    bool Decode(const VariantTensorData&) {
      return false;  // Not supported.
    }
  };

  void AcquireAsync(
      OpKernelContext* c,
      std::function<void(const Status& s, SharedLockReleaser lock)> fn) {
    CancellationManager* cm = c->cancellation_manager();
    CancellationToken token{};
    bool* cancelled = nullptr;
    if (cm) {
      cancelled = new bool(false);  // GUARDED_BY(mu_);
      token = cm->get_cancellation_token();
      const bool already_cancelled =
          !cm->RegisterCallback(token, [this, cancelled]() {
            mutex_lock lock(mu_);
            *cancelled = true;
            cv_.notify_all();
          });
      if (already_cancelled) {
        delete cancelled;
        fn(errors::Cancelled("Lock acquisition cancelled."),
           SharedLockReleaser{nullptr});
        return;
      }
    }
    thread_pool_->Schedule(std::bind(
        [this, c, cm, cancelled,
         token](std::function<void(const Status& s, SharedLockReleaser&& lock)>
                    fn_) {
          bool local_locked;
          {
            mutex_lock lock(mu_);
            while (locked_ && !(cancelled && *cancelled)) {
              cv_.wait(lock);
            }
            local_locked = locked_ = !(cancelled && *cancelled);
          }
          if (cm) {
            cm->DeregisterCallback(token);
            delete cancelled;
          }
          if (local_locked) {  // Not cancelled.
            fn_(Status::OK(),
                SharedLockReleaser{std::make_shared<LockReleaser>(this)});
          } else {
            fn_(errors::Cancelled("Lock acqusition cancelled."),
                SharedLockReleaser{nullptr});
          }
        },
        std::move(fn)));
  }

 private:
  mutex mu_;
  condition_variable cv_ GUARDED_BY(mu_);
  bool locked_ GUARDED_BY(mu_);
  std::unique_ptr<thread::ThreadPool> thread_pool_;
  string name_;
};

}  // namespace

class MutexLockOp : public AsyncOpKernel {
 public:
  explicit MutexLockOp(OpKernelConstruction* c) : AsyncOpKernel(c) {}

 public:
  void ComputeAsync(OpKernelContext* c, DoneCallback done) override {
    Mutex* mutex = nullptr;
    OP_REQUIRES_OK_ASYNC(
        c,
        LookupOrCreateResource<Mutex>(c, HandleFromInput(c, 0), &mutex,
                                      [this, c](Mutex** ptr) {
                                        *ptr = new Mutex(
                                            c, HandleFromInput(c, 0).name());
                                        return Status::OK();
                                      }),
        done);

    Tensor* variant;
    OP_REQUIRES_OK_ASYNC(c, c->allocate_output(0, TensorShape({}), &variant),
                         done);

    mutex->AcquireAsync(
        c, std::bind(
               [this, c, variant, mutex](DoneCallback done_,
                                         // End of bound arguments.
                                         const Status& s,
                                         Mutex::SharedLockReleaser&& lock) {
                 VLOG(2) << "Finished locking mutex " << mutex
                         << " with lock: " << lock.shared_lock.get()
                         << " status: " << s.ToString();
                 if (s.ok()) {
                   variant->scalar<Variant>()() = std::move(lock);
                 } else {
                   c->SetStatus(s);
                 }
                 mutex->Unref();
                 done_();
               },
               std::move(done), std::placeholders::_1, std::placeholders::_2));
  }
};

class ConsumeMutexLockOp : public OpKernel {
 public:
  explicit ConsumeMutexLockOp(OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(OpKernelContext* c) override {
    VLOG(2) << "Executing ConsumeMutexLockOp";
    const Tensor& lock_t = c->input(0);
    OP_REQUIRES(
        c, lock_t.dims() == 0,
        errors::InvalidArgument("Expected input to be a scalar, saw shape: ",
                                lock_t.shape().DebugString()));
    OP_REQUIRES(
        c, lock_t.dtype() == DT_VARIANT,
        errors::InvalidArgument("Expected input to be a variant, saw type: ",
                                DataTypeString(lock_t.dtype())));
    const auto* lock =
        lock_t.scalar<Variant>()().get<Mutex::SharedLockReleaser>();
    OP_REQUIRES(c, lock,
                errors::InvalidArgument(
                    "Expected input to contain a SharedLockReleaser "
                    "object, but saw variant: '",
                    lock_t.scalar<Variant>()().DebugString(), "'"));
    const int use_count = lock->shared_lock.use_count();
    OP_REQUIRES(
        c, use_count == 1,
        errors::InvalidArgument("Expected use count of lock to be 1, but saw: ",
                                use_count));
  }

  bool IsExpensive() override { return false; }
};

REGISTER_KERNEL_BUILDER(Name("MutexLock").Device(DEVICE_CPU), MutexLockOp);

REGISTER_KERNEL_BUILDER(Name("MutexV2").Device(DEVICE_CPU),
                        ResourceHandleOp<Mutex>);

REGISTER_KERNEL_BUILDER(Name("ConsumeMutexLock").Device(DEVICE_CPU),
                        ConsumeMutexLockOp);

}  // namespace tensorflow
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2014 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

// Google Test.
#include <gtest/gtest.h>
// Qt libraries.
#include <QtTest>
// DO-CV libraries.
#include <DO/Graphics/DerivedQObjects/OpenGLWindow.hpp>
// Local libraries.
#include "event_scheduler.hpp"

Q_DECLARE_METATYPE(DO::Event)
Q_DECLARE_METATYPE(Qt::Key)
Q_DECLARE_METATYPE(Qt::MouseButtons)

using namespace DO;

TEST(TestOpenGLWindow, test_construction)
{
  int width = 300;
  int height = 300;
  QString windowName = "OpenGL Window";
  int x = 200;
  int y = 300;

  OpenGLWindow *window = new OpenGLWindow(
    width, height,
    windowName,
    x, y
  );

  EXPECT_EQ(window->width(), width);
  EXPECT_EQ(window->height(), height);
  EXPECT_EQ(window->windowTitle(), windowName);
  EXPECT_TRUE(window->isVisible());

  delete window;
}

class TestOpenGLWindowEvents: public testing::Test
{
protected: // data members.
  OpenGLWindow *test_window_;
  EventScheduler event_scheduler_;
  QPoint mouse_pos_;
  Qt::Key key_;
  int mouse_buttons_type_id_;
  int event_type_id_;

  int wait_ms_;
  int event_time_ms_;

protected: // methods.
  TestOpenGLWindowEvents()
  {
    mouse_buttons_type_id_ = qRegisterMetaType<Qt::MouseButtons>(
      "Qt::MouseButtons"
      );
    event_type_id_ = qRegisterMetaType<Event>("Event");
    test_window_ = new OpenGLWindow(300, 300);
    event_scheduler_.set_receiver(test_window_);
    mouse_pos_ = QPoint(10, 10);
    key_ = Qt::Key_A;

#ifdef _WIN32
    wait_ms_ = 100;
    event_time_ms_ = 10;
#else
    wait_ms_ = 10;
    event_time_ms_ = 10;
#endif
  }

  virtual ~TestOpenGLWindowEvents()
  {
    delete test_window_;
  }

  void compare_key_event(QSignalSpy& spy) const
  {
    spy.wait(2*wait_ms_);
    EXPECT_EQ(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    EXPECT_EQ(arguments.at(0).toInt(), static_cast<int>(key_));
  }

};

TEST_F(TestOpenGLWindowEvents, test_key_press_event)
{
  QSignalSpy spy(test_window_, SIGNAL(pressedKey(int)));
  EXPECT_TRUE(spy.isValid());

  QKeyEvent event(QEvent::KeyPress, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&event, event_time_ms_);

  compare_key_event(spy);
}

TEST_F(TestOpenGLWindowEvents, test_key_release_event)
{
  QSignalSpy spy(test_window_, SIGNAL(releasedKey(int)));
  EXPECT_TRUE(spy.isValid());

  QKeyEvent event(QEvent::KeyRelease, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&event, event_time_ms_);

  compare_key_event(spy);
}

TEST_F(TestOpenGLWindowEvents, test_send_no_event)
{
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, 1));

  // Nothing happens.
  EXPECT_TRUE(spy.wait(10));
  EXPECT_EQ(spy.count(), 1);
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::NO_EVENT);
}

TEST_F(TestOpenGLWindowEvents, test_send_pressed_key_event)
{
  // Spy the sendEvent signal.
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  // Ask the testing window to wait.
  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, wait_ms_));

  // Schedule a key press event.
  QKeyEvent qt_event(QEvent::KeyPress, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&qt_event, event_time_ms_);

  // The spy waits for the events.
  EXPECT_TRUE(spy.wait(2*wait_ms));

  // Check that the spy received one key press event.
  EXPECT_EQ(spy.count(), 1);

  // Check the details of the key press event.
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::KEY_PRESSED);
  EXPECT_EQ(event.key, key_);
}

TEST_F(TestOpenGLWindowEvents, test_send_released_key_event)
{
  // Spy the sendEvent signal.
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  // Ask the testing window to wait for 10 ms.
  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, wait_ms_));

  // Schedule a key press event 30 ms later.
  QKeyEvent qt_event(QEvent::KeyRelease, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&qt_event, event_time_ms_);

  // Check that the spy received one key press event.
  EXPECT_TRUE(spy.wait(2*wait_ms_));
  EXPECT_EQ(spy.count(), 1);

  // Check the details of the key release event.
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::KEY_RELEASED);
  EXPECT_EQ(event.key, key_);
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  app.setAttribute(Qt::AA_Use96Dpi, true);

  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>BUG: fix compilation error.<commit_after>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2014 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

// Google Test.
#include <gtest/gtest.h>
// Qt libraries.
#include <QtTest>
// DO-CV libraries.
#include <DO/Graphics/DerivedQObjects/OpenGLWindow.hpp>
// Local libraries.
#include "event_scheduler.hpp"

Q_DECLARE_METATYPE(DO::Event)
Q_DECLARE_METATYPE(Qt::Key)
Q_DECLARE_METATYPE(Qt::MouseButtons)

using namespace DO;

TEST(TestOpenGLWindow, test_construction)
{
  int width = 300;
  int height = 300;
  QString windowName = "OpenGL Window";
  int x = 200;
  int y = 300;

  OpenGLWindow *window = new OpenGLWindow(
    width, height,
    windowName,
    x, y
  );

  EXPECT_EQ(window->width(), width);
  EXPECT_EQ(window->height(), height);
  EXPECT_EQ(window->windowTitle(), windowName);
  EXPECT_TRUE(window->isVisible());

  delete window;
}

class TestOpenGLWindowEvents: public testing::Test
{
protected: // data members.
  OpenGLWindow *test_window_;
  EventScheduler event_scheduler_;
  QPoint mouse_pos_;
  Qt::Key key_;
  int mouse_buttons_type_id_;
  int event_type_id_;

  int wait_ms_;
  int event_time_ms_;

protected: // methods.
  TestOpenGLWindowEvents()
  {
    mouse_buttons_type_id_ = qRegisterMetaType<Qt::MouseButtons>(
      "Qt::MouseButtons"
      );
    event_type_id_ = qRegisterMetaType<Event>("Event");
    test_window_ = new OpenGLWindow(300, 300);
    event_scheduler_.set_receiver(test_window_);
    mouse_pos_ = QPoint(10, 10);
    key_ = Qt::Key_A;

#ifdef _WIN32
    wait_ms_ = 100;
    event_time_ms_ = 10;
#else
    wait_ms_ = 10;
    event_time_ms_ = 1;
#endif
  }

  virtual ~TestOpenGLWindowEvents()
  {
    delete test_window_;
  }

  void compare_key_event(QSignalSpy& spy) const
  {
    spy.wait(2*wait_ms_);
    EXPECT_EQ(spy.count(), 1);

    QList<QVariant> arguments = spy.takeFirst();
    EXPECT_EQ(arguments.at(0).toInt(), static_cast<int>(key_));
  }

};

TEST_F(TestOpenGLWindowEvents, test_key_press_event)
{
  QSignalSpy spy(test_window_, SIGNAL(pressedKey(int)));
  EXPECT_TRUE(spy.isValid());

  QKeyEvent event(QEvent::KeyPress, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&event, event_time_ms_);

  compare_key_event(spy);
}

TEST_F(TestOpenGLWindowEvents, test_key_release_event)
{
  QSignalSpy spy(test_window_, SIGNAL(releasedKey(int)));
  EXPECT_TRUE(spy.isValid());

  QKeyEvent event(QEvent::KeyRelease, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&event, event_time_ms_);

  compare_key_event(spy);
}

TEST_F(TestOpenGLWindowEvents, test_send_no_event)
{
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, 1));

  // Nothing happens.
  EXPECT_TRUE(spy.wait(10));
  EXPECT_EQ(spy.count(), 1);
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::NO_EVENT);
}

TEST_F(TestOpenGLWindowEvents, test_send_pressed_key_event)
{
  // Spy the sendEvent signal.
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  // Ask the testing window to wait.
  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, wait_ms_));

  // Schedule a key press event.
  QKeyEvent qt_event(QEvent::KeyPress, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&qt_event, event_time_ms_);

  // The spy waits for the events.
  EXPECT_TRUE(spy.wait(2*wait_ms_));

  // Check that the spy received one key press event.
  EXPECT_EQ(spy.count(), 1);

  // Check the details of the key press event.
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::KEY_PRESSED);
  EXPECT_EQ(event.key, key_);
}

TEST_F(TestOpenGLWindowEvents, test_send_released_key_event)
{
  // Spy the sendEvent signal.
  QSignalSpy spy(test_window_, SIGNAL(sendEvent(Event)));
  EXPECT_TRUE(spy.isValid());

  // Ask the testing window to wait for an event.
  QMetaObject::invokeMethod(test_window_, "waitForEvent",
                            Qt::AutoConnection, Q_ARG(int, wait_ms_));

  // Schedule a key press event.
  QKeyEvent qt_event(QEvent::KeyRelease, key_, Qt::NoModifier);
  event_scheduler_.schedule_event(&qt_event, event_time_ms_);

  // Check that the spy received one key press event.
  EXPECT_TRUE(spy.wait(2*wait_ms_));
  EXPECT_EQ(spy.count(), 1);

  // Check the details of the key release event.
  QList<QVariant> arguments = spy.takeFirst();
  QVariant arg = arguments.at(0);
  arg.convert(event_type_id_);
  Event event(arguments.at(0).value<Event>());
  EXPECT_EQ(event.type, DO::KEY_RELEASED);
  EXPECT_EQ(event.key, key_);
}

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);
  app.setAttribute(Qt::AA_Use96Dpi, true);

  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once

#include <fc/exception/exception.hpp>
#include <eosio/chain/protocol.hpp>
#include <boost/core/typeinfo.hpp>

#define EOS_ASSERT( expr, exc_type, FORMAT, ... )                \
   FC_MULTILINE_MACRO_BEGIN                                           \
   if( !(expr) )                                                      \
      FC_THROW_EXCEPTION( exc_type, FORMAT, __VA_ARGS__ );            \
   FC_MULTILINE_MACRO_END

#define EOS_THROW( exc_type, FORMAT, ... ) \
    throw exc_type( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) );

/**
 * Macro inspired from FC_RETHROW_EXCEPTIONS
 * The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
 * This macro will rethrow the exception as the specified "exception_type"
 */
#define EOS_RETHROW_EXCEPTIONS(exception_type, FORMAT, ... ) \
   catch (eosio::chain::chain_exception& e) { \
      FC_RETHROW_EXCEPTION( e, warn, FORMAT, __VA_ARGS__ ); \
   } catch (fc::exception& e) { \
      exception_type new_exception(FC_LOG_MESSAGE( warn, FORMAT, __VA_ARGS__ )); \
      for (const auto& log: e.get_log()) { \
         new_exception.append_log(log); \
      } \
      throw new_exception; \
   } catch( const std::exception& e ) {  \
      exception_type fce(FC_LOG_MESSAGE( warn, FORMAT" (${what})" ,__VA_ARGS__("what",e.what()))); \
      throw fce;\
   } catch( ... ) {  \
      throw fc::unhandled_exception( \
                FC_LOG_MESSAGE( warn, FORMAT,__VA_ARGS__), \
                std::current_exception() ); \
   }

/**
 * Macro inspired from FC_CAPTURE_AND_RETHROW
 * The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
 * This macro will rethrow the exception as the specified "exception_type"
 */
#define EOS_CAPTURE_AND_RETHROW( exception_type, ... ) \
   catch (eosio::chain::chain_exception& e) { \
      FC_RETHROW_EXCEPTION( e, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
   } catch (fc::exception& e) { \
      exception_type new_exception(e.get_log()); \
      throw new_exception; \
   } catch( const std::exception& e ) {  \
      exception_type fce( \
                FC_LOG_MESSAGE( warn, "${what}: ",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)("what",e.what())), \
                fc::std_exception_code,\
                BOOST_CORE_TYPEID(decltype(e)).name(), \
                e.what() ) ; throw fce;\
   } catch( ... ) {  \
      throw fc::unhandled_exception( \
                FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \
                std::current_exception() ); \
   }

#define EOS_RECODE_EXC( cause_type, effect_type ) \
   catch( const cause_type& e ) \
   { throw( effect_type( e.what(), e.get_log() ) ); }

namespace eosio { namespace chain {

   FC_DECLARE_EXCEPTION( chain_exception,
                         3000000, "blockchain exception" )
   /**
    *  chain_exception
    *   |- chain_type_exception
    *   |- fork_database_exception
    *   |- block_validate_exception
    *   |- transaction_exception
    *   |- action_validate_exception
    *   |- database_exception
    *   |- wasm_exception
    *   |- resource_exhausted_exception
    *   |- misc_exception
    *   |- missing_plugin_exception
    *   |- wallet_exception
    */

    FC_DECLARE_DERIVED_EXCEPTION( chain_type_exception, chain_exception,
                                  3010000, "chain type exception" )

      FC_DECLARE_DERIVED_EXCEPTION( name_type_exception,               chain_type_exception,
                                    3010001, "Invalid name" )
      FC_DECLARE_DERIVED_EXCEPTION( public_key_type_exception,         chain_type_exception,
                                    3010002, "Invalid public key" )
      FC_DECLARE_DERIVED_EXCEPTION( private_key_type_exception,        chain_type_exception,
                                    3010003, "Invalid private key" )
      FC_DECLARE_DERIVED_EXCEPTION( authority_type_exception,          chain_type_exception,
                                    3010004, "Invalid authority" )
      FC_DECLARE_DERIVED_EXCEPTION( action_type_exception,             chain_type_exception,
                                    3010005, "Invalid action" )
      FC_DECLARE_DERIVED_EXCEPTION( transaction_type_exception,        chain_type_exception,
                                    3010006, "Invalid transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( abi_type_exception,                chain_type_exception,
                                    3010007, "Invalid ABI" )
      FC_DECLARE_DERIVED_EXCEPTION( block_id_type_exception,           chain_type_exception,
                                    3010008, "Invalid block ID" )
      FC_DECLARE_DERIVED_EXCEPTION( transaction_id_type_exception,     chain_type_exception,
                                    3010009, "Invalid transaction ID" )
      FC_DECLARE_DERIVED_EXCEPTION( packed_transaction_type_exception, chain_type_exception,
                                    3010010, "Invalid packed transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( asset_type_exception,              chain_type_exception,
                                    3010011, "Invalid asset" )

   FC_DECLARE_DERIVED_EXCEPTION( fork_database_exception, chain_exception,
                                 3020000, "fork database exception" )

      FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, chain_exception,
                                    3020001, "unlinkable block" )

   FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception, chain_exception,
                                 3030000, "block exception" )

      FC_DECLARE_DERIVED_EXCEPTION( block_tx_output_exception,   block_validate_exception,
                                    3030001, "transaction outputs in block do not match transaction outputs from applying block" )
      FC_DECLARE_DERIVED_EXCEPTION( block_concurrency_exception, block_validate_exception,
                                    3030002, "block does not guarantee concurrent execution without conflicts" )
      FC_DECLARE_DERIVED_EXCEPTION( block_lock_exception,        block_validate_exception,
                                    3030003, "shard locks in block are incorrect or mal-formed" )
      FC_DECLARE_DERIVED_EXCEPTION( block_resource_exhausted,    block_validate_exception,
                                    3030004, "block exhausted allowed resources" )
      FC_DECLARE_DERIVED_EXCEPTION( block_too_old_exception,     block_validate_exception,
                                    3030005, "block is too old to push" )

   FC_DECLARE_DERIVED_EXCEPTION( transaction_exception,             chain_exception,
                                 3040000, "transaction exception" )

      FC_DECLARE_DERIVED_EXCEPTION( tx_decompression_error,      transaction_exception,
                                    3040001, "Error decompressing transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_no_action,                transaction_exception,
                                    3040002, "transaction should have at least one normal action" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_no_auths,                 transaction_exception,
                                    3040003, "transaction should have at least one required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( cfa_irrelevant_auth,         transaction_exception,
                                    3040004, "context-free action should have no required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( expired_tx_exception,        transaction_exception,
                                    3040005, "Expired Transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_exp_too_far_exception,    transaction_exception,
                                    3040006, "Transaction Expiration Too Far" )
      FC_DECLARE_DERIVED_EXCEPTION( invalid_ref_block_exception, transaction_exception,
                                    3040007, "Invalid Reference Block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_missing_sigs,             transaction_exception,
                                    3040008, "signatures do not satisfy declared authorizations" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_sig,           transaction_exception,
                                    3040009, "irrelevant signature included" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate_sig,            transaction_exception,
                                    3040010, "duplicate signature included" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate,                transaction_exception,
                                    3040011, "duplicate transaction" )

   FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception,
                                 3050000, "action exception" )

      FC_DECLARE_DERIVED_EXCEPTION( account_name_exists_exception, action_validate_exception,
                                    3050001, "account name already exists" )
      FC_DECLARE_DERIVED_EXCEPTION( invalid_action_args_exception, action_validate_exception,
                                    3050002, "Invalid Action Arguments" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_auth_exception,     action_validate_exception,
                                    3050003, "missing required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( irrelevant_auth_exception,  action_validate_exception,
                                    3050004, "irrelevant authority included" )

   FC_DECLARE_DERIVED_EXCEPTION( database_exception, chain_exception,
                                 3060000, "database exception" )

      FC_DECLARE_DERIVED_EXCEPTION( permission_query_exception,     database_exception,
                                    3060001, "Permission Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( account_query_exception,        database_exception,
                                    3060002, "Account Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( contract_table_query_exception, database_exception,
                                    3060003, "Contract Table Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( contract_query_exception,       database_exception,
                                    3060004, "Contract Query Exception" )

   FC_DECLARE_DERIVED_EXCEPTION( wasm_exception, chain_exception,
                                 3070000, "WASM Exception" )

      FC_DECLARE_DERIVED_EXCEPTION( page_memory_error,        wasm_exception,
                                    3070001, "error in WASM page memory" )

      FC_DECLARE_DERIVED_EXCEPTION( wasm_execution_error,     wasm_exception,
                                    3070002, "Runtime Error Processing WASM" )

      FC_DECLARE_DERIVED_EXCEPTION( wasm_serialization_error, wasm_exception,
                                    3070003, "Serialization Error Processing WASM" )

   FC_DECLARE_DERIVED_EXCEPTION( resource_exhausted_exception, chain_exception,
                                 3080000, "resource exhausted exception" )

      FC_DECLARE_DERIVED_EXCEPTION( ram_usage_exceeded, resource_exhausted_exception,
                                    3080001, "account using more than allotted RAM usage" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_net_usage_exceeded, resource_exhausted_exception,
                                    3080002, "transaction exceeded the current network usage limit imposed on the transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_soft_net_usage_exceeded, resource_exhausted_exception,
                                    3080003, "transaction network usage is too much for the remaining allowable usage of the current block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080004, "transaction exceeded the current CPU usage limit imposed on the transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_soft_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080005, "transaction CPU usage is too much for the remaining allowable usage of the current block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_deadline_exceeded, resource_exhausted_exception,
                                    3080006, "transaction took too long" )
      FC_DECLARE_DERIVED_EXCEPTION( action_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080007, "action took too long" )

   FC_DECLARE_DERIVED_EXCEPTION( misc_exception, chain_exception,
                                 3090000, "Miscellaneous exception" )

      FC_DECLARE_DERIVED_EXCEPTION( rate_limiting_state_inconsistent, misc_exception,
                                    3090001, "internal state is no longer consistent" )
      FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception,       misc_exception,
                                    3090002, "unknown block" )
      FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_exception, misc_exception,
                                    3090003, "unknown transaction" )

   FC_DECLARE_DERIVED_EXCEPTION( missing_plugin_exception, chain_exception,
                                 3100000, "missing plugin exception" )

      FC_DECLARE_DERIVED_EXCEPTION( missing_chain_api_plugin_exception,           missing_plugin_exception,
                                    3100001, "Missing Chain API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_wallet_api_plugin_exception,          missing_plugin_exception,
                                    3100002, "Missing Wallet API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_account_history_api_plugin_exception, missing_plugin_exception,
                                    3100003, "Missing Account History API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_net_api_plugin_exception,             missing_plugin_exception,
                                    3100004, "Missing Net API Plugin" )

   FC_DECLARE_DERIVED_EXCEPTION( wallet_exception, chain_exception,
                                 3110000, "wallet exception" )

      FC_DECLARE_DERIVED_EXCEPTION( wallet_exist_exception,            wallet_exception,
                                    3110001, "Wallet already exists" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_nonexistent_exception,      wallet_exception,
                                    3110002, "Nonexistent wallet" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_locked_exception,           wallet_exception,
                                    3110003, "Locked wallet" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_missing_pub_key_exception,  wallet_exception,
                                    3110004, "Missing public key" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_invalid_password_exception, wallet_exception,
                                    3110005, "Invalid wallet password" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_not_available_exception,    wallet_exception,
                                    3110006, "No available wallet" )

} } // eosio::chain
<commit_msg>Add wallet_unlocked_exception<commit_after>/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
#pragma once

#include <fc/exception/exception.hpp>
#include <eosio/chain/protocol.hpp>
#include <boost/core/typeinfo.hpp>

#define EOS_ASSERT( expr, exc_type, FORMAT, ... )                \
   FC_MULTILINE_MACRO_BEGIN                                           \
   if( !(expr) )                                                      \
      FC_THROW_EXCEPTION( exc_type, FORMAT, __VA_ARGS__ );            \
   FC_MULTILINE_MACRO_END

#define EOS_THROW( exc_type, FORMAT, ... ) \
    throw exc_type( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) );

/**
 * Macro inspired from FC_RETHROW_EXCEPTIONS
 * The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
 * This macro will rethrow the exception as the specified "exception_type"
 */
#define EOS_RETHROW_EXCEPTIONS(exception_type, FORMAT, ... ) \
   catch (eosio::chain::chain_exception& e) { \
      FC_RETHROW_EXCEPTION( e, warn, FORMAT, __VA_ARGS__ ); \
   } catch (fc::exception& e) { \
      exception_type new_exception(FC_LOG_MESSAGE( warn, FORMAT, __VA_ARGS__ )); \
      for (const auto& log: e.get_log()) { \
         new_exception.append_log(log); \
      } \
      throw new_exception; \
   } catch( const std::exception& e ) {  \
      exception_type fce(FC_LOG_MESSAGE( warn, FORMAT" (${what})" ,__VA_ARGS__("what",e.what()))); \
      throw fce;\
   } catch( ... ) {  \
      throw fc::unhandled_exception( \
                FC_LOG_MESSAGE( warn, FORMAT,__VA_ARGS__), \
                std::current_exception() ); \
   }

/**
 * Macro inspired from FC_CAPTURE_AND_RETHROW
 * The main difference here is that if the exception caught isn't of type "eosio::chain::chain_exception"
 * This macro will rethrow the exception as the specified "exception_type"
 */
#define EOS_CAPTURE_AND_RETHROW( exception_type, ... ) \
   catch (eosio::chain::chain_exception& e) { \
      FC_RETHROW_EXCEPTION( e, warn, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ); \
   } catch (fc::exception& e) { \
      exception_type new_exception(e.get_log()); \
      throw new_exception; \
   } catch( const std::exception& e ) {  \
      exception_type fce( \
                FC_LOG_MESSAGE( warn, "${what}: ",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)("what",e.what())), \
                fc::std_exception_code,\
                BOOST_CORE_TYPEID(decltype(e)).name(), \
                e.what() ) ; throw fce;\
   } catch( ... ) {  \
      throw fc::unhandled_exception( \
                FC_LOG_MESSAGE( warn, "",FC_FORMAT_ARG_PARAMS(__VA_ARGS__)), \
                std::current_exception() ); \
   }

#define EOS_RECODE_EXC( cause_type, effect_type ) \
   catch( const cause_type& e ) \
   { throw( effect_type( e.what(), e.get_log() ) ); }

namespace eosio { namespace chain {

   FC_DECLARE_EXCEPTION( chain_exception,
                         3000000, "blockchain exception" )
   /**
    *  chain_exception
    *   |- chain_type_exception
    *   |- fork_database_exception
    *   |- block_validate_exception
    *   |- transaction_exception
    *   |- action_validate_exception
    *   |- database_exception
    *   |- wasm_exception
    *   |- resource_exhausted_exception
    *   |- misc_exception
    *   |- missing_plugin_exception
    *   |- wallet_exception
    */

    FC_DECLARE_DERIVED_EXCEPTION( chain_type_exception, chain_exception,
                                  3010000, "chain type exception" )

      FC_DECLARE_DERIVED_EXCEPTION( name_type_exception,               chain_type_exception,
                                    3010001, "Invalid name" )
      FC_DECLARE_DERIVED_EXCEPTION( public_key_type_exception,         chain_type_exception,
                                    3010002, "Invalid public key" )
      FC_DECLARE_DERIVED_EXCEPTION( private_key_type_exception,        chain_type_exception,
                                    3010003, "Invalid private key" )
      FC_DECLARE_DERIVED_EXCEPTION( authority_type_exception,          chain_type_exception,
                                    3010004, "Invalid authority" )
      FC_DECLARE_DERIVED_EXCEPTION( action_type_exception,             chain_type_exception,
                                    3010005, "Invalid action" )
      FC_DECLARE_DERIVED_EXCEPTION( transaction_type_exception,        chain_type_exception,
                                    3010006, "Invalid transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( abi_type_exception,                chain_type_exception,
                                    3010007, "Invalid ABI" )
      FC_DECLARE_DERIVED_EXCEPTION( block_id_type_exception,           chain_type_exception,
                                    3010008, "Invalid block ID" )
      FC_DECLARE_DERIVED_EXCEPTION( transaction_id_type_exception,     chain_type_exception,
                                    3010009, "Invalid transaction ID" )
      FC_DECLARE_DERIVED_EXCEPTION( packed_transaction_type_exception, chain_type_exception,
                                    3010010, "Invalid packed transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( asset_type_exception,              chain_type_exception,
                                    3010011, "Invalid asset" )

   FC_DECLARE_DERIVED_EXCEPTION( fork_database_exception, chain_exception,
                                 3020000, "fork database exception" )

      FC_DECLARE_DERIVED_EXCEPTION( unlinkable_block_exception, chain_exception,
                                    3020001, "unlinkable block" )

   FC_DECLARE_DERIVED_EXCEPTION( block_validate_exception, chain_exception,
                                 3030000, "block exception" )

      FC_DECLARE_DERIVED_EXCEPTION( block_tx_output_exception,   block_validate_exception,
                                    3030001, "transaction outputs in block do not match transaction outputs from applying block" )
      FC_DECLARE_DERIVED_EXCEPTION( block_concurrency_exception, block_validate_exception,
                                    3030002, "block does not guarantee concurrent execution without conflicts" )
      FC_DECLARE_DERIVED_EXCEPTION( block_lock_exception,        block_validate_exception,
                                    3030003, "shard locks in block are incorrect or mal-formed" )
      FC_DECLARE_DERIVED_EXCEPTION( block_resource_exhausted,    block_validate_exception,
                                    3030004, "block exhausted allowed resources" )
      FC_DECLARE_DERIVED_EXCEPTION( block_too_old_exception,     block_validate_exception,
                                    3030005, "block is too old to push" )

   FC_DECLARE_DERIVED_EXCEPTION( transaction_exception,             chain_exception,
                                 3040000, "transaction exception" )

      FC_DECLARE_DERIVED_EXCEPTION( tx_decompression_error,      transaction_exception,
                                    3040001, "Error decompressing transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_no_action,                transaction_exception,
                                    3040002, "transaction should have at least one normal action" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_no_auths,                 transaction_exception,
                                    3040003, "transaction should have at least one required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( cfa_irrelevant_auth,         transaction_exception,
                                    3040004, "context-free action should have no required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( expired_tx_exception,        transaction_exception,
                                    3040005, "Expired Transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_exp_too_far_exception,    transaction_exception,
                                    3040006, "Transaction Expiration Too Far" )
      FC_DECLARE_DERIVED_EXCEPTION( invalid_ref_block_exception, transaction_exception,
                                    3040007, "Invalid Reference Block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_missing_sigs,             transaction_exception,
                                    3040008, "signatures do not satisfy declared authorizations" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_irrelevant_sig,           transaction_exception,
                                    3040009, "irrelevant signature included" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate_sig,            transaction_exception,
                                    3040010, "duplicate signature included" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_duplicate,                transaction_exception,
                                    3040011, "duplicate transaction" )

   FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception,
                                 3050000, "action exception" )

      FC_DECLARE_DERIVED_EXCEPTION( account_name_exists_exception, action_validate_exception,
                                    3050001, "account name already exists" )
      FC_DECLARE_DERIVED_EXCEPTION( invalid_action_args_exception, action_validate_exception,
                                    3050002, "Invalid Action Arguments" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_auth_exception,     action_validate_exception,
                                    3050003, "missing required authority" )
      FC_DECLARE_DERIVED_EXCEPTION( irrelevant_auth_exception,  action_validate_exception,
                                    3050004, "irrelevant authority included" )

   FC_DECLARE_DERIVED_EXCEPTION( database_exception, chain_exception,
                                 3060000, "database exception" )

      FC_DECLARE_DERIVED_EXCEPTION( permission_query_exception,     database_exception,
                                    3060001, "Permission Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( account_query_exception,        database_exception,
                                    3060002, "Account Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( contract_table_query_exception, database_exception,
                                    3060003, "Contract Table Query Exception" )
      FC_DECLARE_DERIVED_EXCEPTION( contract_query_exception,       database_exception,
                                    3060004, "Contract Query Exception" )

   FC_DECLARE_DERIVED_EXCEPTION( wasm_exception, chain_exception,
                                 3070000, "WASM Exception" )

      FC_DECLARE_DERIVED_EXCEPTION( page_memory_error,        wasm_exception,
                                    3070001, "error in WASM page memory" )

      FC_DECLARE_DERIVED_EXCEPTION( wasm_execution_error,     wasm_exception,
                                    3070002, "Runtime Error Processing WASM" )

      FC_DECLARE_DERIVED_EXCEPTION( wasm_serialization_error, wasm_exception,
                                    3070003, "Serialization Error Processing WASM" )

   FC_DECLARE_DERIVED_EXCEPTION( resource_exhausted_exception, chain_exception,
                                 3080000, "resource exhausted exception" )

      FC_DECLARE_DERIVED_EXCEPTION( ram_usage_exceeded, resource_exhausted_exception,
                                    3080001, "account using more than allotted RAM usage" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_net_usage_exceeded, resource_exhausted_exception,
                                    3080002, "transaction exceeded the current network usage limit imposed on the transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_soft_net_usage_exceeded, resource_exhausted_exception,
                                    3080003, "transaction network usage is too much for the remaining allowable usage of the current block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080004, "transaction exceeded the current CPU usage limit imposed on the transaction" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_soft_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080005, "transaction CPU usage is too much for the remaining allowable usage of the current block" )
      FC_DECLARE_DERIVED_EXCEPTION( tx_deadline_exceeded, resource_exhausted_exception,
                                    3080006, "transaction took too long" )
      FC_DECLARE_DERIVED_EXCEPTION( action_cpu_usage_exceeded, resource_exhausted_exception,
                                    3080007, "action took too long" )

   FC_DECLARE_DERIVED_EXCEPTION( misc_exception, chain_exception,
                                 3090000, "Miscellaneous exception" )

      FC_DECLARE_DERIVED_EXCEPTION( rate_limiting_state_inconsistent, misc_exception,
                                    3090001, "internal state is no longer consistent" )
      FC_DECLARE_DERIVED_EXCEPTION( unknown_block_exception,       misc_exception,
                                    3090002, "unknown block" )
      FC_DECLARE_DERIVED_EXCEPTION( unknown_transaction_exception, misc_exception,
                                    3090003, "unknown transaction" )

   FC_DECLARE_DERIVED_EXCEPTION( missing_plugin_exception, chain_exception,
                                 3100000, "missing plugin exception" )

      FC_DECLARE_DERIVED_EXCEPTION( missing_chain_api_plugin_exception,           missing_plugin_exception,
                                    3100001, "Missing Chain API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_wallet_api_plugin_exception,          missing_plugin_exception,
                                    3100002, "Missing Wallet API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_account_history_api_plugin_exception, missing_plugin_exception,
                                    3100003, "Missing Account History API Plugin" )
      FC_DECLARE_DERIVED_EXCEPTION( missing_net_api_plugin_exception,             missing_plugin_exception,
                                    3100004, "Missing Net API Plugin" )

   FC_DECLARE_DERIVED_EXCEPTION( wallet_exception, chain_exception,
                                 3110000, "wallet exception" )

      FC_DECLARE_DERIVED_EXCEPTION( wallet_exist_exception,            wallet_exception,
                                    3110001, "Wallet already exists" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_nonexistent_exception,      wallet_exception,
                                    3110002, "Nonexistent wallet" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_locked_exception,           wallet_exception,
                                    3110003, "Locked wallet" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_missing_pub_key_exception,  wallet_exception,
                                    3110004, "Missing public key" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_invalid_password_exception, wallet_exception,
                                    3110005, "Invalid wallet password" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_not_available_exception,    wallet_exception,
                                    3110006, "No available wallet" )
      FC_DECLARE_DERIVED_EXCEPTION( wallet_unlocked_exception,         wallet_exception,
                                    3110007, "Already unlocked" )

} } // eosio::chain
<|endoftext|>


<commit_before>#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Target target = get_jit_target_from_environment();

    if (1) {
        // Test a tuple reduction on the gpu
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        // Updates to a reduction are atomic.
        f(x, y) = Tuple(f(x, y)[1]*2, f(x, y)[0]*2);
        // now equals ((x - y)*2, (x + y)*2)

        if (target.has_gpu_feature()) {
            f.gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
            f.update().gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
        } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
            f.hexagon(y).vectorize(x, 32);
            f.update().hexagon(y).vectorize(x, 32);
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x - y)*2;
                int correct_b = (x + y)*2;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }
    }

    if (1) {
        // Now test one that alternates between cpu and gpu per update step
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        for (size_t i = 0; i < 10; i++) {
            // Swap the tuple elements and increment both
            f(x, y) = Tuple(f(x, y)[1] + 1, f(x, y)[0] + 1);
        }

        // Schedule the pure step and the odd update steps on the gpu
        if (target.has_gpu_feature()) {
            f.gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
        } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
            f.hexagon(y).vectorize(x, 32);
        }
        for (int i = 0; i < 10; i ++) {
            if (i & 1) {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            } else {
                f.update(i);
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x + y) + 10;
                int correct_b = (x - y) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    if (1) {
        // Same as above, but switches which steps are gpu and cpu
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        for (size_t i = 0; i < 10; i++) {
            // Swap the tuple elements and increment both
            f(x, y) = Tuple(f(x, y)[1] + 1, f(x, y)[0] + 1);
        }

        // Schedule the even update steps on the gpu
        for (int i = 0; i < 10; i ++) {
            if (i & 1) {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            } else {
                f.update(i);
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x + y) + 10;
                int correct_b = (x - y) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    if (1) {
        // In this one, each step only uses one of the tuple elements
        // of the previous step, so only that buffer should get copied
        // back to host or copied to device.
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y - 1000, x - y + 1000);

        for (size_t i = 0; i < 10; i++) {
            f(x, y) = Tuple(f(x, y)[1] - 1, f(x, y)[1] + 1);
        }

        // Schedule the even update steps on the gpu
        for (int i = 0; i < 10; i++) {
            if (i & 1) {
                f.update(i);
            } else {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x - y + 1000) + 8;
                int correct_b = (x - y + 1000) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    printf("Success!\n");
    return 0;
}
<commit_msg>Skip another hexagon test<commit_after>#include "Halide.h"
#include <stdio.h>

using namespace Halide;

int main(int argc, char **argv) {
    Target target = get_jit_target_from_environment();

    if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
        printf("Skipping test due to https://github.com/halide/Halide/issues/2364\n");
        return 0;
    }

    if (1) {
        // Test a tuple reduction on the gpu
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        // Updates to a reduction are atomic.
        f(x, y) = Tuple(f(x, y)[1]*2, f(x, y)[0]*2);
        // now equals ((x - y)*2, (x + y)*2)

        if (target.has_gpu_feature()) {
            f.gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
            f.update().gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
        } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
            f.hexagon(y).vectorize(x, 32);
            f.update().hexagon(y).vectorize(x, 32);
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x - y)*2;
                int correct_b = (x + y)*2;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }
    }

    if (1) {
        // Now test one that alternates between cpu and gpu per update step
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        for (size_t i = 0; i < 10; i++) {
            // Swap the tuple elements and increment both
            f(x, y) = Tuple(f(x, y)[1] + 1, f(x, y)[0] + 1);
        }

        // Schedule the pure step and the odd update steps on the gpu
        if (target.has_gpu_feature()) {
            f.gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
        } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
            f.hexagon(y).vectorize(x, 32);
        }
        for (int i = 0; i < 10; i ++) {
            if (i & 1) {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            } else {
                f.update(i);
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x + y) + 10;
                int correct_b = (x - y) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    if (1) {
        // Same as above, but switches which steps are gpu and cpu
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y, x - y);

        for (size_t i = 0; i < 10; i++) {
            // Swap the tuple elements and increment both
            f(x, y) = Tuple(f(x, y)[1] + 1, f(x, y)[0] + 1);
        }

        // Schedule the even update steps on the gpu
        for (int i = 0; i < 10; i ++) {
            if (i & 1) {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            } else {
                f.update(i);
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x + y) + 10;
                int correct_b = (x - y) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    if (1) {
        // In this one, each step only uses one of the tuple elements
        // of the previous step, so only that buffer should get copied
        // back to host or copied to device.
        Func f;
        Var x, y, xo, yo, xi, yi;

        f(x, y) = Tuple(x + y - 1000, x - y + 1000);

        for (size_t i = 0; i < 10; i++) {
            f(x, y) = Tuple(f(x, y)[1] - 1, f(x, y)[1] + 1);
        }

        // Schedule the even update steps on the gpu
        for (int i = 0; i < 10; i++) {
            if (i & 1) {
                f.update(i);
            } else {
                if (target.has_gpu_feature()) {
                    f.update(i).gpu_tile(x, y, xo, yo, xi, yi, 16, 16);
                } else if (target.features_any_of({Target::HVX_64, Target::HVX_128})) {
                    f.update(i).hexagon(y).vectorize(x, 32);
                }
            }
        }

        Realization result = f.realize(1024, 1024);

        Buffer<int> a = result[0], b = result[1];

        for (int y = 0; y < a.height(); y++) {
            for (int x = 0; x < a.width(); x++) {
                int correct_a = (x - y + 1000) + 8;
                int correct_b = (x - y + 1000) + 10;
                if (a(x, y) != correct_a || b(x, y) != correct_b) {
                    printf("result(%d, %d) = (%d, %d) instead of (%d, %d)\n",
                           x, y, a(x, y), b(x, y), correct_a, correct_b);
                    return -1;
                }
            }
        }

    }

    printf("Success!\n");
    return 0;
}
<|endoftext|>