<commit_before>/*
 * TableJoin.cpp
 *
 * Copyright (C) 2016-2017 by VISUS (University of Stuttgart)
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "TableJoin.h"

#include <limits>

using namespace megamol::stdplugin::datatools;
using namespace megamol::stdplugin::datatools::table;
using namespace megamol;

std::string TableJoin::ModuleName
    = std::string("TableJoin");

size_t hash_combine(size_t lhs, size_t rhs) {
    lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
    return lhs;
}

TableJoin::TableJoin(void) : core::Module(),
    firstTableInSlot("firstTableIn", "First input"),
    secondTableInSlot("secondTableIn", "Second input"),
    dataOutSlot("dataOut", "Output"),
    frameID(-1),
    firstDataHash(std::numeric_limits<unsigned long>::max()), secondDataHash(std::numeric_limits<unsigned long>::max()) {
    this->firstTableInSlot.SetCompatibleCall<TableDataCallDescription>();
    this->MakeSlotAvailable(&this->firstTableInSlot);

    this->secondTableInSlot.SetCompatibleCall<TableDataCallDescription>();
    this->MakeSlotAvailable(&this->secondTableInSlot);

    this->dataOutSlot.SetCallback(TableDataCall::ClassName(),
        TableDataCall::FunctionName(0),
        &TableJoin::processData);
    this->dataOutSlot.SetCallback(TableDataCall::ClassName(),
        TableDataCall::FunctionName(1),
        &TableJoin::getExtent);
    this->MakeSlotAvailable(&this->dataOutSlot);
}

TableJoin::~TableJoin(void) {
    this->Release();
}

bool TableJoin::create(void) {
    return true;
}

void TableJoin::release(void) {

}

bool TableJoin::processData(core::Call &c) {
    try {
        TableDataCall *outCall = dynamic_cast<TableDataCall *>(&c);
        if (outCall == NULL) return false;

        TableDataCall *firstInCall = this->firstTableInSlot.CallAs<TableDataCall>();
        if (firstInCall == NULL) return false;

        TableDataCall *secondInCall = this->secondTableInSlot.CallAs<TableDataCall>();
        if (secondInCall == NULL) return false;

        // call getHash before check of frame count
        if (!(*firstInCall)(1)) return false;
        if (!(*secondInCall)(1)) return false;

        // check time compatibility
        if (firstInCall->GetFrameCount() != secondInCall->GetFrameCount()) {
            megamol::core::utility::log::Log::DefaultLog.WriteError(_T("%hs: Cannot join tables. ")
                _T("They are required to have equal frame count\n"), ModuleName.c_str());
            return false;
        }

        // request frame data
        firstInCall->SetFrameID(outCall->GetFrameID());
        secondInCall->SetFrameID(outCall->GetFrameID());

        // issue calls
        if (!(*firstInCall)()) return false;
        if (!(*secondInCall)()) return false;

        if (this->firstDataHash != firstInCall->DataHash() || this->secondDataHash != secondInCall->DataHash()
            || this->frameID != firstInCall->GetFrameID() || this->frameID != secondInCall->GetFrameID()) {
            this->firstDataHash = firstInCall->DataHash();
            this->secondDataHash = secondInCall->DataHash();
            ASSERT(firstInCall->GetFrameID() == secondInCall->GetFrameID());
            this->frameID = firstInCall->GetFrameID();

            // retrieve data
            auto firstRowsCount = firstInCall->GetRowsCount();
            auto firstColumnCount = firstInCall->GetColumnsCount();
            auto firstColumnInfos = firstInCall->GetColumnsInfos();
            auto firstData = firstInCall->GetData();

            auto secondRowsCount = secondInCall->GetRowsCount();
            auto secondColumnCount = secondInCall->GetColumnsCount();
            auto secondColumnInfos = secondInCall->GetColumnsInfos();
            auto secondData = secondInCall->GetData();

            // concatenate
            this->rows_count = std::max(firstRowsCount, secondRowsCount);
            this->column_count = firstColumnCount + secondColumnCount;
            this->column_info.clear();
            this->column_info.reserve(this->column_count);
            memcpy(this->column_info.data(), firstColumnInfos,
                sizeof(TableDataCall::ColumnInfo)*firstColumnCount);
            memcpy(&(this->column_info.data()[firstColumnCount]), secondColumnInfos,
                sizeof(TableDataCall::ColumnInfo)*secondColumnCount);
            this->data.clear();
            this->data.resize(this->rows_count * this->column_count);

            this->concatenate(this->data.data(), this->rows_count, this->column_count,
				firstData, firstRowsCount, firstColumnCount,
				secondData, secondRowsCount, secondColumnCount);
        }

        outCall->SetFrameCount(firstInCall->GetFrameCount());
        outCall->SetFrameID(this->frameID);
        outCall->SetDataHash(hash_combine(this->firstDataHash, this->secondDataHash));
        outCall->Set(this->column_count, this->rows_count, this->column_info.data(), this->data.data());
    } catch (...) {
        megamol::core::utility::log::Log::DefaultLog.WriteError(_T("Failed to execute %hs::processData\n"),
            ModuleName.c_str());
        return false;
    }

    return true;
}

void TableJoin::concatenate(
	float* const out, const size_t rowCount, const size_t columnCount,
	const float* const first, const size_t firstRowCount, const size_t firstColumnCount,
    const float* const second,  const size_t secondRowCount, const size_t secondColumnCount) {
    assert(rowCount >= firstRowCount && rowCount >= secondRowCount && "Not enough rows");
    assert(columnCount >= firstColumnCount + secondColumnCount && "Not enough columns");
    for (size_t row = 0; row < firstRowCount; row++) {
        float* outR = &out[row * columnCount];
        memcpy(outR, &first[row * firstColumnCount], sizeof(float) * firstColumnCount);
    }
    for (size_t row = firstRowCount; row < rowCount; row++) {
        for (size_t col = 0; col < firstColumnCount; col++) {
            out[col + row * columnCount] = NAN;
        }
    }
    for (size_t row = 0; row < secondRowCount; row++) {
        float* outR = &out[firstColumnCount + row * columnCount];
        memcpy(outR, &second[row * secondColumnCount], sizeof(float) * secondColumnCount);
    }
    for (size_t row = secondRowCount; row < rowCount; row++) {
        for (size_t col = 0; col < secondColumnCount; col++) {
            out[col + firstColumnCount + row * columnCount] = NAN;
        }
    }
}

bool TableJoin::getExtent(core::Call &c) {
    try {
        TableDataCall *outCall = dynamic_cast<TableDataCall *>(&c);
        if (outCall == NULL) return false;

        TableDataCall *inCall = this->firstTableInSlot.CallAs<TableDataCall>();
        if (inCall == NULL) return false;

        inCall->SetFrameID(outCall->GetFrameID());
        if (!(*inCall)(1)) return false;

        outCall->SetFrameCount(inCall->GetFrameCount());
        outCall->SetDataHash(hash_combine(this->firstDataHash, this->secondDataHash));
    }
    catch (...) {
        megamol::core::utility::log::Log::DefaultLog.WriteError(_T("Failed to execute %hs::getExtent\n"), ModuleName.c_str());
        return false;
    }

    return true;
}
<commit_msg>small fix<commit_after>/*
 * TableJoin.cpp
 *
 * Copyright (C) 2016-2017 by VISUS (University of Stuttgart)
 * Alle Rechte vorbehalten.
 */

#include "stdafx.h"
#include "TableJoin.h"

#include <limits>

using namespace megamol::stdplugin::datatools;
using namespace megamol::stdplugin::datatools::table;
using namespace megamol;

std::string TableJoin::ModuleName
    = std::string("TableJoin");

size_t hash_combine(size_t lhs, size_t rhs) {
    lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
    return lhs;
}

TableJoin::TableJoin(void) : core::Module(),
    firstTableInSlot("firstTableIn", "First input"),
    secondTableInSlot("secondTableIn", "Second input"),
    dataOutSlot("dataOut", "Output"),
    frameID(-1),
    firstDataHash(std::numeric_limits<unsigned long>::max()), secondDataHash(std::numeric_limits<unsigned long>::max()) {
    this->firstTableInSlot.SetCompatibleCall<TableDataCallDescription>();
    this->MakeSlotAvailable(&this->firstTableInSlot);

    this->secondTableInSlot.SetCompatibleCall<TableDataCallDescription>();
    this->MakeSlotAvailable(&this->secondTableInSlot);

    this->dataOutSlot.SetCallback(TableDataCall::ClassName(),
        TableDataCall::FunctionName(0),
        &TableJoin::processData);
    this->dataOutSlot.SetCallback(TableDataCall::ClassName(),
        TableDataCall::FunctionName(1),
        &TableJoin::getExtent);
    this->MakeSlotAvailable(&this->dataOutSlot);
}

TableJoin::~TableJoin(void) {
    this->Release();
}

bool TableJoin::create(void) {
    return true;
}

void TableJoin::release(void) {

}

bool TableJoin::processData(core::Call &c) {
    try {
        TableDataCall *outCall = dynamic_cast<TableDataCall *>(&c);
        if (outCall == NULL) return false;

        TableDataCall *firstInCall = this->firstTableInSlot.CallAs<TableDataCall>();
        if (firstInCall == NULL) return false;

        TableDataCall *secondInCall = this->secondTableInSlot.CallAs<TableDataCall>();
        if (secondInCall == NULL) return false;

        // call getHash before check of frame count
        if (!(*firstInCall)(1)) return false;
        if (!(*secondInCall)(1)) return false;

        // check time compatibility
        if (firstInCall->GetFrameCount() != secondInCall->GetFrameCount()) {
            megamol::core::utility::log::Log::DefaultLog.WriteError(_T("%hs: Cannot join tables. ")
                _T("They are required to have equal frame count\n"), ModuleName.c_str());
            return false;
        }

        // request frame data
        firstInCall->SetFrameID(outCall->GetFrameID());
        secondInCall->SetFrameID(outCall->GetFrameID());

        // issue calls
        if (!(*firstInCall)()) return false;
        if (!(*secondInCall)()) return false;

        if (this->firstDataHash != firstInCall->DataHash() || this->secondDataHash != secondInCall->DataHash()
            || this->frameID != firstInCall->GetFrameID() || this->frameID != secondInCall->GetFrameID()) {
            this->firstDataHash = firstInCall->DataHash();
            this->secondDataHash = secondInCall->DataHash();
            ASSERT(firstInCall->GetFrameID() == secondInCall->GetFrameID());
            this->frameID = firstInCall->GetFrameID();

            // retrieve data
            auto firstRowsCount = firstInCall->GetRowsCount();
            auto firstColumnCount = firstInCall->GetColumnsCount();
            auto firstColumnInfos = firstInCall->GetColumnsInfos();
            auto firstData = firstInCall->GetData();

            auto secondRowsCount = secondInCall->GetRowsCount();
            auto secondColumnCount = secondInCall->GetColumnsCount();
            auto secondColumnInfos = secondInCall->GetColumnsInfos();
            auto secondData = secondInCall->GetData();

            // concatenate
            this->rows_count = std::max(firstRowsCount, secondRowsCount);
            this->column_count = firstColumnCount + secondColumnCount;
            this->column_info.clear();
            this->column_info.resize(this->column_count);
            memcpy(this->column_info.data(), firstColumnInfos,
                sizeof(TableDataCall::ColumnInfo)*firstColumnCount);
            memcpy(&(this->column_info.data()[firstColumnCount]), secondColumnInfos,
                sizeof(TableDataCall::ColumnInfo)*secondColumnCount);
            this->data.clear();
            this->data.resize(this->rows_count * this->column_count);

            this->concatenate(this->data.data(), this->rows_count, this->column_count,
				firstData, firstRowsCount, firstColumnCount,
				secondData, secondRowsCount, secondColumnCount);
        }

        outCall->SetFrameCount(firstInCall->GetFrameCount());
        outCall->SetFrameID(this->frameID);
        outCall->SetDataHash(hash_combine(this->firstDataHash, this->secondDataHash));
        outCall->Set(this->column_count, this->rows_count, this->column_info.data(), this->data.data());
    } catch (...) {
        megamol::core::utility::log::Log::DefaultLog.WriteError(_T("Failed to execute %hs::processData\n"),
            ModuleName.c_str());
        return false;
    }

    return true;
}

