<commit_before>#include "CodeGen_Internal.h"
#include "IROperator.h"
#include "CSE.h"
#include "Debug.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;

using namespace llvm;

namespace {

vector<llvm::Type*> llvm_types(const Closure& closure, llvm::StructType *buffer_t, LLVMContext &context) {
    vector<llvm::Type *> res;
    for (const pair<string, Type> &i : closure.vars) {
        res.push_back(llvm_type_of(&context, i.second));
    }
    for (const pair<string, Closure::BufferRef> &i : closure.buffers) {
        res.push_back(llvm_type_of(&context, i.second.type)->getPointerTo());
        res.push_back(buffer_t->getPointerTo());
    }
    return res;
}

}  // namespace

StructType *build_closure_type(const Closure& closure, llvm::StructType *buffer_t, LLVMContext *context) {
    StructType *struct_t = StructType::create(*context, "closure_t");
    struct_t->setBody(llvm_types(closure, buffer_t, *context), false);
    return struct_t;
}

void pack_closure(llvm::Type *
#if LLVM_VERSION >= 37
                  type
#endif
                  ,
                  Value *dst,
                  const Closure& closure,
                  const Scope<Value *> &src,
                  llvm::StructType *buffer_t,
                  IRBuilder<> *builder) {
    // type, type of dst should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = closure.names();
    vector<llvm::Type*> ty = llvm_types(closure, buffer_t, context);
    for (size_t i = 0; i < nm.size(); i++) {
#if LLVM_VERSION >= 37
        Value *ptr = builder->CreateConstInBoundsGEP2_32(type, dst, 0, idx);
#else
        Value *ptr = builder->CreateConstInBoundsGEP2_32(dst, 0, idx);
#endif
        Value *val;
        if (!ends_with(nm[i], ".buffer") || src.contains(nm[i])) {
            val = src.get(nm[i]);
            if (val->getType() != ty[i]) {
                val = builder->CreateBitCast(val, ty[i]);
            }
        } else {
            // Skip over buffers not in the symbol table. They must not be needed.
            val = ConstantPointerNull::get(buffer_t->getPointerTo());
        }
        builder->CreateStore(val, ptr);
        idx++;
    }
}

void unpack_closure(const Closure& closure,
                    Scope<Value *> &dst,
                    llvm::Type *
#if LLVM_VERSION >= 37
                    type
#endif
                    ,
                    Value *src,
                    IRBuilder<> *builder) {
    // type, type of src should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = closure.names();
    for (size_t i = 0; i < nm.size(); i++) {
#if LLVM_VERSION >= 37
        Value *ptr = builder->CreateConstInBoundsGEP2_32(type, src, 0, idx++);
#else
        Value *ptr = builder->CreateConstInBoundsGEP2_32(src, 0, idx++);
#endif
        LoadInst *load = builder->CreateLoad(ptr);
        if (load->getType()->isPointerTy()) {
            // Give it a unique type so that tbaa tells llvm that this can't alias anything
            LLVMMDNodeArgumentType md_args[] = {MDString::get(context, nm[i])};
            load->setMetadata("tbaa", MDNode::get(context, md_args));
        }
        dst.push(nm[i], load);
        load->setName(nm[i]);
    }
}

llvm::Type *llvm_type_of(LLVMContext *c, Halide::Type t) {
    if (t.lanes() == 1) {
        if (t.is_float()) {
            switch (t.bits()) {
            case 16:
                return llvm::Type::getHalfTy(*c);
            case 32:
                return llvm::Type::getFloatTy(*c);
            case 64:
                return llvm::Type::getDoubleTy(*c);
            default:
                internal_error << "There is no llvm type matching this floating-point bit width: " << t << "\n";
                return nullptr;
            }
        } else if (t.is_handle()) {
            return llvm::Type::getInt8PtrTy(*c);
        } else {
            return llvm::Type::getIntNTy(*c, t.bits());
        }
    } else {
        llvm::Type *element_type = llvm_type_of(c, t.element_of());
        return VectorType::get(element_type, t.lanes());
    }
}

// Returns true if the given function name is one of the Halide runtime
// functions that takes a user_context pointer as its first parameter.
bool function_takes_user_context(const std::string &name) {
    static const char *user_context_runtime_funcs[] = {
        "halide_copy_to_host",
        "halide_copy_to_device",
        "halide_current_time_ns",
        "halide_debug_to_file",
        "halide_device_free",
        "halide_device_malloc",
        "halide_device_sync",
        "halide_do_par_for",
        "halide_do_task",
        "halide_error",
        "halide_free",
        "halide_malloc",
        "halide_print",
        "halide_profiler_memory_allocate",
        "halide_profiler_memory_free",
        "halide_profiler_pipeline_start",
        "halide_profiler_pipeline_end",
        "halide_profiler_stack_peak_update",
        "halide_spawn_thread",
        "halide_device_release",
        "halide_start_clock",
        "halide_trace",
        "halide_memoization_cache_lookup",
        "halide_memoization_cache_store",
        "halide_memoization_cache_release",
        "halide_cuda_run",
        "halide_opencl_run",
        "halide_opengl_run",
        "halide_openglcompute_run",
        "halide_renderscript_run",
        "halide_metal_run",
        "halide_cuda_initialize_kernels",
        "halide_opencl_initialize_kernels",
        "halide_opengl_initialize_kernels",
        "halide_openglcompute_initialize_kernels",
        "halide_renderscript_initialize_kernels",
        "halide_metal_initialize_kernels",
        "halide_get_gpu_device",
    };
    const int num_funcs = sizeof(user_context_runtime_funcs) /
        sizeof(user_context_runtime_funcs[0]);
    for (int i = 0; i < num_funcs; ++i) {
        if (name == user_context_runtime_funcs[i]) {
            return true;
        }
    }
    // The error functions all take a user context
    return starts_with(name, "halide_error_");
}

bool can_allocation_fit_on_stack(int32_t size) {
    user_assert(size > 0) << "Allocation size should be a positive number\n";
    return (size <= 1024 * 16);
}

Expr lower_euclidean_div(Expr a, Expr b) {
    internal_assert(a.type() == b.type());
    // IROperator's div_round_to_zero will replace this with a / b for
    // unsigned ops, so create the intrinsic directly.
    Expr q = Call::make(a.type(), Call::div_round_to_zero, {a, b}, Call::PureIntrinsic);
    if (a.type().is_int()) {
        // Signed integer division sucks. It should be defined such
        // that it satisifies (a/b)*b + a%b = a, where 0 <= a%b < |b|,
        // i.e. Euclidean division.

        // We get rounding to work by examining the implied remainder
        // and correcting the quotient.

        /* Here's the C code that we're trying to match:
           int q = a / b;
           int r = a - q * b;
           int bs = b >> (t.bits() - 1);
           int rs = r >> (t.bits() - 1);
           return q - (rs & bs) + (rs & ~bs);
        */

        Expr r = a - q*b;
        Expr bs = b >> (a.type().bits() - 1);
        Expr rs = r >> (a.type().bits() - 1);
        q = q - (rs & bs) + (rs & ~bs);
        return common_subexpression_elimination(q);
    } else {
        return q;
    }
}

Expr lower_euclidean_mod(Expr a, Expr b) {
    internal_assert(a.type() == b.type());
    // IROperator's mod_round_to_zero will replace this with a % b for
    // unsigned ops, so create the intrinsic directly.
    Expr r = Call::make(a.type(), Call::mod_round_to_zero, {a, b}, Call::PureIntrinsic);
    if (a.type().is_int()) {
        // Match this non-overflowing C code
        /*
          T r = a % b;
          r = r + (r < 0 ? abs(b) : 0);
        */

        r = select(r < 0, r + abs(b), r);
        return common_subexpression_elimination(r);
    } else {
        return r;
    }
}

bool get_md_bool(LLVMMDNodeArgumentType value, bool &result) {
    if (!value) {
        return false;
    }
    #if LLVM_VERSION < 36 || defined(WITH_NATIVE_CLIENT)
    llvm::ConstantInt *c = llvm::cast<llvm::ConstantInt>(value);
    #else
    llvm::ConstantAsMetadata *cam = llvm::cast<llvm::ConstantAsMetadata>(value);
    if (!cam) {
        return false;
    }
    llvm::ConstantInt *c = llvm::cast<llvm::ConstantInt>(cam->getValue());
    #endif
    if (!c) {
        return false;
    }
    result = !c->isZero();
    return true;
}

bool get_md_string(LLVMMDNodeArgumentType value, std::string &result) {
    if (!value) {
        result = "";
        return false;
    }
    #if LLVM_VERSION < 36
    if (llvm::dyn_cast<llvm::ConstantAggregateZero>(value)) {
        result = "";
        return true;
    }
    llvm::ConstantDataArray *c = llvm::cast<llvm::ConstantDataArray>(value);
    if (c) {
        result = c->getAsCString();
        return true;
    }
    #else
    llvm::MDString *c = llvm::dyn_cast<llvm::MDString>(value);
    if (c) {
        result = c->getString();
        return true;
    }
    #endif
    return false;
}

