<commit_before>//===-- X86Subtarget.cpp - X86 Subtarget Information ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the X86 specific subclass of TargetSubtarget.
//
//===----------------------------------------------------------------------===//

#include "X86Subtarget.h"
#include "X86GenSubtarget.inc"
#include "llvm/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;

static cl::opt<X86Subtarget::AsmWriterFlavorTy>
AsmWriterFlavor("x86-asm-syntax", cl::init(X86Subtarget::Unset),
  cl::desc("Choose style of code to emit from X86 backend:"),
  cl::values(
    clEnumValN(X86Subtarget::ATT,   "att",   "Emit AT&T-style assembly"),
    clEnumValN(X86Subtarget::Intel, "intel", "Emit Intel-style assembly"),
    clEnumValEnd));

/// True if accessing the GV requires an extra load. For Windows, dllimported
/// symbols are indirect, loading the value at address GV rather then the
/// value of GV itself. This means that the GlobalAddress must be in the base
/// or index register of the address, not the GV offset field.
bool X86Subtarget::GVRequiresExtraLoad(const GlobalValue* GV,
                                       const TargetMachine& TM,
                                       bool isDirectCall) const
{
  // FIXME: PIC
  if (TM.getRelocationModel() != Reloc::Static &&
      TM.getCodeModel() != CodeModel::Large) {
    if (isTargetDarwin()) {
      bool isDecl = GV->isDeclaration() && !GV->hasNotBeenReadFromBitcode();
      if (GV->hasHiddenVisibility() &&
          (Is64Bit || (!isDecl && !GV->hasCommonLinkage())))
        // If symbol visibility is hidden, the extra load is not needed if
        // target is x86-64 or the symbol is definitely defined in the current
        // translation unit.
        return false;
      return !isDirectCall && (isDecl || GV->mayBeOverridden());
    } else if (isTargetELF()) {
      // Extra load is needed for all externally visible.
      if (isDirectCall)
        return false;
      if (GV->hasInternalLinkage() || GV->hasHiddenVisibility())
        return false;
      return true;
    } else if (isTargetCygMing() || isTargetWindows()) {
      return (GV->hasDLLImportLinkage());
    }
  }
  return false;
}

/// True if accessing the GV requires a register.  This is a superset of the
/// cases where GVRequiresExtraLoad is true.  Some variations of PIC require
/// a register, but not an extra load.
bool X86Subtarget::GVRequiresRegister(const GlobalValue *GV,
                                       const TargetMachine& TM,
                                       bool isDirectCall) const
{
  if (GVRequiresExtraLoad(GV, TM, isDirectCall))
    return true;
  // Code below here need only consider cases where GVRequiresExtraLoad
  // returns false.
  if (TM.getRelocationModel() == Reloc::PIC_)
    return !isDirectCall &&
      (GV->hasInternalLinkage() || GV->hasExternalLinkage());
  return false;
}

/// getBZeroEntry - This function returns the name of a function which has an
/// interface like the non-standard bzero function, if such a function exists on
/// the current subtarget and it is considered prefereable over memset with zero
/// passed as the second argument. Otherwise it returns null.
const char *X86Subtarget::getBZeroEntry() const {
  // Darwin 10 has a __bzero entry point for this purpose.
  if (getDarwinVers() >= 10)
    return "__bzero";

  return 0;
}

/// GetCpuIDAndInfo - Execute the specified cpuid and return the 4 values in the
/// specified arguments.  If we can't run cpuid on the host, return true.
bool X86::GetCpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
                          unsigned *rECX, unsigned *rEDX) {
#if defined(__x86_64__)
  // gcc doesn't know cpuid would clobber ebx/rbx. Preseve it manually.
  asm ("movq\t%%rbx, %%rsi\n\t"
       "cpuid\n\t"
       "xchgq\t%%rbx, %%rsi\n\t"
       : "=a" (*rEAX),
         "=S" (*rEBX),
         "=c" (*rECX),
         "=d" (*rEDX)
       :  "a" (value));
  return false;
#elif defined(i386) || defined(__i386__) || defined(__x86__) || defined(_M_IX86)
#if defined(__GNUC__)
  asm ("movl\t%%ebx, %%esi\n\t"
       "cpuid\n\t"
       "xchgl\t%%ebx, %%esi\n\t"
       : "=a" (*rEAX),
         "=S" (*rEBX),
         "=c" (*rECX),
         "=d" (*rEDX)
       :  "a" (value));
  return false;
#elif defined(_MSC_VER)
  __asm {
    mov   eax,value
    cpuid
    mov   esi,rEAX
    mov   dword ptr [esi],eax
    mov   esi,rEBX
    mov   dword ptr [esi],ebx
    mov   esi,rECX
    mov   dword ptr [esi],ecx
    mov   esi,rEDX
    mov   dword ptr [esi],edx
  }
  return false;
#endif
#endif
  return true;
}

void X86Subtarget::AutoDetectSubtargetFeatures() {
  unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0;
  union {
    unsigned u[3];
    char     c[12];
  } text;

  if (X86::GetCpuIDAndInfo(0, &EAX, text.u+0, text.u+2, text.u+1))
    return;

  X86::GetCpuIDAndInfo(0x1, &EAX, &EBX, &ECX, &EDX);

  if ((EDX >> 23) & 0x1) X86SSELevel = MMX;
  if ((EDX >> 25) & 0x1) X86SSELevel = SSE1;
  if ((EDX >> 26) & 0x1) X86SSELevel = SSE2;
  if (ECX & 0x1)         X86SSELevel = SSE3;
  if ((ECX >> 9)  & 0x1) X86SSELevel = SSSE3;
  if ((ECX >> 19) & 0x1) X86SSELevel = SSE41;
  if ((ECX >> 20) & 0x1) X86SSELevel = SSE42;

  if (memcmp(text.c, "GenuineIntel", 12) == 0 ||
      memcmp(text.c, "AuthenticAMD", 12) == 0) {
    X86::GetCpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
    HasX86_64 = (EDX >> 29) & 0x1;
  }
}

static const char *GetCurrentX86CPU() {
  unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0;
  if (X86::GetCpuIDAndInfo(0x1, &EAX, &EBX, &ECX, &EDX))
    return "generic";
  unsigned Family  = (EAX >> 8) & 0xf; // Bits 8 - 11
  unsigned Model   = (EAX >> 4) & 0xf; // Bits 4 - 7
  X86::GetCpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
  bool Em64T = (EDX >> 29) & 0x1;

  union {
    unsigned u[3];
    char     c[12];
  } text;

  X86::GetCpuIDAndInfo(0, &EAX, text.u+0, text.u+2, text.u+1);
  if (memcmp(text.c, "GenuineIntel", 12) == 0) {
    switch (Family) {
      case 3:
        return "i386";
      case 4:
        return "i486";
      case 5:
        switch (Model) {
        case 4:  return "pentium-mmx";
        default: return "pentium";
        }
      case 6:
        switch (Model) {
        case 1:  return "pentiumpro";
        case 3:
        case 5:
        case 6:  return "pentium2";
        case 7:
        case 8:
        case 10:
        case 11: return "pentium3";
        case 9:
        case 13: return "pentium-m";
        case 14: return "yonah";
        case 15: return "core2";
        default: return "i686";
        }
      case 15: {
        switch (Model) {
        case 3:
        case 4:
          return (Em64T) ? "nocona" : "prescott";
        default:
          return (Em64T) ? "x86-64" : "pentium4";
        }
      }

    default:
      return "generic";
    }
  } else if (memcmp(text.c, "AuthenticAMD", 12) == 0) {
    // FIXME: this poorly matches the generated SubtargetFeatureKV table.  There
    // appears to be no way to generate the wide variety of AMD-specific targets
    // from the information returned from CPUID.
    switch (Family) {
      case 4:
        return "i486";
      case 5:
        switch (Model) {
        case 6:
        case 7:  return "k6";
        case 8:  return "k6-2";
        case 9:
        case 13: return "k6-3";
        default: return "pentium";
        }
      case 6:
        switch (Model) {
        case 4:  return "athlon-tbird";
        case 6:
        case 7:
        case 8:  return "athlon-mp";
        case 10: return "athlon-xp";
        default: return "athlon";
        }
      case 15:
        switch (Model) {
        case 1:  return "opteron";
        case 5:  return "athlon-fx"; // also opteron
        default: return "athlon64";
        }
    default:
      return "generic";
    }
  } else {
    return "generic";
  }
}

X86Subtarget::X86Subtarget(const Module &M, const std::string &FS, bool is64Bit)
  : AsmFlavor(AsmWriterFlavor)
  , PICStyle(PICStyles::None)
  , X86SSELevel(NoMMXSSE)
  , X863DNowLevel(NoThreeDNow)
  , HasX86_64(false)
  , DarwinVers(0)
  , IsLinux(false)
  , stackAlignment(8)
  // FIXME: this is a known good value for Yonah. How about others?
  , MaxInlineSizeThreshold(128)
  , Is64Bit(is64Bit)
  , TargetType(isELF) { // Default to ELF unless otherwise specified.

  // Determine default and user specified characteristics
  if (!FS.empty()) {
    // If feature string is not empty, parse features string.
    std::string CPU = GetCurrentX86CPU();
    ParseSubtargetFeatures(FS, CPU);
  } else {
    // Otherwise, use CPUID to auto-detect feature set.
    AutoDetectSubtargetFeatures();
  }

  // If requesting codegen for X86-64, make sure that 64-bit and SSE2 features
  // are enabled.  These are available on all x86-64 CPUs.
  if (Is64Bit) {
    HasX86_64 = true;
    if (X86SSELevel < SSE2)
      X86SSELevel = SSE2;
  }

  // Set the boolean corresponding to the current target triple, or the default
  // if one cannot be determined, to true.
  const std::string& TT = M.getTargetTriple();
  if (TT.length() > 5) {
    size_t Pos;
    if ((Pos = TT.find("-darwin")) != std::string::npos) {
      TargetType = isDarwin;

      // Compute the darwin version number.
      if (isdigit(TT[Pos+7]))
        DarwinVers = atoi(&TT[Pos+7]);
      else
        DarwinVers = 8;  // Minimum supported darwin is Tiger.
    } else if (TT.find("linux") != std::string::npos) {
      // Linux doesn't imply ELF, but we don't currently support anything else.
      TargetType = isELF;
      IsLinux = true;
    } else if (TT.find("cygwin") != std::string::npos) {
      TargetType = isCygwin;
    } else if (TT.find("mingw") != std::string::npos) {
      TargetType = isMingw;
    } else if (TT.find("win32") != std::string::npos) {
      TargetType = isWindows;
    } else if (TT.find("windows") != std::string::npos) {
      TargetType = isWindows;
    }
  } else if (TT.empty()) {
#if defined(__CYGWIN__)
    TargetType = isCygwin;
#elif defined(__MINGW32__) || defined(__MINGW64__)
    TargetType = isMingw;
#elif defined(__APPLE__)
    TargetType = isDarwin;
#if __APPLE_CC__ > 5400
    DarwinVers = 9;  // GCC 5400+ is Leopard.
#else
    DarwinVers = 8;  // Minimum supported darwin is Tiger.
#endif

#elif defined(_WIN32) || defined(_WIN64)
    TargetType = isWindows;
#elif defined(__linux__)
    // Linux doesn't imply ELF, but we don't currently support anything else.
    TargetType = isELF;
    IsLinux = true;
#endif
  }

  // If the asm syntax hasn't been overridden on the command line, use whatever
  // the target wants.
  if (AsmFlavor == X86Subtarget::Unset) {
    AsmFlavor = (TargetType == isWindows)
      ? X86Subtarget::Intel : X86Subtarget::ATT;
  }

  // Stack alignment is 16 bytes on Darwin (both 32 and 64 bit) and for all 64
  // bit targets.
  if (TargetType == isDarwin || Is64Bit)
    stackAlignment = 16;

  if (StackAlignment)
    stackAlignment = StackAlignment;
}
<commit_msg>Perform cheap checks first.<commit_after>//===-- X86Subtarget.cpp - X86 Subtarget Information ------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the X86 specific subclass of TargetSubtarget.
//
//===----------------------------------------------------------------------===//