void TableJoin::concatenate(
	float* const out, const size_t rowCount, const size_t columnCount,
	const float* const first, const size_t firstRowCount, const size_t firstColumnCount,
    const float* const second,  const size_t secondRowCount, const size_t secondColumnCount) {
    assert(rowCount >= firstRowCount && rowCount >= secondRowCount && "Not enough rows");
    assert(columnCount >= firstColumnCount + secondColumnCount && "Not enough columns");
    for (size_t row = 0; row < firstRowCount; row++) {
        float* outR = &out[row * columnCount];
        memcpy(outR, &first[row * firstColumnCount], sizeof(float) * firstColumnCount);
    }
    for (size_t row = firstRowCount; row < rowCount; row++) {
        for (size_t col = 0; col < firstColumnCount; col++) {
            out[col + row * columnCount] = NAN;
        }
    }
    for (size_t row = 0; row < secondRowCount; row++) {
        float* outR = &out[firstColumnCount + row * columnCount];
        memcpy(outR, &second[row * secondColumnCount], sizeof(float) * secondColumnCount);
    }
    for (size_t row = secondRowCount; row < rowCount; row++) {
        for (size_t col = 0; col < secondColumnCount; col++) {
            out[col + firstColumnCount + row * columnCount] = NAN;
        }
    }
}

bool TableJoin::getExtent(core::Call &c) {
    try {
        TableDataCall *outCall = dynamic_cast<TableDataCall *>(&c);
        if (outCall == NULL) return false;

        TableDataCall *inCall = this->firstTableInSlot.CallAs<TableDataCall>();
        if (inCall == NULL) return false;

        inCall->SetFrameID(outCall->GetFrameID());
        if (!(*inCall)(1)) return false;

        outCall->SetFrameCount(inCall->GetFrameCount());
        outCall->SetDataHash(hash_combine(this->firstDataHash, this->secondDataHash));
    }
    catch (...) {
        megamol::core::utility::log::Log::DefaultLog.WriteError(_T("Failed to execute %hs::getExtent\n"), ModuleName.c_str());
        return false;
    }

    return true;
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//      https://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 "iree/compiler/Translation/IREEVM.h"

#include "iree/compiler/Dialect/Flow/Transforms/Passes.h"
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h"
#include "iree/compiler/Dialect/IREE/Transforms/Passes.h"
#include "iree/compiler/Dialect/VM/Target/Bytecode/TranslationFlags.h"
#include "iree/compiler/Dialect/VM/Transforms/Passes.h"
#include "mlir/IR/Module.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Translation.h"

#ifdef IREE_HAVE_EMITC_DIALECT
#include "iree/compiler/Dialect/VM/Target/C/CModuleTarget.h"
#endif  // IREE_HAVE_EMITC_DIALECT

namespace mlir {
namespace iree_compiler {

LogicalResult convertToFlowModule(ModuleOp moduleOp) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::Flow::buildFlowTransformPassPipeline(passManager);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run flow transformation pass pipeline";
  }
  return success();
}

LogicalResult convertToHALModule(ModuleOp moduleOp,
                                 IREE::HAL::TargetOptions executableOptions) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::HAL::buildHALTransformPassPipeline(passManager, executableOptions);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run flow transformation pass pipeline";
  }
  return success();
}

LogicalResult convertToVMModule(ModuleOp moduleOp,
                                IREE::VM::TargetOptions targetOptions) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::VM::buildVMTransformPassPipeline(passManager, targetOptions);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run VM transformation pass pipeline";
  }
  return success();
}

void registerIREEVMTransformPassPipeline() {
  PassPipelineRegistration<> transformPassPipeline(
      "iree-transformation-pipeline",
      "Runs the full IREE input to VM transformation pipeline",
      [](OpPassManager &passManager) {
        IREE::Flow::buildFlowTransformPassPipeline(passManager);
        IREE::HAL::buildHALTransformPassPipeline(
            passManager, IREE::HAL::getTargetOptionsFromFlags());
        IREE::VM::buildVMTransformPassPipeline(
            passManager, IREE::VM::getTargetOptionsFromFlags());
        passManager.addPass(IREE::createDropCompilerHintsPass());
      });
}

static LogicalResult translateFromMLIRToVM(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions) {
  // Convert from our source to a vm.module in canonical form.
  // After this completes we have a non-bytecode-specific vm.module that we
  // could lower to other forms (LLVM IR, C, etc).
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::Flow::buildFlowTransformPassPipeline(passManager);
  IREE::HAL::buildHALTransformPassPipeline(passManager, executableOptions);
  IREE::VM::buildVMTransformPassPipeline(passManager, targetOptions);
  passManager.addPass(mlir::iree_compiler::IREE::createDropCompilerHintsPass());

  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError() << "conversion from source -> vm failed";
  }
  return success();
}

LogicalResult translateFromMLIRToVMBytecodeModule(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions,
    IREE::VM::BytecodeTargetOptions bytecodeOptions,
    llvm::raw_ostream &output) {
  auto result =
      translateFromMLIRToVM(moduleOp, executableOptions, targetOptions);
  if (failed(result)) {
    return result;
  }

  // Serialize to bytecode.
  return translateModuleToBytecode(moduleOp, bytecodeOptions, output);
}

static LogicalResult translateFromMLIRToVMBytecodeModuleWithFlags(
    ModuleOp moduleOp, llvm::raw_ostream &output) {
  mlir::registerPassManagerCLOptions();
  auto halTargetOptions = IREE::HAL::getTargetOptionsFromFlags();
  auto vmTargetOptions = IREE::VM::getTargetOptionsFromFlags();
  auto bytecodeTargetOptions = IREE::VM::getBytecodeTargetOptionsFromFlags();
  return translateFromMLIRToVMBytecodeModule(moduleOp, halTargetOptions,
                                             vmTargetOptions,
                                             bytecodeTargetOptions, output);
}

#ifdef IREE_HAVE_EMITC_DIALECT
LogicalResult translateFromMLIRToVMCModule(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions, llvm::raw_ostream &output) {
  auto result =
      translateFromMLIRToVM(moduleOp, executableOptions, targetOptions);
  if (failed(result)) {
    return result;
  }

  // Serialize to c code.
  return mlir::iree_compiler::IREE::VM::translateModuleToC(moduleOp, output);
}

static LogicalResult translateFromMLIRToVMCModuleWithFlags(
    ModuleOp moduleOp, llvm::raw_ostream &output) {
  mlir::registerPassManagerCLOptions();
  auto halTargetOptions = IREE::HAL::getTargetOptionsFromFlags();
  auto vmTargetOptions = IREE::VM::getTargetOptionsFromFlags();
  return translateFromMLIRToVMCModule(moduleOp, halTargetOptions,
                                      vmTargetOptions, output);
}
#endif  // IREE_HAVE_EMITC_DIALECT

void registerIREEVMTranslation() {
  TranslateFromMLIRRegistration toVMBytecodeModuleWithFlags(
      "iree-mlir-to-vm-bytecode-module",
      translateFromMLIRToVMBytecodeModuleWithFlags);

#ifdef IREE_HAVE_EMITC_DIALECT
  TranslateFromMLIRRegistration toVMCModuleWithFlags(
      "iree-mlir-to-vm-c-module", translateFromMLIRToVMCModuleWithFlags);
#endif  // IREE_HAVE_EMITC_DIALECT
}

}  // namespace iree_compiler
}  // namespace mlir
<commit_msg>[NFC] Fix error message in convertToHALModule (#3446)<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//      https://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 "iree/compiler/Translation/IREEVM.h"

#include "iree/compiler/Dialect/Flow/Transforms/Passes.h"
#include "iree/compiler/Dialect/HAL/Transforms/Passes.h"
#include "iree/compiler/Dialect/IREE/Transforms/Passes.h"
#include "iree/compiler/Dialect/VM/Target/Bytecode/TranslationFlags.h"
#include "iree/compiler/Dialect/VM/Transforms/Passes.h"
#include "mlir/IR/Module.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Translation.h"

#ifdef IREE_HAVE_EMITC_DIALECT
#include "iree/compiler/Dialect/VM/Target/C/CModuleTarget.h"
#endif  // IREE_HAVE_EMITC_DIALECT

namespace mlir {
namespace iree_compiler {

LogicalResult convertToFlowModule(ModuleOp moduleOp) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::Flow::buildFlowTransformPassPipeline(passManager);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run flow transformation pass pipeline";
  }
  return success();
}

LogicalResult convertToHALModule(ModuleOp moduleOp,
                                 IREE::HAL::TargetOptions executableOptions) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::HAL::buildHALTransformPassPipeline(passManager, executableOptions);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run HAL transformation pass pipeline";
  }
  return success();
}

LogicalResult convertToVMModule(ModuleOp moduleOp,
                                IREE::VM::TargetOptions targetOptions) {
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::VM::buildVMTransformPassPipeline(passManager, targetOptions);
  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError()
           << "failed to run VM transformation pass pipeline";
  }
  return success();
}

void registerIREEVMTransformPassPipeline() {
  PassPipelineRegistration<> transformPassPipeline(
      "iree-transformation-pipeline",
      "Runs the full IREE input to VM transformation pipeline",
      [](OpPassManager &passManager) {
        IREE::Flow::buildFlowTransformPassPipeline(passManager);
        IREE::HAL::buildHALTransformPassPipeline(
            passManager, IREE::HAL::getTargetOptionsFromFlags());
        IREE::VM::buildVMTransformPassPipeline(
            passManager, IREE::VM::getTargetOptionsFromFlags());
        passManager.addPass(IREE::createDropCompilerHintsPass());
      });
}

static LogicalResult translateFromMLIRToVM(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions) {
  // Convert from our source to a vm.module in canonical form.
  // After this completes we have a non-bytecode-specific vm.module that we
  // could lower to other forms (LLVM IR, C, etc).
  PassManager passManager(moduleOp.getContext());
  mlir::applyPassManagerCLOptions(passManager);
  IREE::Flow::buildFlowTransformPassPipeline(passManager);
  IREE::HAL::buildHALTransformPassPipeline(passManager, executableOptions);
  IREE::VM::buildVMTransformPassPipeline(passManager, targetOptions);
  passManager.addPass(mlir::iree_compiler::IREE::createDropCompilerHintsPass());

  if (failed(passManager.run(moduleOp))) {
    return moduleOp.emitError() << "conversion from source -> vm failed";
  }
  return success();
}