void get_target_options(const llvm::Module &module, llvm::TargetOptions &options, std::string &mcpu, std::string &mattrs) {
    bool use_soft_float_abi = false;
    get_md_bool(module.getModuleFlag("halide_use_soft_float_abi"), use_soft_float_abi);
    get_md_string(module.getModuleFlag("halide_mcpu"), mcpu);
    get_md_string(module.getModuleFlag("halide_mattrs"), mattrs);

    options = llvm::TargetOptions();
    options.LessPreciseFPMADOption = true;
    options.AllowFPOpFusion = llvm::FPOpFusion::Fast;
    // Disabled because it makes LLVM compile vector division to
    // multiplication by approx reciprocal, which is too inaccurate
    // even for us.
    options.UnsafeFPMath = false;
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    #if LLVM_VERSION < 37
    options.NoFramePointerElim = false;
    options.UseSoftFloat = false;
    #endif
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    #if LLVM_VERSION < 37
    options.DisableTailCalls = false;
    #endif
    options.StackAlignmentOverride = 0;
    #if LLVM_VERSION < 37
    options.TrapFuncName = "";
    #endif
    options.FunctionSections = true;
    #ifdef WITH_NATIVE_CLIENT
    options.UseInitArray = true;
    #else
    options.UseInitArray = false;
    #endif
    options.FloatABIType =
        use_soft_float_abi ? llvm::FloatABI::Soft : llvm::FloatABI::Hard;
}

void clone_target_options(const llvm::Module &from, llvm::Module &to) {
    to.setTargetTriple(from.getTargetTriple());

    llvm::LLVMContext &context = to.getContext();

    bool use_soft_float_abi = false;
    if (get_md_bool(from.getModuleFlag("halide_use_soft_float_abi"), use_soft_float_abi))
        to.addModuleFlag(llvm::Module::Warning, "halide_use_soft_float_abi", use_soft_float_abi ? 1 : 0);

    std::string mcpu;
    if (get_md_string(from.getModuleFlag("halide_mcpu"), mcpu)) {
        #if LLVM_VERSION < 36
        to.addModuleFlag(llvm::Module::Warning, "halide_mcpu", llvm::ConstantDataArray::getString(context, mcpu));
        #else
        to.addModuleFlag(llvm::Module::Warning, "halide_mcpu", llvm::MDString::get(context, mcpu));
        #endif
    }

    std::string mattrs;
    if (get_md_string(from.getModuleFlag("halide_mattrs"), mattrs)) {
        #if LLVM_VERSION < 36
        to.addModuleFlag(llvm::Module::Warning, "halide_mattrs", llvm::ConstantDataArray::getString(context, mattrs));
        #else
        to.addModuleFlag(llvm::Module::Warning, "halide_mattrs", llvm::MDString::get(context, mattrs));
        #endif
    }
}

llvm::TargetMachine *get_target_machine(const llvm::Module &module) {
    std::string error_string;

    const llvm::Target *target = llvm::TargetRegistry::lookupTarget(module.getTargetTriple(), error_string);
    if (!target) {
        std::cout << error_string << std::endl;
        llvm::TargetRegistry::printRegisteredTargetsForVersion();
    }
    internal_assert(target) << "Could not create target for " << module.getTargetTriple() << "\n";

    llvm::TargetOptions options;
    std::string mcpu = "";
    std::string mattrs = "";
    get_target_options(module, options, mcpu, mattrs);

    return target->createTargetMachine(module.getTargetTriple(),
                                       mcpu, mattrs,
                                       options,
                                       llvm::Reloc::PIC_,
                                       llvm::CodeModel::Default,
                                       llvm::CodeGenOpt::Aggressive);
}

}
}
<commit_msg>Turn off unsafe fp math on x86 only<commit_after>#include "CodeGen_Internal.h"
#include "IROperator.h"
#include "CSE.h"
#include "Debug.h"

namespace Halide {
namespace Internal {

using std::string;
using std::map;
using std::vector;
using std::pair;

using namespace llvm;

namespace {

vector<llvm::Type*> llvm_types(const Closure& closure, llvm::StructType *buffer_t, LLVMContext &context) {
    vector<llvm::Type *> res;
    for (const pair<string, Type> &i : closure.vars) {
        res.push_back(llvm_type_of(&context, i.second));
    }
    for (const pair<string, Closure::BufferRef> &i : closure.buffers) {
        res.push_back(llvm_type_of(&context, i.second.type)->getPointerTo());
        res.push_back(buffer_t->getPointerTo());
    }
    return res;
}

}  // namespace

StructType *build_closure_type(const Closure& closure, llvm::StructType *buffer_t, LLVMContext *context) {
    StructType *struct_t = StructType::create(*context, "closure_t");
    struct_t->setBody(llvm_types(closure, buffer_t, *context), false);
    return struct_t;
}

void pack_closure(llvm::Type *
#if LLVM_VERSION >= 37
                  type
#endif
                  ,
                  Value *dst,
                  const Closure& closure,
                  const Scope<Value *> &src,
                  llvm::StructType *buffer_t,
                  IRBuilder<> *builder) {
    // type, type of dst should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = closure.names();
    vector<llvm::Type*> ty = llvm_types(closure, buffer_t, context);
    for (size_t i = 0; i < nm.size(); i++) {
#if LLVM_VERSION >= 37
        Value *ptr = builder->CreateConstInBoundsGEP2_32(type, dst, 0, idx);
#else
        Value *ptr = builder->CreateConstInBoundsGEP2_32(dst, 0, idx);
#endif
        Value *val;
        if (!ends_with(nm[i], ".buffer") || src.contains(nm[i])) {
            val = src.get(nm[i]);
            if (val->getType() != ty[i]) {
                val = builder->CreateBitCast(val, ty[i]);
            }
        } else {
            // Skip over buffers not in the symbol table. They must not be needed.
            val = ConstantPointerNull::get(buffer_t->getPointerTo());
        }
        builder->CreateStore(val, ptr);
        idx++;
    }
}

void unpack_closure(const Closure& closure,
                    Scope<Value *> &dst,
                    llvm::Type *
#if LLVM_VERSION >= 37
                    type
#endif
                    ,
                    Value *src,
                    IRBuilder<> *builder) {
    // type, type of src should be a pointer to a struct of the type returned by build_type
    int idx = 0;
    LLVMContext &context = builder->getContext();
    vector<string> nm = closure.names();
    for (size_t i = 0; i < nm.size(); i++) {
#if LLVM_VERSION >= 37
        Value *ptr = builder->CreateConstInBoundsGEP2_32(type, src, 0, idx++);
#else
        Value *ptr = builder->CreateConstInBoundsGEP2_32(src, 0, idx++);
#endif
        LoadInst *load = builder->CreateLoad(ptr);
        if (load->getType()->isPointerTy()) {
            // Give it a unique type so that tbaa tells llvm that this can't alias anything
            LLVMMDNodeArgumentType md_args[] = {MDString::get(context, nm[i])};
            load->setMetadata("tbaa", MDNode::get(context, md_args));
        }
        dst.push(nm[i], load);
        load->setName(nm[i]);
    }
}

llvm::Type *llvm_type_of(LLVMContext *c, Halide::Type t) {
    if (t.lanes() == 1) {
        if (t.is_float()) {
            switch (t.bits()) {
            case 16:
                return llvm::Type::getHalfTy(*c);
            case 32:
                return llvm::Type::getFloatTy(*c);
            case 64:
                return llvm::Type::getDoubleTy(*c);
            default:
                internal_error << "There is no llvm type matching this floating-point bit width: " << t << "\n";
                return nullptr;
            }
        } else if (t.is_handle()) {
            return llvm::Type::getInt8PtrTy(*c);
        } else {
            return llvm::Type::getIntNTy(*c, t.bits());
        }
    } else {
        llvm::Type *element_type = llvm_type_of(c, t.element_of());
        return VectorType::get(element_type, t.lanes());
    }
}

// Returns true if the given function name is one of the Halide runtime
// functions that takes a user_context pointer as its first parameter.
bool function_takes_user_context(const std::string &name) {
    static const char *user_context_runtime_funcs[] = {
        "halide_copy_to_host",
        "halide_copy_to_device",
        "halide_current_time_ns",
        "halide_debug_to_file",
        "halide_device_free",
        "halide_device_malloc",
        "halide_device_sync",
        "halide_do_par_for",
        "halide_do_task",
        "halide_error",
        "halide_free",
        "halide_malloc",
        "halide_print",
        "halide_profiler_memory_allocate",
        "halide_profiler_memory_free",
        "halide_profiler_pipeline_start",
        "halide_profiler_pipeline_end",
        "halide_profiler_stack_peak_update",
        "halide_spawn_thread",
        "halide_device_release",
        "halide_start_clock",
        "halide_trace",
        "halide_memoization_cache_lookup",
        "halide_memoization_cache_store",
        "halide_memoization_cache_release",
        "halide_cuda_run",
        "halide_opencl_run",
        "halide_opengl_run",
        "halide_openglcompute_run",
        "halide_renderscript_run",
        "halide_metal_run",
        "halide_cuda_initialize_kernels",
        "halide_opencl_initialize_kernels",
        "halide_opengl_initialize_kernels",
        "halide_openglcompute_initialize_kernels",
        "halide_renderscript_initialize_kernels",
        "halide_metal_initialize_kernels",
        "halide_get_gpu_device",
    };
    const int num_funcs = sizeof(user_context_runtime_funcs) /
        sizeof(user_context_runtime_funcs[0]);
    for (int i = 0; i < num_funcs; ++i) {
        if (name == user_context_runtime_funcs[i]) {
            return true;
        }
    }
    // The error functions all take a user context
    return starts_with(name, "halide_error_");
}

bool can_allocation_fit_on_stack(int32_t size) {
    user_assert(size > 0) << "Allocation size should be a positive number\n";
    return (size <= 1024 * 16);
}

Expr lower_euclidean_div(Expr a, Expr b) {
    internal_assert(a.type() == b.type());
    // IROperator's div_round_to_zero will replace this with a / b for
    // unsigned ops, so create the intrinsic directly.
    Expr q = Call::make(a.type(), Call::div_round_to_zero, {a, b}, Call::PureIntrinsic);
    if (a.type().is_int()) {
        // Signed integer division sucks. It should be defined such
        // that it satisifies (a/b)*b + a%b = a, where 0 <= a%b < |b|,
        // i.e. Euclidean division.

        // We get rounding to work by examining the implied remainder
        // and correcting the quotient.

        /* Here's the C code that we're trying to match:
           int q = a / b;
           int r = a - q * b;
           int bs = b >> (t.bits() - 1);
           int rs = r >> (t.bits() - 1);
           return q - (rs & bs) + (rs & ~bs);
        */

        Expr r = a - q*b;
        Expr bs = b >> (a.type().bits() - 1);
        Expr rs = r >> (a.type().bits() - 1);
        q = q - (rs & bs) + (rs & ~bs);
        return common_subexpression_elimination(q);
    } else {
        return q;
    }
}

Expr lower_euclidean_mod(Expr a, Expr b) {
    internal_assert(a.type() == b.type());
    // IROperator's mod_round_to_zero will replace this with a % b for
    // unsigned ops, so create the intrinsic directly.
    Expr r = Call::make(a.type(), Call::mod_round_to_zero, {a, b}, Call::PureIntrinsic);
    if (a.type().is_int()) {
        // Match this non-overflowing C code
        /*
          T r = a % b;
          r = r + (r < 0 ? abs(b) : 0);
        */

        r = select(r < 0, r + abs(b), r);
        return common_subexpression_elimination(r);
    } else {
        return r;
    }
}

bool get_md_bool(LLVMMDNodeArgumentType value, bool &result) {
    if (!value) {
        return false;
    }
    #if LLVM_VERSION < 36 || defined(WITH_NATIVE_CLIENT)
    llvm::ConstantInt *c = llvm::cast<llvm::ConstantInt>(value);
    #else
    llvm::ConstantAsMetadata *cam = llvm::cast<llvm::ConstantAsMetadata>(value);
    if (!cam) {
        return false;
    }
    llvm::ConstantInt *c = llvm::cast<llvm::ConstantInt>(cam->getValue());
    #endif
    if (!c) {
        return false;
    }
    result = !c->isZero();
    return true;
}

bool get_md_string(LLVMMDNodeArgumentType value, std::string &result) {
    if (!value) {
        result = "";
        return false;
    }
    #if LLVM_VERSION < 36
    if (llvm::dyn_cast<llvm::ConstantAggregateZero>(value)) {
        result = "";
        return true;
    }
    llvm::ConstantDataArray *c = llvm::cast<llvm::ConstantDataArray>(value);
    if (c) {
        result = c->getAsCString();
        return true;
    }
    #else
    llvm::MDString *c = llvm::dyn_cast<llvm::MDString>(value);
    if (c) {
        result = c->getString();
        return true;
    }
    #endif
    return false;
}

void get_target_options(const llvm::Module &module, llvm::TargetOptions &options, std::string &mcpu, std::string &mattrs) {
    bool use_soft_float_abi = false;
    get_md_bool(module.getModuleFlag("halide_use_soft_float_abi"), use_soft_float_abi);
    get_md_string(module.getModuleFlag("halide_mcpu"), mcpu);
    get_md_string(module.getModuleFlag("halide_mattrs"), mattrs);

    options = llvm::TargetOptions();
    options.LessPreciseFPMADOption = true;
    options.AllowFPOpFusion = llvm::FPOpFusion::Fast;
    // Disabled on x86 because it makes LLVM compile vector division
    // to multiplication by approx reciprocal, which is too inaccurate
    // even for us.
    options.UnsafeFPMath = !starts_with(module.getTargetTriple(), "x86");
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    #if LLVM_VERSION < 37
    options.NoFramePointerElim = false;
    options.UseSoftFloat = false;
    #endif
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    #if LLVM_VERSION < 37
    options.DisableTailCalls = false;
    #endif
    options.StackAlignmentOverride = 0;
    #if LLVM_VERSION < 37
    options.TrapFuncName = "";
    #endif
    options.FunctionSections = true;
    #ifdef WITH_NATIVE_CLIENT
    options.UseInitArray = true;
    #else
    options.UseInitArray = false;
    #endif
    options.FloatABIType =
        use_soft_float_abi ? llvm::FloatABI::Soft : llvm::FloatABI::Hard;
}

void clone_target_options(const llvm::Module &from, llvm::Module &to) {
    to.setTargetTriple(from.getTargetTriple());

    llvm::LLVMContext &context = to.getContext();

    bool use_soft_float_abi = false;
    if (get_md_bool(from.getModuleFlag("halide_use_soft_float_abi"), use_soft_float_abi))
        to.addModuleFlag(llvm::Module::Warning, "halide_use_soft_float_abi", use_soft_float_abi ? 1 : 0);

    std::string mcpu;
    if (get_md_string(from.getModuleFlag("halide_mcpu"), mcpu)) {
        #if LLVM_VERSION < 36
        to.addModuleFlag(llvm::Module::Warning, "halide_mcpu", llvm::ConstantDataArray::getString(context, mcpu));
        #else
        to.addModuleFlag(llvm::Module::Warning, "halide_mcpu", llvm::MDString::get(context, mcpu));
        #endif
    }

    std::string mattrs;
    if (get_md_string(from.getModuleFlag("halide_mattrs"), mattrs)) {
        #if LLVM_VERSION < 36
        to.addModuleFlag(llvm::Module::Warning, "halide_mattrs", llvm::ConstantDataArray::getString(context, mattrs));
        #else
        to.addModuleFlag(llvm::Module::Warning, "halide_mattrs", llvm::MDString::get(context, mattrs));
        #endif
    }
}

llvm::TargetMachine *get_target_machine(const llvm::Module &module) {
    std::string error_string;

    const llvm::Target *target = llvm::TargetRegistry::lookupTarget(module.getTargetTriple(), error_string);
    if (!target) {
        std::cout << error_string << std::endl;
        llvm::TargetRegistry::printRegisteredTargetsForVersion();
    }
    internal_assert(target) << "Could not create target for " << module.getTargetTriple() << "\n";

    llvm::TargetOptions options;
    std::string mcpu = "";
    std::string mattrs = "";
    get_target_options(module, options, mcpu, mattrs);

    return target->createTargetMachine(module.getTargetTriple(),
                                       mcpu, mattrs,
                                       options,
                                       llvm::Reloc::PIC_,
                                       llvm::CodeModel::Default,
                                       llvm::CodeGenOpt::Aggressive);
}

}
}
<|endoftext|>


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

