<commit_before>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdlib.h>
#include "CppUTest/TestHarness.h"
#undef malloc
#undef free
#undef calloc
#undef realloc

#include "CppUTest/TestRegistry.h"
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>

#include "CppUTest/PlatformSpecificFunctions.h"

static jmp_buf test_exit_jmp_buf[10];
static int jmp_buf_index = 0;

bool Utest::executePlatformSpecificSetup()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      setup();
      jmp_buf_index--;
      return true;
   }
   return false;
}

void Utest::executePlatformSpecificTestBody()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      testBody();
      jmp_buf_index--;
   }
}

void Utest::executePlatformSpecificTeardown()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      teardown();
      jmp_buf_index--;
   }
}

void Utest::executePlatformSpecificRunOneTest(TestPlugin* plugin, TestResult& result)
{
    if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
       jmp_buf_index++;

       int info;
       pid_t pid = (isRunInSeperateProcess()) ? fork() : 0;

       if (pid) {
    	   wait(&info);
    	   if (WIFEXITED(info) && WEXITSTATUS(info) > result.getFailureCount())
    		   result.addFailure(TestFailure(this, "failed in seperate process"));
       }
       else {
		    runOneTest(plugin, result);
		    if (isRunInSeperateProcess()) exit(result.getFailureCount() );
		}

       jmp_buf_index--;
    }
}

void Utest::executePlatformSpecificExitCurrentTest()
{
   jmp_buf_index--;
   longjmp(test_exit_jmp_buf[jmp_buf_index], 1);
}

TestOutput::WorkingEnvironment PlatformSpecificGetWorkingEnvironment()
{
	return TestOutput::eclipse;
}

///////////// Time in millis

static long TimeInMillisImplementation()
{
	struct timeval tv;
	struct timezone tz;
	gettimeofday(&tv, &tz);
	return (tv.tv_sec * 1000) + (long)((double)tv.tv_usec * 0.001);
}

static long (*timeInMillisFp) () = TimeInMillisImplementation;

long GetPlatformSpecificTimeInMillis()
{
	return timeInMillisFp();
}

void SetPlatformSpecificTimeInMillisMethod(long (*platformSpecific) ())
{
	timeInMillisFp = (platformSpecific == 0) ? TimeInMillisImplementation : platformSpecific;
}

///////////// Time in String

static const char* TimeStringImplementation()
{
	time_t tm = time(NULL);
	return ctime(&tm);
}

static const char* (*timeStringFp) () = TimeStringImplementation;

const char* GetPlatformSpecificTimeString()
{
	return timeStringFp();
}

void SetPlatformSpecificTimeStringMethod(const char* (*platformMethod) ())
{
	timeStringFp = (platformMethod == 0) ? TimeStringImplementation : platformMethod;
}

int PlatformSpecificAtoI(const char*str)
{
   return atoi(str);
}

size_t PlatformSpecificStrLen(const char* str)
{
   return strlen(str);
}

char* PlatformSpecificStrCat(char* s1, const char* s2)
{
   return strcat(s1, s2);
}

char* PlatformSpecificStrCpy(char* s1, const char* s2)
{
   return strcpy(s1, s2);
}

char* PlatformSpecificStrNCpy(char* s1, const char* s2, size_t size)
{
   return strncpy(s1, s2, size);
}

int PlatformSpecificStrCmp(const char* s1, const char* s2)
{
   return strcmp(s1, s2);
}

int PlatformSpecificStrNCmp(const char* s1, const char* s2, size_t size)
{
   return strncmp(s1, s2, size);
}
char* PlatformSpecificStrStr(const char* s1, const char* s2)
{
   return (char*) strstr(s1, s2);
}

int PlatformSpecificVSNprintf(char *str, unsigned int size, const char* format, va_list args)
{
   return vsnprintf( str, size, format, args);
}

char PlatformSpecificToLower(char c)
{
	return (char) tolower((char) c);
}

PlatformSpecificFile PlatformSpecificFOpen(const char* filename, const char* flag)
{
   return fopen(filename, flag);
}

void PlatformSpecificFPuts(const char* str, PlatformSpecificFile file)
{
   fputs(str, (FILE*)file);
}

void PlatformSpecificFClose(PlatformSpecificFile file)
{
   fclose((FILE*)file);
}

void PlatformSpecificFlush()
{
  fflush(stdout);
}

int PlatformSpecificPutchar(int c)
{
  return putchar(c);
}

void* PlatformSpecificMalloc(size_t size)
{
   return malloc(size);
}

void* PlatformSpecificRealloc (void* memory, size_t size)
{
   return realloc(memory, size);
}

void PlatformSpecificFree(void* memory)
{
   free(memory);
}

void* PlatformSpecificMemCpy(void* s1, const void* s2, size_t size)
{
   return memcpy(s1, s2, size);
}

void* PlatformSpecificMemset(void* mem, int c, size_t size)
{
	return memset(mem, c, size);
}

double PlatformSpecificFabs(double d)
{
   return fabs(d);
}