LogicalResult translateFromMLIRToVMBytecodeModule(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions,
    IREE::VM::BytecodeTargetOptions bytecodeOptions,
    llvm::raw_ostream &output) {
  auto result =
      translateFromMLIRToVM(moduleOp, executableOptions, targetOptions);
  if (failed(result)) {
    return result;
  }

  // Serialize to bytecode.
  return translateModuleToBytecode(moduleOp, bytecodeOptions, output);
}

static LogicalResult translateFromMLIRToVMBytecodeModuleWithFlags(
    ModuleOp moduleOp, llvm::raw_ostream &output) {
  mlir::registerPassManagerCLOptions();
  auto halTargetOptions = IREE::HAL::getTargetOptionsFromFlags();
  auto vmTargetOptions = IREE::VM::getTargetOptionsFromFlags();
  auto bytecodeTargetOptions = IREE::VM::getBytecodeTargetOptionsFromFlags();
  return translateFromMLIRToVMBytecodeModule(moduleOp, halTargetOptions,
                                             vmTargetOptions,
                                             bytecodeTargetOptions, output);
}

#ifdef IREE_HAVE_EMITC_DIALECT
LogicalResult translateFromMLIRToVMCModule(
    ModuleOp moduleOp, IREE::HAL::TargetOptions executableOptions,
    IREE::VM::TargetOptions targetOptions, llvm::raw_ostream &output) {
  auto result =
      translateFromMLIRToVM(moduleOp, executableOptions, targetOptions);
  if (failed(result)) {
    return result;
  }

  // Serialize to c code.
  return mlir::iree_compiler::IREE::VM::translateModuleToC(moduleOp, output);
}

static LogicalResult translateFromMLIRToVMCModuleWithFlags(
    ModuleOp moduleOp, llvm::raw_ostream &output) {
  mlir::registerPassManagerCLOptions();
  auto halTargetOptions = IREE::HAL::getTargetOptionsFromFlags();
  auto vmTargetOptions = IREE::VM::getTargetOptionsFromFlags();
  return translateFromMLIRToVMCModule(moduleOp, halTargetOptions,
                                      vmTargetOptions, output);
}
#endif  // IREE_HAVE_EMITC_DIALECT

void registerIREEVMTranslation() {
  TranslateFromMLIRRegistration toVMBytecodeModuleWithFlags(
      "iree-mlir-to-vm-bytecode-module",
      translateFromMLIRToVMBytecodeModuleWithFlags);

#ifdef IREE_HAVE_EMITC_DIALECT
  TranslateFromMLIRRegistration toVMCModuleWithFlags(
      "iree-mlir-to-vm-c-module", translateFromMLIRToVMCModuleWithFlags);
#endif  // IREE_HAVE_EMITC_DIALECT
}

}  // namespace iree_compiler
}  // namespace mlir
<|endoftext|>


<commit_before>/*
    main.cpp:
    Copyright (C) 2006 Istvan Varga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "CsoundGUI.hpp"

int main(int argc, char **argv)
{
    CsoundGUIMain   *mainWin;

    if (csoundInitialize(&argc, &argv, 0) < 0)
      return -1;
    Fl::lock();
    mainWin = new CsoundGUIMain;
    mainWin->run(argc, argv);
    delete mainWin;
    Fl::wait(0.0);

    return 0;
}

<commit_msg>Added warning in case csound can't be initialized<commit_after>/*
    main.cpp:
    Copyright (C) 2006 Istvan Varga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*/

#include "CsoundGUI.hpp"
#include <FL/fl_ask.H>

int main(int argc, char **argv)
{
    CsoundGUIMain   *mainWin;

    if (csoundInitialize(&argc, &argv, 0) < 0) {
      fprintf(stderr, "Couldn't Initialize Csound! Try removing command line flags.\nQuitting...\n");
      fl_alert("Couldn't Initialize Csound!\nTry removing command line flags.");
      return -1;
    }
    Fl::lock();
    mainWin = new CsoundGUIMain;
    mainWin->run(argc, argv);
    delete mainWin;
    Fl::wait(0.0);

    return 0;
}

<|endoftext|>


<commit_before>// Copyright (c) 2012. Los Alamos National Security, LLC.

// This material was produced under U.S. Government contract DE-AC52-06NA25396
// for Los Alamos National Laboratory (LANL), which is operated by Los Alamos
// National Security, LLC for the U.S. Department of Energy. The U.S. Government
// has rights to use, reproduce, and distribute this software.

// NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY,
// EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.
// If software is modified to produce derivative works, such modified software should
// be clearly marked, so as not to confuse it with the version available from LANL.

// Additionally, this library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License v 2.1 as published by the
// Free Software Foundation. Accordingly, this library is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt for more details.

//--------------------------------------------------------------------------
// File:    LP.C
// Module:  simx
// Author:  K. Bisset
// Created: June 25 2004
//
// @@
//
//--------------------------------------------------------------------------

#include "simx/LP.h"
#include "simx/DassfLP.h"

#include "simx/EventInfo.h"
#include "simx/DassfEvent.h"
#include "simx/DassfEventInfo.h"
#include "simx/DassfEventInfoManager.h"
#include "simx/DassfPyEventInfoManager.h"
#include "simx/EntityManager.h"
#include "simx/PackedData.h"
#include "simx/logger.h"
#include "simx/config.h"
#include "simx/InfoManager.h"

#include "Common/backtrace.h"

#include "boost/lexical_cast.hpp"

#include "simx/ControlInfoWrapper.h"

#include "simx/simEngine.h"

#ifdef SIMX_USE_PRIME
    #include "simx/Messenger.h"
#endif

#include "simx/simEngine.h"

#include <boost/python.hpp>
#include "simx/PyEventInfoManager.h"
#include "simx/EventInfoManager.h"

using namespace std;
using namespace boost;

#ifdef SIMX_SST
// Original code initializes this static in the main of each application.
// It makes linking with SST more difficult if the static is not self-contained.
// Only initialize here for use with SST to avoid breaking other code.

// Initialize static variable MINDELAY to zero.
// Correct value will by set by LP constructor from config file.
simx::Time  simx::LP::MINDELAY = 1000;
#endif

namespace simx {

LP::LP(LPID id)
  : fId(id),
#ifdef SIMX_USE_PRIME
    fDassfLP( new DassfLP(id, *this) ),
#endif
    fRandom(id)
{
//  SMART_ASSERT( fDassfLP );

  Config::gConfig.GetConfigurationValueRequired( ky_MINDELAY, MINDELAY );
  Logger::debug3() << "LP.C setting mindelay to " << MINDELAY << endl;
#ifdef SIMX_USE_PRIME
  fDassfLP->mapChannels();
#endif
}

LP::~LP()
{
#ifdef SIMX_USE_PRIME
    SMART_ASSERT( fDassfLP );
    delete fDassfLP;
#endif
}

LPID LP::getId() const
{
    return fId;
}

Random::TRandom& LP::getRandom() const
{
    return fRandom;
}

Time LP::getNow() const
{
#ifdef SIMX_USE_PRIME
    SMART_ASSERT( fDassfLP );
    return fDassfLP->now();
#else
    return SimEngine::getNow();
#endif
}

void LP::sendEventInfo(EventInfo& e) const
{
    EntityID entityID = e.getDestEntity();
    Time delay = e.getDelay();

    if( delay < LP::MINDELAY )
    {

	Logger::error() << "LP.C on LP " << getId() << ": too late sending event with delay "
	    << delay << " at time " << getNow() << ", will be delivered later" << endl;
	Logger::error() << "setting delay to LP::MINDELAY of " << LP::MINDELAY;
	delay = LP::MINDELAY;
    }

    LPID destLP = theEntityManager().findEntityLpId( entityID );

    // set the time the event is supposed to execute at
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

    Logger::debug2() << "LP " << fId << ": sending EventInfo: " << e
	<< " to entity " << entityID << " with delay " << delay << endl;

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfEventInfo(e), delay);
#else
    SimEngine::sendEventInfo( destLP, e );
#endif
}

void LP::sendEventInfoManager(EventInfoManager& e) const
{
    LPID destLP = fId;

    Logger::debug2() << "LP " << fId << ": sending EventInfoManager: " << e
	<< " with delay " << e.getDelay()  << endl;

    // set the time the event is supposed to execute at
    Time delay = e.getDelay();
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfEventInfoManager(e), e.getDelay() );
#else
    // simEngine handles this differently: uses an event to Controller
    // TODO: this should really be happining in InfoManager when it is scheduling this
    //   for itself.
    EventInfo ei;
    ei.setTo( EntityID('!', Control::getRank() ), ServiceAddress() );
    ei.setDelay( delay );
    ei.setTime( eventTime );
    boost::shared_ptr<InfoWakeupInfoManager> info;
    theInfoManager().createInfo( info );
    SMART_ASSERT( info );
    info->fFileId = e.getFileId();
    ei.setInfo( info );
    SimEngine::sendEventInfo( destLP, ei );
#endif
}

//TODO: code repetition from function above. refactor.
void LP::sendPyEventInfoManager(PyEventInfoManager& e) const
{
    LPID destLP = fId;

    Logger::debug2() << "LP " << fId << ": sending PyEventInfoManager: " << e
	<< " with delay " << e.getDelay()  << endl;

    // set the time the event is supposed to execute at
    Time delay = e.getDelay();
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfPyEventInfoManager(e), e.getDelay() );
#else
    // simEngine handles this differently: uses an event to Controller
    // TODO: this should really be happining in InfoManager when it is scheduling this
    //   for itself.
    EventInfo ei;
    ei.setTo( EntityID('!', Control::getRank() ), ServiceAddress() );
    ei.setDelay( delay );
    ei.setTime( eventTime );
    boost::shared_ptr<InfoWakeupInfoManager> info;
    theInfoManager().createInfo( info );
    SMART_ASSERT( info );
    info->fPyEvent = true;
    ei.setInfo( info );
    SimEngine::sendEventInfo( destLP, ei );
#endif
}

void LP::sendControlInfo( ControlInfoWrapper& cinfo )
{
#ifdef HAVE_MPI_H
#ifdef SIMX_USE_PRIME
  EntityID entityID = cinfo.getDestEntity();
  //Time delay = cinfo.getDelay();
  //TODO: parameter to send directly to LP specified by user.
  LPID destLP = theEntityManager().findEntityLpId( entityID );
  cinfo.setSentTime( fDassfLP->now() );
  cinfo.setSrcLP( fId );
  cinfo.setDestLP( destLP );
  Logger::debug2() << "LP " << fId << ": sending ControlInfo: " << cinfo
		   << " to entity " << entityID << endl;
  //  bool res;
  if (!( Messenger::sendMessage( fId, destLP, cinfo )) )
    {
      Logger::error() << "simx::LP : Sending ControlInfo failed "
		      << " from Src LP " << fId << " to Dest LP " << destLP
		      << endl;
    }
#else
    Logger::error() << "simx::LP : out-of-band messaging not supported without PRIME" << endl;
#endif
#endif
}

} // namespace