#include "content/common/gpu/texture_image_transport_surface.h"

#include "content/common/gpu/gpu_channel.h"
#include "content/common/gpu/gpu_channel_manager.h"
#include "content/common/gpu/gpu_messages.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gfx/gl/scoped_make_current.h"

using gpu::gles2::ContextGroup;
using gpu::gles2::TextureManager;
typedef TextureManager::TextureInfo TextureInfo;

namespace {

class ScopedFrameBufferBinder {
 public:
  ScopedFrameBufferBinder(unsigned int fbo) {
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo_);
    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
  }

  ~ScopedFrameBufferBinder() {
    glBindFramebufferEXT(GL_FRAMEBUFFER, old_fbo_);
  }

 private:
  int old_fbo_;
};

class ScopedTextureBinder {
 public:
  ScopedTextureBinder(unsigned int id) {
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_id_);
    glBindTexture(GL_TEXTURE_2D, id);
  }

  ~ScopedTextureBinder() {
    glBindTexture(GL_TEXTURE_2D, old_id_);
  }

 private:
  int old_id_;
};

}  // anonymous namespace

TextureImageTransportSurface::TextureImageTransportSurface(
    GpuChannelManager* manager,
    GpuCommandBufferStub* stub,
    const gfx::GLSurfaceHandle& handle)
      : fbo_id_(0),
        front_(0),
        stub_destroyed_(false) {
  GpuChannel* parent_channel = manager->LookupChannel(handle.parent_client_id);
  DCHECK(parent_channel);
  GpuCommandBufferStub* parent_stub = parent_channel->LookupCommandBuffer(
      handle.parent_context_id);
  DCHECK(parent_stub);
  parent_stub_ = parent_stub->AsWeakPtr();
  TextureManager* texture_manager =
      parent_stub_->decoder()->GetContextGroup()->texture_manager();
  DCHECK(texture_manager);

  for (int i = 0; i < 2; ++i) {
    Texture& texture = textures_[i];
    texture.client_id = handle.parent_texture_id[i];
    TextureInfo* info = texture_manager->GetTextureInfo(texture.client_id);
    DCHECK(info);
    if (!info->target())
      texture_manager->SetInfoTarget(info, GL_TEXTURE_2D);
    texture_manager->SetParameter(info, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    texture_manager->SetParameter(info, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    texture_manager->SetParameter(info, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    texture_manager->SetParameter(info, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }

  helper_.reset(new ImageTransportHelper(this,
                                         manager,
                                         stub,
                                         gfx::kNullPluginWindow));

  stub->AddDestructionObserver(this);
}

TextureImageTransportSurface::~TextureImageTransportSurface() {
  DCHECK(stub_destroyed_);
  Destroy();
}

bool TextureImageTransportSurface::Initialize() {
  return helper_->Initialize();
}

void TextureImageTransportSurface::Destroy() {
  for (int i = 0; i < 2; ++i) {
    Texture& texture = textures_[i];
    if (!texture.sent_to_client)
      continue;
    GpuHostMsg_AcceleratedSurfaceRelease_Params params;
    params.identifier = texture.client_id;
    helper_->SendAcceleratedSurfaceRelease(params);
  }

  helper_->Destroy();
}

bool TextureImageTransportSurface::Resize(const gfx::Size&) {
  return true;
}

bool TextureImageTransportSurface::IsOffscreen() {
  return false;
}

bool TextureImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
  if (stub_destroyed_) {
    // Early-exit so that we don't recreate the fbo. We still want to return
    // true, so that the context is made current and the GLES2DecoderImpl can
    // release its own resources.
    return true;
  }

  if (!fbo_id_) {
    glGenFramebuffersEXT(1, &fbo_id_);
    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_);
    CreateBackTexture(gfx::Size(1, 1));

#ifndef NDEBUG
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
      DLOG(ERROR) << "Framebuffer incomplete.";
      return false;
    }
#endif
  }

  return true;
}

unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
  return fbo_id_;
}