#include "X86Subtarget.h"
#include "X86GenSubtarget.inc"
#include "llvm/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;

static cl::opt<X86Subtarget::AsmWriterFlavorTy>
AsmWriterFlavor("x86-asm-syntax", cl::init(X86Subtarget::Unset),
  cl::desc("Choose style of code to emit from X86 backend:"),
  cl::values(
    clEnumValN(X86Subtarget::ATT,   "att",   "Emit AT&T-style assembly"),
    clEnumValN(X86Subtarget::Intel, "intel", "Emit Intel-style assembly"),
    clEnumValEnd));

/// True if accessing the GV requires an extra load. For Windows, dllimported
/// symbols are indirect, loading the value at address GV rather then the
/// value of GV itself. This means that the GlobalAddress must be in the base
/// or index register of the address, not the GV offset field.
bool X86Subtarget::GVRequiresExtraLoad(const GlobalValue* GV,
                                       const TargetMachine& TM,
                                       bool isDirectCall) const
{
  // FIXME: PIC
  if (TM.getRelocationModel() != Reloc::Static &&
      TM.getCodeModel() != CodeModel::Large) {
    if (isTargetDarwin()) {
      if (!isDirectCall)
        return false;
      bool isDecl = GV->isDeclaration() && !GV->hasNotBeenReadFromBitcode();
      if (GV->hasHiddenVisibility() &&
          (Is64Bit || (!isDecl && !GV->hasCommonLinkage())))
        // If symbol visibility is hidden, the extra load is not needed if
        // target is x86-64 or the symbol is definitely defined in the current
        // translation unit.
        return false;
      return isDecl || GV->mayBeOverridden();
    } else if (isTargetELF()) {
      // Extra load is needed for all externally visible.
      if (isDirectCall)
        return false;
      if (GV->hasInternalLinkage() || GV->hasHiddenVisibility())
        return false;
      return true;
    } else if (isTargetCygMing() || isTargetWindows()) {
      return (GV->hasDLLImportLinkage());
    }
  }
  return false;
}

/// True if accessing the GV requires a register.  This is a superset of the
/// cases where GVRequiresExtraLoad is true.  Some variations of PIC require
/// a register, but not an extra load.
bool X86Subtarget::GVRequiresRegister(const GlobalValue *GV,
                                       const TargetMachine& TM,
                                       bool isDirectCall) const
{
  if (GVRequiresExtraLoad(GV, TM, isDirectCall))
    return true;
  // Code below here need only consider cases where GVRequiresExtraLoad
  // returns false.
  if (TM.getRelocationModel() == Reloc::PIC_)
    return !isDirectCall &&
      (GV->hasInternalLinkage() || GV->hasExternalLinkage());
  return false;
}

/// getBZeroEntry - This function returns the name of a function which has an
/// interface like the non-standard bzero function, if such a function exists on
/// the current subtarget and it is considered prefereable over memset with zero
/// passed as the second argument. Otherwise it returns null.
const char *X86Subtarget::getBZeroEntry() const {
  // Darwin 10 has a __bzero entry point for this purpose.
  if (getDarwinVers() >= 10)
    return "__bzero";

  return 0;
}

/// GetCpuIDAndInfo - Execute the specified cpuid and return the 4 values in the
/// specified arguments.  If we can't run cpuid on the host, return true.
bool X86::GetCpuIDAndInfo(unsigned value, unsigned *rEAX, unsigned *rEBX,
                          unsigned *rECX, unsigned *rEDX) {
#if defined(__x86_64__)
  // gcc doesn't know cpuid would clobber ebx/rbx. Preseve it manually.
  asm ("movq\t%%rbx, %%rsi\n\t"
       "cpuid\n\t"
       "xchgq\t%%rbx, %%rsi\n\t"
       : "=a" (*rEAX),
         "=S" (*rEBX),
         "=c" (*rECX),
         "=d" (*rEDX)
       :  "a" (value));
  return false;
#elif defined(i386) || defined(__i386__) || defined(__x86__) || defined(_M_IX86)
#if defined(__GNUC__)
  asm ("movl\t%%ebx, %%esi\n\t"
       "cpuid\n\t"
       "xchgl\t%%ebx, %%esi\n\t"
       : "=a" (*rEAX),
         "=S" (*rEBX),
         "=c" (*rECX),
         "=d" (*rEDX)
       :  "a" (value));
  return false;
#elif defined(_MSC_VER)
  __asm {
    mov   eax,value
    cpuid
    mov   esi,rEAX
    mov   dword ptr [esi],eax
    mov   esi,rEBX
    mov   dword ptr [esi],ebx
    mov   esi,rECX
    mov   dword ptr [esi],ecx
    mov   esi,rEDX
    mov   dword ptr [esi],edx
  }
  return false;
#endif
#endif
  return true;
}

void X86Subtarget::AutoDetectSubtargetFeatures() {
  unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0;
  union {
    unsigned u[3];
    char     c[12];
  } text;

  if (X86::GetCpuIDAndInfo(0, &EAX, text.u+0, text.u+2, text.u+1))
    return;

  X86::GetCpuIDAndInfo(0x1, &EAX, &EBX, &ECX, &EDX);

  if ((EDX >> 23) & 0x1) X86SSELevel = MMX;
  if ((EDX >> 25) & 0x1) X86SSELevel = SSE1;
  if ((EDX >> 26) & 0x1) X86SSELevel = SSE2;
  if (ECX & 0x1)         X86SSELevel = SSE3;
  if ((ECX >> 9)  & 0x1) X86SSELevel = SSSE3;
  if ((ECX >> 19) & 0x1) X86SSELevel = SSE41;
  if ((ECX >> 20) & 0x1) X86SSELevel = SSE42;

  if (memcmp(text.c, "GenuineIntel", 12) == 0 ||
      memcmp(text.c, "AuthenticAMD", 12) == 0) {
    X86::GetCpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
    HasX86_64 = (EDX >> 29) & 0x1;
  }
}

static const char *GetCurrentX86CPU() {
  unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0;
  if (X86::GetCpuIDAndInfo(0x1, &EAX, &EBX, &ECX, &EDX))
    return "generic";
  unsigned Family  = (EAX >> 8) & 0xf; // Bits 8 - 11
  unsigned Model   = (EAX >> 4) & 0xf; // Bits 4 - 7
  X86::GetCpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
  bool Em64T = (EDX >> 29) & 0x1;

  union {
    unsigned u[3];
    char     c[12];
  } text;

  X86::GetCpuIDAndInfo(0, &EAX, text.u+0, text.u+2, text.u+1);
  if (memcmp(text.c, "GenuineIntel", 12) == 0) {
    switch (Family) {
      case 3:
        return "i386";
      case 4:
        return "i486";
      case 5:
        switch (Model) {
        case 4:  return "pentium-mmx";
        default: return "pentium";
        }
      case 6:
        switch (Model) {
        case 1:  return "pentiumpro";
        case 3:
        case 5:
        case 6:  return "pentium2";
        case 7:
        case 8:
        case 10:
        case 11: return "pentium3";
        case 9:
        case 13: return "pentium-m";
        case 14: return "yonah";
        case 15: return "core2";
        default: return "i686";
        }
      case 15: {
        switch (Model) {
        case 3:
        case 4:
          return (Em64T) ? "nocona" : "prescott";
        default:
          return (Em64T) ? "x86-64" : "pentium4";
        }
      }

    default:
      return "generic";
    }
  } else if (memcmp(text.c, "AuthenticAMD", 12) == 0) {
    // FIXME: this poorly matches the generated SubtargetFeatureKV table.  There
    // appears to be no way to generate the wide variety of AMD-specific targets
    // from the information returned from CPUID.
    switch (Family) {
      case 4:
        return "i486";
      case 5:
        switch (Model) {
        case 6:
        case 7:  return "k6";
        case 8:  return "k6-2";
        case 9:
        case 13: return "k6-3";
        default: return "pentium";
        }
      case 6:
        switch (Model) {
        case 4:  return "athlon-tbird";
        case 6:
        case 7:
        case 8:  return "athlon-mp";
        case 10: return "athlon-xp";
        default: return "athlon";
        }
      case 15:
        switch (Model) {
        case 1:  return "opteron";
        case 5:  return "athlon-fx"; // also opteron
        default: return "athlon64";
        }
    default:
      return "generic";
    }
  } else {
    return "generic";
  }
}