void export_LP() {

  python::class_<simx::LP,boost::noncopyable>("LP",python::no_init)
    .def("get_id",&simx::LP::getId )
    .def("get_now",&simx::LP::getNow )
    ;
}
<commit_msg>delay enforcement in LP::sendEventInfo now only enforces LOCAL_MINDELAY for same-LP messages.<commit_after>// Copyright (c) 2012. Los Alamos National Security, LLC.

// This material was produced under U.S. Government contract DE-AC52-06NA25396
// for Los Alamos National Laboratory (LANL), which is operated by Los Alamos
// National Security, LLC for the U.S. Department of Energy. The U.S. Government
// has rights to use, reproduce, and distribute this software.

// NEITHER THE GOVERNMENT NOR LOS ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY,
// EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.
// If software is modified to produce derivative works, such modified software should
// be clearly marked, so as not to confuse it with the version available from LANL.

// Additionally, this library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License v 2.1 as published by the
// Free Software Foundation. Accordingly, this library is distributed in the hope that
// it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt for more details.

//--------------------------------------------------------------------------
// File:    LP.C
// Module:  simx
// Author:  K. Bisset
// Created: June 25 2004
//
// @@
//
//--------------------------------------------------------------------------

#include "simx/LP.h"
#include "simx/DassfLP.h"

#include "simx/EventInfo.h"
#include "simx/DassfEvent.h"
#include "simx/DassfEventInfo.h"
#include "simx/DassfEventInfoManager.h"
#include "simx/DassfPyEventInfoManager.h"
#include "simx/EntityManager.h"
#include "simx/PackedData.h"
#include "simx/logger.h"
#include "simx/config.h"
#include "simx/InfoManager.h"

#include "Common/backtrace.h"

#include "boost/lexical_cast.hpp"

#include "simx/ControlInfoWrapper.h"

#include "simx/simEngine.h"

#ifdef SIMX_USE_PRIME
    #include "simx/Messenger.h"
#endif

#include "simx/simEngine.h"

#include <boost/python.hpp>
#include "simx/PyEventInfoManager.h"
#include "simx/EventInfoManager.h"
#include "simx/constants.h"

using namespace std;
using namespace boost;

#ifdef SIMX_SST
// Original code initializes this static in the main of each application.
// It makes linking with SST more difficult if the static is not self-contained.
// Only initialize here for use with SST to avoid breaking other code.

// Initialize static variable MINDELAY to zero.
// Correct value will by set by LP constructor from config file.
simx::Time  simx::LP::MINDELAY = 1000;
#endif

namespace simx {

LP::LP(LPID id)
  : fId(id),
#ifdef SIMX_USE_PRIME
    fDassfLP( new DassfLP(id, *this) ),
#endif
    fRandom(id)
{
//  SMART_ASSERT( fDassfLP );

  Config::gConfig.GetConfigurationValueRequired( ky_MINDELAY, MINDELAY );
  Logger::debug3() << "LP.C setting mindelay to " << MINDELAY << endl;
#ifdef SIMX_USE_PRIME
  fDassfLP->mapChannels();
#endif
}

LP::~LP()
{
#ifdef SIMX_USE_PRIME
    SMART_ASSERT( fDassfLP );
    delete fDassfLP;
#endif
}

LPID LP::getId() const
{
    return fId;
}

Random::TRandom& LP::getRandom() const
{
    return fRandom;
}

Time LP::getNow() const
{
#ifdef SIMX_USE_PRIME
    SMART_ASSERT( fDassfLP );
    return fDassfLP->now();
#else
    return SimEngine::getNow();
#endif
}

void LP::sendEventInfo(EventInfo& e) const
{
    EntityID entityID = e.getDestEntity();
    Time delay = e.getDelay();
    LPID destLP = theEntityManager().findEntityLpId( entityID );

    if ( destLP == Control::getRank() )
      {
	if (delay < LOCAL_MINDELAY )
	  {
	    Logger::error() << "LP.C on LP " << getId() << ": too late sending event with delay "
			    << delay << " at time " << getNow() << ", will be delivered later" << endl;
	    Logger::error() << "setting delay to LOCAL_MINDELAY of " << LOCAL_MINDELAY;
	    delay = LOCAL_MINDELAY;
	  }
      }
    else { // detLP is different from this LP
      if( delay < LP::MINDELAY )
	{

	  Logger::error() << "LP.C on LP " << getId() << ": too late sending event with delay "
			  << delay << " at time " << getNow() << ", will be delivered later" << endl;
	  Logger::error() << "setting delay to LP::MINDELAY of " << LP::MINDELAY;
	  delay = LP::MINDELAY;
	}
    }

    // set the time the event is supposed to execute at
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

    Logger::debug2() << "LP " << fId << ": sending EventInfo: " << e
	<< " to entity " << entityID << " with delay " << delay << endl;

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfEventInfo(e), delay);
#else
    SimEngine::sendEventInfo( destLP, e );
#endif
}

void LP::sendEventInfoManager(EventInfoManager& e) const
{
    LPID destLP = fId;

    Logger::debug2() << "LP " << fId << ": sending EventInfoManager: " << e
	<< " with delay " << e.getDelay()  << endl;

    // set the time the event is supposed to execute at
    Time delay = e.getDelay();
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfEventInfoManager(e), e.getDelay() );
#else
    // simEngine handles this differently: uses an event to Controller
    // TODO: this should really be happining in InfoManager when it is scheduling this
    //   for itself.
    EventInfo ei;
    ei.setTo( EntityID('!', Control::getRank() ), ServiceAddress() );
    ei.setDelay( delay );
    ei.setTime( eventTime );
    boost::shared_ptr<InfoWakeupInfoManager> info;
    theInfoManager().createInfo( info );
    SMART_ASSERT( info );
    info->fFileId = e.getFileId();
    ei.setInfo( info );
    SimEngine::sendEventInfo( destLP, ei );
#endif
}

//TODO: code repetition from function above. refactor.
void LP::sendPyEventInfoManager(PyEventInfoManager& e) const
{
    LPID destLP = fId;

    Logger::debug2() << "LP " << fId << ": sending PyEventInfoManager: " << e
	<< " with delay " << e.getDelay()  << endl;

    // set the time the event is supposed to execute at
    Time delay = e.getDelay();
    Time eventTime = getNow() + delay;
    e.setTime(eventTime);

#ifdef SIMX_USE_PRIME
    fDassfLP->sendDassfEvent(destLP, new DassfPyEventInfoManager(e), e.getDelay() );
#else
    // simEngine handles this differently: uses an event to Controller
    // TODO: this should really be happining in InfoManager when it is scheduling this
    //   for itself.
    EventInfo ei;
    ei.setTo( EntityID('!', Control::getRank() ), ServiceAddress() );
    ei.setDelay( delay );
    ei.setTime( eventTime );
    boost::shared_ptr<InfoWakeupInfoManager> info;
    theInfoManager().createInfo( info );
    SMART_ASSERT( info );
    info->fPyEvent = true;
    ei.setInfo( info );
    SimEngine::sendEventInfo( destLP, ei );
#endif
}

void LP::sendControlInfo( ControlInfoWrapper& cinfo )
{
#ifdef HAVE_MPI_H
#ifdef SIMX_USE_PRIME
  EntityID entityID = cinfo.getDestEntity();
  //Time delay = cinfo.getDelay();
  //TODO: parameter to send directly to LP specified by user.
  LPID destLP = theEntityManager().findEntityLpId( entityID );
  cinfo.setSentTime( fDassfLP->now() );
  cinfo.setSrcLP( fId );
  cinfo.setDestLP( destLP );
  Logger::debug2() << "LP " << fId << ": sending ControlInfo: " << cinfo
		   << " to entity " << entityID << endl;
  //  bool res;
  if (!( Messenger::sendMessage( fId, destLP, cinfo )) )
    {
      Logger::error() << "simx::LP : Sending ControlInfo failed "
		      << " from Src LP " << fId << " to Dest LP " << destLP
		      << endl;
    }
#else
    Logger::error() << "simx::LP : out-of-band messaging not supported without PRIME" << endl;
#endif
#endif
}

} // namespace

void export_LP() {

  python::class_<simx::LP,boost::noncopyable>("LP",python::no_init)
    .def("get_id",&simx::LP::getId )
    .def("get_now",&simx::LP::getNow )
    ;
}
<|endoftext|>


<commit_before>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define BUFSIZE 121
#define FILENAME "Testfile"
#define TEST_FILENAME "Testfile2"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1

using namespace std;

bool gremlin(Packet * pack, int corruptProb, int lossProb);
bool init(int argc, char** argv);
bool loadFile();
bool sendFile();
bool getFile();
char * recvPkt();
bool isvpack(unsigned char * p);
Packet createPacket(int index);
bool sendPacket();
bool isAck();
void handleAck();
void handleNak(int& x);

int seqNum;
int s;
int probCorrupt;
int probLoss;
string hs;
short int port;
char * file;
unsigned char* window[16]; //packet window
int windowBase; //used to determine position in window of arriving packets
int length;
struct sockaddr_in a;
struct sockaddr_in sa;
socklen_t salen;
string fstr;
bool dropPck;
Packet p;
int delayT;
unsigned char b[BUFSIZE];

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  if(sendto(s, "GET Testfile", BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
    return false;
  }

  getFile();

  return 0;
}