void TextureImageTransportSurface::SetBufferAllocation(
    BufferAllocationState state) {
  if (!helper_->MakeCurrent())
    return;
  switch (state) {
    case BUFFER_ALLOCATION_FRONT_AND_BACK:
      CreateBackTexture(textures_[back()].size);
      break;
    case BUFFER_ALLOCATION_FRONT_ONLY:
    case BUFFER_ALLOCATION_NONE:
      // TODO(piman): Re-enable this (or something else) when we fix the
      // renderer so it doesn't try to draw/swap on deleted back buffers.
      // ReleaseBackTexture();
      break;
  };
}

void* TextureImageTransportSurface::GetShareHandle() {
  return GetHandle();
}

void* TextureImageTransportSurface::GetDisplay() {
  return parent_stub_.get() ? parent_stub_->surface()->GetDisplay() : NULL;
}

void* TextureImageTransportSurface::GetConfig() {
  return parent_stub_.get() ? parent_stub_->surface()->GetConfig() : NULL;
}

void TextureImageTransportSurface::OnResize(gfx::Size size) {
  CreateBackTexture(size);
}

void TextureImageTransportSurface::OnWillDestroyStub(
    GpuCommandBufferStub* stub) {
  glDeleteFramebuffersEXT(1, &fbo_id_);
  CHECK_GL_ERROR();
  fbo_id_ = 0;

  stub->RemoveDestructionObserver(this);
  stub_destroyed_ = true;
}

bool TextureImageTransportSurface::SwapBuffers() {
  glFlush();
  front_ = back();
  previous_damage_rect_ = gfx::Rect(textures_[front_].size);

  GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
  params.surface_handle = textures_[front_].client_id;
  helper_->SendAcceleratedSurfaceBuffersSwapped(params);
  helper_->SetScheduled(false);
  return true;
}

bool TextureImageTransportSurface::PostSubBuffer(
    int x, int y, int width, int height) {
  if (!parent_stub_.get())
    return false;

  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return false;
  int back_texture_service_id = info->service_id();

  info = GetParentInfo(textures_[front_].client_id);
  if (!info)
    return false;
  int front_texture_service_id = info->service_id();

  gfx::Size expected_size = textures_[back()].size;
  bool surfaces_same_size = textures_[front_].size == expected_size;

  const gfx::Rect new_damage_rect(x, y, width, height);
  if (surfaces_same_size) {
    // TODO(backer): Remove temporary sanity checking once we verified that this
    // is not causing the black rects with --enable-partial-swap.
    CHECK_GE(x, 0);
    CHECK_GE(y, 0);
    CHECK_LE(x + width, expected_size.width());
    CHECK_LE(y + height, expected_size.height());

    std::vector<gfx::Rect> regions_to_copy;
    GetRegionsToCopy(previous_damage_rect_, new_damage_rect, &regions_to_copy);

    ScopedFrameBufferBinder fbo_binder(fbo_id_);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
        GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_2D,
        front_texture_service_id,
        0);
    ScopedTextureBinder texture_binder(back_texture_service_id);

    for (size_t i = 0; i < regions_to_copy.size(); ++i) {
      const gfx::Rect& region_to_copy = regions_to_copy[i];
      if (!region_to_copy.IsEmpty()) {
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, region_to_copy.x(),
            region_to_copy.y(), region_to_copy.x(), region_to_copy.y(),
            region_to_copy.width(), region_to_copy.height());
      }
    }
  } else {
    // TODO(backer): Remove temporary sanity checking once we verified that this
    // is not causing the black rects with --enable-partial-swap.
    CHECK(new_damage_rect == gfx::Rect(expected_size));
  }

  glFlush();
  front_ = back();

  GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
  params.surface_handle = textures_[front_].client_id;
  params.x = x;
  params.y = y;
  params.width = width;
  params.height = height;
  helper_->SendAcceleratedSurfacePostSubBuffer(params);
  helper_->SetScheduled(false);

  previous_damage_rect_ = new_damage_rect;
  return true;
}

std::string TextureImageTransportSurface::GetExtensions() {
  std::string extensions = gfx::GLSurface::GetExtensions();
  extensions += extensions.empty() ? "" : " ";
  extensions += "GL_CHROMIUM_front_buffer_cached ";
  extensions += "GL_CHROMIUM_post_sub_buffer";
  return extensions;
}

gfx::Size TextureImageTransportSurface::GetSize() {
  return textures_[back()].size;
}

void* TextureImageTransportSurface::GetHandle() {
  return parent_stub_.get() ? parent_stub_->surface()->GetHandle() : NULL;
}

void TextureImageTransportSurface::OnNewSurfaceACK(
    uint64 surface_handle, TransportDIB::Handle /*shm_handle*/) {
}

void TextureImageTransportSurface::OnBuffersSwappedACK() {
  if (helper_->MakeCurrent()) {
    if (textures_[front_].size != textures_[back()].size) {
      CreateBackTexture(textures_[front_].size);
    } else {
      AttachBackTextureToFBO();
    }
  }

  // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
  // logic.
  helper_->SetScheduled(true);
}

void TextureImageTransportSurface::OnPostSubBufferACK() {
  OnBuffersSwappedACK();
}

void TextureImageTransportSurface::OnResizeViewACK() {
  NOTREACHED();
}

void TextureImageTransportSurface::ReleaseBackTexture() {
  if (!parent_stub_.get())
    return;
  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return;

  GLuint service_id = info->service_id();
  if (!service_id)
    return;
  info->SetServiceId(0);

  {
    ScopedFrameBufferBinder fbo_binder(fbo_id_);
    glDeleteTextures(1, &service_id);
  }
  glFlush();
  CHECK_GL_ERROR();
}

void TextureImageTransportSurface::CreateBackTexture(const gfx::Size& size) {
  if (!parent_stub_.get())
    return;
  Texture& texture = textures_[back()];
  TextureInfo* info = GetParentInfo(texture.client_id);
  if (!info)
    return;

  GLuint service_id = info->service_id();

  if (service_id && texture.size == size)
    return;

  if (!service_id) {
    glGenTextures(1, &service_id);
    info->SetServiceId(service_id);
  }

  if (size != texture.size) {
    texture.size = size;
    TextureManager* texture_manager =
        parent_stub_->decoder()->GetContextGroup()->texture_manager();
    texture_manager->SetLevelInfo(
        info,
        GL_TEXTURE_2D,
        0,
        GL_RGBA,
        size.width(),
        size.height(),
        1,
        0,
        GL_RGBA,
        GL_UNSIGNED_BYTE,
        true);
  }

  {
    ScopedTextureBinder texture_binder(service_id);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
        size.width(), size.height(), 0,
        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    CHECK_GL_ERROR();
  }

  AttachBackTextureToFBO();

  GpuHostMsg_AcceleratedSurfaceNew_Params params;
  params.width = size.width();
  params.height = size.height();
  params.surface_handle = texture.client_id;
  helper_->SendAcceleratedSurfaceNew(params);
  texture.sent_to_client = true;
}

void TextureImageTransportSurface::AttachBackTextureToFBO() {
  if (!parent_stub_.get())
    return;
  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return;

  ScopedFrameBufferBinder fbo_binder(fbo_id_);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
      GL_COLOR_ATTACHMENT0,
      GL_TEXTURE_2D,
      info->service_id(),
      0);
  glFlush();
  CHECK_GL_ERROR();

#ifndef NDEBUG
  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
  if (status != GL_FRAMEBUFFER_COMPLETE) {
    DLOG(ERROR) << "Framebuffer incomplete.";
  }
#endif
}

TextureInfo* TextureImageTransportSurface::GetParentInfo(uint32 client_id) {
  DCHECK(parent_stub_.get());
  TextureManager* texture_manager =
      parent_stub_->decoder()->GetContextGroup()->texture_manager();
  TextureInfo* info = texture_manager->GetTextureInfo(client_id);
  return info;
}
<commit_msg>Revert 126318: "Aura: sanity check for --enable-partial-swap"<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/common/gpu/texture_image_transport_surface.h"

#include "content/common/gpu/gpu_channel.h"
#include "content/common/gpu/gpu_channel_manager.h"
#include "content/common/gpu/gpu_messages.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/gpu_scheduler.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "ui/gfx/gl/scoped_make_current.h"

using gpu::gles2::ContextGroup;
using gpu::gles2::TextureManager;
typedef TextureManager::TextureInfo TextureInfo;

namespace {

class ScopedFrameBufferBinder {
 public:
  ScopedFrameBufferBinder(unsigned int fbo) {
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &old_fbo_);
    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo);
  }

  ~ScopedFrameBufferBinder() {
    glBindFramebufferEXT(GL_FRAMEBUFFER, old_fbo_);
  }

 private:
  int old_fbo_;
};

class ScopedTextureBinder {
 public:
  ScopedTextureBinder(unsigned int id) {
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &old_id_);
    glBindTexture(GL_TEXTURE_2D, id);
  }

  ~ScopedTextureBinder() {
    glBindTexture(GL_TEXTURE_2D, old_id_);
  }

 private:
  int old_id_;
};

}  // anonymous namespace