X86Subtarget::X86Subtarget(const Module &M, const std::string &FS, bool is64Bit)
  : AsmFlavor(AsmWriterFlavor)
  , PICStyle(PICStyles::None)
  , X86SSELevel(NoMMXSSE)
  , X863DNowLevel(NoThreeDNow)
  , HasX86_64(false)
  , DarwinVers(0)
  , IsLinux(false)
  , stackAlignment(8)
  // FIXME: this is a known good value for Yonah. How about others?
  , MaxInlineSizeThreshold(128)
  , Is64Bit(is64Bit)
  , TargetType(isELF) { // Default to ELF unless otherwise specified.

  // Determine default and user specified characteristics
  if (!FS.empty()) {
    // If feature string is not empty, parse features string.
    std::string CPU = GetCurrentX86CPU();
    ParseSubtargetFeatures(FS, CPU);
  } else {
    // Otherwise, use CPUID to auto-detect feature set.
    AutoDetectSubtargetFeatures();
  }

  // If requesting codegen for X86-64, make sure that 64-bit and SSE2 features
  // are enabled.  These are available on all x86-64 CPUs.
  if (Is64Bit) {
    HasX86_64 = true;
    if (X86SSELevel < SSE2)
      X86SSELevel = SSE2;
  }

  // Set the boolean corresponding to the current target triple, or the default
  // if one cannot be determined, to true.
  const std::string& TT = M.getTargetTriple();
  if (TT.length() > 5) {
    size_t Pos;
    if ((Pos = TT.find("-darwin")) != std::string::npos) {
      TargetType = isDarwin;

      // Compute the darwin version number.
      if (isdigit(TT[Pos+7]))
        DarwinVers = atoi(&TT[Pos+7]);
      else
        DarwinVers = 8;  // Minimum supported darwin is Tiger.
    } else if (TT.find("linux") != std::string::npos) {
      // Linux doesn't imply ELF, but we don't currently support anything else.
      TargetType = isELF;
      IsLinux = true;
    } else if (TT.find("cygwin") != std::string::npos) {
      TargetType = isCygwin;
    } else if (TT.find("mingw") != std::string::npos) {
      TargetType = isMingw;
    } else if (TT.find("win32") != std::string::npos) {
      TargetType = isWindows;
    } else if (TT.find("windows") != std::string::npos) {
      TargetType = isWindows;
    }
  } else if (TT.empty()) {
#if defined(__CYGWIN__)
    TargetType = isCygwin;
#elif defined(__MINGW32__) || defined(__MINGW64__)
    TargetType = isMingw;
#elif defined(__APPLE__)
    TargetType = isDarwin;
#if __APPLE_CC__ > 5400
    DarwinVers = 9;  // GCC 5400+ is Leopard.
#else
    DarwinVers = 8;  // Minimum supported darwin is Tiger.
#endif

#elif defined(_WIN32) || defined(_WIN64)
    TargetType = isWindows;
#elif defined(__linux__)
    // Linux doesn't imply ELF, but we don't currently support anything else.
    TargetType = isELF;
    IsLinux = true;
#endif
  }

  // If the asm syntax hasn't been overridden on the command line, use whatever
  // the target wants.
  if (AsmFlavor == X86Subtarget::Unset) {
    AsmFlavor = (TargetType == isWindows)
      ? X86Subtarget::Intel : X86Subtarget::ATT;
  }

  // Stack alignment is 16 bytes on Darwin (both 32 and 64 bit) and for all 64
  // bit targets.
  if (TargetType == isDarwin || Is64Bit)
    stackAlignment = 16;

  if (StackAlignment)
    stackAlignment = StackAlignment;
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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.

// This file provides the embedder's side of random webkit glue functions.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <wininet.h>
#endif

#include "base/clipboard.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/plugin/npobject_util.h"
#include "chrome/renderer/net/render_dns_master.h"
#include "chrome/renderer/render_process.h"
#include "chrome/renderer/render_thread.h"
#include "chrome/renderer/render_view.h"
#include "googleurl/src/url_util.h"
#include "webkit/glue/scoped_clipboard_writer_glue.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webkit_glue.h"

#include "WebKit.h"
#include "WebKitClient.h"
#include "WebString.h"

#include <vector>

#include "SkBitmap.h"

#if defined(OS_WIN)
#include <strsafe.h>  // note: per msdn docs, this must *follow* other includes
#endif

template <typename T, size_t stack_capacity>
class ResizableStackArray {
 public:
  ResizableStackArray()
      : cur_buffer_(stack_buffer_), cur_capacity_(stack_capacity) {
  }
  ~ResizableStackArray() {
    FreeHeap();
  }

  T* get() const {
    return cur_buffer_;
  }

  T& operator[](size_t i) {
    return cur_buffer_[i];
  }

  size_t capacity() const {
    return cur_capacity_;
  }

  void Resize(size_t new_size) {
    if (new_size < cur_capacity_)
      return;  // already big enough
    FreeHeap();
    cur_capacity_ = new_size;
    cur_buffer_ = new T[new_size];
  }

 private:
  // Resets the heap buffer, if any
  void FreeHeap() {
    if (cur_buffer_ != stack_buffer_) {
      delete[] cur_buffer_;
      cur_buffer_ = stack_buffer_;
      cur_capacity_ = stack_capacity;
    }
  }

  T stack_buffer_[stack_capacity];
  T* cur_buffer_;
  size_t cur_capacity_;
};

#if defined(OS_WIN)
// This definition of WriteBitmapFromPixels uses shared memory to communicate
// across processes.
void ScopedClipboardWriterGlue::WriteBitmapFromPixels(const void* pixels,
                                                      const gfx::Size& size) {
  // Do not try to write a bitmap more than once
  if (shared_buf_)
    return;

  size_t buf_size = 4 * size.width() * size.height();

  // Allocate a shared memory buffer to hold the bitmap bits
  shared_buf_ = new base::SharedMemory;
  shared_buf_->Create(L"", false /* read write */, true /* open existing */,
                      buf_size);
  if (!shared_buf_ || !shared_buf_->Map(buf_size)) {
    NOTREACHED();
    return;
  }

  // Copy the bits into shared memory
  memcpy(shared_buf_->memory(), pixels, buf_size);
  shared_buf_->Unmap();

  Clipboard::ObjectMapParam param1, param2;
  base::SharedMemoryHandle smh = shared_buf_->handle();

  const char* shared_handle = reinterpret_cast<const char*>(&smh);
  for (size_t i = 0; i < sizeof base::SharedMemoryHandle; i++)
    param1.push_back(shared_handle[i]);

  const char* size_data = reinterpret_cast<const char*>(&size);
  for (size_t i = 0; i < sizeof gfx::Size; i++)
    param2.push_back(size_data[i]);

  Clipboard::ObjectMapParams params;
  params.push_back(param1);
  params.push_back(param2);
  objects_[Clipboard::CBF_SMBITMAP] = params;
}
#endif

// Define a destructor that makes IPCs to flush the contents to the
// system clipboard.
ScopedClipboardWriterGlue::~ScopedClipboardWriterGlue() {
  if (objects_.empty())
    return;

#if defined(OS_WIN)
  if (shared_buf_) {
    RenderThread::current()->Send(
        new ViewHostMsg_ClipboardWriteObjectsSync(objects_));
    delete shared_buf_;
    return;
  }
#endif

  RenderThread::current()->Send(
      new ViewHostMsg_ClipboardWriteObjectsAsync(objects_));
}

namespace webkit_glue {

// Global variable set during RenderProcess::GlobalInit if video was enabled
// and our media libraries were successfully loaded.
static bool g_media_player_available = false;

void SetMediaPlayerAvailable(bool value) {
  g_media_player_available = value;
}

bool IsMediaPlayerAvailable() {
  return g_media_player_available;
}

void PrecacheUrl(const wchar_t* url, int url_length) {
  // TBD: jar: Need implementation that loads the targetted URL into our cache.
  // For now, at least prefetch DNS lookup
  std::string url_string;
  WideToUTF8(url, url_length, &url_string);
  const std::string host = GURL(url_string).host();
  if (!host.empty())
    DnsPrefetchCString(host.data(), host.length());
}

void AppendToLog(const char* file, int line, const char* msg) {
  logging::LogMessage(file, line).stream() << msg;
}

std::string GetDataResource(int resource_id) {
  return ResourceBundle::GetSharedInstance().GetDataResource(resource_id);
}

#if defined(OS_WIN)
HCURSOR LoadCursor(int cursor_id) {
  return ResourceBundle::GetSharedInstance().LoadCursor(cursor_id);
}
#endif

// Clipboard glue

Clipboard* ClipboardGetClipboard(){
  return NULL;
}

bool ClipboardIsFormatAvailable(const Clipboard::FormatType& format) {
  bool result;
  RenderThread::current()->Send(
      new ViewHostMsg_ClipboardIsFormatAvailable(format, &result));
  return result;
}

void ClipboardReadText(string16* result) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadText(result));
}

void ClipboardReadAsciiText(std::string* result) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadAsciiText(result));
}

void ClipboardReadHTML(string16* markup, GURL* url) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadHTML(markup, url));
}

GURL GetInspectorURL() {
  return GURL("chrome-ui://inspector/inspector.html");
}

std::string GetUIResourceProtocol() {
  return "chrome-ui";
}

bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
  return RenderThread::current()->Send(
      new ViewHostMsg_GetPlugins(refresh, plugins));
}

webkit_glue::ScreenInfo GetScreenInfo(gfx::NativeViewId window) {
  webkit_glue::ScreenInfo results;
  RenderThread::current()->Send(
      new ViewHostMsg_GetScreenInfo(window, &results));
  return results;
}

// static factory function
ResourceLoaderBridge* ResourceLoaderBridge::Create(
    WebFrame* webframe,
    const std::string& method,
    const GURL& url,
    const GURL& policy_url,
    const GURL& referrer,
    const std::string& headers,
    int load_flags,
    int origin_pid,
    ResourceType::Type resource_type,
    bool mixed_content) {
  // TODO(darin): we need to eliminate the webframe parameter because webkit
  // does not always supply it (see ResourceHandle::loadResourceSynchronously).
  // Instead we should add context to ResourceRequest, which will be easy to do
  // once we merge to the latest WebKit (r23806 at least).
  if (!webframe) {
    NOTREACHED() << "no webframe";
    return NULL;
  }

  RenderView* rv = static_cast<RenderView*>(webframe->GetView()->GetDelegate());
  int route_id = rv->routing_id();
  ResourceDispatcher* dispatch = RenderThread::current()->resource_dispatcher();
  return dispatch->CreateBridge(method, url, policy_url, referrer, headers,
                                load_flags, origin_pid, resource_type,
                                mixed_content, 0, route_id);
}

void NotifyCacheStats() {
  // Update the browser about our cache
  // NOTE: Since this can be called from the plugin process, we might not have
  // a RenderThread.  Do nothing in that case.
  if (!IsPluginProcess())
    RenderThread::current()->InformHostOfCacheStatsLater();
}

}  // namespace webkit_glue
<commit_msg>Temp fix for reliablity crashes. Review URL: http://codereview.chromium.org/46017<commit_after>// Copyright (c) 2006-2008 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.

// This file provides the embedder's side of random webkit glue functions.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <wininet.h>
#endif

#include "base/clipboard.h"
#include "base/scoped_clipboard_writer.h"
#include "base/string_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/resource_bundle.h"
#include "chrome/plugin/npobject_util.h"
#include "chrome/renderer/net/render_dns_master.h"
#include "chrome/renderer/render_process.h"
#include "chrome/renderer/render_thread.h"
#include "chrome/renderer/render_view.h"
#include "googleurl/src/url_util.h"
#include "webkit/glue/scoped_clipboard_writer_glue.h"
#include "webkit/glue/webframe.h"
#include "webkit/glue/webkit_glue.h"