int PlatformSpecificIsNan(double d)
{
	return isnan((float)d);
}
<commit_msg><commit_after>/*
 * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the <organization> nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdlib.h>
#include "CppUTest/TestHarness.h"
#undef malloc
#undef free
#undef calloc
#undef realloc

#include "CppUTest/TestRegistry.h"
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <setjmp.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>

#include "CppUTest/PlatformSpecificFunctions.h"

static jmp_buf test_exit_jmp_buf[10];
static int jmp_buf_index = 0;

bool Utest::executePlatformSpecificSetup()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      setup();
      jmp_buf_index--;
      return true;
   }
   return false;
}

void Utest::executePlatformSpecificTestBody()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      testBody();
      jmp_buf_index--;
   }
}

void Utest::executePlatformSpecificTeardown()
{
   if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
      jmp_buf_index++;
      teardown();
      jmp_buf_index--;
   }
}

void Utest::executePlatformSpecificRunOneTest(TestPlugin* plugin, TestResult& result)
{
    if (0 == setjmp(test_exit_jmp_buf[jmp_buf_index])) {
       jmp_buf_index++;

       int info;
       pid_t pid = (isRunInSeperateProcess()) ? fork() : 0;

       if (pid) {
    	   wait(&info);
    	   if (WIFEXITED(info) && WEXITSTATUS(info) > result.getFailureCount())
    		   result.addFailure(TestFailure(this, "failed in seperate process"));
       }
       else {
		    runOneTest(plugin, result);
		    if (isRunInSeperateProcess()) exit(result.getFailureCount() );
		}

       jmp_buf_index--;
    }
}

void Utest::executePlatformSpecificExitCurrentTest()
{
   jmp_buf_index--;
   longjmp(test_exit_jmp_buf[jmp_buf_index], 1);
}

TestOutput::WorkingEnvironment PlatformSpecificGetWorkingEnvironment()
{
	return TestOutput::eclipse;
}

///////////// Time in millis

static long TimeInMillisImplementation()
{
	struct timeval tv;
	struct timezone tz;
	gettimeofday(&tv, &tz);
	return (tv.tv_sec * 1000) + (long)((double)tv.tv_usec * 0.001);
}

static long (*timeInMillisFp) () = TimeInMillisImplementation;

long GetPlatformSpecificTimeInMillis()
{
	return timeInMillisFp();
}

void SetPlatformSpecificTimeInMillisMethod(long (*platformSpecific) ())
{
	timeInMillisFp = (platformSpecific == 0) ? TimeInMillisImplementation : platformSpecific;
}

///////////// Time in String

static const char* TimeStringImplementation()
{
	time_t tm = time(NULL);
	return ctime(&tm);
}

static const char* (*timeStringFp) () = TimeStringImplementation;

const char* GetPlatformSpecificTimeString()
{
	return timeStringFp();
}

void SetPlatformSpecificTimeStringMethod(const char* (*platformMethod) ())
{
	timeStringFp = (platformMethod == 0) ? TimeStringImplementation : platformMethod;
}

int PlatformSpecificAtoI(const char*str)
{
   return atoi(str);
}

size_t PlatformSpecificStrLen(const char* str)
{
   return strlen(str);
}

char* PlatformSpecificStrCat(char* s1, const char* s2)
{
   return strcat(s1, s2);
}

char* PlatformSpecificStrCpy(char* s1, const char* s2)
{
   return strcpy(s1, s2);
}

char* PlatformSpecificStrNCpy(char* s1, const char* s2, size_t size)
{
   return strncpy(s1, s2, size);
}

int PlatformSpecificStrCmp(const char* s1, const char* s2)
{
   return strcmp(s1, s2);
}

int PlatformSpecificStrNCmp(const char* s1, const char* s2, size_t size)
{
   return strncmp(s1, s2, size);
}
char* PlatformSpecificStrStr(const char* s1, const char* s2)
{
   return (char*) strstr(s1, s2);
}

int PlatformSpecificVSNprintf(char *str, unsigned int size, const char* format, va_list args)
{
   return vsnprintf( str, size, format, args);
}

char PlatformSpecificToLower(char c)
{
	return (char) tolower((char) c);
}

PlatformSpecificFile PlatformSpecificFOpen(const char* filename, const char* flag)
{
   return fopen(filename, flag);
}

void PlatformSpecificFPuts(const char* str, PlatformSpecificFile file)
{
   fputs(str, (FILE*)file);
}

void PlatformSpecificFClose(PlatformSpecificFile file)
{
   fclose((FILE*)file);
}

void PlatformSpecificFlush()
{
  fflush(stdout);
}

int PlatformSpecificPutchar(int c)
{
  return putchar(c);
}

void* PlatformSpecificMalloc(size_t size)
{
   return malloc(size);
}

void* PlatformSpecificRealloc (void* memory, size_t size)
{
   return realloc(memory, size);
}

void PlatformSpecificFree(void* memory)
{
   free(memory);
}

void* PlatformSpecificMemCpy(void* s1, const void* s2, size_t size)
{
   return memcpy(s1, s2, size);
}

void* PlatformSpecificMemset(void* mem, int c, size_t size)
{
	return memset(mem, c, size);
}

double PlatformSpecificFabs(double d)
{
   return fabs(d);
}

int PlatformSpecificIsNan(double d)
{
	return isnan((float)d);
}
<|endoftext|>


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

#include "chrome/browser/service/service_process_control.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/process_util.h"
#include "base/stl_util-inl.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/upgrade_detector.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/service_messages.h"
#include "chrome/common/service_process_util.h"
#include "content/browser/browser_thread.h"
#include "content/common/child_process_host.h"
#include "content/common/notification_service.h"
#include "ui/base/ui_base_switches.h"

// ServiceProcessControl implementation.
ServiceProcessControl::ServiceProcessControl(Profile* profile)
    : profile_(profile) {
}

ServiceProcessControl::~ServiceProcessControl() {
  STLDeleteElements(&connect_done_tasks_);
  STLDeleteElements(&connect_success_tasks_);
  STLDeleteElements(&connect_failure_tasks_);
}

void ServiceProcessControl::ConnectInternal() {
  // If the channel has already been established then we run the task
  // and return.
  if (channel_.get()) {
    RunConnectDoneTasks();
    return;
  }

  // Actually going to connect.
  VLOG(1) << "Connecting to Service Process IPC Server";
  // Run the IPC channel on the shared IO thread.
  base::Thread* io_thread = g_browser_process->io_thread();

  // TODO(hclam): Handle error connecting to channel.
  const IPC::ChannelHandle channel_id = GetServiceProcessChannel();
  channel_.reset(
      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_NAMED_CLIENT, this,
                           io_thread->message_loop(), true,
                           g_browser_process->shutdown_event()));
}

void ServiceProcessControl::RunConnectDoneTasks() {
  // The tasks executed here may add more tasks to the vector. So copy
  // them to the stack before executing them. This way recursion is
  // avoided.
  TaskList tasks;
  tasks.swap(connect_done_tasks_);
  RunAllTasksHelper(&tasks);
  DCHECK(tasks.empty());

  if (is_connected()) {
    tasks.swap(connect_success_tasks_);
    RunAllTasksHelper(&tasks);
    DCHECK(tasks.empty());

    STLDeleteElements(&connect_failure_tasks_);
  } else {
    tasks.swap(connect_failure_tasks_);
    RunAllTasksHelper(&tasks);
    DCHECK(tasks.empty());

    STLDeleteElements(&connect_success_tasks_);
  }

  DCHECK(connect_done_tasks_.empty());
  DCHECK(connect_success_tasks_.empty());
  DCHECK(connect_failure_tasks_.empty());
}

// static
void ServiceProcessControl::RunAllTasksHelper(TaskList* task_list) {
  TaskList::iterator index = task_list->begin();
  while (index != task_list->end()) {
    (*index)->Run();
    delete (*index);
    index = task_list->erase(index);
  }
}

void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (success_task) {
    if (success_task == failure_task) {
      // If the tasks are the same, then the same task needs to be invoked
      // for success and failure.
      failure_task = NULL;
      connect_done_tasks_.push_back(success_task);
    } else {
      connect_success_tasks_.push_back(success_task);
    }
  }

  if (failure_task)
    connect_failure_tasks_.push_back(failure_task);

  // If we already in the process of launching, then we are done.
  if (launcher_) {
    return;
  }

  // If the service process is already running then connects to it.
  if (CheckServiceProcessReady()) {
    ConnectInternal();
    return;
  }

  // A service process should have a different mechanism for starting, but now
  // we start it as if it is a child process.
  FilePath exe_path = ChildProcessHost::GetChildPath(true);
  if (exe_path.empty()) {
    NOTREACHED() << "Unable to get service process binary name.";
  }

  CommandLine* cmd_line = new CommandLine(exe_path);
  cmd_line->AppendSwitchASCII(switches::kProcessType,
                              switches::kServiceProcess);

  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
  FilePath user_data_dir =
      browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
  if (!user_data_dir.empty())
    cmd_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);

  std::string logging_level = browser_command_line.GetSwitchValueASCII(
      switches::kLoggingLevel);
  if (!logging_level.empty())
    cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level);

  std::string v_level = browser_command_line.GetSwitchValueASCII(
      switches::kV);
  if (!v_level.empty())
    cmd_line->AppendSwitchASCII(switches::kV, v_level);

  if (browser_command_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
    cmd_line->AppendSwitch(switches::kWaitForDebugger);
  }

  if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
    cmd_line->AppendSwitch(switches::kEnableLogging);
  }

  std::string locale = g_browser_process->GetApplicationLocale();
  cmd_line->AppendSwitchASCII(switches::kLang, locale);

  // And then start the process asynchronously.
  launcher_ = new Launcher(this, cmd_line);
  launcher_->Run(
      NewRunnableMethod(this, &ServiceProcessControl::OnProcessLaunched));
}

void ServiceProcessControl::OnProcessLaunched() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (launcher_->launched()) {
    // After we have successfully created the service process we try to connect
    // to it. The launch task is transfered to a connect task.
    ConnectInternal();
  } else {
    // If we don't have process handle that means launching the service process
    // has failed.
    RunConnectDoneTasks();
  }

  // We don't need the launcher anymore.
  launcher_ = NULL;
}

bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
    IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_IsEnabled,
                        OnCloudPrintProxyIsEnabled)
    IPC_MESSAGE_HANDLER(ServiceHostMsg_RemotingHost_HostInfo,
                         OnRemotingHostInfo)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  channel_->set_sync_messages_with_no_timeout_allowed(false);

  // We just established a channel with the service process. Notify it if an
  // upgrade is available.
  if (UpgradeDetector::GetInstance()->notify_upgrade()) {
    Send(new ServiceMsg_UpdateAvailable);
  } else {
    if (registrar_.IsEmpty())
      registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED,
                     NotificationService::AllSources());
  }
  RunConnectDoneTasks();
}

void ServiceProcessControl::OnChannelError() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  channel_.reset();
  RunConnectDoneTasks();
}

bool ServiceProcessControl::Send(IPC::Message* message) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!channel_.get())
    return false;
  return channel_->Send(message);
}

// NotificationObserver implementation.
void ServiceProcessControl::Observe(NotificationType type,
                                    const NotificationSource& source,
                                    const NotificationDetails& details) {
  if (type == NotificationType::UPGRADE_RECOMMENDED) {
    Send(new ServiceMsg_UpdateAvailable);
  }
}

void ServiceProcessControl::OnCloudPrintProxyIsEnabled(bool enabled,
                                                       std::string email) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (cloud_print_status_callback_ != NULL) {
    cloud_print_status_callback_->Run(enabled, email);
    cloud_print_status_callback_.reset();
  }
}

void ServiceProcessControl::OnRemotingHostInfo(
    const remoting::ChromotingHostInfo& host_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  for (std::set<MessageHandler*>::iterator it = message_handlers_.begin();
       it != message_handlers_.end(); ++it) {
    (*it)->OnRemotingHostInfo(host_info);
  }
}

bool ServiceProcessControl::GetCloudPrintProxyStatus(
    Callback2<bool, std::string>::Type* cloud_print_status_callback) {
  DCHECK(cloud_print_status_callback);
  cloud_print_status_callback_.reset(cloud_print_status_callback);
  return Send(new ServiceMsg_IsCloudPrintProxyEnabled);
}

bool ServiceProcessControl::Shutdown() {
  bool ret = Send(new ServiceMsg_Shutdown());
  channel_.reset();
  return ret;
}

bool ServiceProcessControl::SetRemotingHostCredentials(
    const std::string& user,
    const std::string& talk_token) {
  return Send(
      new ServiceMsg_SetRemotingHostCredentials(user, talk_token));
}

bool ServiceProcessControl::EnableRemotingHost() {
  return Send(new ServiceMsg_EnableRemotingHost());
}

bool ServiceProcessControl::DisableRemotingHost() {
  return Send(new ServiceMsg_DisableRemotingHost());
}

bool ServiceProcessControl::RequestRemotingHostStatus() {
  if (CheckServiceProcessReady()) {
    remoting::ChromotingHostInfo failure_host_info;
    failure_host_info.enabled = false;

    Launch(NewRunnableMethod(this, &ServiceProcessControl::Send,
                             new ServiceMsg_GetRemotingHostInfo),
           NewRunnableMethod(this,
                             &ServiceProcessControl::OnRemotingHostInfo,
                             failure_host_info));
    return true;
  }
  return false;
}

void ServiceProcessControl::AddMessageHandler(
    MessageHandler* message_handler) {
  message_handlers_.insert(message_handler);
}

void ServiceProcessControl::RemoveMessageHandler(
    MessageHandler* message_handler) {
  message_handlers_.erase(message_handler);
}

DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl);

ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process,
                                          CommandLine* cmd_line)
    : process_(process),
      cmd_line_(cmd_line),
      launched_(false),
      retry_count_(0) {
}

// Execute the command line to start the process asynchronously.
// After the command is executed, |task| is called with the process handle on
// the UI thread.
void ServiceProcessControl::Launcher::Run(Task* task) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  notify_task_.reset(task);
  BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
                         NewRunnableMethod(this, &Launcher::DoRun));
}

ServiceProcessControl::Launcher::~Launcher() {}

void ServiceProcessControl::Launcher::Notify() {
  DCHECK(notify_task_.get());
  notify_task_->Run();
  notify_task_.reset();
}

#if !defined(OS_MACOSX)
void ServiceProcessControl::Launcher::DoDetectLaunched() {
  DCHECK(notify_task_.get());
  const uint32 kMaxLaunchDetectRetries = 10;
  launched_ = CheckServiceProcessReady();
  if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this, &Launcher::Notify));
    return;
  }
  retry_count_++;

  // If the service process is not launched yet then check again in 2 seconds.
  const int kDetectLaunchRetry = 2000;
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &Launcher::DoDetectLaunched),
      kDetectLaunchRetry);
}

void ServiceProcessControl::Launcher::DoRun() {
  DCHECK(notify_task_.get());
  if (base::LaunchApp(*cmd_line_, false, true, NULL)) {
    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                            NewRunnableMethod(this,
                                              &Launcher::DoDetectLaunched));
  } else {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                            NewRunnableMethod(this, &Launcher::Notify));
  }
}
#endif  // !OS_MACOSX
<commit_msg>BUG=none TEST=Start Chrome with a --vmodule command-line flag, and verify that the service process produces verbose logging accordingly.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/service/service_process_control.h"

#include "base/command_line.h"
#include "base/file_path.h"
#include "base/process_util.h"
#include "base/stl_util-inl.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/io_thread.h"
#include "chrome/browser/upgrade_detector.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/service_messages.h"
#include "chrome/common/service_process_util.h"
#include "content/browser/browser_thread.h"
#include "content/common/child_process_host.h"
#include "content/common/notification_service.h"
#include "ui/base/ui_base_switches.h"

// ServiceProcessControl implementation.
ServiceProcessControl::ServiceProcessControl(Profile* profile)
    : profile_(profile) {
}

ServiceProcessControl::~ServiceProcessControl() {
  STLDeleteElements(&connect_done_tasks_);
  STLDeleteElements(&connect_success_tasks_);
  STLDeleteElements(&connect_failure_tasks_);
}

void ServiceProcessControl::ConnectInternal() {
  // If the channel has already been established then we run the task
  // and return.
  if (channel_.get()) {
    RunConnectDoneTasks();
    return;
  }

  // Actually going to connect.
  VLOG(1) << "Connecting to Service Process IPC Server";
  // Run the IPC channel on the shared IO thread.
  base::Thread* io_thread = g_browser_process->io_thread();

  // TODO(hclam): Handle error connecting to channel.
  const IPC::ChannelHandle channel_id = GetServiceProcessChannel();
  channel_.reset(
      new IPC::SyncChannel(channel_id, IPC::Channel::MODE_NAMED_CLIENT, this,
                           io_thread->message_loop(), true,
                           g_browser_process->shutdown_event()));
}

void ServiceProcessControl::RunConnectDoneTasks() {
  // The tasks executed here may add more tasks to the vector. So copy
  // them to the stack before executing them. This way recursion is
  // avoided.
  TaskList tasks;
  tasks.swap(connect_done_tasks_);
  RunAllTasksHelper(&tasks);
  DCHECK(tasks.empty());

  if (is_connected()) {
    tasks.swap(connect_success_tasks_);
    RunAllTasksHelper(&tasks);
    DCHECK(tasks.empty());

    STLDeleteElements(&connect_failure_tasks_);
  } else {
    tasks.swap(connect_failure_tasks_);
    RunAllTasksHelper(&tasks);
    DCHECK(tasks.empty());

    STLDeleteElements(&connect_success_tasks_);
  }

  DCHECK(connect_done_tasks_.empty());
  DCHECK(connect_success_tasks_.empty());
  DCHECK(connect_failure_tasks_.empty());
}

// static
void ServiceProcessControl::RunAllTasksHelper(TaskList* task_list) {
  TaskList::iterator index = task_list->begin();
  while (index != task_list->end()) {
    (*index)->Run();
    delete (*index);
    index = task_list->erase(index);
  }
}

void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (success_task) {
    if (success_task == failure_task) {
      // If the tasks are the same, then the same task needs to be invoked
      // for success and failure.
      failure_task = NULL;
      connect_done_tasks_.push_back(success_task);
    } else {
      connect_success_tasks_.push_back(success_task);
    }
  }

  if (failure_task)
    connect_failure_tasks_.push_back(failure_task);

  // If we already in the process of launching, then we are done.
  if (launcher_) {
    return;
  }

  // If the service process is already running then connects to it.
  if (CheckServiceProcessReady()) {
    ConnectInternal();
    return;
  }

  // A service process should have a different mechanism for starting, but now
  // we start it as if it is a child process.
  FilePath exe_path = ChildProcessHost::GetChildPath(true);
  if (exe_path.empty()) {
    NOTREACHED() << "Unable to get service process binary name.";
  }

  CommandLine* cmd_line = new CommandLine(exe_path);
  cmd_line->AppendSwitchASCII(switches::kProcessType,
                              switches::kServiceProcess);

  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
  FilePath user_data_dir =
      browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
  if (!user_data_dir.empty())
    cmd_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir);

  std::string logging_level = browser_command_line.GetSwitchValueASCII(
      switches::kLoggingLevel);
  if (!logging_level.empty())
    cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level);

  std::string v_level = browser_command_line.GetSwitchValueASCII(
      switches::kV);
  if (!v_level.empty())
    cmd_line->AppendSwitchASCII(switches::kV, v_level);

  std::string v_modules = browser_command_line.GetSwitchValueASCII(
      switches::kVModule);
  if (!v_modules.empty())
    cmd_line->AppendSwitchASCII(switches::kVModule, v_modules);

  if (browser_command_line.HasSwitch(switches::kWaitForDebuggerChildren)) {
    cmd_line->AppendSwitch(switches::kWaitForDebugger);
  }

  if (browser_command_line.HasSwitch(switches::kEnableLogging)) {
    cmd_line->AppendSwitch(switches::kEnableLogging);
  }

  std::string locale = g_browser_process->GetApplicationLocale();
  cmd_line->AppendSwitchASCII(switches::kLang, locale);

  // And then start the process asynchronously.
  launcher_ = new Launcher(this, cmd_line);
  launcher_->Run(
      NewRunnableMethod(this, &ServiceProcessControl::OnProcessLaunched));
}

void ServiceProcessControl::OnProcessLaunched() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (launcher_->launched()) {
    // After we have successfully created the service process we try to connect
    // to it. The launch task is transfered to a connect task.
    ConnectInternal();
  } else {
    // If we don't have process handle that means launching the service process
    // has failed.
    RunConnectDoneTasks();
  }

  // We don't need the launcher anymore.
  launcher_ = NULL;
}

bool ServiceProcessControl::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(ServiceProcessControl, message)
    IPC_MESSAGE_HANDLER(ServiceHostMsg_CloudPrintProxy_IsEnabled,
                        OnCloudPrintProxyIsEnabled)
    IPC_MESSAGE_HANDLER(ServiceHostMsg_RemotingHost_HostInfo,
                         OnRemotingHostInfo)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void ServiceProcessControl::OnChannelConnected(int32 peer_pid) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  channel_->set_sync_messages_with_no_timeout_allowed(false);

  // We just established a channel with the service process. Notify it if an
  // upgrade is available.
  if (UpgradeDetector::GetInstance()->notify_upgrade()) {
    Send(new ServiceMsg_UpdateAvailable);
  } else {
    if (registrar_.IsEmpty())
      registrar_.Add(this, NotificationType::UPGRADE_RECOMMENDED,
                     NotificationService::AllSources());
  }
  RunConnectDoneTasks();
}

void ServiceProcessControl::OnChannelError() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  channel_.reset();
  RunConnectDoneTasks();
}

bool ServiceProcessControl::Send(IPC::Message* message) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (!channel_.get())
    return false;
  return channel_->Send(message);
}

// NotificationObserver implementation.
void ServiceProcessControl::Observe(NotificationType type,
                                    const NotificationSource& source,
                                    const NotificationDetails& details) {
  if (type == NotificationType::UPGRADE_RECOMMENDED) {
    Send(new ServiceMsg_UpdateAvailable);
  }
}

void ServiceProcessControl::OnCloudPrintProxyIsEnabled(bool enabled,
                                                       std::string email) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  if (cloud_print_status_callback_ != NULL) {
    cloud_print_status_callback_->Run(enabled, email);
    cloud_print_status_callback_.reset();
  }
}

void ServiceProcessControl::OnRemotingHostInfo(
    const remoting::ChromotingHostInfo& host_info) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  for (std::set<MessageHandler*>::iterator it = message_handlers_.begin();
       it != message_handlers_.end(); ++it) {
    (*it)->OnRemotingHostInfo(host_info);
  }
}

bool ServiceProcessControl::GetCloudPrintProxyStatus(
    Callback2<bool, std::string>::Type* cloud_print_status_callback) {
  DCHECK(cloud_print_status_callback);
  cloud_print_status_callback_.reset(cloud_print_status_callback);
  return Send(new ServiceMsg_IsCloudPrintProxyEnabled);
}

bool ServiceProcessControl::Shutdown() {
  bool ret = Send(new ServiceMsg_Shutdown());
  channel_.reset();
  return ret;
}

bool ServiceProcessControl::SetRemotingHostCredentials(
    const std::string& user,
    const std::string& talk_token) {
  return Send(
      new ServiceMsg_SetRemotingHostCredentials(user, talk_token));
}

bool ServiceProcessControl::EnableRemotingHost() {
  return Send(new ServiceMsg_EnableRemotingHost());
}

bool ServiceProcessControl::DisableRemotingHost() {
  return Send(new ServiceMsg_DisableRemotingHost());
}

bool ServiceProcessControl::RequestRemotingHostStatus() {
  if (CheckServiceProcessReady()) {
    remoting::ChromotingHostInfo failure_host_info;
    failure_host_info.enabled = false;

    Launch(NewRunnableMethod(this, &ServiceProcessControl::Send,
                             new ServiceMsg_GetRemotingHostInfo),
           NewRunnableMethod(this,
                             &ServiceProcessControl::OnRemotingHostInfo,
                             failure_host_info));
    return true;
  }
  return false;
}

void ServiceProcessControl::AddMessageHandler(
    MessageHandler* message_handler) {
  message_handlers_.insert(message_handler);
}

void ServiceProcessControl::RemoveMessageHandler(
    MessageHandler* message_handler) {
  message_handlers_.erase(message_handler);
}

DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl);

ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process,
                                          CommandLine* cmd_line)
    : process_(process),
      cmd_line_(cmd_line),
      launched_(false),
      retry_count_(0) {
}

// Execute the command line to start the process asynchronously.
// After the command is executed, |task| is called with the process handle on
// the UI thread.
void ServiceProcessControl::Launcher::Run(Task* task) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  notify_task_.reset(task);
  BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
                         NewRunnableMethod(this, &Launcher::DoRun));
}

ServiceProcessControl::Launcher::~Launcher() {}

void ServiceProcessControl::Launcher::Notify() {
  DCHECK(notify_task_.get());
  notify_task_->Run();
  notify_task_.reset();
}

#if !defined(OS_MACOSX)
void ServiceProcessControl::Launcher::DoDetectLaunched() {
  DCHECK(notify_task_.get());
  const uint32 kMaxLaunchDetectRetries = 10;
  launched_ = CheckServiceProcessReady();
  if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        NewRunnableMethod(this, &Launcher::Notify));
    return;
  }
  retry_count_++;

  // If the service process is not launched yet then check again in 2 seconds.
  const int kDetectLaunchRetry = 2000;
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE,
      NewRunnableMethod(this, &Launcher::DoDetectLaunched),
      kDetectLaunchRetry);
}

void ServiceProcessControl::Launcher::DoRun() {
  DCHECK(notify_task_.get());
  if (base::LaunchApp(*cmd_line_, false, true, NULL)) {
    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
                            NewRunnableMethod(this,
                                              &Launcher::DoDetectLaunched));
  } else {
    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                            NewRunnableMethod(this, &Launcher::Notify));
  }
}
#endif  // !OS_MACOSX
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "replication_common.h"
#include "rpc_replicated.h"

namespace dsn { namespace replication {

using namespace ::dsn::service;

void replication_app_client_base::load_meta_servers(
        configuration_ptr& cf,
        __out_param std::vector<end_point>& servers
        )
{
    // read meta_servers from machine list file
    servers.clear();

    std::vector<std::string> server_ss;
    cf->get_all_keys("replication.meta_servers", server_ss);
    for (auto& s : server_ss)
    {
        // name:port
        auto pos1 = s.find_first_of(':');
        if (pos1 != std::string::npos)
        {
            end_point ep(s.substr(0, pos1).c_str(), atoi(s.substr(pos1 + 1).c_str()));
            servers.push_back(ep);
        }
    }
}

replication_app_client_base::replication_app_client_base(
    const std::vector<end_point>& meta_servers,
    const char* app_name
    )
{
    _app_name = std::string(app_name);
    _meta_servers = meta_servers;

    _app_id = -1;
    _app_partition_count = -1;
    _last_contact_point = end_point::INVALID;
}

replication_app_client_base::~replication_app_client_base()
{
    clear_all_pending_tasks();
}

void replication_app_client_base::clear_all_pending_tasks()
{
    message_ptr nil(nullptr);

    service::zauto_lock l(_requests_lock);
    for (auto& pc : _pending_requests)
    {
        if (pc.second->query_config_task != nullptr)
            pc.second->query_config_task->cancel(true);

        for (auto& rc : pc.second->requests)
        {
            end_request(rc, ERR_TIMEOUT, nil);
            delete rc;
        }
        delete pc.second;
    }
    _pending_requests.clear();
}

void replication_app_client_base::on_user_request_timeout(request_context* rc)
{
    message_ptr nil(nullptr);
    rc->callback_task->enqueue(ERR_TIMEOUT, nil);
}

DEFINE_TASK_CODE(LPC_REPLICATION_CLIENT_REQUEST_TIMEOUT, TASK_PRIORITY_COMMON, THREAD_POOL_DEFAULT)
DEFINE_TASK_CODE(LPC_REPLICATION_DELAY_QUERY_CONFIG, TASK_PRIORITY_COMMON, THREAD_POOL_DEFAULT)

replication_app_client_base::request_context* replication_app_client_base::create_write_context(
    int partition_index,
    task_code code,
    rpc_response_task_ptr& callback,
    int reply_hash
    )
{
    auto rc = new request_context;
    rc->callback_task = callback;
    rc->is_read = false;
    rc->partition_index = partition_index;
    rc->write_header.gpid.app_id = _app_id;
    rc->write_header.gpid.pidx = partition_index;
    rc->write_header.code = code;
    rc->timeout_timer = nullptr;

    if (rc->write_header.gpid.app_id == -1)
    {
        rc->header_pos = callback->get_request()->writer().write_placeholder();
        dbg_dassert(rc->header_pos != 0xffff, "");
    }
    else
    {
        rc->header_pos = 0xffff;
        marshall(callback->get_request()->writer(), rc->write_header);
        callback->get_request()->header().client.hash = gpid_to_hash(rc->write_header.gpid);
    }

    return rc;
}

replication_app_client_base::request_context* replication_app_client_base::create_read_context(
    int partition_index,
    task_code code,
    rpc_response_task_ptr& callback,
    read_semantic_t read_semantic,
    decree snapshot_decree, // only used when ReadSnapshot
    int reply_hash
    )
{
    auto rc = new request_context;
    rc->callback_task = callback;
    rc->is_read = true;
    rc->partition_index = partition_index;
    rc->read_header.gpid.app_id = _app_id;
    rc->read_header.gpid.pidx = partition_index;
    rc->read_header.code = code;
    rc->read_header.semantic = read_semantic;
    rc->read_header.version_decree = snapshot_decree;
    rc->timeout_timer = nullptr;

    if (rc->read_header.gpid.app_id == -1)
    {
        rc->header_pos = callback->get_request()->writer().write_placeholder();
        dbg_dassert(rc->header_pos != 0xffff, "");
    }
    else
    {
        rc->header_pos = 0xffff;
        marshall(callback->get_request()->writer(), rc->read_header);
        callback->get_request()->header().client.hash = gpid_to_hash(rc->read_header.gpid);
    }

    return rc;
}

void replication_app_client_base::end_request(request_context* request, error_code err, message_ptr& resp)
{
    if (request->timeout_timer == nullptr || request->timeout_timer->cancel(true))
    {
        request->callback_task->enqueue(err, resp);
    }
}

void replication_app_client_base::call(request_context* request, bool no_delay)
{
    auto& msg = request->callback_task->get_request();
    auto nts = ::dsn::service::env::now_us();
    if (nts + 100 >= msg->header().client.timeout_ts_us) // < 100us
    {
        message_ptr nil(nullptr);
        end_request(request, ERR_TIMEOUT, nil);
        delete request;
        return;
    }

    end_point addr;
    int app_id;

    error_code err = get_address(
        request->partition_index,
        !request->is_read,
        addr,
        app_id,
        request->read_header.semantic
        );

    // target node in cache
    if (err == ERR_OK)
    {
        dbg_dassert(addr != end_point::INVALID, "");
        dassert(app_id > 0, "");

        if (request->header_pos != 0xffff)
        {
            if (request->is_read)
            {
                request->read_header.gpid.app_id = app_id;
                marshall(msg->writer(), request->read_header, request->header_pos);
                msg->header().client.hash = gpid_to_hash(request->read_header.gpid);
            }
            else
            {
                request->write_header.gpid.app_id = app_id;
                marshall(msg->writer(), request->write_header, request->header_pos);
                msg->header().client.hash = gpid_to_hash(request->write_header.gpid);
            }

            request->header_pos = 0xffff;
        }

        rpc::call(
            addr,
            msg,
            this,
            std::bind(
            &replication_app_client_base::replica_rw_reply,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3,
            request
            )
            );
    }

    // target node not known
    else if (!no_delay)
    {
        // delay 1 second for further config query
        tasking::enqueue(LPC_REPLICATION_DELAY_QUERY_CONFIG, this,
            std::bind(&replication_app_client_base::call, this, request, true),
            0,
            1000
            );
    }

    else
    {
        zauto_lock l(_requests_lock);

        // init timeout timer if necessary
        if (request->timeout_timer == nullptr)
        {
            request->timeout_timer = tasking::enqueue(
                LPC_REPLICATION_CLIENT_REQUEST_TIMEOUT,
                this,
                std::bind(&replication_app_client_base::on_user_request_timeout, this, request),
                0,
                static_cast<int>((msg->header().client.timeout_ts_us - nts) / 1000)
                );
        }

        // put into pending queue of querying target partition
        auto it = _pending_requests.find(request->partition_index);
        if (it == _pending_requests.end())
        {
            auto pc = new partition_context;
            pc->query_config_task = nullptr;
            it = _pending_requests.insert(pending_requests::value_type(request->partition_index, pc)).first;
        }

        it->second->requests.push_back(request);

        // init configuration query task if necessary
        if (it->second->query_config_task == nullptr)
        {
            message_ptr msg = message::create_request(RPC_CM_CALL);

            meta_request_header hdr;
            hdr.rpc_tag = RPC_CM_QUERY_PARTITION_CONFIG_BY_INDEX;
            marshall(msg->writer(), hdr);

            configuration_query_by_index_request req;
            req.app_name = _app_name;
            req.partition_indices.push_back(request->partition_index);
            marshall(msg->writer(), req);

            it->second->query_config_task = rpc::call_replicated(
                _last_contact_point,
                _meta_servers,
                msg,

                this,
                std::bind(&replication_app_client_base::query_partition_configuration_reply,
                    this,
                    std::placeholders::_1,
                    std::placeholders::_2,
                    std::placeholders::_3,
                    request->partition_index
                    )
                );
        }
    }
}

void replication_app_client_base::replica_rw_reply(
    error_code err,
    message_ptr& request,
    message_ptr& response,
    request_context* rc
    )
{
    if (err != ERR_OK)
    {
        goto Retry;
    }

    int err2;
    response->reader().read(err2);

    if (err2 != ERR_OK && err2 != ERR_HANDLER_NOT_FOUND)
    {
        goto Retry;
    }
    else
    {
        error_code err3;
        err3.set(err2);
        end_request(rc, err3, response);
        delete rc;
    }
    return;

Retry:
    // clear partition configuration as it could be wrong
    {
        zauto_write_lock l(_config_lock);
        _config_cache.erase(rc->is_read ? rc->read_header.gpid.pidx : rc->write_header.gpid.pidx);
    }

    // then retry
    call(rc, false);
}

error_code replication_app_client_base::get_address(int pidx, bool is_write, __out_param end_point& addr, __out_param int& app_id, read_semantic_t semantic)
{
    error_code err;
    partition_configuration config;

    {
    zauto_read_lock l(_config_lock);
    auto it = _config_cache.find(pidx);
    if (it != _config_cache.end())
    {
        err = ERR_OK;
        config = it->second;
    }
    else
    {
        err = ERR_IO_PENDING;
    }
    }

    if (err == ERR_OK)
    {
        app_id = _app_id;
        if (is_write)
        {
            addr = config.primary;
        }
        else
        {
            addr = get_read_address(semantic, config);
        }

        if (dsn::end_point::INVALID == addr)
        {
            err = ERR_IO_PENDING;
        }
    }
    return err;
}

void replication_app_client_base::query_partition_configuration_reply(error_code err, message_ptr& request, message_ptr& response, int pidx)
{
    if (!err)
    {
        configuration_query_by_index_response resp;
        unmarshall(response->reader(), resp);
        if (resp.err == ERR_OK)
        {
            zauto_write_lock l(_config_lock);
            _last_contact_point = response->header().from_address;

            if (resp.partitions.size() > 0)
            {
                if (_app_id != -1 && _app_id != resp.partitions[0].gpid.app_id)
                {
                    dassert(false, "app id is changed (mostly the app was removed and created with the same name), local Vs remote: %u vs %u ",
                        _app_id, resp.partitions[0].gpid.app_id);
                }

                _app_id = resp.partitions[0].gpid.app_id;
                _app_partition_count = resp.partition_count;
            }

            for (auto it = resp.partitions.begin(); it != resp.partitions.end(); it++)
            {
                partition_configuration& new_config = *it;
                auto it2 = _config_cache.find(new_config.gpid.pidx);
                if (it2 == _config_cache.end())
                {
                    _config_cache[new_config.gpid.pidx] = new_config;
                }
                else if (it2->second.ballot < new_config.ballot)
                {
                    it2->second = new_config;
                }
            }
        }
    }

    // send pending client msgs
    partition_context* pc = nullptr;
    {
        zauto_lock l(_requests_lock);
        auto it = _pending_requests.find(pidx);
        if (it != _pending_requests.end())
        {
            pc = it->second;
            _pending_requests.erase(pidx);
        }
    }

    if (pc != nullptr)
    {
        for (auto& req : pc->requests)
        {
            call(req, false);
        }
        pc->requests.clear();
        delete pc;
    }
}

end_point replication_app_client_base::get_read_address(read_semantic_t semantic, const partition_configuration& config)
{
    if (semantic == read_semantic_t::ReadLastUpdate)
        return config.primary;

    // readsnapshot or readoutdated, using random
    else
    {
        bool has_primary = false;
        int N = static_cast<int>(config.secondaries.size());
        if (config.primary != dsn::end_point::INVALID)
        {
            N++;
            has_primary = true;
        }

        if (0 == N) return config.primary;

        int r = random32(0, 1000) % N;
        if (has_primary && r == N - 1)
            return config.primary;
        else
            return config.secondaries[r];
    }
}

}} // end namespace
<commit_msg>add a new tasking::enqueue function to ensure happens-before relationship between task caller and task itself, to avoid some race conditions<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "replication_common.h"
#include "rpc_replicated.h"

namespace dsn { namespace replication {

using namespace ::dsn::service;

void replication_app_client_base::load_meta_servers(
        configuration_ptr& cf,
        __out_param std::vector<end_point>& servers
        )
{
    // read meta_servers from machine list file
    servers.clear();

    std::vector<std::string> server_ss;
    cf->get_all_keys("replication.meta_servers", server_ss);
    for (auto& s : server_ss)
    {
        // name:port
        auto pos1 = s.find_first_of(':');
        if (pos1 != std::string::npos)
        {
            end_point ep(s.substr(0, pos1).c_str(), atoi(s.substr(pos1 + 1).c_str()));
            servers.push_back(ep);
        }
    }
}

replication_app_client_base::replication_app_client_base(
    const std::vector<end_point>& meta_servers,
    const char* app_name
    )
{
    _app_name = std::string(app_name);
    _meta_servers = meta_servers;

    _app_id = -1;
    _app_partition_count = -1;
    _last_contact_point = end_point::INVALID;
}

replication_app_client_base::~replication_app_client_base()
{
    clear_all_pending_tasks();
}

void replication_app_client_base::clear_all_pending_tasks()
{
    message_ptr nil(nullptr);

    service::zauto_lock l(_requests_lock);
    for (auto& pc : _pending_requests)
    {
        if (pc.second->query_config_task != nullptr)
            pc.second->query_config_task->cancel(true);

        for (auto& rc : pc.second->requests)
        {
            end_request(rc, ERR_TIMEOUT, nil);
            delete rc;
        }
        delete pc.second;
    }
    _pending_requests.clear();
}

void replication_app_client_base::on_user_request_timeout(request_context* rc)
{
    message_ptr nil(nullptr);
    rc->callback_task->enqueue(ERR_TIMEOUT, nil);
}

DEFINE_TASK_CODE(LPC_REPLICATION_CLIENT_REQUEST_TIMEOUT, TASK_PRIORITY_COMMON, THREAD_POOL_DEFAULT)
DEFINE_TASK_CODE(LPC_REPLICATION_DELAY_QUERY_CONFIG, TASK_PRIORITY_COMMON, THREAD_POOL_DEFAULT)

replication_app_client_base::request_context* replication_app_client_base::create_write_context(
    int partition_index,
    task_code code,
    rpc_response_task_ptr& callback,
    int reply_hash
    )
{
    auto rc = new request_context;
    rc->callback_task = callback;
    rc->is_read = false;
    rc->partition_index = partition_index;
    rc->write_header.gpid.app_id = _app_id;
    rc->write_header.gpid.pidx = partition_index;
    rc->write_header.code = code;
    rc->timeout_timer = nullptr;

    if (rc->write_header.gpid.app_id == -1)
    {
        rc->header_pos = callback->get_request()->writer().write_placeholder();
        dbg_dassert(rc->header_pos != 0xffff, "");
    }
    else
    {
        rc->header_pos = 0xffff;
        marshall(callback->get_request()->writer(), rc->write_header);
        callback->get_request()->header().client.hash = gpid_to_hash(rc->write_header.gpid);
    }

    return rc;
}

replication_app_client_base::request_context* replication_app_client_base::create_read_context(
    int partition_index,
    task_code code,
    rpc_response_task_ptr& callback,
    read_semantic_t read_semantic,
    decree snapshot_decree, // only used when ReadSnapshot
    int reply_hash
    )
{
    auto rc = new request_context;
    rc->callback_task = callback;
    rc->is_read = true;
    rc->partition_index = partition_index;
    rc->read_header.gpid.app_id = _app_id;
    rc->read_header.gpid.pidx = partition_index;
    rc->read_header.code = code;
    rc->read_header.semantic = read_semantic;
    rc->read_header.version_decree = snapshot_decree;
    rc->timeout_timer = nullptr;

    if (rc->read_header.gpid.app_id == -1)
    {
        rc->header_pos = callback->get_request()->writer().write_placeholder();
        dbg_dassert(rc->header_pos != 0xffff, "");
    }
    else
    {
        rc->header_pos = 0xffff;
        marshall(callback->get_request()->writer(), rc->read_header);
        callback->get_request()->header().client.hash = gpid_to_hash(rc->read_header.gpid);
    }

    return rc;
}

void replication_app_client_base::end_request(request_context* request, error_code err, message_ptr& resp)
{
    if (request->timeout_timer == nullptr || request->timeout_timer->cancel(true))
    {
        request->callback_task->enqueue(err, resp);
    }
}

void replication_app_client_base::call(request_context* request, bool no_delay)
{
    auto& msg = request->callback_task->get_request();
    auto nts = ::dsn::service::env::now_us();
    if (nts + 100 >= msg->header().client.timeout_ts_us) // < 100us
    {
        message_ptr nil(nullptr);
        end_request(request, ERR_TIMEOUT, nil);
        delete request;
        return;
    }

    end_point addr;
    int app_id;

    error_code err = get_address(
        request->partition_index,
        !request->is_read,
        addr,
        app_id,
        request->read_header.semantic
        );

    // target node in cache
    if (err == ERR_OK)
    {
        dbg_dassert(addr != end_point::INVALID, "");
        dassert(app_id > 0, "");

        if (request->header_pos != 0xffff)
        {
            if (request->is_read)
            {
                request->read_header.gpid.app_id = app_id;
                marshall(msg->writer(), request->read_header, request->header_pos);
                msg->header().client.hash = gpid_to_hash(request->read_header.gpid);
            }
            else
            {
                request->write_header.gpid.app_id = app_id;
                marshall(msg->writer(), request->write_header, request->header_pos);
                msg->header().client.hash = gpid_to_hash(request->write_header.gpid);
            }

            request->header_pos = 0xffff;
        }

        rpc::call(
            addr,
            msg,
            this,
            std::bind(
            &replication_app_client_base::replica_rw_reply,
            this,
            std::placeholders::_1,
            std::placeholders::_2,
            std::placeholders::_3,
            request
            )
            );
    }

    // target node not known
    else if (!no_delay)
    {
        // delay 1 second for further config query
        tasking::enqueue(LPC_REPLICATION_DELAY_QUERY_CONFIG, this,
            std::bind(&replication_app_client_base::call, this, request, true),
            0,
            1000
            );
    }

    else
    {
        zauto_lock l(_requests_lock);

        // init timeout timer if necessary
        if (request->timeout_timer == nullptr)
        {
            tasking::enqueue(
                request->timeout_timer,
                LPC_REPLICATION_CLIENT_REQUEST_TIMEOUT,
                this,
                std::bind(&replication_app_client_base::on_user_request_timeout, this, request),
                0,
                static_cast<int>((msg->header().client.timeout_ts_us - nts) / 1000)
                );
        }

        // put into pending queue of querying target partition
        auto it = _pending_requests.find(request->partition_index);
        if (it == _pending_requests.end())
        {
            auto pc = new partition_context;
            pc->query_config_task = nullptr;
            it = _pending_requests.insert(pending_requests::value_type(request->partition_index, pc)).first;
        }

        it->second->requests.push_back(request);

        // init configuration query task if necessary
        if (it->second->query_config_task == nullptr)
        {
            message_ptr msg = message::create_request(RPC_CM_CALL);

            meta_request_header hdr;
            hdr.rpc_tag = RPC_CM_QUERY_PARTITION_CONFIG_BY_INDEX;
            marshall(msg->writer(), hdr);

            configuration_query_by_index_request req;
            req.app_name = _app_name;
            req.partition_indices.push_back(request->partition_index);
            marshall(msg->writer(), req);

            it->second->query_config_task = rpc::call_replicated(
                _last_contact_point,
                _meta_servers,
                msg,

                this,
                std::bind(&replication_app_client_base::query_partition_configuration_reply,
                    this,
                    std::placeholders::_1,
                    std::placeholders::_2,
                    std::placeholders::_3,
                    request->partition_index
                    )
                );
        }
    }
}

void replication_app_client_base::replica_rw_reply(
    error_code err,
    message_ptr& request,
    message_ptr& response,
    request_context* rc
    )
{
    if (err != ERR_OK)
    {
        goto Retry;
    }

    int err2;
    response->reader().read(err2);

    if (err2 != ERR_OK && err2 != ERR_HANDLER_NOT_FOUND)
    {
        goto Retry;
    }
    else
    {
        error_code err3;
        err3.set(err2);
        end_request(rc, err3, response);
        delete rc;
    }
    return;

Retry:
    // clear partition configuration as it could be wrong
    {
        zauto_write_lock l(_config_lock);
        _config_cache.erase(rc->is_read ? rc->read_header.gpid.pidx : rc->write_header.gpid.pidx);
    }

    // then retry
    call(rc, false);
}

error_code replication_app_client_base::get_address(int pidx, bool is_write, __out_param end_point& addr, __out_param int& app_id, read_semantic_t semantic)
{
    error_code err;
    partition_configuration config;

    {
    zauto_read_lock l(_config_lock);
    auto it = _config_cache.find(pidx);
    if (it != _config_cache.end())
    {
        err = ERR_OK;
        config = it->second;
    }
    else
    {
        err = ERR_IO_PENDING;
    }
    }

    if (err == ERR_OK)
    {
        app_id = _app_id;
        if (is_write)
        {
            addr = config.primary;
        }
        else
        {
            addr = get_read_address(semantic, config);
        }

        if (dsn::end_point::INVALID == addr)
        {
            err = ERR_IO_PENDING;
        }
    }
    return err;
}

void replication_app_client_base::query_partition_configuration_reply(error_code err, message_ptr& request, message_ptr& response, int pidx)
{
    if (!err)
    {
        configuration_query_by_index_response resp;
        unmarshall(response->reader(), resp);
        if (resp.err == ERR_OK)
        {
            zauto_write_lock l(_config_lock);
            _last_contact_point = response->header().from_address;

            if (resp.partitions.size() > 0)
            {
                if (_app_id != -1 && _app_id != resp.partitions[0].gpid.app_id)
                {
                    dassert(false, "app id is changed (mostly the app was removed and created with the same name), local Vs remote: %u vs %u ",
                        _app_id, resp.partitions[0].gpid.app_id);
                }

                _app_id = resp.partitions[0].gpid.app_id;
                _app_partition_count = resp.partition_count;
            }

            for (auto it = resp.partitions.begin(); it != resp.partitions.end(); it++)
            {
                partition_configuration& new_config = *it;
                auto it2 = _config_cache.find(new_config.gpid.pidx);
                if (it2 == _config_cache.end())
                {
                    _config_cache[new_config.gpid.pidx] = new_config;
                }
                else if (it2->second.ballot < new_config.ballot)
                {
                    it2->second = new_config;
                }
            }
        }
    }

    // send pending client msgs
    partition_context* pc = nullptr;
    {
        zauto_lock l(_requests_lock);
        auto it = _pending_requests.find(pidx);
        if (it != _pending_requests.end())
        {
            pc = it->second;
            _pending_requests.erase(pidx);
        }
    }

    if (pc != nullptr)
    {
        for (auto& req : pc->requests)
        {
            call(req, false);
        }
        pc->requests.clear();
        delete pc;
    }
}

end_point replication_app_client_base::get_read_address(read_semantic_t semantic, const partition_configuration& config)
{
    if (semantic == read_semantic_t::ReadLastUpdate)
        return config.primary;

    // readsnapshot or readoutdated, using random
    else
    {
        bool has_primary = false;
        int N = static_cast<int>(config.secondaries.size());
        if (config.primary != dsn::end_point::INVALID)
        {
            N++;
            has_primary = true;
        }

        if (0 == N) return config.primary;

        int r = random32(0, 1000) % N;
        if (has_primary && r == N - 1)
            return config.primary;
        else
            return config.secondaries[r];
    }
}

}} // end namespace
<|endoftext|>


<commit_before>/* http_bidder_interface.cc
   Eric Robert, 2 April 2014
   Copyright (c) 2011 Datacratic.  All rights reserved.
*/