TextureImageTransportSurface::TextureImageTransportSurface(
    GpuChannelManager* manager,
    GpuCommandBufferStub* stub,
    const gfx::GLSurfaceHandle& handle)
      : fbo_id_(0),
        front_(0),
        stub_destroyed_(false) {
  GpuChannel* parent_channel = manager->LookupChannel(handle.parent_client_id);
  DCHECK(parent_channel);
  GpuCommandBufferStub* parent_stub = parent_channel->LookupCommandBuffer(
      handle.parent_context_id);
  DCHECK(parent_stub);
  parent_stub_ = parent_stub->AsWeakPtr();
  TextureManager* texture_manager =
      parent_stub_->decoder()->GetContextGroup()->texture_manager();
  DCHECK(texture_manager);

  for (int i = 0; i < 2; ++i) {
    Texture& texture = textures_[i];
    texture.client_id = handle.parent_texture_id[i];
    TextureInfo* info = texture_manager->GetTextureInfo(texture.client_id);
    DCHECK(info);
    if (!info->target())
      texture_manager->SetInfoTarget(info, GL_TEXTURE_2D);
    texture_manager->SetParameter(info, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    texture_manager->SetParameter(info, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    texture_manager->SetParameter(info, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    texture_manager->SetParameter(info, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  }

  helper_.reset(new ImageTransportHelper(this,
                                         manager,
                                         stub,
                                         gfx::kNullPluginWindow));

  stub->AddDestructionObserver(this);
}

TextureImageTransportSurface::~TextureImageTransportSurface() {
  DCHECK(stub_destroyed_);
  Destroy();
}

bool TextureImageTransportSurface::Initialize() {
  return helper_->Initialize();
}

void TextureImageTransportSurface::Destroy() {
  for (int i = 0; i < 2; ++i) {
    Texture& texture = textures_[i];
    if (!texture.sent_to_client)
      continue;
    GpuHostMsg_AcceleratedSurfaceRelease_Params params;
    params.identifier = texture.client_id;
    helper_->SendAcceleratedSurfaceRelease(params);
  }

  helper_->Destroy();
}

bool TextureImageTransportSurface::Resize(const gfx::Size&) {
  return true;
}

bool TextureImageTransportSurface::IsOffscreen() {
  return false;
}

bool TextureImageTransportSurface::OnMakeCurrent(gfx::GLContext* context) {
  if (stub_destroyed_) {
    // Early-exit so that we don't recreate the fbo. We still want to return
    // true, so that the context is made current and the GLES2DecoderImpl can
    // release its own resources.
    return true;
  }

  if (!fbo_id_) {
    glGenFramebuffersEXT(1, &fbo_id_);
    glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_id_);
    CreateBackTexture(gfx::Size(1, 1));

#ifndef NDEBUG
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
      DLOG(ERROR) << "Framebuffer incomplete.";
      return false;
    }
#endif
  }

  return true;
}

unsigned int TextureImageTransportSurface::GetBackingFrameBufferObject() {
  return fbo_id_;
}

void TextureImageTransportSurface::SetBufferAllocation(
    BufferAllocationState state) {
  if (!helper_->MakeCurrent())
    return;
  switch (state) {
    case BUFFER_ALLOCATION_FRONT_AND_BACK:
      CreateBackTexture(textures_[back()].size);
      break;
    case BUFFER_ALLOCATION_FRONT_ONLY:
    case BUFFER_ALLOCATION_NONE:
      // TODO(piman): Re-enable this (or something else) when we fix the
      // renderer so it doesn't try to draw/swap on deleted back buffers.
      // ReleaseBackTexture();
      break;
  };
}

void* TextureImageTransportSurface::GetShareHandle() {
  return GetHandle();
}

void* TextureImageTransportSurface::GetDisplay() {
  return parent_stub_.get() ? parent_stub_->surface()->GetDisplay() : NULL;
}

void* TextureImageTransportSurface::GetConfig() {
  return parent_stub_.get() ? parent_stub_->surface()->GetConfig() : NULL;
}

void TextureImageTransportSurface::OnResize(gfx::Size size) {
  CreateBackTexture(size);
}

void TextureImageTransportSurface::OnWillDestroyStub(
    GpuCommandBufferStub* stub) {
  glDeleteFramebuffersEXT(1, &fbo_id_);
  CHECK_GL_ERROR();
  fbo_id_ = 0;

  stub->RemoveDestructionObserver(this);
  stub_destroyed_ = true;
}

bool TextureImageTransportSurface::SwapBuffers() {
  glFlush();
  front_ = back();
  previous_damage_rect_ = gfx::Rect(textures_[front_].size);

  GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
  params.surface_handle = textures_[front_].client_id;
  helper_->SendAcceleratedSurfaceBuffersSwapped(params);
  helper_->SetScheduled(false);
  return true;
}

bool TextureImageTransportSurface::PostSubBuffer(
    int x, int y, int width, int height) {
  if (!parent_stub_.get())
    return false;

  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return false;
  int back_texture_service_id = info->service_id();

  info = GetParentInfo(textures_[front_].client_id);
  if (!info)
    return false;
  int front_texture_service_id = info->service_id();

  gfx::Size expected_size = textures_[back()].size;
  bool surfaces_same_size = textures_[front_].size == expected_size;

  const gfx::Rect new_damage_rect(x, y, width, height);
  if (surfaces_same_size) {
    std::vector<gfx::Rect> regions_to_copy;
    GetRegionsToCopy(previous_damage_rect_, new_damage_rect, &regions_to_copy);

    ScopedFrameBufferBinder fbo_binder(fbo_id_);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
        GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_2D,
        front_texture_service_id,
        0);
    ScopedTextureBinder texture_binder(back_texture_service_id);

    for (size_t i = 0; i < regions_to_copy.size(); ++i) {
      const gfx::Rect& region_to_copy = regions_to_copy[i];
      if (!region_to_copy.IsEmpty()) {
        glCopyTexSubImage2D(GL_TEXTURE_2D, 0, region_to_copy.x(),
            region_to_copy.y(), region_to_copy.x(), region_to_copy.y(),
            region_to_copy.width(), region_to_copy.height());
      }
    }
  } else {
    DCHECK(new_damage_rect == gfx::Rect(expected_size));
  }

  glFlush();
  front_ = back();

  GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params;
  params.surface_handle = textures_[front_].client_id;
  params.x = x;
  params.y = y;
  params.width = width;
  params.height = height;
  helper_->SendAcceleratedSurfacePostSubBuffer(params);
  helper_->SetScheduled(false);

  previous_damage_rect_ = new_damage_rect;
  return true;
}

std::string TextureImageTransportSurface::GetExtensions() {
  std::string extensions = gfx::GLSurface::GetExtensions();
  extensions += extensions.empty() ? "" : " ";
  extensions += "GL_CHROMIUM_front_buffer_cached ";
  extensions += "GL_CHROMIUM_post_sub_buffer";
  return extensions;
}

gfx::Size TextureImageTransportSurface::GetSize() {
  return textures_[back()].size;
}

void* TextureImageTransportSurface::GetHandle() {
  return parent_stub_.get() ? parent_stub_->surface()->GetHandle() : NULL;
}

void TextureImageTransportSurface::OnNewSurfaceACK(
    uint64 surface_handle, TransportDIB::Handle /*shm_handle*/) {
}

void TextureImageTransportSurface::OnBuffersSwappedACK() {
  if (helper_->MakeCurrent()) {
    if (textures_[front_].size != textures_[back()].size) {
      CreateBackTexture(textures_[front_].size);
    } else {
      AttachBackTextureToFBO();
    }
  }

  // Even if MakeCurrent fails, schedule anyway, to trigger the lost context
  // logic.
  helper_->SetScheduled(true);
}

void TextureImageTransportSurface::OnPostSubBufferACK() {
  OnBuffersSwappedACK();
}

void TextureImageTransportSurface::OnResizeViewACK() {
  NOTREACHED();
}

void TextureImageTransportSurface::ReleaseBackTexture() {
  if (!parent_stub_.get())
    return;
  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return;

  GLuint service_id = info->service_id();
  if (!service_id)
    return;
  info->SetServiceId(0);

  {
    ScopedFrameBufferBinder fbo_binder(fbo_id_);
    glDeleteTextures(1, &service_id);
  }
  glFlush();
  CHECK_GL_ERROR();
}

void TextureImageTransportSurface::CreateBackTexture(const gfx::Size& size) {
  if (!parent_stub_.get())
    return;
  Texture& texture = textures_[back()];
  TextureInfo* info = GetParentInfo(texture.client_id);
  if (!info)
    return;

  GLuint service_id = info->service_id();

  if (service_id && texture.size == size)
    return;

  if (!service_id) {
    glGenTextures(1, &service_id);
    info->SetServiceId(service_id);
  }

  if (size != texture.size) {
    texture.size = size;
    TextureManager* texture_manager =
        parent_stub_->decoder()->GetContextGroup()->texture_manager();
    texture_manager->SetLevelInfo(
        info,
        GL_TEXTURE_2D,
        0,
        GL_RGBA,
        size.width(),
        size.height(),
        1,
        0,
        GL_RGBA,
        GL_UNSIGNED_BYTE,
        true);
  }

  {
    ScopedTextureBinder texture_binder(service_id);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
        size.width(), size.height(), 0,
        GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    CHECK_GL_ERROR();
  }

  AttachBackTextureToFBO();

  GpuHostMsg_AcceleratedSurfaceNew_Params params;
  params.width = size.width();
  params.height = size.height();
  params.surface_handle = texture.client_id;
  helper_->SendAcceleratedSurfaceNew(params);
  texture.sent_to_client = true;
}

void TextureImageTransportSurface::AttachBackTextureToFBO() {
  if (!parent_stub_.get())
    return;
  TextureInfo* info = GetParentInfo(textures_[back()].client_id);
  if (!info)
    return;

  ScopedFrameBufferBinder fbo_binder(fbo_id_);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER,
      GL_COLOR_ATTACHMENT0,
      GL_TEXTURE_2D,
      info->service_id(),
      0);
  glFlush();
  CHECK_GL_ERROR();

#ifndef NDEBUG
  GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER);
  if (status != GL_FRAMEBUFFER_COMPLETE) {
    DLOG(ERROR) << "Framebuffer incomplete.";
  }