#include "WebKit.h"
#include "WebKitClient.h"
#include "WebString.h"

#include <vector>

#include "SkBitmap.h"

#if defined(OS_WIN)
#include <strsafe.h>  // note: per msdn docs, this must *follow* other includes
#endif

template <typename T, size_t stack_capacity>
class ResizableStackArray {
 public:
  ResizableStackArray()
      : cur_buffer_(stack_buffer_), cur_capacity_(stack_capacity) {
  }
  ~ResizableStackArray() {
    FreeHeap();
  }

  T* get() const {
    return cur_buffer_;
  }

  T& operator[](size_t i) {
    return cur_buffer_[i];
  }

  size_t capacity() const {
    return cur_capacity_;
  }

  void Resize(size_t new_size) {
    if (new_size < cur_capacity_)
      return;  // already big enough
    FreeHeap();
    cur_capacity_ = new_size;
    cur_buffer_ = new T[new_size];
  }

 private:
  // Resets the heap buffer, if any
  void FreeHeap() {
    if (cur_buffer_ != stack_buffer_) {
      delete[] cur_buffer_;
      cur_buffer_ = stack_buffer_;
      cur_capacity_ = stack_capacity;
    }
  }

  T stack_buffer_[stack_capacity];
  T* cur_buffer_;
  size_t cur_capacity_;
};

#if defined(OS_WIN)
// This definition of WriteBitmapFromPixels uses shared memory to communicate
// across processes.
void ScopedClipboardWriterGlue::WriteBitmapFromPixels(const void* pixels,
                                                      const gfx::Size& size) {
  // Do not try to write a bitmap more than once
  if (shared_buf_)
    return;

  size_t buf_size = 4 * size.width() * size.height();

  // Allocate a shared memory buffer to hold the bitmap bits
  shared_buf_ = new base::SharedMemory;
  shared_buf_->Create(L"", false /* read write */, true /* open existing */,
                      buf_size);
  if (!shared_buf_ || !shared_buf_->Map(buf_size)) {
    NOTREACHED();
    return;
  }

  // Copy the bits into shared memory
  memcpy(shared_buf_->memory(), pixels, buf_size);
  shared_buf_->Unmap();

  Clipboard::ObjectMapParam param1, param2;
  base::SharedMemoryHandle smh = shared_buf_->handle();

  const char* shared_handle = reinterpret_cast<const char*>(&smh);
  for (size_t i = 0; i < sizeof base::SharedMemoryHandle; i++)
    param1.push_back(shared_handle[i]);

  const char* size_data = reinterpret_cast<const char*>(&size);
  for (size_t i = 0; i < sizeof gfx::Size; i++)
    param2.push_back(size_data[i]);

  Clipboard::ObjectMapParams params;
  params.push_back(param1);
  params.push_back(param2);
  objects_[Clipboard::CBF_SMBITMAP] = params;
}
#endif

// Define a destructor that makes IPCs to flush the contents to the
// system clipboard.
ScopedClipboardWriterGlue::~ScopedClipboardWriterGlue() {
  if (objects_.empty())
    return;

#if defined(OS_WIN)
  if (shared_buf_) {
    RenderThread::current()->Send(
        new ViewHostMsg_ClipboardWriteObjectsSync(objects_));
    delete shared_buf_;
    return;
  }
#endif

  RenderThread::current()->Send(
      new ViewHostMsg_ClipboardWriteObjectsAsync(objects_));
}

namespace webkit_glue {

// Global variable set during RenderProcess::GlobalInit if video was enabled
// and our media libraries were successfully loaded.
static bool g_media_player_available = false;

void SetMediaPlayerAvailable(bool value) {
  g_media_player_available = value;
}

bool IsMediaPlayerAvailable() {
  return g_media_player_available;
}

void PrecacheUrl(const wchar_t* url, int url_length) {
  // TBD: jar: Need implementation that loads the targetted URL into our cache.
  // For now, at least prefetch DNS lookup
  std::string url_string;
  WideToUTF8(url, url_length, &url_string);
  const std::string host = GURL(url_string).host();
  if (!host.empty())
    DnsPrefetchCString(host.data(), host.length());
}

void AppendToLog(const char* file, int line, const char* msg) {
  logging::LogMessage(file, line).stream() << msg;
}

std::string GetDataResource(int resource_id) {
  return ResourceBundle::GetSharedInstance().GetDataResource(resource_id);
}

#if defined(OS_WIN)
HCURSOR LoadCursor(int cursor_id) {
  return ResourceBundle::GetSharedInstance().LoadCursor(cursor_id);
}
#endif

// Clipboard glue

Clipboard* ClipboardGetClipboard(){
  return NULL;
}

bool ClipboardIsFormatAvailable(const Clipboard::FormatType& format) {
  bool result;
  RenderThread::current()->Send(
      new ViewHostMsg_ClipboardIsFormatAvailable(format, &result));
  return result;
}

void ClipboardReadText(string16* result) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadText(result));
}

void ClipboardReadAsciiText(std::string* result) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadAsciiText(result));
}

void ClipboardReadHTML(string16* markup, GURL* url) {
  RenderThread::current()->Send(new ViewHostMsg_ClipboardReadHTML(markup, url));
}

GURL GetInspectorURL() {
  return GURL("chrome-ui://inspector/inspector.html");
}

std::string GetUIResourceProtocol() {
  return "chrome-ui";
}

bool GetPlugins(bool refresh, std::vector<WebPluginInfo>* plugins) {
  return RenderThread::current()->Send(
      new ViewHostMsg_GetPlugins(refresh, plugins));
}

webkit_glue::ScreenInfo GetScreenInfo(gfx::NativeViewId window) {
  webkit_glue::ScreenInfo results;
  RenderThread::current()->Send(
      new ViewHostMsg_GetScreenInfo(window, &results));
  return results;
}

// static factory function
ResourceLoaderBridge* ResourceLoaderBridge::Create(
    WebFrame* webframe,
    const std::string& method,
    const GURL& url,
    const GURL& policy_url,
    const GURL& referrer,
    const std::string& headers,
    int load_flags,
    int origin_pid,
    ResourceType::Type resource_type,
    bool mixed_content) {
  // TODO(darin): we need to eliminate the webframe parameter because webkit
  // does not always supply it (see ResourceHandle::loadResourceSynchronously).
  // Instead we should add context to ResourceRequest, which will be easy to do
  // once we merge to the latest WebKit (r23806 at least).
  if (!webframe) {
    NOTREACHED() << "no webframe";
    return NULL;
  }

  if (!webframe->GetView()->GetDelegate())
    return NULL;

  RenderView* rv = static_cast<RenderView*>(webframe->GetView()->GetDelegate());
  int route_id = rv->routing_id();
  ResourceDispatcher* dispatch = RenderThread::current()->resource_dispatcher();
  return dispatch->CreateBridge(method, url, policy_url, referrer, headers,
                                load_flags, origin_pid, resource_type,
                                mixed_content, 0, route_id);
}

void NotifyCacheStats() {
  // Update the browser about our cache
  // NOTE: Since this can be called from the plugin process, we might not have
  // a RenderThread.  Do nothing in that case.
  if (!IsPluginProcess())
    RenderThread::current()->InformHostOfCacheStatsLater();
}

}  // namespace webkit_glue
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium OS 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 "update_engine/chrome_browser_proxy_resolver.h"

#include <map>
#include <string>

#include <base/string_tokenizer.h>
#include <base/string_util.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <google/protobuf/stubs/common.h>

#include "update_engine/dbus_constants.h"
#include "update_engine/utils.h"

namespace chromeos_update_engine {

using google::protobuf::Closure;
using google::protobuf::NewCallback;
using std::deque;
using std::make_pair;
using std::multimap;
using std::pair;
using std::string;

#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE                 \
  "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
const char kLibCrosServiceResolveNetworkProxyMethodName[] =
    "ResolveNetworkProxy";
const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
const char kLibCrosProxyResolveSignalInterface[] =
    LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
    "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
    "path='/', "
    "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
#undef LIB_CROS_PROXY_RESOLVE_NAME

namespace {
const int kTimeout = 5;  // seconds

DBusGProxy* GetProxy(DbusGlibInterface* dbus) {
  GError* error = NULL;

  DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error);
  if (!bus) {
    LOG(ERROR) << "Failed to get bus";
    return NULL;
  }
  DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus,
                                                 kLibCrosServiceName,
                                                 kLibCrosServicePath,
                                                 kLibCrosServiceInterface,
                                                 &error);
  if (!proxy) {
    LOG(ERROR) << "Error getting dbus proxy for "
               << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error);
    return NULL;
  }
  return proxy;
}

}  // namespace {}

ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
    : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}

bool ChromeBrowserProxyResolver::Init() {
  // Set up signal handler. Code lifted from libcros
  if (proxy_) {
    // Already initialized
    return true;
  }
  GError* gerror = NULL;
  DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
  TEST_AND_RETURN_FALSE(gbus);
  DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
  TEST_AND_RETURN_FALSE(connection);
  DBusError error;
  dbus_error_init(&error);
  dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error);
  TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error));
  TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
      connection,
      &ChromeBrowserProxyResolver::StaticFilterMessage,
      this,
      NULL));

  proxy_ = GetProxy(dbus_);
  if (!proxy_) {
    dbus_->DbusConnectionRemoveFilter(
        connection,
        &ChromeBrowserProxyResolver::StaticFilterMessage,
        this);
  }
  TEST_AND_RETURN_FALSE(proxy_);  // For the error log
  return true;
}

ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
  if (proxy_) {
    GError* gerror = NULL;
    DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
    TEST_AND_RETURN(gbus);
    DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
    dbus_->DbusConnectionRemoveFilter(
        connection,
        &ChromeBrowserProxyResolver::StaticFilterMessage,
        this);
  }
  // Kill outstanding timers
  for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
       ++it) {
    g_source_destroy(it->second);
    it->second = NULL;
  }
}

bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
                                                  ProxiesResolvedFn callback,
                                                  void* data) {
  GError* error = NULL;
  guint timeout = timeout_;
  if (!proxy_ || !dbus_->ProxyCall(
          proxy_,
          kLibCrosServiceResolveNetworkProxyMethodName,
          &error,
          G_TYPE_STRING, url.c_str(),
          G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
          G_TYPE_STRING, kLibCrosProxyResolveName,
          G_TYPE_INVALID, G_TYPE_INVALID)) {
    LOG(WARNING) << "dbus_g_proxy_call failed: "
                 << utils::GetGErrorMessage(error)
                 << " Continuing with no proxy.";
    timeout = 0;
  }
  callbacks_.insert(make_pair(url, make_pair(callback, data)));
  Closure* closure = NewCallback(this,
                                 &ChromeBrowserProxyResolver::HandleTimeout,
                                 url);
  GSource* timer = g_timeout_source_new_seconds(timeout);
  g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
  g_source_attach(timer, NULL);
  timers_.insert(make_pair(url, timer));
  return true;
}

DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
    DBusConnection* connection,
    DBusMessage* message) {
  // Code lifted from libcros.
  if (!dbus_->DbusMessageIsSignal(message,
                                  kLibCrosProxyResolveSignalInterface,
                                  kLibCrosProxyResolveName)) {
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  }
  // Get args
  char* source_url = NULL;
  char* proxy_list = NULL;
  char* error = NULL;
  DBusError arg_error;
  dbus_error_init(&arg_error);
  if (!dbus_->DbusMessageGetArgs(message, &arg_error,
                                 DBUS_TYPE_STRING, &source_url,
                                 DBUS_TYPE_STRING, &proxy_list,
                                 DBUS_TYPE_STRING, &error,
                                 DBUS_TYPE_INVALID)) {
    LOG(ERROR) << "Error reading dbus signal.";
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  if (!source_url || !proxy_list) {
    LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  HandleReply(source_url, proxy_list);
  return DBUS_HANDLER_RESULT_HANDLED;
}

bool ChromeBrowserProxyResolver::DeleteUrlState(
    const string& source_url,
    bool delete_timer,
    pair<ProxiesResolvedFn, void*>* callback) {
  {
    CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
    TEST_AND_RETURN_FALSE(it != callbacks_.end());
    TEST_AND_RETURN_FALSE(it->first == source_url);
    if (callback)
      *callback = it->second;
    callbacks_.erase(it);
  }
  {
    TimeoutsMap::iterator it = timers_.lower_bound(source_url);
    TEST_AND_RETURN_FALSE(it != timers_.end());
    TEST_AND_RETURN_FALSE(it->first == source_url);
    if (delete_timer)
      g_source_destroy(it->second);
    timers_.erase(it);
  }
  return true;
}

void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
                                             const string& proxy_list) {
  pair<ProxiesResolvedFn, void*> callback;
  TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
  (*callback.first)(ParseProxyString(proxy_list), callback.second);
}

void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
  pair<ProxiesResolvedFn, void*> callback;
  TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
  deque<string> proxies;
  proxies.push_back(kNoProxy);
  (*callback.first)(proxies, callback.second);
}

deque<string> ChromeBrowserProxyResolver::ParseProxyString(
    const string& input) {
  deque<string> ret;
  // Some of this code taken from
  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
  StringTokenizer entry_tok(input, ";");
  while (entry_tok.GetNext()) {
    string token = entry_tok.token();
    TrimWhitespaceASCII(token, TRIM_ALL, &token);

    // Start by finding the first space (if any).
    std::string::iterator space;
    for (space = token.begin(); space != token.end(); ++space) {
      if (IsAsciiWhitespace(*space)) {
        break;
      }
    }

    string scheme = string(token.begin(), space);
    StringToLowerASCII(&scheme);
    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
    if (scheme == "socks")
      scheme += "4";
    else if (scheme == "proxy")
      scheme = "http";
    else if (scheme != "https" &&
             scheme != "socks4" &&
             scheme != "socks5" &&
             scheme != "direct")
      continue;  // Invalid proxy scheme

    string host_and_port = string(space, token.end());
    TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
    if (scheme != "direct" && host_and_port.empty())
      continue;  // Must supply host/port when non-direct proxy used.
    ret.push_back(scheme + "://" + host_and_port);
  }
  if (ret.empty() || *ret.rbegin() != kNoProxy)
    ret.push_back(kNoProxy);
  return ret;
}

}  // namespace chromeos_update_engine
<commit_msg>Remove path='/' from kLibCrosProxyResolveSignalFilter.<commit_after>// Copyright (c) 2011 The Chromium OS 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 "update_engine/chrome_browser_proxy_resolver.h"

#include <map>
#include <string>

#include <base/string_tokenizer.h>
#include <base/string_util.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <google/protobuf/stubs/common.h>

#include "update_engine/dbus_constants.h"
#include "update_engine/utils.h"

namespace chromeos_update_engine {

using google::protobuf::Closure;
using google::protobuf::NewCallback;
using std::deque;
using std::make_pair;
using std::multimap;
using std::pair;
using std::string;

#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE                 \
  "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
const char kLibCrosServiceResolveNetworkProxyMethodName[] =
    "ResolveNetworkProxy";
const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
const char kLibCrosProxyResolveSignalInterface[] =
    LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
    "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
    "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
#undef LIB_CROS_PROXY_RESOLVE_NAME

namespace {
const int kTimeout = 5;  // seconds

DBusGProxy* GetProxy(DbusGlibInterface* dbus) {
  GError* error = NULL;

  DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error);
  if (!bus) {
    LOG(ERROR) << "Failed to get bus";
    return NULL;
  }
  DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus,
                                                 kLibCrosServiceName,
                                                 kLibCrosServicePath,
                                                 kLibCrosServiceInterface,
                                                 &error);
  if (!proxy) {
    LOG(ERROR) << "Error getting dbus proxy for "
               << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error);
    return NULL;
  }
  return proxy;
}

}  // namespace {}

ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
    : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}

bool ChromeBrowserProxyResolver::Init() {
  // Set up signal handler. Code lifted from libcros
  if (proxy_) {
    // Already initialized
    return true;
  }
  GError* gerror = NULL;
  DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
  TEST_AND_RETURN_FALSE(gbus);
  DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
  TEST_AND_RETURN_FALSE(connection);
  DBusError error;
  dbus_error_init(&error);
  dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error);
  TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error));
  TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
      connection,
      &ChromeBrowserProxyResolver::StaticFilterMessage,
      this,
      NULL));

  proxy_ = GetProxy(dbus_);
  if (!proxy_) {
    dbus_->DbusConnectionRemoveFilter(
        connection,
        &ChromeBrowserProxyResolver::StaticFilterMessage,
        this);
  }
  TEST_AND_RETURN_FALSE(proxy_);  // For the error log
  return true;
}

ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
  if (proxy_) {
    GError* gerror = NULL;
    DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
    TEST_AND_RETURN(gbus);
    DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
    dbus_->DbusConnectionRemoveFilter(
        connection,
        &ChromeBrowserProxyResolver::StaticFilterMessage,
        this);
  }
  // Kill outstanding timers
  for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
       ++it) {
    g_source_destroy(it->second);
    it->second = NULL;
  }
}

bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
                                                  ProxiesResolvedFn callback,
                                                  void* data) {
  GError* error = NULL;
  guint timeout = timeout_;
  if (!proxy_ || !dbus_->ProxyCall(
          proxy_,
          kLibCrosServiceResolveNetworkProxyMethodName,
          &error,
          G_TYPE_STRING, url.c_str(),
          G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
          G_TYPE_STRING, kLibCrosProxyResolveName,
          G_TYPE_INVALID, G_TYPE_INVALID)) {
    LOG(WARNING) << "dbus_g_proxy_call failed: "
                 << utils::GetGErrorMessage(error)
                 << " Continuing with no proxy.";
    timeout = 0;
  }
  callbacks_.insert(make_pair(url, make_pair(callback, data)));
  Closure* closure = NewCallback(this,
                                 &ChromeBrowserProxyResolver::HandleTimeout,
                                 url);
  GSource* timer = g_timeout_source_new_seconds(timeout);
  g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
  g_source_attach(timer, NULL);
  timers_.insert(make_pair(url, timer));
  return true;
}

DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
    DBusConnection* connection,
    DBusMessage* message) {
  // Code lifted from libcros.
  if (!dbus_->DbusMessageIsSignal(message,
                                  kLibCrosProxyResolveSignalInterface,
                                  kLibCrosProxyResolveName)) {
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  }
  // Get args
  char* source_url = NULL;
  char* proxy_list = NULL;
  char* error = NULL;
  DBusError arg_error;
  dbus_error_init(&arg_error);
  if (!dbus_->DbusMessageGetArgs(message, &arg_error,
                                 DBUS_TYPE_STRING, &source_url,
                                 DBUS_TYPE_STRING, &proxy_list,
                                 DBUS_TYPE_STRING, &error,
                                 DBUS_TYPE_INVALID)) {
    LOG(ERROR) << "Error reading dbus signal.";
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  if (!source_url || !proxy_list) {
    LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
    return DBUS_HANDLER_RESULT_HANDLED;
  }
  HandleReply(source_url, proxy_list);
  return DBUS_HANDLER_RESULT_HANDLED;
}

bool ChromeBrowserProxyResolver::DeleteUrlState(
    const string& source_url,
    bool delete_timer,
    pair<ProxiesResolvedFn, void*>* callback) {
  {
    CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
    TEST_AND_RETURN_FALSE(it != callbacks_.end());
    TEST_AND_RETURN_FALSE(it->first == source_url);
    if (callback)
      *callback = it->second;
    callbacks_.erase(it);
  }
  {
    TimeoutsMap::iterator it = timers_.lower_bound(source_url);
    TEST_AND_RETURN_FALSE(it != timers_.end());
    TEST_AND_RETURN_FALSE(it->first == source_url);
    if (delete_timer)
      g_source_destroy(it->second);
    timers_.erase(it);
  }
  return true;
}

void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
                                             const string& proxy_list) {
  pair<ProxiesResolvedFn, void*> callback;
  TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
  (*callback.first)(ParseProxyString(proxy_list), callback.second);
}

void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
  LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
  pair<ProxiesResolvedFn, void*> callback;
  TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
  deque<string> proxies;
  proxies.push_back(kNoProxy);
  (*callback.first)(proxies, callback.second);
}

deque<string> ChromeBrowserProxyResolver::ParseProxyString(
    const string& input) {
  deque<string> ret;
  // Some of this code taken from
  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
  StringTokenizer entry_tok(input, ";");
  while (entry_tok.GetNext()) {
    string token = entry_tok.token();
    TrimWhitespaceASCII(token, TRIM_ALL, &token);

    // Start by finding the first space (if any).
    std::string::iterator space;
    for (space = token.begin(); space != token.end(); ++space) {
      if (IsAsciiWhitespace(*space)) {
        break;
      }
    }

    string scheme = string(token.begin(), space);
    StringToLowerASCII(&scheme);
    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
    if (scheme == "socks")
      scheme += "4";
    else if (scheme == "proxy")
      scheme = "http";
    else if (scheme != "https" &&
             scheme != "socks4" &&
             scheme != "socks5" &&
             scheme != "direct")
      continue;  // Invalid proxy scheme

    string host_and_port = string(space, token.end());
    TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
    if (scheme != "direct" && host_and_port.empty())
      continue;  // Must supply host/port when non-direct proxy used.
    ret.push_back(scheme + "://" + host_and_port);
  }
  if (ret.empty() || *ret.rbegin() != kNoProxy)
    ret.push_back(kNoProxy);
  return ret;
}

}  // namespace chromeos_update_engine
<|endoftext|>