#include "http_bidder_interface.h"
#include "jml/db/persistent.h"
#include "soa/service/http_client.h"
#include "soa/utils/generic_utils.h"
#include "rtbkit/common/messages.h"
#include "rtbkit/plugins/bid_request/openrtb_bid_request.h"
#include "rtbkit/openrtb/openrtb_parsing.h"
#include "rtbkit/core/router/router.h"

using namespace Datacratic;
using namespace RTBKIT;

namespace {
    DefaultDescription<OpenRTB::BidRequest> desc;

    void tagRequest(OpenRTB::BidRequest &request,
                    const std::map<std::string, BidInfo> &bidders)
    {

        for (const auto &bidder: bidders) {
            const auto &agentConfig = bidder.second.agentConfig;
            const auto &spots = bidder.second.imp;
            const auto &creatives = agentConfig->creatives;

            Json::Value creativesValue(Json::arrayValue);
            for (const auto &spot: spots) {
                const int adSpotIndex = spot.first;
                ExcCheck(adSpotIndex >= 0 && adSpotIndex < request.imp.size(),
                         "adSpotIndex out of range");
                auto &imp = request.imp[adSpotIndex];
                auto &ext = imp.ext;
                for (int creativeIndex: spot.second) {
                    ExcCheck(creativeIndex >= 0 && creativeIndex < creatives.size(),
                             "creativeIndex out of range");
                    const int creativeId = creatives[creativeIndex].id;
                    creativesValue.append(creativeId);
                }

                ext["allowed_ids"][std::to_string(agentConfig->externalId)] =
                    std::move(creativesValue);

            }

        }

    }
}