#endif
}

TextureInfo* TextureImageTransportSurface::GetParentInfo(uint32 client_id) {
  DCHECK(parent_stub_.get());
  TextureManager* texture_manager =
      parent_stub_->decoder()->GetContextGroup()->texture_manager();
  TextureInfo* info = texture_manager->GetTextureInfo(client_id);
  return info;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of signon
 *
 * Copyright (C) 2010 Nokia Corporation.
 *
 * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "debug.h"
#include "device-lock-code-handler.h"
#include "key-manager.h"
#include "sim-data-handler.h"

using namespace SignOn;

KeyManager::KeyManager(QObject *parent):
    AbstractKeyManager(parent)
{
    qDebug() << Q_FUNC_INFO << "Constructed";
}

KeyManager::~KeyManager()
{
}

void KeyManager::setup()
{
    qDebug() << Q_FUNC_INFO << "Initialized";

    dlcHandler = new DeviceLockCodeHandler(this);
    connect(dlcHandler,
            SIGNAL(lockCodeSet(const QByteArray, const QByteArray)),
            SLOT(onLockCodeSet(const QByteArray, const QByteArray)));

    simHandler = new SimDataHandler(this);
    connect(simHandler,
            SIGNAL(simAvailable(const QByteArray)),
            SLOT(onSimAvailable(const QByteArray)));
    connect(simHandler,
            SIGNAL(simRemoved(const QByteArray)),
            SLOT(onSimRemoved(const QByteArray)));
    simHandler->querySim();
}

void KeyManager::authorizeKey(const Key &key, const QString &message)
{
    Q_UNUSED(message);

    TRACE() << "key:" << key.toHex();

    if (key.isEmpty()) {
        emit keyAuthorized(key, false);
        return;
    }

    /* were we expecting this request?  If so, just authorized the key and if
     * an old one had to be removed, do it.
     */
    if (key == newDeviceLockKey) {
        emit keyAuthorized(key, true);
        newDeviceLockKey = QByteArray();

        if (!keyToBeRemoved.isEmpty()) {
            emit keyRemoved(keyToBeRemoved);
            keyToBeRemoved = QByteArray();
        }
        return;
    }

    /* Pop up a dialog to authorize the key; we'll get the answer in the
     * onLockCodeSet slot */
    keysToBeAuthorized << key;
    Q_ASSERT(dlcHandler != 0);
    dlcHandler->queryLockCode();
}

void KeyManager::queryKeys()
{
    TRACE();

    /* Pop up a dialog to authorize the key; we'll get the answer in the
     * onLockCodeSet slot */
    Q_ASSERT(dlcHandler != 0);
    dlcHandler->queryLockCode();
}

void KeyManager::onLockCodeSet(const QByteArray lockCode,
                               const QByteArray oldLockCode)
{
    TRACE() << "new:" << lockCode.toHex() << "old:" << oldLockCode.toHex();

    if (!oldLockCode.isEmpty()) {
        /* the device lock code has changed: we should remove the old key; but
         * we'll do that only after the new key has been authorized. So, for
         * the moment just keep the old key in memory
         */
        keyToBeRemoved = oldLockCode;
    }

    if (!lockCode.isEmpty()) {
        /* we tell signond about the new key; signond will most likely ask us
         * to authorize the key; so we keep it in memory, till the
         * authorizeKey() method is called.
         */
        newDeviceLockKey = lockCode;
        emit keyInserted(lockCode);
    }

    /* If we have some keys to authorize, do so */
    bool authorized = !lockCode.isEmpty();
    foreach (Key key, keysToBeAuthorized) {
        emit keyAuthorized(key, authorized);
    }
    keysToBeAuthorized.clear();
}

void KeyManager::onSimAvailable(const QByteArray simData)
{
    TRACE() << "data:" << simData.toHex();
    emit keyInserted(simData);
}

void KeyManager::onSimRemoved(const QByteArray simData)
{
    TRACE() << "data:" << simData.toHex();
    emit keyDisabled(simData);
}

<commit_msg>Disable device lock code once a sim is authorized<commit_after>/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of signon
 *
 * Copyright (C) 2010 Nokia Corporation.
 *
 * Contact: Aurel Popirtac <ext-aurel.popirtac@nokia.com>
 * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#include "debug.h"
#include "device-lock-code-handler.h"
#include "key-manager.h"
#include "sim-data-handler.h"

using namespace SignOn;

KeyManager::KeyManager(QObject *parent):
    AbstractKeyManager(parent)
{
    qDebug() << Q_FUNC_INFO << "Constructed";
}

KeyManager::~KeyManager()
{
}

void KeyManager::setup()
{
    qDebug() << Q_FUNC_INFO << "Initialized";

    dlcHandler = new DeviceLockCodeHandler(this);
    connect(dlcHandler,
            SIGNAL(lockCodeSet(const QByteArray, const QByteArray)),
            SLOT(onLockCodeSet(const QByteArray, const QByteArray)));

    simHandler = new SimDataHandler(this);
    connect(simHandler,
            SIGNAL(simAvailable(const QByteArray)),
            SLOT(onSimAvailable(const QByteArray)));
    connect(simHandler,
            SIGNAL(simRemoved(const QByteArray)),
            SLOT(onSimRemoved(const QByteArray)));
    simHandler->querySim();
}

void KeyManager::authorizeKey(const Key &key, const QString &message)
{
    Q_UNUSED(message);

    TRACE() << "key:" << key.toHex();

    if (key.isEmpty()) {
        emit keyAuthorized(key, false);
        return;
    }

    /* were we expecting this request?  If so, just authorized the key and if
     * an old one had to be removed, do it.
     */
    if (key == newDeviceLockKey) {
        emit keyAuthorized(key, true);
        newDeviceLockKey = QByteArray();

        if (!keyToBeRemoved.isEmpty()) {
            emit keyRemoved(keyToBeRemoved);
            keyToBeRemoved = QByteArray();
        }
        return;
    }

    /* Pop up a dialog to authorize the key; we'll get the answer in the
     * onLockCodeSet slot */
    keysToBeAuthorized << key;
    Q_ASSERT(dlcHandler != 0);
    dlcHandler->queryLockCode();
}

void KeyManager::queryKeys()
{
    TRACE();

    /* Pop up a dialog to authorize the key; we'll get the answer in the
     * onLockCodeSet slot */
    Q_ASSERT(dlcHandler != 0);
    dlcHandler->queryLockCode();
}

void KeyManager::onLockCodeSet(const QByteArray lockCode,
                               const QByteArray oldLockCode)
{
    TRACE() << "new:" << lockCode.toHex() << "old:" << oldLockCode.toHex();

    if (!oldLockCode.isEmpty()) {
        /* the device lock code has changed: we should remove the old key; but
         * we'll do that only after the new key has been authorized. So, for
         * the moment just keep the old key in memory
         */
        keyToBeRemoved = oldLockCode;
    }

    if (!lockCode.isEmpty()) {
        /* we tell signond about the new key; signond will most likely ask us
         * to authorize the key; so we keep it in memory, till the
         * authorizeKey() method is called.
         */
        newDeviceLockKey = lockCode;
        emit keyInserted(lockCode);
    }

    /* If we have some keys to authorize, do so */
    bool authorized = !lockCode.isEmpty();
    foreach (Key key, keysToBeAuthorized) {
        emit keyAuthorized(key, authorized);
    }

    if (!keysToBeAuthorized.isEmpty()) {
        /* if we have authorized some keys, disable the device-lock-code */
        emit keyDisabled(lockCode);
    }

    keysToBeAuthorized.clear();
}

void KeyManager::onSimAvailable(const QByteArray simData)
{
    TRACE() << "data:" << simData.toHex();
    emit keyInserted(simData);
}

void KeyManager::onSimRemoved(const QByteArray simData)
{
    TRACE() << "data:" << simData.toHex();
    emit keyDisabled(simData);
}

<|endoftext|>


<commit_before>#ifdef _WIN32

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, size_t bufferSize, const u16string& path)
    : path(path)
    , status(NOT_LISTENING) {
    wstring pathW(path.begin(), path.end());
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );
    if (directoryHandle == INVALID_HANDLE_VALUE) {
        throw FileWatcherException("Couldn't add watch", path, GetLastError());
    }
    this->directoryHandle = directoryHandle;

    this->server = server;
    this->buffer.reserve(bufferSize);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    switch (listen()) {
        case SUCCESS:
            break;
        case DELETED:
            throw FileWatcherException("Couldn't start watching because path is not a directory", path);
    }
}

bool WatchPoint::cancel() {
    if (status == LISTENING) {
        logToJava(FINE, "Cancelling %s", utf16ToUtf8String(path).c_str());
        status = CANCELLED;
        bool cancelled = (bool) CancelIoEx(directoryHandle, &overlapped);
        if (!cancelled) {
            status = FINISHED;
            DWORD lastError = GetLastError();
            if (lastError == ERROR_NOT_FOUND) {
                // Do nothing, looks like this is a typical scenario
                logToJava(FINE, "Watch point already finished %s", utf16ToUtf8String(path).c_str());
            } else {
                throw FileWatcherException("Couldn't cancel watch point", path, lastError);
            }
        }
        return cancelled;
    }
    return false;
}