<commit_before>// See http://www.zaphoyd.com/websocketpp/manual/reference/cpp11-support
// for prepocessor directives and choosing between boost and cpp11
// C++11 STL tokens
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_NO_CPP11_THREAD_

#include <cpp-pcp-client/connector/connection.hpp>
#include <cpp-pcp-client/connector/errors.hpp>
#include <cpp-pcp-client/protocol/message.hpp>
#include <cpp-pcp-client/util/thread.hpp>
#include <cpp-pcp-client/util/chrono.hpp>

#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE CPP_PCP_CLIENT_LOGGING_PREFIX".connection"

#include <leatherman/logging/logging.hpp>

#include <cstdio>
#include <iostream>
#include <random>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

namespace PCPClient {

//
// Constants
//

static const uint32_t CONNECTION_MIN_INTERVAL { 200 };  // [ms]
static const uint32_t CONNECTION_BACKOFF_LIMIT { 33000 };  // [ms]
static const uint32_t CONNECTION_BACKOFF_MULTIPLIER { 2 };

//
// Connection
//

Connection::Connection(const std::string& broker_ws_uri,
                       const ClientMetadata& client_metadata)
        : broker_ws_uri_ { broker_ws_uri },
          client_metadata_ { client_metadata },
          connection_state_ { ConnectionStateValues::initialized },
          consecutive_pong_timeouts_ { 0 },
          endpoint_ { new WS_Client_Type() }{
    // Turn off websocketpp logging to avoid runtime errors (see CTH-69)
    endpoint_->clear_access_channels(websocketpp::log::alevel::all);
    endpoint_->clear_error_channels(websocketpp::log::elevel::all);

    // Initialize the transport system. Note that in perpetual mode,
    // the event loop does not terminate when there are no connections
    endpoint_->init_asio();
    endpoint_->start_perpetual();

    try {
        endpoint_->set_tls_init_handler(
            std::bind(&Connection::onTlsInit, this, std::placeholders::_1));
        endpoint_->set_open_handler(
            std::bind(&Connection::onOpen, this, std::placeholders::_1));
        endpoint_->set_close_handler(
            std::bind(&Connection::onClose, this, std::placeholders::_1));
        endpoint_->set_fail_handler(
            std::bind(&Connection::onFail, this, std::placeholders::_1));
        endpoint_->set_message_handler(
            std::bind(&Connection::onMessage, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_ping_handler(
            std::bind(&Connection::onPing, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_pong_handler(
            std::bind(&Connection::onPong, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_pong_timeout_handler(
            std::bind(&Connection::onPongTimeout, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_tcp_pre_init_handler(
            std::bind(&Connection::onPreTCPInit, this, std::placeholders::_1));
        endpoint_->set_tcp_post_init_handler(
            std::bind(&Connection::onPostTCPInit, this, std::placeholders::_1));

        // Start the event loop thread
        endpoint_thread_.reset(new Util::thread(&WS_Client_Type::run, endpoint_.get()));
    } catch (...) {
        LOG_DEBUG("Failed to configure the WebSocket endpoint; about to stop "
                  "the event loop");
        cleanUp();
        throw connection_config_error { "failed to initialize" };
    }
}

Connection::~Connection() {
    cleanUp();
}

ConnectionState Connection::getConnectionState() {
    return connection_state_.load();
}

//
// Callback modifiers
//

void Connection::setOnOpenCallback(std::function<void()> c_b) {
    onOpen_callback = c_b;
}

void Connection::setOnMessageCallback(std::function<void(std::string msg)> c_b) {
    onMessage_callback_ = c_b;
}

void Connection::resetCallbacks() {
    onOpen_callback = [](){};  // NOLINT [false positive readability/braces]
    onMessage_callback_ = [](std::string message){};  // NOLINT [false positive readability/braces]
}

//
// Synchronous calls
//

void Connection::connect(int max_connect_attempts) {
    // FSM
    //  - states are ConnectionStateValues:
    //      * initialized - connecting - open - closing - closed
    //  - for the transitions, we assume that the connection_state_:
    //      * can be set to 'initialized' only by the Connection constructor;
    //      * is set to 'connecting' by connect_();
    //      * after a connect_() call, it will become, eventually, open or
    //        closed.

    ConnectionState previous_c_s = connection_state_.load();
    ConnectionState current_c_s;
    int idx { 0 };
    bool try_again { true };
    bool got_max_backoff { false };
    std::random_device rd;
    std::default_random_engine engine(rd());
    std::uniform_int_distribution<int> dist(-250, 250);

    do {
        current_c_s = connection_state_.load();
        idx++;
        if (max_connect_attempts) {
            try_again = (idx < max_connect_attempts);
        }
        got_max_backoff |= (connection_backoff_ms_ * CONNECTION_BACKOFF_MULTIPLIER
                            >= CONNECTION_BACKOFF_LIMIT);

        switch (current_c_s) {
        case(ConnectionStateValues::initialized):
            assert(previous_c_s == ConnectionStateValues::initialized);
            connect_();
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            break;

        case(ConnectionStateValues::connecting):
            previous_c_s = ConnectionStateValues::connecting;
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            continue;

        case(ConnectionStateValues::open):
            if (previous_c_s != ConnectionStateValues::open) {
                connection_backoff_ms_ = CONNECTION_BACKOFF_MS;
            }
            return;

        case(ConnectionStateValues::closing):
            previous_c_s = ConnectionStateValues::closing;
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            continue;

        case(ConnectionStateValues::closed):
            if (previous_c_s == ConnectionStateValues::closed) {
                connect_();
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
                previous_c_s = ConnectionStateValues::connecting;
            } else {
                LOG_WARNING("Failed to establish a WebSocket connection; "
                            "retrying in %1% seconds",
                            static_cast<int>(connection_backoff_ms_ / 1000));
                // Randomly adjust the interval slightly to help calm a
                // thundering herd
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(connection_backoff_ms_ + dist(engine)));
                connect_();
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
                if (try_again && !got_max_backoff) {
                    connection_backoff_ms_ *= CONNECTION_BACKOFF_MULTIPLIER;
                }
            }
            break;
        }
    } while (try_again);

    connection_backoff_ms_ = CONNECTION_BACKOFF_MS;
    throw connection_fatal_error { "failed to establish a WebSocket connection "
                                   "after " + std::to_string(idx) + " attempt"
                                   + (idx > 1 ? "s" : "") };
}

void Connection::send(const std::string& msg) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   msg,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::send(void* const serialized_msg_ptr, size_t msg_len) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   serialized_msg_ptr,
                   msg_len,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::ping(const std::string& binary_payload) {
    websocketpp::lib::error_code ec;
    endpoint_->ping(connection_handle_, binary_payload, ec);
    if (ec) {
        throw connection_processing_error { "failed to send WebSocket ping: "
                                            + ec.message() };
    }
}

void Connection::close(CloseCode code, const std::string& reason) {
    LOG_DEBUG("About to close connection");
    websocketpp::lib::error_code ec;
    endpoint_->close(connection_handle_, code, reason, ec);
    if (ec) {
        throw connection_processing_error { "failed to close WebSocket "
                                            "connection: " + ec.message() };
    }
}

//
// Private interface
//

void Connection::cleanUp() {
    endpoint_->stop_perpetual();
    if (connection_state_ == ConnectionStateValues::open) {
        try {
            close();
        } catch (connection_processing_error& e) {
            LOG_ERROR("Failed to close the WebSocket connection: %1%", e.what());
        }
    }
    if (endpoint_thread_ != nullptr && endpoint_thread_->joinable()) {
        endpoint_thread_->join();
    }
}

void Connection::connect_() {
    connection_state_ = ConnectionStateValues::connecting;
    connection_timings_ = ConnectionTimings();
    websocketpp::lib::error_code ec;
    WS_Client_Type::connection_ptr connection_ptr {
        endpoint_->get_connection(broker_ws_uri_, ec) };
    if (ec) {
        throw connection_processing_error { "failed to establish the WebSocket "
                                            "connection with " + broker_ws_uri_
                                            + ": " + ec.message() };
    }
    connection_handle_ = connection_ptr->get_handle();
    endpoint_->connect(connection_ptr);
}

//
// Event handlers (private)
//

WS_Context_Ptr Connection::onTlsInit(WS_Connection_Handle hdl) {
    LOG_TRACE("WebSocket TLS initialization event");
    // NB: for TLS certificates, refer to:
    // www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/ssl__context.html
    WS_Context_Ptr ctx {
        new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) };
    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);
        ctx->use_certificate_file(client_metadata_.crt,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->use_private_key_file(client_metadata_.key,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->load_verify_file(client_metadata_.ca);
    } catch (std::exception& e) {
        LOG_ERROR("Failed to configure TLS: %1%", e.what());
    }
    return ctx;
}

void Connection::onClose(WS_Connection_Handle hdl) {
    connection_timings_.close = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket connection closed");
    connection_state_ = ConnectionStateValues::closed;
}

void Connection::onFail(WS_Connection_Handle hdl) {
    connection_timings_.close = Util::chrono::high_resolution_clock::now();
    connection_timings_.connection_failed = true;
    LOG_DEBUG("WebSocket on fail event - %1%", connection_timings_.toString());
    connection_state_ = ConnectionStateValues::closed;
}

bool Connection::onPing(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_TRACE("WebSocket onPing event - payload: %1%", binary_payload);
    // Returning true so the transport layer will send back a pong
    return true;
}

void Connection::onPong(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_DEBUG("WebSocket onPong event");
    if (consecutive_pong_timeouts_) {
        consecutive_pong_timeouts_ = 0;
    }
}

void Connection::onPongTimeout(WS_Connection_Handle hdl,
                               std::string binary_payload) {
    LOG_WARNING("WebSocket onPongTimeout event (%1% consecutive)",
                consecutive_pong_timeouts_++);
}

void Connection::onPreTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_pre_init = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket pre-TCP initialization event");
}

void Connection::onPostTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_post_init = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket post-TCP initialization event");
}

void Connection::onOpen(WS_Connection_Handle hdl) {
    connection_timings_.open = Util::chrono::high_resolution_clock::now();
    connection_timings_.connection_started = true;
    LOG_DEBUG("WebSocket on open event - %1%", connection_timings_.toString());
    LOG_INFO("WebSocket connection established");
    connection_state_ = ConnectionStateValues::open;

    if (onOpen_callback) {
        try {
            onOpen_callback();
            return;
        } catch (std::exception&  e) {
            LOG_ERROR("onOpen callback failure: %1%; closing the "
                      "WebSocket connection", e.what());
        } catch (...) {
            LOG_ERROR("onOpen callback failure; closing the WebSocket "
                      "connection");
        }

        close(CloseCodeValues::normal, "onOpen callback failure");
    }
}

void Connection::onMessage(WS_Connection_Handle hdl,
                           WS_Client_Type::message_ptr msg) {
    if (onMessage_callback_) {
        try {
            // NB: on_message_callback_ should not raise; in case of
            // failure; it must be able to notify back the error...
            onMessage_callback_(msg->get_payload());
        } catch (std::exception&  e) {
            LOG_ERROR("onMessage WebSocket callback failure: %1%", e.what());
        } catch (...) {
            LOG_ERROR("onMessage WebSocket callback failure: unexpected error");
        }
    }
}

}  // namespace PCPClient
<commit_msg>(PCP-5) Improve TLS init event log message<commit_after>// See http://www.zaphoyd.com/websocketpp/manual/reference/cpp11-support
// for prepocessor directives and choosing between boost and cpp11
// C++11 STL tokens
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_NO_CPP11_THREAD_

#include <cpp-pcp-client/connector/connection.hpp>
#include <cpp-pcp-client/connector/errors.hpp>
#include <cpp-pcp-client/protocol/message.hpp>
#include <cpp-pcp-client/util/thread.hpp>
#include <cpp-pcp-client/util/chrono.hpp>

#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/config/asio_client.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE CPP_PCP_CLIENT_LOGGING_PREFIX".connection"

#include <leatherman/logging/logging.hpp>

#include <cstdio>
#include <iostream>
#include <random>

// TODO(ale): disable assert() once we're confident with the code...
// To disable assert()
// #define NDEBUG
#include <cassert>

namespace PCPClient {

//
// Constants
//

static const uint32_t CONNECTION_MIN_INTERVAL { 200 };  // [ms]
static const uint32_t CONNECTION_BACKOFF_LIMIT { 33000 };  // [ms]
static const uint32_t CONNECTION_BACKOFF_MULTIPLIER { 2 };

//
// Connection
//

Connection::Connection(const std::string& broker_ws_uri,
                       const ClientMetadata& client_metadata)
        : broker_ws_uri_ { broker_ws_uri },
          client_metadata_ { client_metadata },
          connection_state_ { ConnectionStateValues::initialized },
          consecutive_pong_timeouts_ { 0 },
          endpoint_ { new WS_Client_Type() }{
    // Turn off websocketpp logging to avoid runtime errors (see CTH-69)
    endpoint_->clear_access_channels(websocketpp::log::alevel::all);
    endpoint_->clear_error_channels(websocketpp::log::elevel::all);

    // Initialize the transport system. Note that in perpetual mode,
    // the event loop does not terminate when there are no connections
    endpoint_->init_asio();
    endpoint_->start_perpetual();

    try {
        endpoint_->set_tls_init_handler(
            std::bind(&Connection::onTlsInit, this, std::placeholders::_1));
        endpoint_->set_open_handler(
            std::bind(&Connection::onOpen, this, std::placeholders::_1));
        endpoint_->set_close_handler(
            std::bind(&Connection::onClose, this, std::placeholders::_1));
        endpoint_->set_fail_handler(
            std::bind(&Connection::onFail, this, std::placeholders::_1));
        endpoint_->set_message_handler(
            std::bind(&Connection::onMessage, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_ping_handler(
            std::bind(&Connection::onPing, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_pong_handler(
            std::bind(&Connection::onPong, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_pong_timeout_handler(
            std::bind(&Connection::onPongTimeout, this,
                      std::placeholders::_1, std::placeholders::_2));
        endpoint_->set_tcp_pre_init_handler(
            std::bind(&Connection::onPreTCPInit, this, std::placeholders::_1));
        endpoint_->set_tcp_post_init_handler(
            std::bind(&Connection::onPostTCPInit, this, std::placeholders::_1));

        // Start the event loop thread
        endpoint_thread_.reset(new Util::thread(&WS_Client_Type::run, endpoint_.get()));
    } catch (...) {
        LOG_DEBUG("Failed to configure the WebSocket endpoint; about to stop "
                  "the event loop");
        cleanUp();
        throw connection_config_error { "failed to initialize" };
    }
}

Connection::~Connection() {
    cleanUp();
}

ConnectionState Connection::getConnectionState() {
    return connection_state_.load();
}

//
// Callback modifiers
//

void Connection::setOnOpenCallback(std::function<void()> c_b) {
    onOpen_callback = c_b;
}

void Connection::setOnMessageCallback(std::function<void(std::string msg)> c_b) {
    onMessage_callback_ = c_b;
}

void Connection::resetCallbacks() {
    onOpen_callback = [](){};  // NOLINT [false positive readability/braces]
    onMessage_callback_ = [](std::string message){};  // NOLINT [false positive readability/braces]
}

//
// Synchronous calls
//

void Connection::connect(int max_connect_attempts) {
    // FSM
    //  - states are ConnectionStateValues:
    //      * initialized - connecting - open - closing - closed
    //  - for the transitions, we assume that the connection_state_:
    //      * can be set to 'initialized' only by the Connection constructor;
    //      * is set to 'connecting' by connect_();
    //      * after a connect_() call, it will become, eventually, open or
    //        closed.

    ConnectionState previous_c_s = connection_state_.load();
    ConnectionState current_c_s;
    int idx { 0 };
    bool try_again { true };
    bool got_max_backoff { false };
    std::random_device rd;
    std::default_random_engine engine(rd());
    std::uniform_int_distribution<int> dist(-250, 250);

    do {
        current_c_s = connection_state_.load();
        idx++;
        if (max_connect_attempts) {
            try_again = (idx < max_connect_attempts);
        }
        got_max_backoff |= (connection_backoff_ms_ * CONNECTION_BACKOFF_MULTIPLIER
                            >= CONNECTION_BACKOFF_LIMIT);

        switch (current_c_s) {
        case(ConnectionStateValues::initialized):
            assert(previous_c_s == ConnectionStateValues::initialized);
            connect_();
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            break;

        case(ConnectionStateValues::connecting):
            previous_c_s = ConnectionStateValues::connecting;
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            continue;

        case(ConnectionStateValues::open):
            if (previous_c_s != ConnectionStateValues::open) {
                connection_backoff_ms_ = CONNECTION_BACKOFF_MS;
            }
            return;

        case(ConnectionStateValues::closing):
            previous_c_s = ConnectionStateValues::closing;
            Util::this_thread::sleep_for(
                Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
            continue;

        case(ConnectionStateValues::closed):
            if (previous_c_s == ConnectionStateValues::closed) {
                connect_();
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
                previous_c_s = ConnectionStateValues::connecting;
            } else {
                LOG_WARNING("Failed to establish a WebSocket connection; "
                            "retrying in %1% seconds",
                            static_cast<int>(connection_backoff_ms_ / 1000));
                // Randomly adjust the interval slightly to help calm a
                // thundering herd
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(connection_backoff_ms_ + dist(engine)));
                connect_();
                Util::this_thread::sleep_for(
                    Util::chrono::milliseconds(CONNECTION_MIN_INTERVAL));
                if (try_again && !got_max_backoff) {
                    connection_backoff_ms_ *= CONNECTION_BACKOFF_MULTIPLIER;
                }
            }
            break;
        }
    } while (try_again);

    connection_backoff_ms_ = CONNECTION_BACKOFF_MS;
    throw connection_fatal_error { "failed to establish a WebSocket connection "
                                   "after " + std::to_string(idx) + " attempt"
                                   + (idx > 1 ? "s" : "") };
}

void Connection::send(const std::string& msg) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   msg,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::send(void* const serialized_msg_ptr, size_t msg_len) {
    websocketpp::lib::error_code ec;
    endpoint_->send(connection_handle_,
                   serialized_msg_ptr,
                   msg_len,
                   websocketpp::frame::opcode::binary,
                   ec);
    if (ec) {
        throw connection_processing_error { "failed to send message: "
                                            + ec.message() };
    }
}

void Connection::ping(const std::string& binary_payload) {
    websocketpp::lib::error_code ec;
    endpoint_->ping(connection_handle_, binary_payload, ec);
    if (ec) {
        throw connection_processing_error { "failed to send WebSocket ping: "
                                            + ec.message() };
    }
}

void Connection::close(CloseCode code, const std::string& reason) {
    LOG_DEBUG("About to close connection");
    websocketpp::lib::error_code ec;
    endpoint_->close(connection_handle_, code, reason, ec);
    if (ec) {
        throw connection_processing_error { "failed to close WebSocket "
                                            "connection: " + ec.message() };
    }
}

//
// Private interface
//

void Connection::cleanUp() {
    endpoint_->stop_perpetual();
    if (connection_state_ == ConnectionStateValues::open) {
        try {
            close();
        } catch (connection_processing_error& e) {
            LOG_ERROR("Failed to close the WebSocket connection: %1%", e.what());
        }
    }
    if (endpoint_thread_ != nullptr && endpoint_thread_->joinable()) {
        endpoint_thread_->join();
    }
}

void Connection::connect_() {
    connection_state_ = ConnectionStateValues::connecting;
    connection_timings_ = ConnectionTimings();
    websocketpp::lib::error_code ec;
    WS_Client_Type::connection_ptr connection_ptr {
        endpoint_->get_connection(broker_ws_uri_, ec) };
    if (ec) {
        throw connection_processing_error { "failed to establish the WebSocket "
                                            "connection with " + broker_ws_uri_
                                            + ": " + ec.message() };
    }
    connection_handle_ = connection_ptr->get_handle();
    endpoint_->connect(connection_ptr);
}

//
// Event handlers (private)
//

WS_Context_Ptr Connection::onTlsInit(WS_Connection_Handle hdl) {
    LOG_INFO("WebSocket TLS initialization event; about to validate the certificate");
    // NB: for TLS certificates, refer to:
    // www.boost.org/doc/libs/1_56_0/doc/html/boost_asio/reference/ssl__context.html
    WS_Context_Ptr ctx {
        new boost::asio::ssl::context(boost::asio::ssl::context::tlsv1) };
    try {
        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);
        ctx->use_certificate_file(client_metadata_.crt,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->use_private_key_file(client_metadata_.key,
                                  boost::asio::ssl::context::file_format::pem);
        ctx->load_verify_file(client_metadata_.ca);
    } catch (std::exception& e) {
        LOG_ERROR("Failed to configure TLS: %1%", e.what());
    }
    return ctx;
}

void Connection::onClose(WS_Connection_Handle hdl) {
    connection_timings_.close = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket connection closed");
    connection_state_ = ConnectionStateValues::closed;
}

void Connection::onFail(WS_Connection_Handle hdl) {
    connection_timings_.close = Util::chrono::high_resolution_clock::now();
    connection_timings_.connection_failed = true;
    LOG_DEBUG("WebSocket on fail event - %1%", connection_timings_.toString());
    connection_state_ = ConnectionStateValues::closed;
}

bool Connection::onPing(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_TRACE("WebSocket onPing event - payload: %1%", binary_payload);
    // Returning true so the transport layer will send back a pong
    return true;
}

void Connection::onPong(WS_Connection_Handle hdl, std::string binary_payload) {
    LOG_DEBUG("WebSocket onPong event");
    if (consecutive_pong_timeouts_) {
        consecutive_pong_timeouts_ = 0;
    }
}

void Connection::onPongTimeout(WS_Connection_Handle hdl,
                               std::string binary_payload) {
    LOG_WARNING("WebSocket onPongTimeout event (%1% consecutive)",
                consecutive_pong_timeouts_++);
}

void Connection::onPreTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_pre_init = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket pre-TCP initialization event");
}

void Connection::onPostTCPInit(WS_Connection_Handle hdl) {
    connection_timings_.tcp_post_init = Util::chrono::high_resolution_clock::now();
    LOG_TRACE("WebSocket post-TCP initialization event");
}

void Connection::onOpen(WS_Connection_Handle hdl) {
    connection_timings_.open = Util::chrono::high_resolution_clock::now();
    connection_timings_.connection_started = true;
    LOG_DEBUG("WebSocket on open event - %1%", connection_timings_.toString());
    LOG_INFO("WebSocket connection established");
    connection_state_ = ConnectionStateValues::open;

    if (onOpen_callback) {
        try {
            onOpen_callback();
            return;
        } catch (std::exception&  e) {
            LOG_ERROR("onOpen callback failure: %1%; closing the "
                      "WebSocket connection", e.what());
        } catch (...) {
            LOG_ERROR("onOpen callback failure; closing the WebSocket "
                      "connection");
        }

        close(CloseCodeValues::normal, "onOpen callback failure");
    }
}

void Connection::onMessage(WS_Connection_Handle hdl,
                           WS_Client_Type::message_ptr msg) {
    if (onMessage_callback_) {
        try {
            // NB: on_message_callback_ should not raise; in case of
            // failure; it must be able to notify back the error...
            onMessage_callback_(msg->get_payload());
        } catch (std::exception&  e) {
            LOG_ERROR("onMessage WebSocket callback failure: %1%", e.what());
        } catch (...) {
            LOG_ERROR("onMessage WebSocket callback failure: unexpected error");
        }
    }
}

}  // namespace PCPClient
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

#define CAF_SUITE io_remote_spawn
#include "caf/test/dsl.hpp"

#include <thread>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <functional>

#include "caf/all.hpp"
#include "caf/io/all.hpp"

using namespace caf;

namespace {

using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;

using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>,
                               replies_to<sub_atom, int, int>::with<int>>;

// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
  return behavior{
    [](add_atom, int a, int b) {
      return a + b;
    },
    [](sub_atom, int a, int b) {
      return a - b;
    }
  };
}

// function-based, statically typed, event-based API
calculator::behavior_type typed_calculator_fun() {
  return {
    [](add_atom, int a, int b) {
      return a + b;
    },
    [](sub_atom, int a, int b) {
      return a - b;
    }
  };
}

struct config : actor_system_config {
  config(int argc, char** argv) {
    parse(argc, argv);
    load<io::middleman>();
    add_actor_type("calculator", calculator_fun);
    add_actor_type("typed_calculator", typed_calculator_fun);
  }
};

void run_client(int argc, char** argv, uint16_t port) {
  config cfg{argc, argv};
  actor_system system{cfg};
  auto& mm = system.middleman();
  auto nid = mm.connect("localhost", port);
  CAF_REQUIRE(nid);
  CAF_REQUIRE_NOT_EQUAL(system.node(), *nid);
  auto calc = mm.remote_spawn<calculator>(*nid, "calculator", make_message());
  CAF_REQUIRE(!calc);
  CAF_REQUIRE_EQUAL(calc.error().category(), atom("system"));
  CAF_REQUIRE_EQUAL(static_cast<sec>(calc.error().code()),
                    sec::unexpected_actor_messaging_interface);
  calc = mm.remote_spawn<calculator>(*nid, "typed_calculator", make_message());
  CAF_REQUIRE(calc);
  auto f1 = make_function_view(*calc);
  CAF_REQUIRE_EQUAL(f1(add_atom::value, 10, 20), 30);
  CAF_REQUIRE_EQUAL(f1(sub_atom::value, 10, 20), -10);
  f1.reset();
  anon_send_exit(*calc, exit_reason::kill);
  mm.close(port);
}

void run_server(int argc, char** argv) {
  config cfg{argc, argv};
  actor_system system{cfg};
  auto port = unbox(system.middleman().open(0));
  std::thread child{[=] { run_client(argc, argv, port); }};
  child.join();
}

} // namespace <anonymous>

CAF_TEST(remote_spawn) {
  auto argc = test::engine::argc();
  auto argv = test::engine::argv();
  run_server(argc, argv);
}
<commit_msg>Add test for remote spawning of class-based actors<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

#define CAF_SUITE io_remote_spawn
#include "caf/test/dsl.hpp"

#include <thread>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <functional>

#include "caf/all.hpp"
#include "caf/io/all.hpp"

using namespace caf;

namespace {

using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;

using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>,
                               replies_to<sub_atom, int, int>::with<int>>;

// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
  return behavior{
    [](add_atom, int a, int b) {
      return a + b;
    },
    [](sub_atom, int a, int b) {
      return a - b;
    }
  };
}

class calculator_class : public event_based_actor {
public:
  calculator_class(actor_config& cfg) : event_based_actor(cfg) {
    // nop
  }

  behavior make_behavior() override {
    return {
      [](add_atom, int a, int b) {
        return a + b;
      },
      [](sub_atom, int a, int b) {
        return a - b;
      }
    };
  }
};

// function-based, statically typed, event-based API
calculator::behavior_type typed_calculator_fun() {
  return {
    [](add_atom, int a, int b) {
      return a + b;
    },
    [](sub_atom, int a, int b) {
      return a - b;
    }
  };
}

struct config : actor_system_config {
  config(int argc, char** argv) {
    parse(argc, argv);
    load<io::middleman>();
    add_actor_type<calculator_class>("calculator-class");
    add_actor_type("calculator", calculator_fun);
    add_actor_type("typed_calculator", typed_calculator_fun);
  }
};

void run_client(int argc, char** argv, uint16_t port) {
  config cfg{argc, argv};
  actor_system sys{cfg};
  scoped_actor self{sys};
  auto& mm = sys.middleman();
  auto nid = mm.connect("localhost", port);
  CAF_REQUIRE(nid);
  CAF_REQUIRE_NOT_EQUAL(sys.node(), *nid);
  auto calc = mm.remote_spawn<calculator>(*nid, "calculator", make_message());
  CAF_REQUIRE(!calc);
  CAF_REQUIRE_EQUAL(calc.error().category(), atom("system"));
  CAF_REQUIRE_EQUAL(static_cast<sec>(calc.error().code()),
                    sec::unexpected_actor_messaging_interface);
  calc = mm.remote_spawn<calculator>(*nid, "typed_calculator", make_message());
  CAF_REQUIRE(calc);
  auto f1 = make_function_view(*calc);
  CAF_REQUIRE_EQUAL(f1(add_atom::value, 10, 20), 30);
  CAF_REQUIRE_EQUAL(f1(sub_atom::value, 10, 20), -10);
  f1.reset();
  anon_send_exit(*calc, exit_reason::kill);
  auto dyn_calc = unbox(mm.remote_spawn<actor>(*nid, "calculator-class", make_message()));
  CAF_REQUIRE(dyn_calc);
  self->request(dyn_calc, infinite, add_atom::value, 10, 20).receive(
    [](int result) {
      CAF_CHECK_EQUAL(result, 30);
    },
    [&](const error& err) {
      CAF_FAIL("error: " << sys.render(err));
    });
  anon_send_exit(dyn_calc, exit_reason::kill);
  mm.close(port);
}

void run_server(int argc, char** argv) {
  config cfg{argc, argv};
  actor_system system{cfg};
  auto port = unbox(system.middleman().open(0));
  std::thread child{[=] { run_client(argc, argv, port); }};
  child.join();
}

} // namespace <anonymous>

CAF_TEST(remote_spawn) {
  auto argc = test::engine::argc();
  auto argv = test::engine::argv();
  run_server(argc, argv);
}
<|endoftext|>


<commit_before>/*  LibMary - C++ library for high-performance network servers
    Copyright (C) 2011 Dmitry Shatrov

    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <libmary/async_output_stream.h>

namespace M {

mt_throws AsyncIoResult
AsyncOutputStream::writev (struct iovec * const iovs,
			   Count          const num_iovs,
			   Size         * const ret_nwritten)
{
    if (ret_nwritten)
	*ret_nwritten = 0;

    Size total_written = 0;
    for (Count i = 0; i < num_iovs; ++i) {
	Size nwritten;
	AsyncIoResult const res = write (ConstMemory ((Byte const *) iovs [i].iov_base, iovs [i].iov_len), &nwritten);
	total_written += nwritten;
	if (res != AsyncIoResult::Normal) {
	    if (ret_nwritten)
		*ret_nwritten = total_written;

	    return res;
	}
    }

    if (ret_nwritten)
	*ret_nwritten = total_written;

    return AsyncIoResult::Normal;
}

}

<commit_msg>writev() emulation fix.<commit_after>/*  LibMary - C++ library for high-performance network servers
    Copyright (C) 2011 Dmitry Shatrov

    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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <libmary/async_output_stream.h>

namespace M {

mt_throws AsyncIoResult
AsyncOutputStream::writev (struct iovec * const iovs,
			   Count          const num_iovs,
			   Size         * const ret_nwritten)
{
    if (ret_nwritten)
	*ret_nwritten = 0;

    Size total_written = 0;
    for (Count i = 0; i < num_iovs; ++i) {
	Size nwritten = 0;
	AsyncIoResult const res = write (ConstMemory ((Byte const *) iovs [i].iov_base, iovs [i].iov_len), &nwritten);
	total_written += nwritten;
	if (res != AsyncIoResult::Normal) {
	    if (ret_nwritten)
		*ret_nwritten = total_written;

            if (res == AsyncIoResult::Normal_Again)
                return AsyncIoResult::Normal_Again;

            if (res == AsyncIoResult::Again) {
                if (total_written > 0)
                    return AsyncIoResult::Normal_Again;

                return AsyncIoResult::Again;
            }

            if (res == AsyncIoResult::Normal_Eof)
                return AsyncIoResult::Normal_Eof;

            if (res == AsyncIoResult::Eof) {
                if (total_written > 0)
                    return AsyncIoResult::Normal_Eof;

                return AsyncIoResult::Eof;
            }

            assert (res == AsyncIoResult::Error);
	    return AsyncIoResult::Error;
	}
    }

    if (ret_nwritten)
	*ret_nwritten = total_written;

    return AsyncIoResult::Normal;
}

}

<|endoftext|>