HttpBidderInterface::HttpBidderInterface(std::string name,
                                         std::shared_ptr<ServiceProxies> proxies,
                                         Json::Value const & json) {
    host = json["host"].asString();
    path = json["path"].asString();
    httpClient.reset(new HttpClient(host));
    loop.addSource("HttpBidderInterface::httpClient", httpClient);
}

void HttpBidderInterface::sendAuctionMessage(std::shared_ptr<Auction> const & auction,
                                             double timeLeftMs,
                                             std::map<std::string, BidInfo> const & bidders) {
    using namespace std;
    for(auto & item : bidders) {
        auto & agent = item.first;
        auto & info = router->agents[agent];
        const auto &config = info.config;
        BidRequest originalRequest = *auction->request;
        WinCostModel wcm = auction->exchangeConnector->getWinCostModel(*auction, *info.config);

        OpenRTB::BidRequest openRtbRequest = toOpenRtb(originalRequest);
        bool ok = prepareRequest(openRtbRequest, originalRequest, bidders);
        /* If we took too much time processing the request, then we don't send it.
           Instead, we're making null bids for each impression
        */
        if (!ok) {
            Bids bids;
            for_each(begin(openRtbRequest.imp), end(openRtbRequest.imp),
                     [&](const OpenRTB::Impression &imp) {
                Bid theBid;
                theBid.price = USD_CPM(0);
                bids.push_back(move(theBid));
            });
            submitBids(agent, auction->id, bids, wcm);
            return;
        }
        StructuredJsonPrintingContext context;
        desc.printJson(&openRtbRequest, context);
        auto requestStr = context.output.toString();

        /* We need to capture by copy inside the lambda otherwise we might get
           a dangling reference if we go out of scope before receiving the http response
        */
        auto callbacks = std::make_shared<HttpClientSimpleCallbacks>(
                [=](const HttpRequest &, HttpClientError errorCode,
                    int, const std::string &, const std::string &body)
                {
                    if (errorCode != HttpClientError::NONE) {
                        auto toErrorString = [](HttpClientError code) -> std::string {
                            switch (code) {
                                #define CASE(code) \
                                    case code: \
                                        return #code;
                                CASE(HttpClientError::NONE)
                                CASE(HttpClientError::UNKNOWN)
                                CASE(HttpClientError::TIMEOUT)
                                CASE(HttpClientError::HOST_NOT_FOUND)
                                CASE(HttpClientError::COULD_NOT_CONNECT)
                                #undef CASE
                            }
                            ExcCheck(false, "Invalid code path");
                            return "";
                        };
                        cerr << "Error requesting " << host
                                  << ": " << toErrorString(errorCode);
                      }
                      else {
                        // cerr << "Response: " << body << endl;
                         OpenRTB::BidResponse response;
                         ML::Parse_Context context("payload",
                               body.c_str(), body.size());
                         StreamingJsonParsingContext jsonContext(context);
                         static DefaultDescription<OpenRTB::BidResponse> respDesc;
                         respDesc.parseJson(&response, jsonContext);

                         for (const auto &seatbid: response.seatbid) {
                             Bids bids;

                             for (const auto &bid: seatbid.bid) {
                                 Bid theBid;

                                 int crid = bid.crid.toInt();
                                 int creativeIndex = indexOf(config->creatives,
                                     &Creative::id, crid);

                                 if (creativeIndex == -1) {
                                     throw ML::Exception(ML::format(
                                        "Unknown creative id: %d", crid));
                                 }

                                 theBid.creativeIndex = creativeIndex;
                                 theBid.price = USD_CPM(bid.price.val);
                                 theBid.priority = 0.0;

                                 int spotIndex = indexOf(openRtbRequest.imp,
                                                        &OpenRTB::Impression::id, bid.impid);
                                 if (spotIndex == -1) {
                                     throw ML::Exception(ML::format(
                                         "Unknown impression id: %s", bid.impid.toString()));
                                 }

                                 theBid.spotIndex = spotIndex;

                                 bids.push_back(std::move(theBid));
                             }
                             submitBids(agent, auction->id, bids, wcm);
                         }
                     }
                }
        );

        HttpRequest::Content reqContent { requestStr, "application/json" };
        RestParams headers { { "x-openrtb-version", "2.1" } };
       // std::cerr << "Sending HTTP POST to: " << host << " " << path << std::endl;
       // std::cerr << "Content " << reqContent.str << std::endl;

        httpClient->post(path, callbacks, reqContent,
                         { } /* queryParams */, headers);
    }
}