WatchPoint::~WatchPoint() {
    try {
        if (cancel()) {
            SleepEx(0, true);
        }
    } catch (const exception& ex) {
        logToJava(WARNING, "Couldn't cancel watch point %s: %s", utf16ToUtf8String(path).c_str(), ex.what());
    }
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEventsInBuffer(errorCode, bytesTransferred);
}

bool WatchPoint::isValidDirectory() {
    wstring pathW(path.begin(), path.end());
    DWORD attrib = GetFileAttributesW(pathW.c_str());

    return (attrib != INVALID_FILE_ATTRIBUTES)
        && ((attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

ListenResult WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,              // handle to directory
        &buffer[0],                   // read results buffer
        (DWORD) buffer.capacity(),    // length of buffer
        TRUE,                         // include children
        EVENT_MASK,                   // filter conditions
        NULL,                         // bytes returned
        &overlapped,                  // overlapped buffer
        &handleEventCallback          // completion routine
    );
    if (success) {
        status = LISTENING;
        return SUCCESS;
    } else {
        status = FINISHED;
        DWORD lastError = GetLastError();
        if (lastError == ERROR_ACCESS_DENIED && !isValidDirectory()) {
            return DELETED;
        } else {
            throw FileWatcherException("Couldn't start watching", path, lastError);
        }
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    if (errorCode == ERROR_OPERATION_ABORTED) {
        logToJava(FINE, "Finished watching '%s', status = %d", utf16ToUtf8String(path).c_str(), status);
        BOOL ret = CloseHandle(directoryHandle);
        if (!ret) {
            logToJava(SEVERE, "Couldn't close handle %p for '%ls': %d", directoryHandle, utf16ToUtf8String(path).c_str(), GetLastError());
        }
        status = FINISHED;
        return;
    }

    if (status != LISTENING) {
        logToJava(FINE, "Ignoring incoming events for %s as watch-point is not listening (%d bytes, errorCode = %d, status = %d)",
            utf16ToUtf8String(path).c_str(), bytesTransferred, errorCode, status);
        return;
    }
    status = NOT_LISTENING;
    server->handleEvents(this, errorCode, buffer, bytesTransferred);
}

void Server::handleEvents(WatchPoint* watchPoint, DWORD errorCode, const vector<BYTE>& buffer, DWORD bytesTransferred) {
    JNIEnv* env = getThreadEnv();
    const u16string& path = watchPoint->path;

    try {
        if (errorCode != ERROR_SUCCESS) {
            if (errorCode == ERROR_ACCESS_DENIED && !watchPoint->isValidDirectory()) {
                reportChange(env, FILE_EVENT_REMOVED, path);
            } else {
                throw FileWatcherException("Error received when handling events", path, errorCode);
            }
        }

        if (terminated) {
            logToJava(FINE, "Ignoring incoming events for %s because server is terminating (%d bytes, status = %d)",
                utf16ToUtf8String(path).c_str(), bytesTransferred, watchPoint->status);
            return;
        }

        if (bytesTransferred == 0) {
            // Got a buffer overflow => current changes lost => send INVALIDATE on root
            logToJava(INFO, "Detected overflow for %s", utf16ToUtf8String(path).c_str());
            reportChange(env, FILE_EVENT_INVALIDATE, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*) &buffer[index];
                handleEvent(env, path, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        switch (watchPoint->listen()) {
            case SUCCESS:
                break;
            case DELETED:
                reportChange(env, FILE_EVENT_REMOVED, path);
                break;
        }
    } catch (const exception& ex) {
        reportError(env, ex);
    }
}

bool isAbsoluteLocalPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((u'a' <= path[0] && path[0] <= u'z') || (u'A' <= path[0] && path[0] <= u'Z'))
        && path[1] == u':'
        && path[2] == u'\\';
}

bool isAbsoluteUncPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == u'\\' && path[1] == u'\\';
}

bool isLongPath(const u16string& path) {
    return path.length() >= 4 && path.substr(0, 4) == u"\\\\?\\";
}

bool isUncLongPath(const u16string& path) {
    return path.length() >= 8 && path.substr(0, 8) == u"\\\\?\\UNC\\";
}

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(u16string& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    // It is already a long path, nothing to do here
    if (isLongPath(path)) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, u"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

void Server::handleEvent(JNIEnv* env, const u16string& path, FILE_NOTIFY_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    u16string changedPath(changedPathW.begin(), changedPathW.end());
    if (!changedPath.empty()) {
        changedPath.insert(0, 1, u'\\');
    }
    changedPath.insert(0, path);
    // TODO Remove long prefix for path once?
    if (isLongPath(changedPath)) {
        if (isUncLongPath(changedPath)) {
            changedPath.erase(0, 8).insert(0, u"\\\\");
        } else {
            changedPath.erase(0, 4);
        }
    }

    logToJava(FINE, "Change detected: 0x%x '%s'", info->Action, utf16ToUtf8String(changedPath).c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        logToJava(WARNING, "Unknown event 0x%x for %s", info->Action, utf16ToUtf8String(changedPath).c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    reportChange(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, size_t bufferSize, jobject watcherCallback)
    : AbstractServer(env, watcherCallback)
    , bufferSize(bufferSize) {
}

void Server::initializeRunLoop() {
    // TODO For some reason GetCurrentThread() returns a thread that doesn't accept APCs
    threadHandle = OpenThread(
        THREAD_ALL_ACCESS,
        false,
        GetCurrentThreadId()
    );
    if (threadHandle == NULL) {
        throw FileWatcherException("Couldn't open current thread", GetLastError());
    }
}

void Server::terminateRunLoop() {
    executeOnRunLoop([this]() {
        terminated = true;
        return true;
    });
}

Server::~Server() {
    terminate();
}

void Server::runLoop() {
    while (!terminated) {
        SleepEx(INFINITE, true);
    }

    // We have received termination, cancel all watchers
    unique_lock<mutex> lock(mutationMutex);
    logToJava(FINE, "Finished with run loop, now cancelling remaining watch points", NULL);
    int pendingWatchPoints = 0;
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case LISTENING:
                try {
                    if (watchPoint.cancel()) {
                        pendingWatchPoints++;
                    }
                } catch (const exception& ex) {
                    logToJava(SEVERE, "%s", ex.what());
                }
                break;
            case CANCELLED:
                pendingWatchPoints++;
                break;
            default:
                break;
        }
    }

    // If there are any pending watchers, wait for them to finish
    if (pendingWatchPoints > 0) {
        logToJava(FINE, "Waiting for %d pending watch points to finish", pendingWatchPoints);
        SleepEx(0, true);
    }

    // Warn about  any unfinished watchpoints
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case NOT_LISTENING:
            case FINISHED:
                break;
            default:
                logToJava(WARNING, "Watch point %s did not finish before termination timeout (status = %d)",
                    utf16ToUtf8String(watchPoint.path).c_str(), watchPoint.status);
                break;
        }
    }

    CloseHandle(threadHandle);
}

static void CALLBACK executeOnRunLoopCallback(_In_ ULONG_PTR info) {
    Command* command = (Command*) info;
    try {
        command->result = command->function();
    } catch (const exception&) {
        command->failure = current_exception();
    }
    unique_lock<mutex> lock(command->server->executionMutex);
    command->executed.notify_all();
}

bool Server::executeOnRunLoop(function<bool()> function) {
    Command command;
    command.function = function;
    command.server = this;
    DWORD ret = QueueUserAPC(executeOnRunLoopCallback, threadHandle, (ULONG_PTR) &command);
    if (ret == 0) {
        throw FileWatcherException("Received error while queuing APC", GetLastError());
    }
    unique_lock<mutex> lock(executionMutex);
    auto status = command.executed.wait_for(lock, THREAD_TIMEOUT);
    if (status == cv_status::timeout) {
        throw FileWatcherException("Execution timed out");
    } else if (command.failure) {
        rethrow_exception(command.failure);
    } else {
        return command.result;
    }
}

void Server::registerPaths(const vector<u16string>& paths) {
    executeOnRunLoop([this, paths]() {
        AbstractServer::registerPaths(paths);
        return true;
    });
}

bool Server::unregisterPaths(const vector<u16string>& paths) {
    return executeOnRunLoop([this, paths]() {
        return AbstractServer::unregisterPaths(paths);
    });
}

void Server::registerPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    auto it = watchPoints.find(longPath);
    if (it != watchPoints.end()) {
        if (it->second.status != FINISHED) {
            throw FileWatcherException("Already watching path", path);
        }
        watchPoints.erase(it);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPath),
        forward_as_tuple(this, bufferSize, longPath));
}

bool Server::unregisterPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    if (watchPoints.erase(longPath) == 0) {
        logToJava(INFO, "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return false;
    }
    return true;
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher0(JNIEnv* env, jclass target, jint bufferSize, jobject javaCallback) {
    return wrapServer(env, new Server(env, bufferSize, javaCallback));
}

#endif
<commit_msg>Handle events under lock on Windows, too<commit_after>#ifdef _WIN32

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, size_t bufferSize, const u16string& path)
    : path(path)
    , status(NOT_LISTENING) {
    wstring pathW(path.begin(), path.end());
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );
    if (directoryHandle == INVALID_HANDLE_VALUE) {
        throw FileWatcherException("Couldn't add watch", path, GetLastError());
    }
    this->directoryHandle = directoryHandle;

    this->server = server;
    this->buffer.reserve(bufferSize);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    switch (listen()) {
        case SUCCESS:
            break;
        case DELETED:
            throw FileWatcherException("Couldn't start watching because path is not a directory", path);
    }
}