bool init(int argc, char** argv) {
  windowBase = 0; //initialize windowBase
  s = 0;

  hs = string("131.204.14.") + argv[1]; /* Needs to be updated? Might be a string like "tux175.engr.auburn.edu." */
  port = 10038; /* Can be any port within 10038-10041, inclusive. */

  char* delayTStr = argv[2];
  delayT = boost::lexical_cast<int>(delayTStr);

  /*if(!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }*/

  if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY); //why does this always give us 0?
  a.sin_port = htons(0);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0){
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return false;
  }

  memset((char *)&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  inet_pton(AF_INET, hs.c_str(), &(sa.sin_addr));

  cout << endl;

  cout << "Server address (inet mode): " << inet_ntoa(sa.sin_addr) << endl;
  cout << "Port: " << ntohs(sa.sin_port) << endl;

  cout << endl << endl;

  /*fstr = string(file);

  cout << "File: " << endl << fstr << endl << endl;*/

  seqNum = 0;
  dropPck = false;
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= length / BUFSIZE; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);
    if(index * BUFSIZE + BUFSIZE > length) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss;
    if((dropPck = gremlin(&p, pc, pl)) == false){
      if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
        return false;
      }
      return true;
    } else return false;
}
bool isAck() {
    recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&sa, &salen);

    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool gremlin(Packet * pack, int corruptProb, int lossProb){
  bool dropPacket = false;
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return dropPacket;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[3];
  memcpy(sns, &p[0], 3);
  sns[2] = '\0';

  char * css = new char[6];
  memcpy(css, &p[2], 6);
  css[5] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[8], 121);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){
  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  int rlen;
  int ack;

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&sa, &salen);

	/* Begin Window Loading */
	int tempSeqNum = boost::lexical_cast<int>(packet[0]);
	int properIndex = tempSeqNum - windowBase;

	window[properIndex] = packet;
	cout << "Packet loaded into window" << endl;
	char* tempTest = new char[6];
		memcpy(tempTest, &window[1], 0);
		tempTest[5] = '\0';

	cout << "The Checksum pulled from client window: " << tempTest[0] << endl;

	for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
	  char * sns = new char[3];
	  memcpy(sns, &packet[0], 3);
	  sns[2] = '\0';

      char * css = new char[6];
      memcpy(css, &packet[2], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << sns << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      if(isvpack(packet)) {

		if(boost::lexical_cast<int>(sns) == windowBase) windowBase++; //increment base of window //FIXME
        file << dataPull;
		file.flush();
      }
      cout << "Sent response: ";
      cout << "ACK " << windowBase << endl;

	  if(packet[6] == '1') usleep(delayT*1000);

	  string wbs = to_string((long long)windowBase);
	  const char * ackval = wbs.c_str();

      if(sendto(s, ackval, PAKSIZE, 0, (struct sockaddr *)&sa, salen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}
<commit_msg>Fix dropping character<commit_after>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include "packet.h"

#define BUFSIZE 121
#define FILENAME "Testfile"
#define TEST_FILENAME "Testfile2"
#define PORT 10038
#define PAKSIZE 128
#define ACK 0
#define NAK 1

using namespace std;

bool gremlin(Packet * pack, int corruptProb, int lossProb);
bool init(int argc, char** argv);
bool loadFile();
bool sendFile();
bool getFile();
char * recvPkt();
bool isvpack(unsigned char * p);
Packet createPacket(int index);
bool sendPacket();
bool isAck();
void handleAck();
void handleNak(int& x);

int seqNum;
int s;
int probCorrupt;
int probLoss;
string hs;
short int port;
char * file;
unsigned char* window[16]; //packet window
int windowBase; //used to determine position in window of arriving packets
int length;
struct sockaddr_in a;
struct sockaddr_in sa;
socklen_t salen;
string fstr;
bool dropPck;
Packet p;
int delayT;
unsigned char b[BUFSIZE];

int main(int argc, char** argv) {

  if(!init(argc, argv)) return -1;

  if(sendto(s, "GET Testfile", BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
    cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
    return false;
  }

  getFile();

  return 0;
}

bool init(int argc, char** argv) {
  windowBase = 0; //initialize windowBase
  s = 0;

  hs = string("131.204.14.") + argv[1]; /* Needs to be updated? Might be a string like "tux175.engr.auburn.edu." */
  port = 10038; /* Can be any port within 10038-10041, inclusive. */

  char* delayTStr = argv[2];
  delayT = boost::lexical_cast<int>(delayTStr);

  /*if(!loadFile()) {
    cout << "Loading file failed. (filename FILENAME)" << endl;
    return false;
  }*/

  if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
    cout << "Socket creation failed. (socket s)" << endl;
    return false;
  }

  memset((char *)&a, 0, sizeof(a));
  a.sin_family = AF_INET;
  a.sin_addr.s_addr = htonl(INADDR_ANY); //why does this always give us 0?
  a.sin_port = htons(0);

  if (bind(s, (struct sockaddr *)&a, sizeof(a)) < 0){
    cout << "Socket binding failed. (socket s, address a)" << endl;
    return false;
  }

  memset((char *)&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  inet_pton(AF_INET, hs.c_str(), &(sa.sin_addr));

  cout << endl;

  cout << "Server address (inet mode): " << inet_ntoa(sa.sin_addr) << endl;
  cout << "Port: " << ntohs(sa.sin_port) << endl;

  cout << endl << endl;

  /*fstr = string(file);

  cout << "File: " << endl << fstr << endl << endl;*/

  seqNum = 0;
  dropPck = false;
  return true;
}

bool loadFile() {

  ifstream is (FILENAME, ifstream::binary);

  if(is) {
    is.seekg(0, is.end);
    length = is.tellg();
    is.seekg(0, is.beg);

    file = new char[length];

    cout << "Reading " << length << " characters..." << endl;
    is.read(file, length);

    if(!is) { cout << "File reading failed. (filename " << FILENAME << "). Only " << is.gcount() << " could be read."; return false; }
    is.close();
  }
  return true;
}

bool sendFile() {

  for(int x = 0; x <= length / BUFSIZE; x++) {
    p = createPacket(x);
    if(!sendPacket()) continue;

    if(isAck()) {
      handleAck();
    } else {
      handleNak(x);
    }

    memset(b, 0, BUFSIZE);
  }
  return true;
}

Packet createPacket(int index){
    cout << endl;
    cout << "=== TRANSMISSION START" << endl;
    string mstr = fstr.substr(index * BUFSIZE, BUFSIZE);
    if(index * BUFSIZE + BUFSIZE > length) {
      mstr[length - (index * BUFSIZE)] = '\0';
    }
    return Packet (seqNum, mstr.c_str());
}

bool sendPacket(){
    int pc = probCorrupt; int pl = probLoss;
    if((dropPck = gremlin(&p, pc, pl)) == false){
      if(sendto(s, p.str(), BUFSIZE + 7, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
        cout << "Package sending failed. (socket s, server address sa, message m)" << endl;
        return false;
      }
      return true;
    } else return false;
}
bool isAck() {
    recvfrom(s, b, BUFSIZE + 7, 0, (struct sockaddr *)&sa, &salen);

    cout << endl << "=== SERVER RESPONSE TEST" << endl;
    cout << "Data: " << b << endl;
    if(b[6] == '0') return true;
    else return false;
}
void handleAck() {

}
void handleNak(int& x) {

      char * sns = new char[2];
      memcpy(sns, &b[0], 1);
      sns[1] = '\0';

      char * css = new char[5];
      memcpy(css, &b[1], 5);

      char * db = new char[BUFSIZE + 1];
      memcpy(db, &b[2], BUFSIZE);
      db[BUFSIZE] = '\0';

      cout << "Sequence number: " << sns << endl;
      cout << "Checksum: " << css << endl;

      Packet pk (0, db);
      pk.setSequenceNum(boost::lexical_cast<int>(sns));
      pk.setCheckSum(boost::lexical_cast<int>(css));

      if(!pk.chksm()) x--;
      else x = (x - 2 > 0) ? x - 2 : 0;
}

bool gremlin(Packet * pack, int corruptProb, int lossProb){
  bool dropPacket = false;
  int r = rand() % 100;

  cout << "Corruption probability: " << corruptProb << endl;
  cout << "Random number: " << r << endl;

  if(r <= (lossProb)){
    dropPacket = true;
    cout << "Dropped!" << endl;
  }
  else if(r <= (corruptProb)){
    cout << "Corrupted!" << endl;
    pack->loadDataBuffer((char*)"GREMLIN LOL");
  }
  else seqNum = (seqNum) ? false : true;
  cout << "Seq. num: " << pack->getSequenceNum() << endl;
  cout << "Checksum: " << pack->getCheckSum() << endl;
  cout << "Message: "  << pack->getDataBuffer() << endl;

  return dropPacket;
}

bool isvpack(unsigned char * p) {
  cout << endl << "=== IS VALID PACKET TESTING" << endl;

  char * sns = new char[3];
  memcpy(sns, &p[0], 3);
  sns[2] = '\0';

  char * css = new char[6];
  memcpy(css, &p[2], 6);
  css[5] = '\0';

  char * db = new char[121 + 1];
  memcpy(db, &p[8], 122);
  db[121] = '\0';

  cout << "Seq. num: " << sns << endl;
  cout << "Checksum: " << css << endl;
  cout << "Message: " << db << endl;

  int sn = boost::lexical_cast<int>(sns);
  int cs = boost::lexical_cast<int>(css);

  Packet pk (0, db);
  pk.setSequenceNum(sn);

  // change to validate based on checksum and sequence number

  if(sn == seqNum) return false;
  if(cs != pk.generateCheckSum()) return false;
  return true;
}

bool getFile(){
  /* Loop forever, waiting for messages from a client. */
  cout << "Waiting on port " << PORT << "..." << endl;

  ofstream file("Dumpfile");

  int rlen;
  int ack;

  for (;;) {
    unsigned char packet[PAKSIZE + 1];
    unsigned char dataPull[PAKSIZE - 7 + 1];
    rlen = recvfrom(s, packet, PAKSIZE, 0, (struct sockaddr *)&sa, &salen);

	/* Begin Window Loading */
	int tempSeqNum = boost::lexical_cast<int>(packet[0]);
	int properIndex = tempSeqNum - windowBase;

	window[properIndex] = packet;
	cout << "Packet loaded into window" << endl;
	char* tempTest = new char[6];
		memcpy(tempTest, &window[1], 0);
		tempTest[5] = '\0';

	cout << "The Checksum pulled from client window: " << tempTest[0] << endl;

	for(int x = 0; x < PAKSIZE - 7; x++) {
      dataPull[x] = packet[x + 7];
    }
    dataPull[PAKSIZE - 7] = '\0';
    packet[PAKSIZE] = '\0';
    if (rlen > 0) {
	  char * sns = new char[3];
	  memcpy(sns, &packet[0], 3);
	  sns[2] = '\0';

      char * css = new char[6];
      memcpy(css, &packet[2], 5);
      css[5] = '\0';
      cout << endl << endl << "=== RECEIPT" << endl;
      cout << "Seq. num: " << sns << endl;
      cout << "Checksum: " << css << endl;
      cout << "Received message: " << dataPull << endl;
      if(isvpack(packet)) {

		if(boost::lexical_cast<int>(sns) == windowBase) windowBase++; //increment base of window //FIXME
        file << dataPull;
		file.flush();
      }
      cout << "Sent response: ";
      cout << "ACK " << windowBase << endl;

	  if(packet[6] == '1') usleep(delayT*1000);

	  string wbs = to_string((long long)windowBase);
	  const char * ackval = wbs.c_str();

      if(sendto(s, ackval, PAKSIZE, 0, (struct sockaddr *)&sa, salen) < 0) {
        cout << "Acknowledgement failed. (socket s, acknowledgement message ack, client address ca, client address length calen)" << endl;
        return 0;
      }
      delete css;
    }
  }
  file.close();
  return true;
}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/btree/btree.h>
#include <vespa/vespalib/btree/btreebuilder.h>
#include <vespa/vespalib/btree/btreenodeallocator.h>
#include <vespa/vespalib/btree/btreeroot.h>
#include <vespa/vespalib/btree/btreestore.h>

#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/rand48.h>
#include <vespa/vespalib/util/size_literals.h>
#include <vespa/vespalib/util/threadstackexecutor.h>

#include <vespa/vespalib/btree/btreenodeallocator.hpp>
#include <vespa/vespalib/btree/btreenode.hpp>
#include <vespa/vespalib/btree/btreenodestore.hpp>
#include <vespa/vespalib/btree/btreeiterator.hpp>
#include <vespa/vespalib/btree/btreeroot.hpp>
#include <vespa/vespalib/btree/btreebuilder.hpp>
#include <vespa/vespalib/btree/btree.hpp>
#include <vespa/vespalib/btree/btreestore.hpp>
#include <vespa/vespalib/btree/btreeaggregator.hpp>

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

using MyTree = vespalib::btree::BTree<uint32_t, uint32_t>;
using MyTreeIterator = typename MyTree::Iterator;
using MyTreeConstIterator = typename MyTree::ConstIterator;
using GenerationHandler = vespalib::GenerationHandler;
using vespalib::makeLambdaTask;

struct Fixture
{
    GenerationHandler _generationHandler;
    MyTree _tree;
    MyTreeIterator _writeItr;
    vespalib::ThreadStackExecutor _writer; // 1 write thread
    vespalib::ThreadStackExecutor _readers; // multiple reader threads
    vespalib::Rand48 _rnd;
    uint32_t _keyLimit;
    std::atomic<long> _readSeed;
    std::atomic<long> _doneWriteWork;
    std::atomic<long> _doneReadWork;
    std::atomic<int> _stopRead;
    bool _reportWork;

    Fixture();
    ~Fixture();
    void commit();
    void adjustWriteIterator(uint32_t key);
    void insert(uint32_t key);
    void remove(uint32_t key);

    void readWork(uint32_t cnt);
    void readWork();
    void writeWork(uint32_t cnt);
};

Fixture::Fixture()
    : _generationHandler(),
      _tree(),
      _writeItr(_tree.begin()),
      _writer(1, 128_Ki),
      _readers(4, 128_Ki),
      _rnd(),
      _keyLimit(1000000),
      _readSeed(50),
      _doneWriteWork(0),
      _doneReadWork(0),
      _stopRead(0),
      _reportWork(false)
{
    _rnd.srand48(32);
}

Fixture::~Fixture()
{
    _readers.sync();
    _readers.shutdown();
    _writer.sync();
    _writer.shutdown();
    commit();
    if (_reportWork) {
        LOG(info,
            "readWork=%ld, writeWork=%ld",
            _doneReadWork.load(), _doneWriteWork.load());
    }
}

void
Fixture::commit()
{
    auto &allocator = _tree.getAllocator();
    allocator.freeze();
    allocator.transferHoldLists(_generationHandler.getCurrentGeneration());
    _generationHandler.incGeneration();
    allocator.trimHoldLists(_generationHandler.getFirstUsedGeneration());
}

void
Fixture::adjustWriteIterator(uint32_t key)
{
    if (_writeItr.valid() && _writeItr.getKey() < key) {
        _writeItr.binarySeek(key);
    } else {
        _writeItr.lower_bound(key);
    }
}

void
Fixture::insert(uint32_t key)
{
    adjustWriteIterator(key);
    assert(!_writeItr.valid() || _writeItr.getKey() >= key);
    if (!_writeItr.valid() || _writeItr.getKey() != key) {
        _tree.insert(_writeItr, key, 0u);
    }
}

void
Fixture::remove(uint32_t key)
{
    adjustWriteIterator(key);
    assert(!_writeItr.valid() || _writeItr.getKey() >= key);
    if (_writeItr.valid() && _writeItr.getKey() == key) {
        _tree.remove(_writeItr);
    }
}

void
Fixture::readWork(uint32_t cnt)
{
    vespalib::Rand48 rnd;
    rnd.srand48(++_readSeed);
    uint32_t i;
    for (i = 0; i < cnt && _stopRead.load() == 0; ++i) {
        auto guard = _generationHandler.takeGuard();
        uint32_t key = rnd.lrand48() % (_keyLimit + 1);
        MyTreeConstIterator itr = _tree.getFrozenView().lowerBound(key);
        assert(!itr.valid() || itr.getKey() >= key);
    }
    _doneReadWork += i;
    LOG(info, "done %u read work", i);
}

void
Fixture::readWork()
{
    readWork(std::numeric_limits<uint32_t>::max());
}

void
Fixture::writeWork(uint32_t cnt)
{
    vespalib::Rand48 &rnd(_rnd);
    for (uint32_t i = 0; i < cnt; ++i) {
        uint32_t key = rnd.lrand48() % _keyLimit;
        if ((rnd.lrand48() & 1) == 0) {
            insert(key);
        } else {
            remove(key);
        }
        commit();
    }
    _doneWriteWork += cnt;
    _stopRead = 1;
    LOG(info, "done %u write work", cnt);
}

TEST_F("Test manual lower bound call", Fixture)
{
    f.insert(1);
    f.remove(2);
    f.insert(1);
    f.insert(5);
    f.insert(4);
    f.remove(3);
    f.remove(5);
    f.commit();
    auto itr = f._tree.getFrozenView().lowerBound(3);
    EXPECT_TRUE(itr.valid());
    EXPECT_EQUAL(4u, itr.getKey());
}

TEST_F("Test single threaded lower_bound reader without updates", Fixture)
{
    f._reportWork = true;
    f.writeWork(10);
    f._stopRead = 0;
    f.readWork(10);
}

TEST_F("Test single threaded lower_bound reader during updates", Fixture)
{
    uint32_t cnt = 1000000;
    f._reportWork = true;
    f._writer.execute(makeLambdaTask([this, cnt]() { f.writeWork(cnt); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
}

TEST_F("Test multithreaded lower_bound reader during updates", Fixture)
{
    uint32_t cnt = 1000000;
    f._reportWork = true;
    f._writer.execute(makeLambdaTask([this, cnt]() { f.writeWork(cnt); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>Sync executors at end of test to keep *this live until tasks have complete.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/vespalib/btree/btree.h>
#include <vespa/vespalib/btree/btreebuilder.h>
#include <vespa/vespalib/btree/btreenodeallocator.h>
#include <vespa/vespalib/btree/btreeroot.h>
#include <vespa/vespalib/btree/btreestore.h>

#include <vespa/vespalib/util/lambdatask.h>
#include <vespa/vespalib/util/rand48.h>
#include <vespa/vespalib/util/size_literals.h>
#include <vespa/vespalib/util/threadstackexecutor.h>

#include <vespa/vespalib/btree/btreenodeallocator.hpp>
#include <vespa/vespalib/btree/btreenode.hpp>
#include <vespa/vespalib/btree/btreenodestore.hpp>
#include <vespa/vespalib/btree/btreeiterator.hpp>
#include <vespa/vespalib/btree/btreeroot.hpp>
#include <vespa/vespalib/btree/btreebuilder.hpp>
#include <vespa/vespalib/btree/btree.hpp>
#include <vespa/vespalib/btree/btreestore.hpp>
#include <vespa/vespalib/btree/btreeaggregator.hpp>

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

using MyTree = vespalib::btree::BTree<uint32_t, uint32_t>;
using MyTreeIterator = typename MyTree::Iterator;
using MyTreeConstIterator = typename MyTree::ConstIterator;
using GenerationHandler = vespalib::GenerationHandler;
using vespalib::makeLambdaTask;

struct Fixture
{
    GenerationHandler _generationHandler;
    MyTree _tree;
    MyTreeIterator _writeItr;
    vespalib::ThreadStackExecutor _writer; // 1 write thread
    vespalib::ThreadStackExecutor _readers; // multiple reader threads
    vespalib::Rand48 _rnd;
    uint32_t _keyLimit;
    std::atomic<long> _readSeed;
    std::atomic<long> _doneWriteWork;
    std::atomic<long> _doneReadWork;
    std::atomic<int> _stopRead;
    bool _reportWork;

    Fixture();
    ~Fixture();
    void commit();
    void adjustWriteIterator(uint32_t key);
    void insert(uint32_t key);
    void remove(uint32_t key);

    void readWork(uint32_t cnt);
    void readWork();
    void writeWork(uint32_t cnt);
};

Fixture::Fixture()
    : _generationHandler(),
      _tree(),
      _writeItr(_tree.begin()),
      _writer(1, 128_Ki),
      _readers(4, 128_Ki),
      _rnd(),
      _keyLimit(1000000),
      _readSeed(50),
      _doneWriteWork(0),
      _doneReadWork(0),
      _stopRead(0),
      _reportWork(false)
{
    _rnd.srand48(32);
}

Fixture::~Fixture()
{
    _readers.sync();
    _readers.shutdown();
    _writer.sync();
    _writer.shutdown();
    commit();
    if (_reportWork) {
        LOG(info,
            "readWork=%ld, writeWork=%ld",
            _doneReadWork.load(), _doneWriteWork.load());
    }
}

void
Fixture::commit()
{
    auto &allocator = _tree.getAllocator();
    allocator.freeze();
    allocator.transferHoldLists(_generationHandler.getCurrentGeneration());
    _generationHandler.incGeneration();
    allocator.trimHoldLists(_generationHandler.getFirstUsedGeneration());
}

void
Fixture::adjustWriteIterator(uint32_t key)
{
    if (_writeItr.valid() && _writeItr.getKey() < key) {
        _writeItr.binarySeek(key);
    } else {
        _writeItr.lower_bound(key);
    }
}

void
Fixture::insert(uint32_t key)
{
    adjustWriteIterator(key);
    assert(!_writeItr.valid() || _writeItr.getKey() >= key);
    if (!_writeItr.valid() || _writeItr.getKey() != key) {
        _tree.insert(_writeItr, key, 0u);
    }
}

void
Fixture::remove(uint32_t key)
{
    adjustWriteIterator(key);
    assert(!_writeItr.valid() || _writeItr.getKey() >= key);
    if (_writeItr.valid() && _writeItr.getKey() == key) {
        _tree.remove(_writeItr);
    }
}

void
Fixture::readWork(uint32_t cnt)
{
    vespalib::Rand48 rnd;
    rnd.srand48(++_readSeed);
    uint32_t i;
    for (i = 0; i < cnt && _stopRead.load() == 0; ++i) {
        auto guard = _generationHandler.takeGuard();
        uint32_t key = rnd.lrand48() % (_keyLimit + 1);
        MyTreeConstIterator itr = _tree.getFrozenView().lowerBound(key);
        assert(!itr.valid() || itr.getKey() >= key);
    }
    _doneReadWork += i;
    LOG(info, "done %u read work", i);
}

void
Fixture::readWork()
{
    readWork(std::numeric_limits<uint32_t>::max());
}

void
Fixture::writeWork(uint32_t cnt)
{
    vespalib::Rand48 &rnd(_rnd);
    for (uint32_t i = 0; i < cnt; ++i) {
        uint32_t key = rnd.lrand48() % _keyLimit;
        if ((rnd.lrand48() & 1) == 0) {
            insert(key);
        } else {
            remove(key);
        }
        commit();
    }
    _doneWriteWork += cnt;
    _stopRead = 1;
    LOG(info, "done %u write work", cnt);
}

TEST_F("Test manual lower bound call", Fixture)
{
    f.insert(1);
    f.remove(2);
    f.insert(1);
    f.insert(5);
    f.insert(4);
    f.remove(3);
    f.remove(5);
    f.commit();
    auto itr = f._tree.getFrozenView().lowerBound(3);
    EXPECT_TRUE(itr.valid());
    EXPECT_EQUAL(4u, itr.getKey());
}

TEST_F("Test single threaded lower_bound reader without updates", Fixture)
{
    f._reportWork = true;
    f.writeWork(10);
    f._stopRead = 0;
    f.readWork(10);
}

TEST_F("Test single threaded lower_bound reader during updates", Fixture)
{
    uint32_t cnt = 1000000;
    f._reportWork = true;
    f._writer.execute(makeLambdaTask([this, cnt]() { f.writeWork(cnt); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._writer.sync();
    f._readers.sync();
}

TEST_F("Test multithreaded lower_bound reader during updates", Fixture)
{
    uint32_t cnt = 1000000;
    f._reportWork = true;
    f._writer.execute(makeLambdaTask([this, cnt]() { f.writeWork(cnt); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._readers.execute(makeLambdaTask([this]() { f.readWork(); }));
    f._writer.sync();
    f._readers.sync();
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>/**
 * \file
 * \brief testCases object definition
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * 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/.
 *
 * \date 2014-12-06
 */

#include "testCases.hpp"

#include "Thread/threadTestCases.hpp"
#include "SoftwareTimer/softwareTimerTestCases.hpp"
#include "Semaphore/semaphoreTestCases.hpp"
#include "Mutex/mutexTestCases.hpp"
#include "ConditionVariable/conditionVariableTestCases.hpp"
#include "FifoQueue/fifoQueueTestCases.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local objects
+---------------------------------------------------------------------------------------------------------------------*/

/// array with references to TestCaseRange objects
const TestCaseRangeRange::value_type testCases_[]
{
		TestCaseRangeRange::value_type{threadTestCases},
		TestCaseRangeRange::value_type{softwareTimerTestCases},
		TestCaseRangeRange::value_type{semaphoreTestCases},
		TestCaseRangeRange::value_type{mutexTestCases},
		TestCaseRangeRange::value_type{conditionVariableTestCases},
		TestCaseRangeRange::value_type{fifoQueueTestCases},
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global objects
+---------------------------------------------------------------------------------------------------------------------*/

const TestCaseRangeRange testCases {testCases_};

}	// namespace test

}	// namespace distortos
<commit_msg>test: add rawFifoQueueTestCases to executed test cases<commit_after>/**
 * \file
 * \brief testCases object definition
 *
 * \author Copyright (C) 2014 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * 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/.
 *
 * \date 2015-01-02
 */

#include "testCases.hpp"

#include "Thread/threadTestCases.hpp"
#include "SoftwareTimer/softwareTimerTestCases.hpp"
#include "Semaphore/semaphoreTestCases.hpp"
#include "Mutex/mutexTestCases.hpp"
#include "ConditionVariable/conditionVariableTestCases.hpp"
#include "FifoQueue/fifoQueueTestCases.hpp"
#include "RawFifoQueue/rawFifoQueueTestCases.hpp"

namespace distortos
{

namespace test
{

namespace
{

/*---------------------------------------------------------------------------------------------------------------------+
| local objects
+---------------------------------------------------------------------------------------------------------------------*/

/// array with references to TestCaseRange objects
const TestCaseRangeRange::value_type testCases_[]
{
		TestCaseRangeRange::value_type{threadTestCases},
		TestCaseRangeRange::value_type{softwareTimerTestCases},
		TestCaseRangeRange::value_type{semaphoreTestCases},
		TestCaseRangeRange::value_type{mutexTestCases},
		TestCaseRangeRange::value_type{conditionVariableTestCases},
		TestCaseRangeRange::value_type{fifoQueueTestCases},
		TestCaseRangeRange::value_type{rawFifoQueueTestCases},
};

}	// namespace

/*---------------------------------------------------------------------------------------------------------------------+
| global objects
+---------------------------------------------------------------------------------------------------------------------*/

const TestCaseRangeRange testCases {testCases_};

}	// namespace test

}	// namespace distortos
<|endoftext|>


<commit_before>#ifndef _clause_hpp_INCLUDED
#define _clause_hpp_INCLUDED

#include <cstdlib>

#include "iterator.hpp"

namespace CaDiCaL {

// The 'Clause' data structure is very important. There are usually many
// clauses and accessing them is a hot-spot.  Thus we use three common
// optimizations to reduce their memory foot print and improve cache usage.
// Even though is induces some complexity in understanding the actual
// implementation, though arguably not the usage of this data-structure,
// we deem these optimizations for essential.
//
//   (1) The most important optimization is to 'embed' the actual literals
//   in the clause.  This requires a variadic size structure and thus
//   strictly is not 'C' conformant, but supported by all compilers we used.
//   The alternative is to store the actual literals somewhere else, which
//   not only needs more memory but more importantly also requires another
//   memory access and thus is so costly that even for CaDiCaL we want to
//   use this optimization.
//
//   (2) The boolean flags only need one bit each and thus there is enough
//   space left to merge them with a 'glue' bit field (which is less
//   accessed than 'size').  This saves 4 bytes and also keeps the header
//   without 'resolved' nicely in 8 bytes.  We currently use 28 bits and
//   actually since we do not want to mess with 'unsigned' versus 'signed'
//   issues just use 27 out of them.  If more boolean flags are needed this
//   number has to be adapted accordingly.
//
//   (3) Original clauses and clauses with small glue or size are kept
//   anyhow and do not need the activity counter 'resolved'.  Thus we can
//   omit these 8 bytes used for 'resolved' for these clauses.  Redundant
//   clauses of long size and with large glue have a 'resolved' field and
//   are called 'extended'.  The non extended clauses need 8 bytes less and
//   accessing 'resolved' for them is not allowed.
//
// With these three optimizations a binary original clause only needs 16
// bytes instead of 40 bytes without embedding and 32 bytes with embedding
// the literals.  The last two optimizations reduce memory usage of very
// large formulas slightly but are otherwise not that important.
//
// If you want to add few additional boolean flags, add them after the
// sequence of already existing ones.  This makes sure that these bits and
// the following 'glue' field are put into a 4 byte word by the compiler. Of
// course you need to adapt 'LD_MAX_GLUE' accordingly.  Adding one flag
// reduces it by one.
//
// Similarly if you want to add more data to extended clauses put these
// fields after 'resolved' and before the flags section.  Then adapt the
// 'EXTENDED_OFFSET' accordingly.
//
// Additional fields needed for all clauses are safely put between 'glue'
// and 'size' without the need to change anything else.  In general these
// optimizations are local to 'clause.[hc]pp' and otherwise can be ignored
// except that you should for instance never access 'resolved' of a clauses
// which is not extended.  This can be checked with for instance 'valgrind'
// but is also guarded by making the actual '_resolved' field private and
// checking this contract in the 'resolved ()' accessor functions.

#define LD_MAX_GLUE     27      // 32 bits - (5 boolean flags)
#define EXTENDED_OFFSET  8      // sizeof (_resolved)

#define MAX_GLUE ((1<<(LD_MAX_GLUE-1))-1)       // 1 bit less since signed

class Clause {

  long _resolved;        // time stamp when resolved last time

public:

  unsigned extended:1;  // 'resolved' field only valid this is true
  unsigned redundant:1; // aka 'learned' so not 'irredundant' (original)
  unsigned garbage:1;   // can be garbage collected unless it is a 'reason'
  unsigned reason:1;    // reason / antecedent clause can not be collected
  unsigned moved:1;     // moved during garbage collector ('copy' valid)

  // This is the 'glue' = 'glucose level' = 'LBD' of a redundant clause.
  // We actually only use 'CLAUSE_LD_MAX_GLUE-1' bits since the field is
  // 'signed' to avoid surprises due to 'unsigned' versus 'sign' semantics.
  // Also note that 'C' does not explicitly define 'signedness' of 'int' bit
  // fields and thus we explicitly have to use 'signed' here (on an IBM main
  // frame or on Sparc 'int a:1' might be 'unsigned').
  //
  signed int glue : LD_MAX_GLUE;

  int size;             // actual size of 'literals' (at least 2)
  int literals[2];      // of variadic 'size' (not just 2) in general

  long & resolved () { assert (extended); return _resolved; }
  const long & resolved () const { assert (extended); return _resolved; }

  literal_iterator       begin ()       { return literals; }
  literal_iterator         end ()       { return literals + size; }
  const_literal_iterator begin () const { return literals; }
  const_literal_iterator   end () const { return literals + size; }

  // Actual start of allocated memory, bytes allocated and offset are only
  // used for memory (de)allocation in 'delete_clause' and in the moving
  // garbage collector 'move_non_garbage_clauses'.
  //
  char * start () const;        // actual start of allocated memory
  size_t bytes () const;        // actual number of bytes allocated
  size_t offset () const;       // offset of valid bytes (start - this)

  // Ready to be collected and deleted.
  //
  bool collect () const { return !reason && garbage; }
};

struct resolved_earlier {
  bool operator () (const Clause * a, const Clause * b) {
    assert (a->extended), assert (b->extended);
    return a->resolved () < b->resolved ();
  }
};

};

#endif
<commit_msg>new comment<commit_after>#ifndef _clause_hpp_INCLUDED
#define _clause_hpp_INCLUDED

#include <cstdlib>

#include "iterator.hpp"

namespace CaDiCaL {

// The 'Clause' data structure is very important. There are usually many
// clauses and accessing them is a hot-spot.  Thus we use three common
// optimizations to reduce their memory foot print and improve cache usage.
// Even though is induces some complexity in understanding the actual
// implementation, though arguably not the usage of this data-structure,
// we deem these optimizations for essential.
//
//   (1) The most important optimization is to 'embed' the actual literals
//   in the clause.  This requires a variadic size structure and thus
//   strictly is not 'C' conformant, but supported by all compilers we used.
//   The alternative is to store the actual literals somewhere else, which
//   not only needs more memory but more importantly also requires another
//   memory access and thus is so costly that even for CaDiCaL we want to
//   use this optimization.
//
//   (2) The boolean flags only need one bit each and thus there is enough
//   space left to merge them with a 'glue' bit field (which is less
//   accessed than 'size').  This saves 4 bytes and also keeps the header
//   without 'resolved' nicely in 8 bytes.  We currently use 28 bits and
//   actually since we do not want to mess with 'unsigned' versus 'signed'
//   issues just use 27 out of them.  If more boolean flags are needed this
//   number has to be adapted accordingly.
//
//   (3) Original clauses and clauses with small glue or size are kept
//   anyhow and do not need the activity counter 'resolved'.  Thus we can
//   omit these 8 bytes used for 'resolved' for these clauses.  Redundant
//   clauses of long size and with large glue have a 'resolved' field and
//   are called 'extended'.  The non extended clauses need 8 bytes less and
//   accessing 'resolved' for them is not allowed.
//
// With these three optimizations a binary original clause only needs 16
// bytes instead of 40 bytes without embedding and 32 bytes with embedding
// the literals.  The last two optimizations reduce memory usage of very
// large formulas slightly but are otherwise not that important.
//
// If you want to add few additional boolean flags, add them after the
// sequence of already existing ones.  This makes sure that these bits and
// the following 'glue' field are put into a 4 byte word by the compiler. Of
// course you need to adapt 'LD_MAX_GLUE' accordingly.  Adding one flag
// reduces it by one.
//
// Similarly if you want to add more data to extended clauses put these
// fields after 'resolved' and before the flags section.  Then adapt the
// 'EXTENDED_OFFSET' accordingly.
//
// Additional fields needed for all clauses are safely put between 'glue'
// and 'size' without the need to change anything else.  In general these
// optimizations are local to 'clause.[hc]pp' and otherwise can be ignored
// except that you should for instance never access 'resolved' of a clauses
// which is not extended.  This can be checked with for instance 'valgrind'
// but is also guarded by making the actual '_resolved' field private and
// checking this contract in the 'resolved ()' accessor functions.

#define LD_MAX_GLUE     27      // 32 bits - (5 boolean flags)
#define EXTENDED_OFFSET  8      // sizeof (_resolved)

#define MAX_GLUE ((1<<(LD_MAX_GLUE-1))-1)       // 1 bit less since signed

class Clause {

  long _resolved;        // time stamp when resolved last time

public:

  unsigned extended:1;  // 'resolved' field only valid this is true
  unsigned redundant:1; // aka 'learned' so not 'irredundant' (original)
  unsigned garbage:1;   // can be garbage collected unless it is a 'reason'
  unsigned reason:1;    // reason / antecedent clause can not be collected
  unsigned moved:1;     // moved during garbage collector ('copy' valid)

  // This is the 'glue' = 'glucose level' = 'LBD' of a redundant clause.
  // We actually only use 'CLAUSE_LD_MAX_GLUE-1' bits since the field is
  // 'signed' to avoid surprises due to 'unsigned' versus 'sign' semantics.
  // Also note that 'C' does not explicitly define 'signedness' of 'int' bit
  // fields and thus we explicitly have to use 'signed' here (on an IBM main
  // frame or on Sparc 'int a:1' might be 'unsigned').
  //
  signed int glue : LD_MAX_GLUE;

  int size;             // actual size of 'literals' (at least 2)
  int literals[2];      // of variadic 'size' (not just 2) in general

  long & resolved () { assert (extended); return _resolved; }
  const long & resolved () const { assert (extended); return _resolved; }

  literal_iterator       begin ()       { return literals; }
  literal_iterator         end ()       { return literals + size; }
  const_literal_iterator begin () const { return literals; }
  const_literal_iterator   end () const { return literals + size; }

  // Actual start of allocated memory, bytes allocated and offset are only
  // used for memory (de)allocation in 'delete_clause' and in the moving
  // garbage collector 'move_non_garbage_clauses' and 'move_clause'.
  //
  char * start () const;        // actual start of allocated memory
  size_t bytes () const;        // actual number of bytes allocated
  size_t offset () const;       // offset of valid bytes (start - this)

  // Ready to be collected and deleted.
  //
  bool collect () const { return !reason && garbage; }
};

struct resolved_earlier {
  bool operator () (const Clause * a, const Clause * b) {
    assert (a->extended), assert (b->extended);
    return a->resolved () < b->resolved ();
  }
};

};

#endif
<|endoftext|>


<commit_before>#include <tuple>
#include <cassert>
#include "client.hpp"

namespace net
{

void tcp_client::on_connect(uv_connect_t* req, int status)
{
	if (status == 0) {
		uv_read_start(req->handle,
				net::tcp_client::on_tcp_alloc_buffer,
				net::tcp_client::on_tcp_read);

		auto tcp = reinterpret_cast<client_socket*>(req->handle->data);
		auto client = tcp->manager();

		tcp->set_status(tcp_status::connected);

		if (client->_tcp_connect) client->_tcp_connect(tcp);

		uv_check_start(&(client->_send_loop), net::tcp_client::send_loop);
	} else {

	}

	delete req;
}

void tcp_client::on_tcp_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
	auto tcp = reinterpret_cast<client_socket*>(handle->data);
	auto buffer = tcp->input();

	void* buff = nullptr;
	size_t len = 0;
	std::tie(buff, len) = buffer->malloc();
	buf->base = reinterpret_cast<char*>(buff);
	buf->len = len;
}

void tcp_client::on_tcp_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
	auto tcp = reinterpret_cast<client_socket*>(stream->data);
	auto client = tcp->manager();

	if (nread > 0) {
		auto buffer = tcp->input();
		buffer->skip(buffer::skip_type::write, nread);
		if (client->_tcp_read) client->_tcp_read(tcp);
	} else {
		client->disconnect();
	}
}

void tcp_client::on_tcp_close(uv_handle_t* handle)
{
	auto tcp = reinterpret_cast<client_socket*>(handle->data);
	auto client = tcp->manager();

	client->_socket = nullptr;
	delete tcp;
}

void tcp_client::send_loop(uv_check_t* handle)
{
	tcp_client* client = reinterpret_cast<tcp_client*>(handle->data);

	if (client->_socket == nullptr) return;

	auto tcp = client->_socket;
	auto buffer = tcp->output();
	for (;;) {
		auto block = buffer->pop();
		if (block == nullptr) break;

		if (block->size() < 1) {
			buffer->free(block);
			continue;
		}

		uv_write_t* req = new uv_write_t;
		req->data = reinterpret_cast<void*>(block);

		uv_buf_t buf = {
			.base = reinterpret_cast<char*>(block->data()),
			.len = block->size()
		};

		uv_write(req, reinterpret_cast<uv_stream_t*>(tcp->socket()),
				&buf, 1, net::tcp_client::on_data_write);
	}
}

void tcp_client::on_data_write(uv_write_t* req, int status)
{
	auto block = reinterpret_cast<buffer::block*>(req->data);
	auto tcp = reinterpret_cast<client_socket*>(req->handle->data);
	tcp->output()->free(block);

	if (status < 0) {
		auto client = tcp->manager();
		client->disconnect();
	}

	delete req;
}

tcp_client::tcp_client(uv_loop_t* loop)
{
	_loop = loop;

	uv_check_init(loop, &_send_loop);
	_send_loop.data = reinterpret_cast<void*>(this);
}

tcp_client::~tcp_client()
{
	disconnect();
}

bool tcp_client::disconnect()
{
	if (_socket == nullptr) return true;
	if (_socket->status() == tcp_status::destroy) return true;

	_socket->set_status(tcp_status::destroy);

	if (_tcp_close) _tcp_close(_socket);

	uv_check_stop(&_send_loop);
	uv_close(reinterpret_cast<uv_handle_t*>(_socket->socket()), tcp_client::on_tcp_close);

	return true;
}

bool tcp_client::is_connect()
{
	if (_socket == nullptr) return false;
	return _socket->status() == tcp_status::connected;
}

bool tcp_client::connect(char* ip, int port)
{
	assert(_socket == nullptr);

	struct sockaddr_in dest;
	if (uv_ip4_addr(ip, port, &dest) != 0) return false;

	_socket = new client_socket(_loop, this);
	uv_connect_t* connect = new uv_connect_t;
	int r = uv_tcp_connect(connect, _socket->socket(),
			reinterpret_cast<struct sockaddr*>(&dest),
			net::tcp_client::on_connect);

	if (r != 0) {
		delete _socket;
		_socket = nullptr;
	}

	return r == 0;
}

bool tcp_client::send(char* src, size_t len)
{
	if (_socket == nullptr) return false;
	if (_socket->status() != tcp_status::connected) return false;

	_socket->output()->write(src, len);
	return true;
}

void tcp_client::set_tcp_connection_cb(std::function<void(client_socket*)> cb)
{
	_tcp_connect = cb;
}

void tcp_client::set_tcp_close_cb(std::function<void(client_socket*)> cb)
{
	_tcp_close = cb;
}

void tcp_client::set_read_cb(std::function<void(client_socket*)> cb)
{
	_tcp_read = cb;
}

}
<commit_msg>connect fail del socket<commit_after>#include <tuple>
#include <cassert>
#include "client.hpp"

namespace net
{

void tcp_client::on_connect(uv_connect_t* req, int status)
{
	auto tcp = reinterpret_cast<client_socket*>(req->handle->data);
	auto client = tcp->manager();

	if (status == 0) {
		uv_read_start(req->handle,
				net::tcp_client::on_tcp_alloc_buffer,
				net::tcp_client::on_tcp_read);

		tcp->set_status(tcp_status::connected);

		if (client->_tcp_connect) client->_tcp_connect(tcp);

		uv_check_start(&(client->_send_loop), net::tcp_client::send_loop);
	} else {
		delete client->_socket;
		client->_socket = nullptr;
	}

	delete req;
}

void tcp_client::on_tcp_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
{
	auto tcp = reinterpret_cast<client_socket*>(handle->data);
	auto buffer = tcp->input();

	void* buff = nullptr;
	size_t len = 0;
	std::tie(buff, len) = buffer->malloc();
	buf->base = reinterpret_cast<char*>(buff);
	buf->len = len;
}

void tcp_client::on_tcp_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
{
	auto tcp = reinterpret_cast<client_socket*>(stream->data);
	auto client = tcp->manager();

	if (nread > 0) {
		auto buffer = tcp->input();
		buffer->skip(buffer::skip_type::write, nread);
		if (client->_tcp_read) client->_tcp_read(tcp);
	} else {
		client->disconnect();
	}
}

void tcp_client::on_tcp_close(uv_handle_t* handle)
{
	auto tcp = reinterpret_cast<client_socket*>(handle->data);
	auto client = tcp->manager();

	client->_socket = nullptr;
	delete tcp;
}

void tcp_client::send_loop(uv_check_t* handle)
{
	tcp_client* client = reinterpret_cast<tcp_client*>(handle->data);

	if (client->_socket == nullptr) return;

	auto tcp = client->_socket;
	auto buffer = tcp->output();
	for (;;) {
		auto block = buffer->pop();
		if (block == nullptr) break;

		if (block->size() < 1) {
			buffer->free(block);
			continue;
		}

		uv_write_t* req = new uv_write_t;
		req->data = reinterpret_cast<void*>(block);

		uv_buf_t buf = {
			.base = reinterpret_cast<char*>(block->data()),
			.len = block->size()
		};

		uv_write(req, reinterpret_cast<uv_stream_t*>(tcp->socket()),
				&buf, 1, net::tcp_client::on_data_write);
	}
}

void tcp_client::on_data_write(uv_write_t* req, int status)
{
	auto block = reinterpret_cast<buffer::block*>(req->data);
	auto tcp = reinterpret_cast<client_socket*>(req->handle->data);
	tcp->output()->free(block);

	if (status < 0) {
		auto client = tcp->manager();
		client->disconnect();
	}

	delete req;
}

tcp_client::tcp_client(uv_loop_t* loop)
{
	_loop = loop;

	uv_check_init(loop, &_send_loop);
	_send_loop.data = reinterpret_cast<void*>(this);
}

tcp_client::~tcp_client()
{
	disconnect();
}

bool tcp_client::disconnect()
{
	if (_socket == nullptr) return true;
	if (_socket->status() == tcp_status::destroy) return true;

	_socket->set_status(tcp_status::destroy);

	if (_tcp_close) _tcp_close(_socket);

	uv_check_stop(&_send_loop);
	uv_close(reinterpret_cast<uv_handle_t*>(_socket->socket()), tcp_client::on_tcp_close);

	return true;
}

bool tcp_client::is_connect()
{
	if (_socket == nullptr) return false;
	return _socket->status() == tcp_status::connected;
}

bool tcp_client::connect(char* ip, int port)
{
	assert(_socket == nullptr);

	struct sockaddr_in dest;
	if (uv_ip4_addr(ip, port, &dest) != 0) return false;

	_socket = new client_socket(_loop, this);
	uv_connect_t* connect = new uv_connect_t;
	int r = uv_tcp_connect(connect, _socket->socket(),
			reinterpret_cast<struct sockaddr*>(&dest),
			net::tcp_client::on_connect);

	if (r != 0) {
		delete _socket;
		_socket = nullptr;
	}

	return r == 0;
}

bool tcp_client::send(char* src, size_t len)
{
	if (_socket == nullptr) return false;
	if (_socket->status() != tcp_status::connected) return false;

	_socket->output()->write(src, len);
	return true;
}

void tcp_client::set_tcp_connection_cb(std::function<void(client_socket*)> cb)
{
	_tcp_connect = cb;
}

void tcp_client::set_tcp_close_cb(std::function<void(client_socket*)> cb)
{
	_tcp_close = cb;
}

void tcp_client::set_read_cb(std::function<void(client_socket*)> cb)
{
	_tcp_read = cb;
}

}
<|endoftext|>