void HttpBidderInterface::sendWinMessage(std::string const & agent,
                                         std::string const & id,
                                         Amount price) {
}

void HttpBidderInterface::sendWinMessage(std::string const & agent,
                                         Amount price,
                                         FinishedInfo const & event) {
}

void HttpBidderInterface::sendLateWinMessage(std::string const & agent,
                                             Amount price,
                                             FinishedInfo const & event) {
}

void HttpBidderInterface::sendLossMessage(std::string const & agent,
                                          std::string const & id) {
}

void HttpBidderInterface::sendLossMessage(std::string const & agent,
                                          FinishedInfo const & event) {
}

void HttpBidderInterface::sendCampaignEventMessage(std::string const & agent,
                                                   std::string const & label,
                                                   FinishedInfo const & event) {
}

void HttpBidderInterface::sendBidLostMessage(std::string const & agent,
                                             std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendBidDroppedMessage(std::string const & agent,
                                                std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendBidInvalidMessage(std::string const & agent,
                                                std::string const & reason,
                                                std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendNoBudgetMessage(std::string const & agent,
                                              std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendTooLateMessage(std::string const & agent,
                                             std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendMessage(std::string const & agent,
                                      std::string const & message) {
}

void HttpBidderInterface::sendErrorMessage(std::string const & agent,
                                           std::string const & error,
                                           std::vector<std::string> const & payload) {
}

void HttpBidderInterface::sendPingMessage(std::string const & agent,
                                          int ping) {
    ExcCheck(ping == 0 || ping == 1, "Bad PING level, must be either 0 or 1");

    auto encodeDate = [](Date date) {
        return ML::format("%.5f", date.secondsSinceEpoch());
    };

    const std::string sentTime = encodeDate(Date::now());
    const std::string receivedTime = sentTime;
    const std::string pong = (ping == 0 ? "PONG0" : "PONG1");
    std::vector<std::string> message { agent, pong, sentTime, receivedTime };
    router->handleAgentMessage(ping, message);
}

void HttpBidderInterface::send(std::shared_ptr<PostAuctionEvent> const & event) {
}

bool HttpBidderInterface::prepareRequest(OpenRTB::BidRequest &request,
                                         const RTBKIT::BidRequest &originalRequest,
                                         const std::map<std::string, BidInfo> &bidders) const {
    tagRequest(request, bidders);

    // We update the tmax value before sending the BidRequest to substract our processing time
    double processingTimeMs = originalRequest.timestamp.secondsUntil(Date::now()) * 1000;
    int oldTmax = request.tmax.value();
    int newTmax = oldTmax - static_cast<int>(std::round(processingTimeMs));
    if (newTmax <= 0) {
        return false;
    }
#if 0
    std::cerr << "old tmax = " << oldTmax << std::endl
              << "new tmax = " << newTmax << std::endl;
#endif
    ExcCheck(newTmax <= oldTmax, "Wrong tmax calculation");
    request.tmax.val = newTmax;
    return true;
}

void HttpBidderInterface::submitBids(const std::string &agent, Id auctionId,
                                     const Bids &bids, WinCostModel wcm)
{
     Json::FastWriter writer;
     std::vector<std::string> message { agent, "BID" };
     message.push_back(auctionId.toString());
     std::string bidsStr = writer.write(bids.toJson());
     std::string wcmStr = writer.write(wcm.toJson());
     message.push_back(std::move(bidsStr));
     message.push_back(std::move(wcmStr));

     router->doBid(message);
}

//
// factory
//

namespace {

struct AtInit {
    AtInit()
    {
        BidderInterface::registerFactory("http", [](std::string const & name , std::shared_ptr<ServiceProxies> const & proxies, Json::Value const & json) {
            return new HttpBidderInterface(name, proxies, json);
        });
    }
} atInit;

}

<commit_msg>HTTP Bidder does not crash anymore when receiving a 204 response<commit_after>/* http_bidder_interface.cc
   Eric Robert, 2 April 2014
   Copyright (c) 2011 Datacratic.  All rights reserved.
*/

#include "http_bidder_interface.h"
#include "jml/db/persistent.h"
#include "soa/service/http_client.h"
#include "soa/utils/generic_utils.h"
#include "rtbkit/common/messages.h"
#include "rtbkit/plugins/bid_request/openrtb_bid_request.h"
#include "rtbkit/openrtb/openrtb_parsing.h"
#include "rtbkit/core/router/router.h"

using namespace Datacratic;
using namespace RTBKIT;

namespace {
    DefaultDescription<OpenRTB::BidRequest> desc;

    void tagRequest(OpenRTB::BidRequest &request,
                    const std::map<std::string, BidInfo> &bidders)
    {

        for (const auto &bidder: bidders) {
            const auto &agentConfig = bidder.second.agentConfig;
            const auto &spots = bidder.second.imp;
            const auto &creatives = agentConfig->creatives;

            Json::Value creativesValue(Json::arrayValue);
            for (const auto &spot: spots) {
                const int adSpotIndex = spot.first;
                ExcCheck(adSpotIndex >= 0 && adSpotIndex < request.imp.size(),
                         "adSpotIndex out of range");
                auto &imp = request.imp[adSpotIndex];
                auto &ext = imp.ext;
                for (int creativeIndex: spot.second) {
                    ExcCheck(creativeIndex >= 0 && creativeIndex < creatives.size(),
                             "creativeIndex out of range");
                    const int creativeId = creatives[creativeIndex].id;
                    creativesValue.append(creativeId);
                }

                ext["allowed_ids"][std::to_string(agentConfig->externalId)] =
                    std::move(creativesValue);

            }

        }

    }
}

HttpBidderInterface::HttpBidderInterface(std::string name,
                                         std::shared_ptr<ServiceProxies> proxies,
                                         Json::Value const & json) {
    host = json["host"].asString();
    path = json["path"].asString();
    httpClient.reset(new HttpClient(host));
    loop.addSource("HttpBidderInterface::httpClient", httpClient);
}

void HttpBidderInterface::sendAuctionMessage(std::shared_ptr<Auction> const & auction,
                                             double timeLeftMs,
                                             std::map<std::string, BidInfo> const & bidders) {
    using namespace std;
    for(auto & item : bidders) {
        auto & agent = item.first;
        auto & info = router->agents[agent];
        const auto &config = info.config;
        BidRequest originalRequest = *auction->request;
        WinCostModel wcm = auction->exchangeConnector->getWinCostModel(*auction, *info.config);

        OpenRTB::BidRequest openRtbRequest = toOpenRtb(originalRequest);
        bool ok = prepareRequest(openRtbRequest, originalRequest, bidders);
        auto submitNoBid = [&]() {
            Bids bids;
            for_each(begin(openRtbRequest.imp), end(openRtbRequest.imp),
                     [&](const OpenRTB::Impression &imp) {
                Bid theBid;
                theBid.price = USD_CPM(0);
                bids.push_back(move(theBid));
            });
            submitBids(agent, auction->id, bids, wcm);
        };

        /* If we took too much time processing the request, then we don't send it.
           Instead, we're making null bids for each impression
        */
        if (!ok) {
            submitNoBid();
            return;
        }
        StructuredJsonPrintingContext context;
        desc.printJson(&openRtbRequest, context);
        auto requestStr = context.output.toString();

        /* We need to capture by copy inside the lambda otherwise we might get
           a dangling reference if we go out of scope before receiving the http response
        */
        auto callbacks = std::make_shared<HttpClientSimpleCallbacks>(
                [=](const HttpRequest &, HttpClientError errorCode,
                    int statusCode, const std::string &, std::string &&body)
                {
                    if (errorCode != HttpClientError::NONE) {
                        auto toErrorString = [](HttpClientError code) -> std::string {
                            switch (code) {
                                #define CASE(code) \
                                    case code: \
                                        return #code;
                                CASE(HttpClientError::NONE)
                                CASE(HttpClientError::UNKNOWN)
                                CASE(HttpClientError::TIMEOUT)
                                CASE(HttpClientError::HOST_NOT_FOUND)
                                CASE(HttpClientError::COULD_NOT_CONNECT)
                                #undef CASE
                            }
                            ExcCheck(false, "Invalid code path");
                            return "";
                        };
                        cerr << "Error requesting " << host
                                  << ": " << toErrorString(errorCode);
                      }
                      else if (statusCode == 204) {
                         submitNoBid();
                      }
                      else if (statusCode == 200) {
                        // cerr << "Response: " << body << endl;
                         OpenRTB::BidResponse response;
                         ML::Parse_Context context("payload",
                               body.c_str(), body.size());
                         StreamingJsonParsingContext jsonContext(context);
                         static DefaultDescription<OpenRTB::BidResponse> respDesc;
                         respDesc.parseJson(&response, jsonContext);

                         for (const auto &seatbid: response.seatbid) {
                             Bids bids;

                             for (const auto &bid: seatbid.bid) {
                                 Bid theBid;

                                 int crid = bid.crid.toInt();
                                 int creativeIndex = indexOf(config->creatives,
                                     &Creative::id, crid);

                                 if (creativeIndex == -1) {
                                     throw ML::Exception(ML::format(
                                        "Unknown creative id: %d", crid));
                                 }

                                 theBid.creativeIndex = creativeIndex;
                                 theBid.price = USD_CPM(bid.price.val);
                                 theBid.priority = 0.0;

                                 int spotIndex = indexOf(openRtbRequest.imp,
                                                        &OpenRTB::Impression::id, bid.impid);
                                 if (spotIndex == -1) {
                                     throw ML::Exception(ML::format(
                                         "Unknown impression id: %s", bid.impid.toString()));
                                 }

                                 theBid.spotIndex = spotIndex;

                                 bids.push_back(std::move(theBid));
                             }
                             submitBids(agent, auction->id, bids, wcm);
                         }
                     }
                }
        );

        HttpRequest::Content reqContent { requestStr, "application/json" };
        RestParams headers { { "x-openrtb-version", "2.1" } };
       // std::cerr << "Sending HTTP POST to: " << host << " " << path << std::endl;
       // std::cerr << "Content " << reqContent.str << std::endl;

        httpClient->post(path, callbacks, reqContent,
                         { } /* queryParams */, headers);
    }
}

void HttpBidderInterface::sendWinMessage(std::string const & agent,
                                         std::string const & id,
                                         Amount price) {
}

void HttpBidderInterface::sendWinMessage(std::string const & agent,
                                         Amount price,
                                         FinishedInfo const & event) {
}

void HttpBidderInterface::sendLateWinMessage(std::string const & agent,
                                             Amount price,
                                             FinishedInfo const & event) {
}

void HttpBidderInterface::sendLossMessage(std::string const & agent,
                                          std::string const & id) {
}

void HttpBidderInterface::sendLossMessage(std::string const & agent,
                                          FinishedInfo const & event) {
}

void HttpBidderInterface::sendCampaignEventMessage(std::string const & agent,
                                                   std::string const & label,
                                                   FinishedInfo const & event) {
}

void HttpBidderInterface::sendBidLostMessage(std::string const & agent,
                                             std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendBidDroppedMessage(std::string const & agent,
                                                std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendBidInvalidMessage(std::string const & agent,
                                                std::string const & reason,
                                                std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendNoBudgetMessage(std::string const & agent,
                                              std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendTooLateMessage(std::string const & agent,
                                             std::shared_ptr<Auction> const & auction) {
}

void HttpBidderInterface::sendMessage(std::string const & agent,
                                      std::string const & message) {
}

void HttpBidderInterface::sendErrorMessage(std::string const & agent,
                                           std::string const & error,
                                           std::vector<std::string> const & payload) {
}

void HttpBidderInterface::sendPingMessage(std::string const & agent,
                                          int ping) {
    ExcCheck(ping == 0 || ping == 1, "Bad PING level, must be either 0 or 1");

    auto encodeDate = [](Date date) {
        return ML::format("%.5f", date.secondsSinceEpoch());
    };

    const std::string sentTime = encodeDate(Date::now());
    const std::string receivedTime = sentTime;
    const std::string pong = (ping == 0 ? "PONG0" : "PONG1");
    std::vector<std::string> message { agent, pong, sentTime, receivedTime };
    router->handleAgentMessage(message);
}

void HttpBidderInterface::send(std::shared_ptr<PostAuctionEvent> const & event) {
}

bool HttpBidderInterface::prepareRequest(OpenRTB::BidRequest &request,
                                         const RTBKIT::BidRequest &originalRequest,
                                         const std::map<std::string, BidInfo> &bidders) const {
    tagRequest(request, bidders);

    // We update the tmax value before sending the BidRequest to substract our processing time
    double processingTimeMs = originalRequest.timestamp.secondsUntil(Date::now()) * 1000;
    int oldTmax = request.tmax.value();
    int newTmax = oldTmax - static_cast<int>(std::round(processingTimeMs));
    if (newTmax <= 0) {
        return false;
    }
#if 0
    std::cerr << "old tmax = " << oldTmax << std::endl
              << "new tmax = " << newTmax << std::endl;
#endif
    ExcCheck(newTmax <= oldTmax, "Wrong tmax calculation");
    request.tmax.val = newTmax;
    return true;
}

void HttpBidderInterface::submitBids(const std::string &agent, Id auctionId,
                                     const Bids &bids, WinCostModel wcm)
{
     Json::FastWriter writer;
     std::vector<std::string> message { agent, "BID" };
     message.push_back(auctionId.toString());
     std::string bidsStr = writer.write(bids.toJson());
     std::string wcmStr = writer.write(wcm.toJson());
     message.push_back(std::move(bidsStr));
     message.push_back(std::move(wcmStr));

     router->doBid(message);
}

//
// factory
//

namespace {

struct AtInit {
    AtInit()
    {
        BidderInterface::registerFactory("http", [](std::string const & name , std::shared_ptr<ServiceProxies> const & proxies, Json::Value const & json) {
            return new HttpBidderInterface(name, proxies, json);
        });
    }
} atInit;

}

<|endoftext|>


<commit_before>/*
    Copyright (c) 2017 Xavier Leclercq

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#include "WxCombinationChartFrame.h"
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/charts/wxcharts.h>

WxCombinationChartFrame::WxCombinationChartFrame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title)
{
    // Create a top-level panel to hold all the contents of the frame
    wxPanel* panel = new wxPanel(this, wxID_ANY);

    // Create the combination chart widget
    wxCombinationChartCtrl* combinationChartCtrl = new wxCombinationChartCtrl(panel, wxID_ANY);

    // Create the data for the bar chart widget
    wxVector<wxString> labels;
    labels.push_back("January");
    labels.push_back("February");
    labels.push_back("March");
    labels.push_back("April");
    labels.push_back("May");
    labels.push_back("June");
    labels.push_back("July");
    wxBarChartData chartData(labels);

    // Add the first dataset
    wxVector<wxDouble> points1;
    points1.push_back(3);
    points1.push_back(2.5);
    points1.push_back(1.2);
    points1.push_back(3);
    points1.push_back(6);
    points1.push_back(5);
    points1.push_back(1);
    wxBarChartDataset::ptr dataset1(
        new wxBarChartDataset(
            wxColor(220, 220, 220, 0x7F),
            wxColor(220, 220, 220, 0xCC),
            points1)
    );
    chartData.AddDataset(dataset1);
    combinationChartCtrl->AddBarChart(chartData);

    // Set up the sizer for the panel
    wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
    panelSizer->Add(combinationChartCtrl, 1, wxEXPAND);
    panel->SetSizer(panelSizer);

    // Set up the sizer for the frame
    wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
    topSizer->Add(panel, 1, wxEXPAND);
    SetSizerAndFit(topSizer);
}
<commit_msg>Fixed function name<commit_after>/*
    Copyright (c) 2017 Xavier Leclercq

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
    IN THE SOFTWARE.
*/

#include "WxCombinationChartFrame.h"
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/charts/wxcharts.h>

WxCombinationChartFrame::WxCombinationChartFrame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title)
{
    // Create a top-level panel to hold all the contents of the frame
    wxPanel* panel = new wxPanel(this, wxID_ANY);

    // Create the combination chart widget
    wxCombinationChartCtrl* combinationChartCtrl = new wxCombinationChartCtrl(panel, wxID_ANY);

    // Create the data for the bar chart widget
    wxVector<wxString> labels;
    labels.push_back("January");
    labels.push_back("February");
    labels.push_back("March");
    labels.push_back("April");
    labels.push_back("May");
    labels.push_back("June");
    labels.push_back("July");
    wxBarChartData chartData(labels);

    // Add the first dataset
    wxVector<wxDouble> points1;
    points1.push_back(3);
    points1.push_back(2.5);
    points1.push_back(1.2);
    points1.push_back(3);
    points1.push_back(6);
    points1.push_back(5);
    points1.push_back(1);
    wxBarChartDataset::ptr dataset1(
        new wxBarChartDataset(
            wxColor(220, 220, 220, 0x7F),
            wxColor(220, 220, 220, 0xCC),
            points1)
    );
    chartData.AddDataset(dataset1);
    combinationChartCtrl->AddColumnChart(chartData);

    // Set up the sizer for the panel
    wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
    panelSizer->Add(combinationChartCtrl, 1, wxEXPAND);
    panel->SetSizer(panelSizer);

    // Set up the sizer for the frame
    wxBoxSizer* topSizer = new wxBoxSizer(wxHORIZONTAL);
    topSizer->Add(panel, 1, wxEXPAND);
    SetSizerAndFit(topSizer);
}
<|endoftext|>