bool WatchPoint::cancel() {
    if (status == LISTENING) {
        logToJava(FINE, "Cancelling %s", utf16ToUtf8String(path).c_str());
        status = CANCELLED;
        bool cancelled = (bool) CancelIoEx(directoryHandle, &overlapped);
        if (!cancelled) {
            status = FINISHED;
            DWORD lastError = GetLastError();
            if (lastError == ERROR_NOT_FOUND) {
                // Do nothing, looks like this is a typical scenario
                logToJava(FINE, "Watch point already finished %s", utf16ToUtf8String(path).c_str());
            } else {
                throw FileWatcherException("Couldn't cancel watch point", path, lastError);
            }
        }
        return cancelled;
    }
    return false;
}

WatchPoint::~WatchPoint() {
    try {
        if (cancel()) {
            SleepEx(0, true);
        }
    } catch (const exception& ex) {
        logToJava(WARNING, "Couldn't cancel watch point %s: %s", utf16ToUtf8String(path).c_str(), ex.what());
    }
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEventsInBuffer(errorCode, bytesTransferred);
}

bool WatchPoint::isValidDirectory() {
    wstring pathW(path.begin(), path.end());
    DWORD attrib = GetFileAttributesW(pathW.c_str());

    return (attrib != INVALID_FILE_ATTRIBUTES)
        && ((attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

ListenResult WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,              // handle to directory
        &buffer[0],                   // read results buffer
        (DWORD) buffer.capacity(),    // length of buffer
        TRUE,                         // include children
        EVENT_MASK,                   // filter conditions
        NULL,                         // bytes returned
        &overlapped,                  // overlapped buffer
        &handleEventCallback          // completion routine
    );
    if (success) {
        status = LISTENING;
        return SUCCESS;
    } else {
        status = FINISHED;
        DWORD lastError = GetLastError();
        if (lastError == ERROR_ACCESS_DENIED && !isValidDirectory()) {
            return DELETED;
        } else {
            throw FileWatcherException("Couldn't start watching", path, lastError);
        }
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    if (errorCode == ERROR_OPERATION_ABORTED) {
        logToJava(FINE, "Finished watching '%s', status = %d", utf16ToUtf8String(path).c_str(), status);
        BOOL ret = CloseHandle(directoryHandle);
        if (!ret) {
            logToJava(SEVERE, "Couldn't close handle %p for '%ls': %d", directoryHandle, utf16ToUtf8String(path).c_str(), GetLastError());
        }
        status = FINISHED;
        return;
    }

    if (status != LISTENING) {
        logToJava(FINE, "Ignoring incoming events for %s as watch-point is not listening (%d bytes, errorCode = %d, status = %d)",
            utf16ToUtf8String(path).c_str(), bytesTransferred, errorCode, status);
        return;
    }
    status = NOT_LISTENING;
    server->handleEvents(this, errorCode, buffer, bytesTransferred);
}

void Server::handleEvents(WatchPoint* watchPoint, DWORD errorCode, const vector<BYTE>& buffer, DWORD bytesTransferred) {
    unique_lock<mutex> lock(mutationMutex);
    JNIEnv* env = getThreadEnv();
    const u16string& path = watchPoint->path;

    try {
        if (errorCode != ERROR_SUCCESS) {
            if (errorCode == ERROR_ACCESS_DENIED && !watchPoint->isValidDirectory()) {
                reportChange(env, FILE_EVENT_REMOVED, path);
            } else {
                throw FileWatcherException("Error received when handling events", path, errorCode);
            }
        }

        if (terminated) {
            logToJava(FINE, "Ignoring incoming events for %s because server is terminating (%d bytes, status = %d)",
                utf16ToUtf8String(path).c_str(), bytesTransferred, watchPoint->status);
            return;
        }

        if (bytesTransferred == 0) {
            // Got a buffer overflow => current changes lost => send INVALIDATE on root
            logToJava(INFO, "Detected overflow for %s", utf16ToUtf8String(path).c_str());
            reportChange(env, FILE_EVENT_INVALIDATE, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*) &buffer[index];
                handleEvent(env, path, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        switch (watchPoint->listen()) {
            case SUCCESS:
                break;
            case DELETED:
                reportChange(env, FILE_EVENT_REMOVED, path);
                break;
        }
    } catch (const exception& ex) {
        reportError(env, ex);
    }
}

bool isAbsoluteLocalPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((u'a' <= path[0] && path[0] <= u'z') || (u'A' <= path[0] && path[0] <= u'Z'))
        && path[1] == u':'
        && path[2] == u'\\';
}

bool isAbsoluteUncPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == u'\\' && path[1] == u'\\';
}

bool isLongPath(const u16string& path) {
    return path.length() >= 4 && path.substr(0, 4) == u"\\\\?\\";
}

bool isUncLongPath(const u16string& path) {
    return path.length() >= 8 && path.substr(0, 8) == u"\\\\?\\UNC\\";
}

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(u16string& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    // It is already a long path, nothing to do here
    if (isLongPath(path)) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, u"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

void Server::handleEvent(JNIEnv* env, const u16string& path, FILE_NOTIFY_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    u16string changedPath(changedPathW.begin(), changedPathW.end());
    if (!changedPath.empty()) {
        changedPath.insert(0, 1, u'\\');
    }
    changedPath.insert(0, path);
    // TODO Remove long prefix for path once?
    if (isLongPath(changedPath)) {
        if (isUncLongPath(changedPath)) {
            changedPath.erase(0, 8).insert(0, u"\\\\");
        } else {
            changedPath.erase(0, 4);
        }
    }

    logToJava(FINE, "Change detected: 0x%x '%s'", info->Action, utf16ToUtf8String(changedPath).c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        logToJava(WARNING, "Unknown event 0x%x for %s", info->Action, utf16ToUtf8String(changedPath).c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    reportChange(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, size_t bufferSize, jobject watcherCallback)
    : AbstractServer(env, watcherCallback)
    , bufferSize(bufferSize) {
}

void Server::initializeRunLoop() {
    // TODO For some reason GetCurrentThread() returns a thread that doesn't accept APCs
    threadHandle = OpenThread(
        THREAD_ALL_ACCESS,
        false,
        GetCurrentThreadId()
    );
    if (threadHandle == NULL) {
        throw FileWatcherException("Couldn't open current thread", GetLastError());
    }
}

void Server::terminateRunLoop() {
    executeOnRunLoop([this]() {
        terminated = true;
        return true;
    });
}

Server::~Server() {
    terminate();
}

void Server::runLoop() {
    while (!terminated) {
        SleepEx(INFINITE, true);
    }

    // We have received termination, cancel all watchers
    unique_lock<mutex> lock(mutationMutex);
    logToJava(FINE, "Finished with run loop, now cancelling remaining watch points", NULL);
    int pendingWatchPoints = 0;
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case LISTENING:
                try {
                    if (watchPoint.cancel()) {
                        pendingWatchPoints++;
                    }
                } catch (const exception& ex) {
                    logToJava(SEVERE, "%s", ex.what());
                }
                break;
            case CANCELLED:
                pendingWatchPoints++;
                break;
            default:
                break;
        }
    }

    // If there are any pending watchers, wait for them to finish
    if (pendingWatchPoints > 0) {
        logToJava(FINE, "Waiting for %d pending watch points to finish", pendingWatchPoints);
        SleepEx(0, true);
    }

    // Warn about  any unfinished watchpoints
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case NOT_LISTENING:
            case FINISHED:
                break;
            default:
                logToJava(WARNING, "Watch point %s did not finish before termination timeout (status = %d)",
                    utf16ToUtf8String(watchPoint.path).c_str(), watchPoint.status);
                break;
        }
    }

    CloseHandle(threadHandle);
}

static void CALLBACK executeOnRunLoopCallback(_In_ ULONG_PTR info) {
    Command* command = (Command*) info;
    try {
        command->result = command->function();
    } catch (const exception&) {
        command->failure = current_exception();
    }
    unique_lock<mutex> lock(command->server->executionMutex);
    command->executed.notify_all();
}

bool Server::executeOnRunLoop(function<bool()> function) {
    Command command;
    command.function = function;
    command.server = this;
    DWORD ret = QueueUserAPC(executeOnRunLoopCallback, threadHandle, (ULONG_PTR) &command);
    if (ret == 0) {
        throw FileWatcherException("Received error while queuing APC", GetLastError());
    }
    unique_lock<mutex> lock(executionMutex);
    auto status = command.executed.wait_for(lock, THREAD_TIMEOUT);
    if (status == cv_status::timeout) {
        throw FileWatcherException("Execution timed out");
    } else if (command.failure) {
        rethrow_exception(command.failure);
    } else {
        return command.result;
    }
}

void Server::registerPaths(const vector<u16string>& paths) {
    executeOnRunLoop([this, paths]() {
        AbstractServer::registerPaths(paths);
        return true;
    });
}

bool Server::unregisterPaths(const vector<u16string>& paths) {
    return executeOnRunLoop([this, paths]() {
        return AbstractServer::unregisterPaths(paths);
    });
}

void Server::registerPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    auto it = watchPoints.find(longPath);
    if (it != watchPoints.end()) {
        if (it->second.status != FINISHED) {
            throw FileWatcherException("Already watching path", path);
        }
        watchPoints.erase(it);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPath),
        forward_as_tuple(this, bufferSize, longPath));
}

bool Server::unregisterPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    if (watchPoints.erase(longPath) == 0) {
        logToJava(INFO, "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return false;
    }
    return true;
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher0(JNIEnv* env, jclass target, jint bufferSize, jobject javaCallback) {
    return wrapServer(env, new Server(env, bufferSize, javaCallback));
}

#endif
<|endoftext|>