<commit_before>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 Google Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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.

// Author: kenton@google.com (Kenton Varda)

#include <google/protobuf/compiler/subprocess.h>

#ifndef _WIN32
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>
#endif

#include <algorithm>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/substitute.h>

namespace google {
namespace protobuf {
namespace compiler {

#ifdef _WIN32

static void CloseHandleOrDie(HANDLE handle) {
  if (!CloseHandle(handle)) {
    GOOGLE_LOG(FATAL) << "CloseHandle: "
                      << Subprocess::Win32ErrorMessage(GetLastError());
  }
}

Subprocess::Subprocess()
    : process_start_error_(ERROR_SUCCESS),
      child_handle_(NULL), child_stdin_(NULL), child_stdout_(NULL) {}

Subprocess::~Subprocess() {
  if (child_stdin_ != NULL) {
    CloseHandleOrDie(child_stdin_);
  }
  if (child_stdout_ != NULL) {
    CloseHandleOrDie(child_stdout_);
  }
}

void Subprocess::Start(const string& program, SearchMode search_mode) {
  // Create the pipes.
  HANDLE stdin_pipe_read;
  HANDLE stdin_pipe_write;
  HANDLE stdout_pipe_read;
  HANDLE stdout_pipe_write;

  if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, NULL, 0)) {
    GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
  }
  if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, NULL, 0)) {
    GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
  }

  // Make child side of the pipes inheritable.
  if (!SetHandleInformation(stdin_pipe_read,
                            HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
    GOOGLE_LOG(FATAL) << "SetHandleInformation: "
                      << Win32ErrorMessage(GetLastError());
  }
  if (!SetHandleInformation(stdout_pipe_write,
                            HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
    GOOGLE_LOG(FATAL) << "SetHandleInformation: "
                      << Win32ErrorMessage(GetLastError());
  }

  // Setup STARTUPINFO to redirect handles.
  STARTUPINFOA startup_info;
  ZeroMemory(&startup_info, sizeof(startup_info));
  startup_info.cb = sizeof(startup_info);
  startup_info.dwFlags = STARTF_USESTDHANDLES;
  startup_info.hStdInput = stdin_pipe_read;
  startup_info.hStdOutput = stdout_pipe_write;
  startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);

  if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
    GOOGLE_LOG(FATAL) << "GetStdHandle: "
                      << Win32ErrorMessage(GetLastError());
  }

  // CreateProcess() mutates its second parameter.  WTF?
  char* name_copy = strdup(program.c_str());

  // Create the process.
  PROCESS_INFORMATION process_info;

  if (CreateProcessA((search_mode == SEARCH_PATH) ? NULL : program.c_str(),
                     (search_mode == SEARCH_PATH) ? name_copy : NULL,
                     NULL,  // process security attributes
                     NULL,  // thread security attributes
                     TRUE,  // inherit handles?
                     0,     // obscure creation flags
                     NULL,  // environment (inherit from parent)
                     NULL,  // current directory (inherit from parent)
                     &startup_info,
                     &process_info)) {
    child_handle_ = process_info.hProcess;
    CloseHandleOrDie(process_info.hThread);
    child_stdin_ = stdin_pipe_write;
    child_stdout_ = stdout_pipe_read;
  } else {
    process_start_error_ = GetLastError();
    CloseHandleOrDie(stdin_pipe_write);
    CloseHandleOrDie(stdout_pipe_read);
  }

  CloseHandleOrDie(stdin_pipe_read);
  CloseHandleOrDie(stdout_pipe_write);
  free(name_copy);
}

bool Subprocess::Communicate(const Message& input, Message* output,
                             string* error) {
  if (process_start_error_ != ERROR_SUCCESS) {
    *error = Win32ErrorMessage(process_start_error_);
    return false;
  }

  GOOGLE_CHECK(child_handle_ != NULL) << "Must call Start() first.";

  string input_data = input.SerializeAsString();
  string output_data;

  int input_pos = 0;

  while (child_stdout_ != NULL) {
    HANDLE handles[2];
    int handle_count = 0;

    if (child_stdin_ != NULL) {
      handles[handle_count++] = child_stdin_;
    }
    if (child_stdout_ != NULL) {
      handles[handle_count++] = child_stdout_;
    }

    DWORD wait_result =
        WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);

    HANDLE signaled_handle;
    if (wait_result >= WAIT_OBJECT_0 &&
        wait_result < WAIT_OBJECT_0 + handle_count) {
      signaled_handle = handles[wait_result - WAIT_OBJECT_0];
    } else if (wait_result == WAIT_FAILED) {
      GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: "
                        << Win32ErrorMessage(GetLastError());
    } else {
      GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
                        << wait_result;
    }

    if (signaled_handle == child_stdin_) {
      DWORD n;
      if (!WriteFile(child_stdin_,
                     input_data.data() + input_pos,
                     input_data.size() - input_pos,
                     &n, NULL)) {
        // Child closed pipe.  Presumably it will report an error later.
        // Pretend we're done for now.
        input_pos = input_data.size();
      } else {
        input_pos += n;
      }

      if (input_pos == input_data.size()) {
        // We're done writing.  Close.
        CloseHandleOrDie(child_stdin_);
        child_stdin_ = NULL;
      }
    } else if (signaled_handle == child_stdout_) {
      char buffer[4096];
      DWORD n;

      if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, NULL)) {
        // We're done reading.  Close.
        CloseHandleOrDie(child_stdout_);
        child_stdout_ = NULL;
      } else {
        output_data.append(buffer, n);
      }
    }
  }

  if (child_stdin_ != NULL) {
    // Child did not finish reading input before it closed the output.
    // Presumably it exited with an error.
    CloseHandleOrDie(child_stdin_);
    child_stdin_ = NULL;
  }

  DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);

  if (wait_result == WAIT_FAILED) {
    GOOGLE_LOG(FATAL) << "WaitForSingleObject: "
                      << Win32ErrorMessage(GetLastError());
  } else if (wait_result != WAIT_OBJECT_0) {
    GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
                      << wait_result;
  }

  DWORD exit_code;
  if (!GetExitCodeProcess(child_handle_, &exit_code)) {
    GOOGLE_LOG(FATAL) << "GetExitCodeProcess: "
                      << Win32ErrorMessage(GetLastError());
  }

  CloseHandleOrDie(child_handle_);
  child_handle_ = NULL;

  if (exit_code != 0) {
    *error = strings::Substitute(
        "Plugin failed with status code $0.", exit_code);
    return false;
  }

  if (!output->ParseFromString(output_data)) {
    *error = "Plugin output is unparseable: " + CEscape(output_data);
    return false;
  }

  return true;
}

string Subprocess::Win32ErrorMessage(DWORD error_code) {
  char* message;

  // WTF?
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, error_code, 0,
                (LPTSTR)&message,  // NOT A BUG!
                0, NULL);

  string result = message;
  LocalFree(message);
  return result;
}

// ===================================================================

#else  // _WIN32

Subprocess::Subprocess()
    : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}

Subprocess::~Subprocess() {
  if (child_stdin_ != -1) {
    close(child_stdin_);
  }
  if (child_stdout_ != -1) {
    close(child_stdout_);
  }
}

void Subprocess::Start(const string& program, SearchMode search_mode) {
  // Note that we assume that there are no other threads, thus we don't have to
  // do crazy stuff like using socket pairs or avoiding libc locks.

  // [0] is read end, [1] is write end.
  int stdin_pipe[2];
  int stdout_pipe[2];

  pipe(stdin_pipe);
  pipe(stdout_pipe);

  char* argv[2] = { strdup(program.c_str()), NULL };

  child_pid_ = fork();
  if (child_pid_ == -1) {
    GOOGLE_LOG(FATAL) << "fork: " << strerror(errno);
  } else if (child_pid_ == 0) {
    // We are the child.
    dup2(stdin_pipe[0], STDIN_FILENO);
    dup2(stdout_pipe[1], STDOUT_FILENO);

    close(stdin_pipe[0]);
    close(stdin_pipe[1]);
    close(stdout_pipe[0]);
    close(stdout_pipe[1]);

    switch (search_mode) {
      case SEARCH_PATH:
        execvp(argv[0], argv);
        break;
      case EXACT_NAME:
        execv(argv[0], argv);
        break;
    }

    // Write directly to STDERR_FILENO to avoid stdio code paths that may do
    // stuff that is unsafe here.
    write(STDERR_FILENO, argv[0], strlen(argv[0]));
    const char* message = ": program not found or is not executable\n";
    write(STDERR_FILENO, message, strlen(message));

    // Must use _exit() rather than exit() to avoid flushing output buffers
    // that will also be flushed by the parent.
    _exit(1);
  } else {
    free(argv[0]);

    close(stdin_pipe[0]);
    close(stdout_pipe[1]);

    child_stdin_ = stdin_pipe[1];
    child_stdout_ = stdout_pipe[0];
  }
}

bool Subprocess::Communicate(const Message& input, Message* output,
                             string* error) {

  GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first.";

  // The "sighandler_t" typedef is GNU-specific, so define our own.
  typedef void SignalHandler(int);

  // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
  SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);

  string input_data = input.SerializeAsString();
  string output_data;

  int input_pos = 0;
  int max_fd = max(child_stdin_, child_stdout_);

  while (child_stdout_ != -1) {
    fd_set read_fds;
    fd_set write_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    if (child_stdout_ != -1) {
      FD_SET(child_stdout_, &read_fds);
    }
    if (child_stdin_ != -1) {
      FD_SET(child_stdin_, &write_fds);
    }

    if (select(max_fd + 1, &read_fds, &write_fds, NULL, NULL) < 0) {
      if (errno == EINTR) {
        // Interrupted by signal.  Try again.
        continue;
      } else {
        GOOGLE_LOG(FATAL) << "select: " << strerror(errno);
      }
    }

    if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
      int n = write(child_stdin_, input_data.data() + input_pos,
                                  input_data.size() - input_pos);
      if (n < 0) {
        // Child closed pipe.  Presumably it will report an error later.
        // Pretend we're done for now.
        input_pos = input_data.size();
      } else {
        input_pos += n;
      }

      if (input_pos == input_data.size()) {
        // We're done writing.  Close.
        close(child_stdin_);
        child_stdin_ = -1;
      }
    }

    if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
      char buffer[4096];
      int n = read(child_stdout_, buffer, sizeof(buffer));

      if (n > 0) {
        output_data.append(buffer, n);
      } else {
        // We're done reading.  Close.
        close(child_stdout_);
        child_stdout_ = -1;
      }
    }
  }

  if (child_stdin_ != -1) {
    // Child did not finish reading input before it closed the output.
    // Presumably it exited with an error.
    close(child_stdin_);
    child_stdin_ = -1;
  }

  int status;
  while (waitpid(child_pid_, &status, 0) == -1) {
    if (errno != EINTR) {
      GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno);
    }
  }

  // Restore SIGPIPE handling.
  signal(SIGPIPE, old_pipe_handler);

  if (WIFEXITED(status)) {
    if (WEXITSTATUS(status) != 0) {
      int error_code = WEXITSTATUS(status);
      *error = strings::Substitute(
          "Plugin failed with status code $0.", error_code);
      return false;
    }
  } else if (WIFSIGNALED(status)) {
    int signal = WTERMSIG(status);
    *error = strings::Substitute(
        "Plugin killed by signal $0.", signal);
    return false;
  } else {
    *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
    return false;
  }

  if (!output->ParseFromString(output_data)) {
    *error = "Plugin output is unparseable.";
    return false;
  }

  return true;
}

#endif  // !_WIN32

}  // namespace compiler
}  // namespace protobuf
}  // namespace google
<commit_msg>Add missing include (that apparently didn't break anything on most platforms).<commit_after>// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://code.google.com/p/protobuf/
//
// 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 Google Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
// "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 THE COPYRIGHT
// OWNER OR CONTRIBUTORS 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.

// Author: kenton@google.com (Kenton Varda)

#include <google/protobuf/compiler/subprocess.h>

#ifndef _WIN32
#include <errno.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <signal.h>
#endif

#include <algorithm>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/message.h>
#include <google/protobuf/stubs/substitute.h>

namespace google {
namespace protobuf {
namespace compiler {

#ifdef _WIN32

static void CloseHandleOrDie(HANDLE handle) {
  if (!CloseHandle(handle)) {
    GOOGLE_LOG(FATAL) << "CloseHandle: "
                      << Subprocess::Win32ErrorMessage(GetLastError());
  }
}

Subprocess::Subprocess()
    : process_start_error_(ERROR_SUCCESS),
      child_handle_(NULL), child_stdin_(NULL), child_stdout_(NULL) {}

Subprocess::~Subprocess() {
  if (child_stdin_ != NULL) {
    CloseHandleOrDie(child_stdin_);
  }
  if (child_stdout_ != NULL) {
    CloseHandleOrDie(child_stdout_);
  }
}

void Subprocess::Start(const string& program, SearchMode search_mode) {
  // Create the pipes.
  HANDLE stdin_pipe_read;
  HANDLE stdin_pipe_write;
  HANDLE stdout_pipe_read;
  HANDLE stdout_pipe_write;

  if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, NULL, 0)) {
    GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
  }
  if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, NULL, 0)) {
    GOOGLE_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
  }

  // Make child side of the pipes inheritable.
  if (!SetHandleInformation(stdin_pipe_read,
                            HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
    GOOGLE_LOG(FATAL) << "SetHandleInformation: "
                      << Win32ErrorMessage(GetLastError());
  }
  if (!SetHandleInformation(stdout_pipe_write,
                            HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
    GOOGLE_LOG(FATAL) << "SetHandleInformation: "
                      << Win32ErrorMessage(GetLastError());
  }

  // Setup STARTUPINFO to redirect handles.
  STARTUPINFOA startup_info;
  ZeroMemory(&startup_info, sizeof(startup_info));
  startup_info.cb = sizeof(startup_info);
  startup_info.dwFlags = STARTF_USESTDHANDLES;
  startup_info.hStdInput = stdin_pipe_read;
  startup_info.hStdOutput = stdout_pipe_write;
  startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);

  if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
    GOOGLE_LOG(FATAL) << "GetStdHandle: "
                      << Win32ErrorMessage(GetLastError());
  }

  // CreateProcess() mutates its second parameter.  WTF?
  char* name_copy = strdup(program.c_str());

  // Create the process.
  PROCESS_INFORMATION process_info;

  if (CreateProcessA((search_mode == SEARCH_PATH) ? NULL : program.c_str(),
                     (search_mode == SEARCH_PATH) ? name_copy : NULL,
                     NULL,  // process security attributes
                     NULL,  // thread security attributes
                     TRUE,  // inherit handles?
                     0,     // obscure creation flags
                     NULL,  // environment (inherit from parent)
                     NULL,  // current directory (inherit from parent)
                     &startup_info,
                     &process_info)) {
    child_handle_ = process_info.hProcess;
    CloseHandleOrDie(process_info.hThread);
    child_stdin_ = stdin_pipe_write;
    child_stdout_ = stdout_pipe_read;
  } else {
    process_start_error_ = GetLastError();
    CloseHandleOrDie(stdin_pipe_write);
    CloseHandleOrDie(stdout_pipe_read);
  }

  CloseHandleOrDie(stdin_pipe_read);
  CloseHandleOrDie(stdout_pipe_write);
  free(name_copy);
}

bool Subprocess::Communicate(const Message& input, Message* output,
                             string* error) {
  if (process_start_error_ != ERROR_SUCCESS) {
    *error = Win32ErrorMessage(process_start_error_);
    return false;
  }

  GOOGLE_CHECK(child_handle_ != NULL) << "Must call Start() first.";

  string input_data = input.SerializeAsString();
  string output_data;

  int input_pos = 0;

  while (child_stdout_ != NULL) {
    HANDLE handles[2];
    int handle_count = 0;

    if (child_stdin_ != NULL) {
      handles[handle_count++] = child_stdin_;
    }
    if (child_stdout_ != NULL) {
      handles[handle_count++] = child_stdout_;
    }

    DWORD wait_result =
        WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);

    HANDLE signaled_handle;
    if (wait_result >= WAIT_OBJECT_0 &&
        wait_result < WAIT_OBJECT_0 + handle_count) {
      signaled_handle = handles[wait_result - WAIT_OBJECT_0];
    } else if (wait_result == WAIT_FAILED) {
      GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: "
                        << Win32ErrorMessage(GetLastError());
    } else {
      GOOGLE_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
                        << wait_result;
    }

    if (signaled_handle == child_stdin_) {
      DWORD n;
      if (!WriteFile(child_stdin_,
                     input_data.data() + input_pos,
                     input_data.size() - input_pos,
                     &n, NULL)) {
        // Child closed pipe.  Presumably it will report an error later.
        // Pretend we're done for now.
        input_pos = input_data.size();
      } else {
        input_pos += n;
      }

      if (input_pos == input_data.size()) {
        // We're done writing.  Close.
        CloseHandleOrDie(child_stdin_);
        child_stdin_ = NULL;
      }
    } else if (signaled_handle == child_stdout_) {
      char buffer[4096];
      DWORD n;

      if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, NULL)) {
        // We're done reading.  Close.
        CloseHandleOrDie(child_stdout_);
        child_stdout_ = NULL;
      } else {
        output_data.append(buffer, n);
      }
    }
  }

  if (child_stdin_ != NULL) {
    // Child did not finish reading input before it closed the output.
    // Presumably it exited with an error.
    CloseHandleOrDie(child_stdin_);
    child_stdin_ = NULL;
  }

  DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);

  if (wait_result == WAIT_FAILED) {
    GOOGLE_LOG(FATAL) << "WaitForSingleObject: "
                      << Win32ErrorMessage(GetLastError());
  } else if (wait_result != WAIT_OBJECT_0) {
    GOOGLE_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
                      << wait_result;
  }

  DWORD exit_code;
  if (!GetExitCodeProcess(child_handle_, &exit_code)) {
    GOOGLE_LOG(FATAL) << "GetExitCodeProcess: "
                      << Win32ErrorMessage(GetLastError());
  }

  CloseHandleOrDie(child_handle_);
  child_handle_ = NULL;

  if (exit_code != 0) {
    *error = strings::Substitute(
        "Plugin failed with status code $0.", exit_code);
    return false;
  }

  if (!output->ParseFromString(output_data)) {
    *error = "Plugin output is unparseable: " + CEscape(output_data);
    return false;
  }

  return true;
}

string Subprocess::Win32ErrorMessage(DWORD error_code) {
  char* message;

  // WTF?
  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL, error_code, 0,
                (LPTSTR)&message,  // NOT A BUG!
                0, NULL);

  string result = message;
  LocalFree(message);
  return result;
}

// ===================================================================

#else  // _WIN32

Subprocess::Subprocess()
    : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}

Subprocess::~Subprocess() {
  if (child_stdin_ != -1) {
    close(child_stdin_);
  }
  if (child_stdout_ != -1) {
    close(child_stdout_);
  }
}

void Subprocess::Start(const string& program, SearchMode search_mode) {
  // Note that we assume that there are no other threads, thus we don't have to
  // do crazy stuff like using socket pairs or avoiding libc locks.

  // [0] is read end, [1] is write end.
  int stdin_pipe[2];
  int stdout_pipe[2];

  pipe(stdin_pipe);
  pipe(stdout_pipe);

  char* argv[2] = { strdup(program.c_str()), NULL };

  child_pid_ = fork();
  if (child_pid_ == -1) {
    GOOGLE_LOG(FATAL) << "fork: " << strerror(errno);
  } else if (child_pid_ == 0) {
    // We are the child.
    dup2(stdin_pipe[0], STDIN_FILENO);
    dup2(stdout_pipe[1], STDOUT_FILENO);

    close(stdin_pipe[0]);
    close(stdin_pipe[1]);
    close(stdout_pipe[0]);
    close(stdout_pipe[1]);

    switch (search_mode) {
      case SEARCH_PATH:
        execvp(argv[0], argv);
        break;
      case EXACT_NAME:
        execv(argv[0], argv);
        break;
    }

    // Write directly to STDERR_FILENO to avoid stdio code paths that may do
    // stuff that is unsafe here.
    write(STDERR_FILENO, argv[0], strlen(argv[0]));
    const char* message = ": program not found or is not executable\n";
    write(STDERR_FILENO, message, strlen(message));

    // Must use _exit() rather than exit() to avoid flushing output buffers
    // that will also be flushed by the parent.
    _exit(1);
  } else {
    free(argv[0]);

    close(stdin_pipe[0]);
    close(stdout_pipe[1]);

    child_stdin_ = stdin_pipe[1];
    child_stdout_ = stdout_pipe[0];
  }
}

bool Subprocess::Communicate(const Message& input, Message* output,
                             string* error) {

  GOOGLE_CHECK_NE(child_stdin_, -1) << "Must call Start() first.";

  // The "sighandler_t" typedef is GNU-specific, so define our own.
  typedef void SignalHandler(int);

  // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
  SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);

  string input_data = input.SerializeAsString();
  string output_data;

  int input_pos = 0;
  int max_fd = max(child_stdin_, child_stdout_);

  while (child_stdout_ != -1) {
    fd_set read_fds;
    fd_set write_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    if (child_stdout_ != -1) {
      FD_SET(child_stdout_, &read_fds);
    }
    if (child_stdin_ != -1) {
      FD_SET(child_stdin_, &write_fds);
    }

    if (select(max_fd + 1, &read_fds, &write_fds, NULL, NULL) < 0) {
      if (errno == EINTR) {
        // Interrupted by signal.  Try again.
        continue;
      } else {
        GOOGLE_LOG(FATAL) << "select: " << strerror(errno);
      }
    }

    if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
      int n = write(child_stdin_, input_data.data() + input_pos,
                                  input_data.size() - input_pos);
      if (n < 0) {
        // Child closed pipe.  Presumably it will report an error later.
        // Pretend we're done for now.
        input_pos = input_data.size();
      } else {
        input_pos += n;
      }

      if (input_pos == input_data.size()) {
        // We're done writing.  Close.
        close(child_stdin_);
        child_stdin_ = -1;
      }
    }

    if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
      char buffer[4096];
      int n = read(child_stdout_, buffer, sizeof(buffer));

      if (n > 0) {
        output_data.append(buffer, n);
      } else {
        // We're done reading.  Close.
        close(child_stdout_);
        child_stdout_ = -1;
      }
    }
  }

  if (child_stdin_ != -1) {
    // Child did not finish reading input before it closed the output.
    // Presumably it exited with an error.
    close(child_stdin_);
    child_stdin_ = -1;
  }

  int status;
  while (waitpid(child_pid_, &status, 0) == -1) {
    if (errno != EINTR) {
      GOOGLE_LOG(FATAL) << "waitpid: " << strerror(errno);
    }
  }

  // Restore SIGPIPE handling.
  signal(SIGPIPE, old_pipe_handler);

  if (WIFEXITED(status)) {
    if (WEXITSTATUS(status) != 0) {
      int error_code = WEXITSTATUS(status);
      *error = strings::Substitute(
          "Plugin failed with status code $0.", error_code);
      return false;
    }
  } else if (WIFSIGNALED(status)) {
    int signal = WTERMSIG(status);
    *error = strings::Substitute(
        "Plugin killed by signal $0.", signal);
    return false;
  } else {
    *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
    return false;
  }

  if (!output->ParseFromString(output_data)) {
    *error = "Plugin output is unparseable.";
    return false;
  }

  return true;
}

#endif  // !_WIN32

}  // namespace compiler
}  // namespace protobuf
}  // namespace google
<|endoftext|>


<commit_before>#include "widgets/window.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/helper/shortcut.hpp"
#include "widgets/notebook.hpp"
#include "widgets/settingsdialog.hpp"
#include "widgets/split.hpp"

#include <QPalette>
#include <QShortcut>
#include <QVBoxLayout>

namespace chatterino {
namespace widgets {

Window::Window(singletons::ThemeManager &_themeManager, WindowType _type)
    : BaseWindow(_themeManager, nullptr, true)
    , type(_type)
    , dpi(this->getScale())
    , notebook(this, !this->hasCustomWindowFrame())
{
    singletons::AccountManager::getInstance().Twitch.currentUsername.connect(
        [this](const std::string &newUsername, auto) {
            if (newUsername.empty()) {
                this->refreshWindowTitle("Not logged in");
            } else {
                this->refreshWindowTitle(QString::fromStdString(newUsername));
            }
        });

    if (this->hasCustomWindowFrame() && _type == Window::Main) {
        this->addTitleBarButton(TitleBarButton::Settings, [] {
            singletons::WindowManager::getInstance().showSettingsDialog();
        });
        auto user = this->addTitleBarLabel([this] {
            singletons::WindowManager::getInstance().showAccountSelectPopup(QCursor::pos());
        });

        singletons::AccountManager::getInstance().Twitch.userChanged.connect([this, user] {
            user->getLabel().setText(
                singletons::AccountManager::getInstance().Twitch.getCurrent()->getUserName());
        });
    }

    if (_type == Window::Main) {
        this->resize((int)(600 * this->getScale()), (int)(500 * this->getScale()));
    } else {
        this->resize((int)(300 * this->getScale()), (int)(500 * this->getScale()));
    }

    QVBoxLayout *layout = new QVBoxLayout(this);

    layout->addWidget(&this->notebook);
    this->getLayoutContainer()->setLayout(layout);

    // set margin
    layout->setMargin(0);

    this->themeRefreshEvent();

    /// Initialize program-wide hotkeys
    // CTRL+P: Open Settings Dialog
    CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); });

    // CTRL+Number: Switch to n'th tab
    CreateWindowShortcut(this, "CTRL+1", [this] { this->notebook.selectIndex(0); });
    CreateWindowShortcut(this, "CTRL+2", [this] { this->notebook.selectIndex(1); });
    CreateWindowShortcut(this, "CTRL+3", [this] { this->notebook.selectIndex(2); });
    CreateWindowShortcut(this, "CTRL+4", [this] { this->notebook.selectIndex(3); });
    CreateWindowShortcut(this, "CTRL+5", [this] { this->notebook.selectIndex(4); });
    CreateWindowShortcut(this, "CTRL+6", [this] { this->notebook.selectIndex(5); });
    CreateWindowShortcut(this, "CTRL+7", [this] { this->notebook.selectIndex(6); });
    CreateWindowShortcut(this, "CTRL+8", [this] { this->notebook.selectIndex(7); });
    CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); });

    // CTRL+SHIFT+T: New tab
    CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(true); });

    // CTRL+SHIFT+W: Close current tab
    CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); });

    std::vector<QString> cheerMessages;
    // clang-format off
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=2000;color=#B22222;display-name=arzenhuz;emotes=185989:33-37;id=1ae336ac-8e1a-4d6b-8b00-9fcee26e8337;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783470139;turbo=0;user-id=111553331;user-type= :arzenhuz!arzenhuz@arzenhuz.tmi.twitch.tv PRIVMSG #pajlada :pajacheer2000 Buy pizza for both pajaH)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=37;color=#3FBF72;display-name=VADIKUS007;emotes=;id=eedd95fd-2a17-4da1-879c-a1e76ffce582;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783184352;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :cheer37)");
    cheerMessages.emplace_back(R"(@badges=moderator/1,subscriber/24,bits/100;bits=1;color=#DCD3E6;display-name=swiftapples;emotes=80803:7-13;id=1c4647f6-f1a8-4acc-a9b2-b5d23d91258d;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1515538318854;turbo=0;user-id=80526177;user-type=mod :swiftapples!swiftapples@swiftapples.tmi.twitch.tv PRIVMSG #pajlada :cheer1 pajaHey)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,turbo/1;bits=1;color=#0A2927;display-name=Binkelderk;emotes=;id=a1d9bdc6-6f6a-4c03-8554-d5b34721a878;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515538899479;turbo=1;user-id=89081828;user-type= :binkelderk!binkelderk@binkelderk.tmi.twitch.tv PRIVMSG #pajlada :pajacheer1)");
    cheerMessages.emplace_back(R"(@badges=moderator/1,subscriber/24,bits/100;bits=1;color=#DCD3E6;display-name=swiftapples;emotes=80803:6-12;id=e9e21793-0b58-4ac6-8a1e-c19e165dbc9f;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1515539073209;turbo=0;user-id=80526177;user-type=mod :swiftapples!swiftapples@swiftapples.tmi.twitch.tv PRIVMSG #pajlada :bday1 pajaHey)");
    cheerMessages.emplace_back(R"(@badges=partner/1;bits=1;color=#CC44FF;display-name=pajlada;emotes=;id=ca89214e-4fb5-48ec-853e-d2e6b41355ea;mod=0;room-id=39705480;subscriber=0;tmi-sent-ts=1515546977622;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #leesherwhy :test123 owocheer1 456test)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=1;color=#3FBF72;display-name=VADIKUS007;emotes=;id=c4c5061b-f5c6-464b-8bff-7f1ac816caa7;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782817171;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    cheerMessages.emplace_back(R"(@badges=;bits=1;color=#FF0000;display-name=?????;emotes=;id=979b6b4f-be9a-42fb-a54c-88fcb0aca18d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1515782819084;turbo=0;user-id=70656218;user-type= :stels_tv!stels_tv@stels_tv.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    cheerMessages.emplace_back(R"(@badges=subscriber/3,premium/1;bits=1;color=#FF0000;display-name=kalvarenga;emotes=;id=4744d6f0-de1d-475d-a3ff-38647113265a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782860740;turbo=0;user-id=108393131;user-type= :kalvarenga!kalvarenga@kalvarenga.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    // clang-format on

    //    CreateWindowShortcut(this, "F5", [cheerMessages] {
    //        auto &ircManager = singletons::IrcManager::getInstance();
    //        static int index = 0;
    //        ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]);
    //    });
}

Window::WindowType Window::getType()
{
    return this->type;
}

void Window::repaintVisibleChatWidgets(Channel *channel)
{
    auto *page = this->notebook.getOrAddSelectedPage();

    if (page == nullptr) {
        return;
    }

    for (const auto &split : page->getSplits()) {
        if (channel == nullptr || channel == split->getChannel().get()) {
            split->layoutMessages();
        }
    }
}

Notebook &Window::getNotebook()
{
    return this->notebook;
}

void Window::refreshWindowTitle(const QString &username)
{
    this->setWindowTitle(username + " - Chatterino for Twitch");
}

bool Window::event(QEvent *e)
{
    switch (e->type()) {
        case QEvent::WindowActivate:
            break;

        case QEvent::WindowDeactivate: {
            auto page = this->notebook.getOrAddSelectedPage();

            if (page != nullptr) {
                std::vector<Split *> splits = page->getSplits();

                for (Split *split : splits) {
                    split->updateLastReadMessage();
                }
            }
        } break;
    };
    return BaseWindow::event(e);
}

void Window::closeEvent(QCloseEvent *event)
{
    if (this->type == Window::Main) {
        singletons::WindowManager::getInstance().save();
        singletons::WindowManager::getInstance().closeAll();
    }

    this->closed.invoke();
}

}  // namespace widgets
}  // namespace chatterino
<commit_msg>Fixes #325 Option tab stays open when closing the main window<commit_after>#include "widgets/window.hpp"
#include "singletons/accountmanager.hpp"
#include "singletons/channelmanager.hpp"
#include "singletons/ircmanager.hpp"
#include "singletons/settingsmanager.hpp"
#include "singletons/thememanager.hpp"
#include "singletons/windowmanager.hpp"
#include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/helper/shortcut.hpp"
#include "widgets/notebook.hpp"
#include "widgets/settingsdialog.hpp"
#include "widgets/split.hpp"

#include <QApplication>
#include <QPalette>
#include <QShortcut>
#include <QVBoxLayout>

namespace chatterino {
namespace widgets {

Window::Window(singletons::ThemeManager &_themeManager, WindowType _type)
    : BaseWindow(_themeManager, nullptr, true)
    , type(_type)
    , dpi(this->getScale())
    , notebook(this, !this->hasCustomWindowFrame())
{
    singletons::AccountManager::getInstance().Twitch.currentUsername.connect(
        [this](const std::string &newUsername, auto) {
            if (newUsername.empty()) {
                this->refreshWindowTitle("Not logged in");
            } else {
                this->refreshWindowTitle(QString::fromStdString(newUsername));
            }
        });

    if (this->hasCustomWindowFrame() && _type == Window::Main) {
        this->addTitleBarButton(TitleBarButton::Settings, [] {
            singletons::WindowManager::getInstance().showSettingsDialog();
        });
        auto user = this->addTitleBarLabel([this] {
            singletons::WindowManager::getInstance().showAccountSelectPopup(QCursor::pos());
        });

        singletons::AccountManager::getInstance().Twitch.userChanged.connect([this, user] {
            user->getLabel().setText(
                singletons::AccountManager::getInstance().Twitch.getCurrent()->getUserName());
        });
    }

    if (_type == Window::Main) {
        this->resize((int)(600 * this->getScale()), (int)(500 * this->getScale()));
    } else {
        this->resize((int)(300 * this->getScale()), (int)(500 * this->getScale()));
    }

    QVBoxLayout *layout = new QVBoxLayout(this);

    layout->addWidget(&this->notebook);
    this->getLayoutContainer()->setLayout(layout);

    // set margin
    layout->setMargin(0);

    this->themeRefreshEvent();

    /// Initialize program-wide hotkeys
    // CTRL+P: Open Settings Dialog
    CreateWindowShortcut(this, "CTRL+P", [] { SettingsDialog::showDialog(); });

    // CTRL+Number: Switch to n'th tab
    CreateWindowShortcut(this, "CTRL+1", [this] { this->notebook.selectIndex(0); });
    CreateWindowShortcut(this, "CTRL+2", [this] { this->notebook.selectIndex(1); });
    CreateWindowShortcut(this, "CTRL+3", [this] { this->notebook.selectIndex(2); });
    CreateWindowShortcut(this, "CTRL+4", [this] { this->notebook.selectIndex(3); });
    CreateWindowShortcut(this, "CTRL+5", [this] { this->notebook.selectIndex(4); });
    CreateWindowShortcut(this, "CTRL+6", [this] { this->notebook.selectIndex(5); });
    CreateWindowShortcut(this, "CTRL+7", [this] { this->notebook.selectIndex(6); });
    CreateWindowShortcut(this, "CTRL+8", [this] { this->notebook.selectIndex(7); });
    CreateWindowShortcut(this, "CTRL+9", [this] { this->notebook.selectIndex(8); });

    // CTRL+SHIFT+T: New tab
    CreateWindowShortcut(this, "CTRL+SHIFT+T", [this] { this->notebook.addNewPage(true); });

    // CTRL+SHIFT+W: Close current tab
    CreateWindowShortcut(this, "CTRL+SHIFT+W", [this] { this->notebook.removeCurrentPage(); });

    std::vector<QString> cheerMessages;
    // clang-format off
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=2000;color=#B22222;display-name=arzenhuz;emotes=185989:33-37;id=1ae336ac-8e1a-4d6b-8b00-9fcee26e8337;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783470139;turbo=0;user-id=111553331;user-type= :arzenhuz!arzenhuz@arzenhuz.tmi.twitch.tv PRIVMSG #pajlada :pajacheer2000 Buy pizza for both pajaH)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=37;color=#3FBF72;display-name=VADIKUS007;emotes=;id=eedd95fd-2a17-4da1-879c-a1e76ffce582;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515783184352;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :cheer37)");
    cheerMessages.emplace_back(R"(@badges=moderator/1,subscriber/24,bits/100;bits=1;color=#DCD3E6;display-name=swiftapples;emotes=80803:7-13;id=1c4647f6-f1a8-4acc-a9b2-b5d23d91258d;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1515538318854;turbo=0;user-id=80526177;user-type=mod :swiftapples!swiftapples@swiftapples.tmi.twitch.tv PRIVMSG #pajlada :cheer1 pajaHey)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,turbo/1;bits=1;color=#0A2927;display-name=Binkelderk;emotes=;id=a1d9bdc6-6f6a-4c03-8554-d5b34721a878;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515538899479;turbo=1;user-id=89081828;user-type= :binkelderk!binkelderk@binkelderk.tmi.twitch.tv PRIVMSG #pajlada :pajacheer1)");
    cheerMessages.emplace_back(R"(@badges=moderator/1,subscriber/24,bits/100;bits=1;color=#DCD3E6;display-name=swiftapples;emotes=80803:6-12;id=e9e21793-0b58-4ac6-8a1e-c19e165dbc9f;mod=1;room-id=11148817;subscriber=1;tmi-sent-ts=1515539073209;turbo=0;user-id=80526177;user-type=mod :swiftapples!swiftapples@swiftapples.tmi.twitch.tv PRIVMSG #pajlada :bday1 pajaHey)");
    cheerMessages.emplace_back(R"(@badges=partner/1;bits=1;color=#CC44FF;display-name=pajlada;emotes=;id=ca89214e-4fb5-48ec-853e-d2e6b41355ea;mod=0;room-id=39705480;subscriber=0;tmi-sent-ts=1515546977622;turbo=0;user-id=11148817;user-type= :pajlada!pajlada@pajlada.tmi.twitch.tv PRIVMSG #leesherwhy :test123 owocheer1 456test)");
    cheerMessages.emplace_back(R"(@badges=subscriber/12,premium/1;bits=1;color=#3FBF72;display-name=VADIKUS007;emotes=;id=c4c5061b-f5c6-464b-8bff-7f1ac816caa7;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782817171;turbo=0;user-id=72256775;user-type= :vadikus007!vadikus007@vadikus007.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    cheerMessages.emplace_back(R"(@badges=;bits=1;color=#FF0000;display-name=?????;emotes=;id=979b6b4f-be9a-42fb-a54c-88fcb0aca18d;mod=0;room-id=11148817;subscriber=0;tmi-sent-ts=1515782819084;turbo=0;user-id=70656218;user-type= :stels_tv!stels_tv@stels_tv.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    cheerMessages.emplace_back(R"(@badges=subscriber/3,premium/1;bits=1;color=#FF0000;display-name=kalvarenga;emotes=;id=4744d6f0-de1d-475d-a3ff-38647113265a;mod=0;room-id=11148817;subscriber=1;tmi-sent-ts=1515782860740;turbo=0;user-id=108393131;user-type= :kalvarenga!kalvarenga@kalvarenga.tmi.twitch.tv PRIVMSG #pajlada :trihard1)");
    // clang-format on

    //    CreateWindowShortcut(this, "F5", [cheerMessages] {
    //        auto &ircManager = singletons::IrcManager::getInstance();
    //        static int index = 0;
    //        ircManager.addFakeMessage(cheerMessages[index++ % cheerMessages.size()]);
    //    });
}

Window::WindowType Window::getType()
{
    return this->type;
}

void Window::repaintVisibleChatWidgets(Channel *channel)
{
    auto *page = this->notebook.getOrAddSelectedPage();

    if (page == nullptr) {
        return;
    }

    for (const auto &split : page->getSplits()) {
        if (channel == nullptr || channel == split->getChannel().get()) {
            split->layoutMessages();
        }
    }
}

Notebook &Window::getNotebook()
{
    return this->notebook;
}

void Window::refreshWindowTitle(const QString &username)
{
    this->setWindowTitle(username + " - Chatterino for Twitch");
}

bool Window::event(QEvent *e)
{
    switch (e->type()) {
        case QEvent::WindowActivate:
            break;

        case QEvent::WindowDeactivate: {
            auto page = this->notebook.getOrAddSelectedPage();

            if (page != nullptr) {
                std::vector<Split *> splits = page->getSplits();

                for (Split *split : splits) {
                    split->updateLastReadMessage();
                }
            }
        } break;
    };
    return BaseWindow::event(e);
}

void Window::closeEvent(QCloseEvent *event)
{
    if (this->type == Window::Main) {
        singletons::WindowManager::getInstance().save();
        singletons::WindowManager::getInstance().closeAll();
    }

    this->closed.invoke();

    QApplication::exit();
}

}  // namespace widgets
}  // namespace chatterino
<|endoftext|>


<commit_before>#include "writer/IWriter.hpp"

namespace Writer
{
    IWriter::IWriter(const Initialization::WriterPlan & in)
            : _resultFile(in.filePath),
              _initialized(false)
    {
    }

    IWriter::IWriter(const IWriter* in)
    : _resultFile(in->_resultFile), _initialized(false)
    {
    }

    const string_type& IWriter::getResultFile() const
    {
        return _resultFile;
    }

    void IWriter::initialize()
    {
        _initialized = true;
    }

    void IWriter::deinitialize()
    {
        _initialized = false;
    }

    bool IWriter::isInitialized() const
    {
        return _initialized;
    }

    void IWriter::write(tuple<real_type, vector<FMI::ValueCollection> > toWrite)
    {
        appendTime(get<0>(toWrite));
        string_type dummy("");
        for (size_type i = 0; i < get<1>(toWrite).size(); ++i)
            appendResults(dummy, (get<1>(toWrite)[i]));
        flushResults();
    }

} /* namespace Synchronization */

<commit_msg>Cosmetic changes<commit_after>#include "writer/IWriter.hpp"

namespace Writer
{
    IWriter::IWriter(const Initialization::WriterPlan & in)
            : _resultFile(in.filePath),
              _initialized(false)
    {
    }

    IWriter::IWriter(const IWriter* in)
            : _resultFile(in->_resultFile),
              _initialized(false)
    {
    }

    const string_type& IWriter::getResultFile() const
    {
        return _resultFile;
    }

    void IWriter::initialize()
    {
        _initialized = true;
    }

    void IWriter::deinitialize()
    {
        _initialized = false;
    }

    bool IWriter::isInitialized() const
    {
        return _initialized;
    }

    void IWriter::write(tuple<real_type, vector<FMI::ValueCollection> > toWrite)
    {
        appendTime(get<0>(toWrite));
        string_type dummy("");
        for (size_type i = 0; i < get<1>(toWrite).size(); ++i)
            appendResults(dummy, (get<1>(toWrite)[i]));
        flushResults();
    }

} /* namespace Synchronization */

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"
#include "ep_engine.h"
#include "restore.hh"

#ifdef USE_SYSTEM_LIBSQLITE3
#include <sqlite3.h>
#else
#include "embedded/sqlite3.h"
#endif

static const char *query =
    "select cpoint_op.vbucket_id,op,key,flg,exp,cas,val "
    "from cpoint_state "
    "  join cpoint_op on (cpoint_op.vbucket_id = cpoint_state.vbucket_id and"
    "                     cpoint_op.cpoint_id = cpoint_state.cpoint_id) "
    "where cpoint_state.state = \"closed\" "
    "order by cpoint_op.cpoint_id desc";

static const int vbucket_id_idx = 0;
static const int op_idx = 1;
static const int key_idx = 2;
static const int flag_idx = 3;
static const int exp_idx = 4;
static const int cas_idx = 5;
static const int val_idx = 6;

extern "C" {
    static void *restoreThreadMain(void *arg);
}

/**
 * A helper class to track the state of the restore manager
 */
class State {
public:
    State(const char *s) : name(s) {}
    const std::string &toString() const {
        return name;
    }

    static const State Uninitialized;
    static const State Initialized;
    static const State Starting;
    static const State Running;
    static const State Zombie;

private:
    const std::string name;
};

const State State::Uninitialized("uninitialized");
const State State::Initialized("initialized");
const State State::Starting("starting");
const State State::Running("running");
const State State::Zombie("zombie");

/**
 * Hehe.. Since we're applying all of the incremental backups in the
 * let's let the name reflect that ;-)
 *
 * The DecrementalRestorer is responsible for processing a single
 * incremental restore file and add all of it's content to epengine.
 *
 */
class DecrementalRestorer {
public:
    /**
     * Create a new instance of DecrementalRestorer and initialize
     * its member variable.
     * @param theEngine where to restore the data
     * @param dbname the name of the incremental database to restore
     */
    DecrementalRestorer(EventuallyPersistentEngine &theEngine,
                        const std::string &dbname) :
        db(NULL), statement(NULL), engine(theEngine),
        store(*engine.getEpStore()), file(dbname),
        expired(0), wrongVBucket(0), restored(0), skipped(0), busy(0)
    {
        // None needed
    }

    /**
     * Release all allocated resources. We might have the database in an
     * open state if an exception is thrown during restore.
     */
    ~DecrementalRestorer() {
        if (db != NULL) {
            (void)sqlite3_finalize(statement);
            (void)sqlite3_close(db);
        }
    }

    const std::string &getDbFile() const
    {
        return file;
    }

    uint32_t getNumBusy() const {
        return busy;
    }

    uint32_t getNumRestored() const {
        return restored;
    }

    uint32_t getNumSkipped() const {
        return skipped;
    }

    uint32_t getNumWrongVBucket() const {
        return wrongVBucket;
    }

    uint32_t getNumExpired() const {
        return expired;
    }

    /**
     * Process this database file
     * @throw a string describing why an error occured
     */
    void process() throw (std::string) {
        if (sqlite3_open(file.c_str(), &db) !=  SQLITE_OK) {
            db = NULL;
            throw std::string("Failed to open database");
        }

        if (sqlite3_prepare_v2(db, query,
                               strlen(query),
                               &statement, NULL) != SQLITE_OK) {
            (void)sqlite3_finalize(statement);
            (void)sqlite3_close(db);
            db = NULL;
            throw std::string("Failed to prepare statement");
        }

        int rc;
        while ((rc = sqlite3_step(statement)) != SQLITE_DONE) {
            if (rc == SQLITE_ROW) {
                processEntry();
            } else if (rc == SQLITE_BUSY) {
                ++busy;
            } else {
                std::stringstream ss;
                ss << "sqlite error: " << sqlite3_errmsg(db);
                throw std::string(ss.str());
            }
        }

        (void)sqlite3_finalize(statement);
        (void)sqlite3_close(db);
        db = NULL;
    }

private:

    /**
     * callback to process the current item
     */
    void processEntry() throw(std::string) {
        uint32_t exptime = sqlite3_column_int(statement, exp_idx);
        if (exptime != 0 && exptime <  static_cast<int64_t>(ep_real_time())) {
            ++expired;
            return ;
        }
        std::string key((const char*)sqlite3_column_text(statement, key_idx),
                        sqlite3_column_bytes(statement, key_idx));

        enum queue_operation op = queue_op_set;
        if ((sqlite3_column_bytes(statement, op_idx) > 0) &&
            sqlite3_column_text(statement, op_idx)[0] == 'd') {
            op = queue_op_del;
        }

        value_t value(Blob::New((const char*)sqlite3_column_text(statement,
                                                                 val_idx),
                                sqlite3_column_bytes(statement, val_idx)));

        uint16_t vbid =  (uint16_t)sqlite3_column_int(statement,
                                                      vbucket_id_idx);
        int r = store.addUnlessThere(key, vbid, op, value,
                                     sqlite3_column_int(statement, flag_idx),
                                     sqlite3_column_int(statement, exp_idx),
                                     sqlite3_column_int(statement, cas_idx));
        if (r == 0) {
            ++restored;
        } else if (r == 1) {
            ++skipped;
        } else {
            ++wrongVBucket;
        }
    }

    sqlite3 *db;
    sqlite3_stmt *statement;
    EventuallyPersistentEngine &engine;
    EventuallyPersistentStore &store;
    const std::string file;
    uint32_t expired;
    uint32_t wrongVBucket;
    uint32_t restored;
    uint32_t skipped;
    uint32_t busy;
};

class RestoreManagerImpl : public RestoreManager {
public:
    RestoreManagerImpl(EventuallyPersistentEngine &theEngine) :
        RestoreManager(theEngine),
        instance(NULL),
        expired(0),
        wrongVBucket(0),
        restored(0),
        skipped(0),
        busy(0),
        state(&State::Uninitialized)
    {
        // None needed
    }

    virtual void initialize(const std::string &config) throw(std::string)
    {
        if (access(config.c_str(), F_OK) != 0) {
            throw std::string("File not found");
        }

        LockHolder lh(mutex);
        errorMsg.resize(0);
        if (state != &State::Uninitialized) {
            // Only allow the zombie state, because then we can just reap it..
            if (state != &State::Zombie) {
                throw std::string("restorer isn't idle!");
            }
            // reap the zombie!
            reap_UNLOCKED();
        }

        assert(instance == NULL);
        instance = new DecrementalRestorer(engine, config);
        setState_UNLOCKED(State::Initialized);
    }

    virtual void start() throw (std::string)
    {
        LockHolder lh(mutex);
        if (instance == NULL) {
            lh.unlock();
            throw std::string("you need to call initialize before start");
        }

        if (state != &State::Initialized) {
            lh.unlock();
            throw std::string("Restore already running");
        }

        state = &State::Starting;
        int ret = pthread_create(&thread, NULL, restoreThreadMain, this);
        if (ret != 0) {
            state = &State::Uninitialized;
            collectResults();
            delete instance;
            instance = NULL;
            lh.unlock();
            std::stringstream ss;
            ss << "Failed to create restore thread: " << strerror(ret);
            throw ss.str();
        }
    }

    virtual void abort() throw (std::string)
    {
        LockHolder lh(mutex);
        terminate = true;
    }

    virtual void wait() throw (std::string)
    {
        LockHolder lh(mutex);
        if (state != &State::Initialized && state != &State::Uninitialized) {
            reap_UNLOCKED();
        }
    }

    virtual void stats(const void* cookie, ADD_STAT add_stat)
    {
        RestoreManager::stats(cookie, add_stat);
        addStat(cookie, "engine", "RestoreManagerImpl", add_stat);

        LockHolder lh(mutex);
        addStat(cookie, "state", state->toString(), add_stat);
        if (errorMsg.length() > 0) {
            addStat(cookie, "last_error", errorMsg, add_stat);
        }

        if (instance == NULL) {
            addStat(cookie, "number_busy", busy, add_stat);
            addStat(cookie, "number_skipped", skipped, add_stat);
            addStat(cookie, "number_restored", restored, add_stat);
            addStat(cookie, "number_expired", expired, add_stat);
            addStat(cookie, "number_wrong_vbucket", wrongVBucket, add_stat);
        } else {
            addStat(cookie, "file", instance->getDbFile(), add_stat);
            addStat(cookie, "number_busy", instance->getNumBusy() + busy,
                    add_stat);
            addStat(cookie, "number_skipped", instance->getNumSkipped() + skipped,
                    add_stat);
            addStat(cookie, "number_restored", instance->getNumRestored() + restored,
                    add_stat);
            addStat(cookie, "number_expired",
                    instance->getNumExpired() + expired, add_stat);
            addStat(cookie, "number_wrong_vbucket",
                    instance->getNumWrongVBucket() + wrongVBucket, add_stat);
            addStat(cookie, "terminate", terminate, add_stat);
        }
    }

    virtual bool isRunning() {
        LockHolder lh(mutex);
        return (state == &State::Starting ||
                state == &State::Running);
    }

    virtual ~RestoreManagerImpl() {
        wait();
    }

    void *run(void) {
        ObjectRegistry::onSwitchThread(&engine);
        setState(State::Running);
        try {
            instance->process();
        } catch (std::string message) {
            LockHolder lh(mutex);
            errorMsg.assign(message);
        }
        setState(State::Zombie);
        return NULL;
    }

private:
    void collectResults() {
        skipped += instance->getNumSkipped();
        busy += instance->getNumBusy();
        restored += instance->getNumRestored();
    }

    void reap_UNLOCKED() throw (std::string) {
        if (instance != NULL) {
            void *rcode;
            int ret = pthread_join(thread, &rcode);
            if (ret != 0 && ret != ESRCH) {
                std::stringstream ss;
                ss << "Failed to join restore thread: " << strerror(ret);
                throw ss.str();
            }
            collectResults();
            delete instance;
            instance = NULL;
            setState_UNLOCKED(State::Uninitialized);
        }
    }

    void setState_UNLOCKED(const State &s) {
        state = &s;
    }

    void setState(const State &s) {
        LockHolder lh(mutex);
        setState_UNLOCKED(s);
    }

    // Access to the variables in here are all protected by a single mutex.
    // I know this doesn't scale much, but if you're having performance
    // problems you should stop calling stats all of the times ;-)
    Mutex mutex;
    DecrementalRestorer *instance;
    std::string errorMsg;
    uint32_t expired;
    uint32_t wrongVBucket;
    uint32_t restored;
    uint32_t skipped;
    uint32_t busy;

    // should we abort or not?
    Atomic<bool> terminate;

    const State *state;
    // The thread running the backup
    pthread_t thread;
};

RestoreManager* create_restore_manager(EventuallyPersistentEngine &engine)
{
    return new RestoreManagerImpl(engine);
}

void destroy_restore_manager(RestoreManager *manager)
{
    delete manager;
}

static void *restoreThreadMain(void *arg)
{
    RestoreManagerImpl *instance;
    instance = reinterpret_cast<RestoreManagerImpl*>(arg);
    return instance->run();
}

<commit_msg>Add restore_checkpoint in raw restore stats<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2011 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
#include "config.h"
#include "ep_engine.h"
#include "restore.hh"

#ifdef USE_SYSTEM_LIBSQLITE3
#include <sqlite3.h>
#else
#include "embedded/sqlite3.h"
#endif

static const char *query =
    "select cpoint_op.vbucket_id,op,key,flg,exp,cas,val,cpoint_op.cpoint_id "
    "from cpoint_state "
    "  join cpoint_op on (cpoint_op.vbucket_id = cpoint_state.vbucket_id and"
    "                     cpoint_op.cpoint_id = cpoint_state.cpoint_id) "
    "where cpoint_state.state = \"closed\" "
    "order by cpoint_op.cpoint_id desc";

static const int vbucket_id_idx = 0;
static const int op_idx = 1;
static const int key_idx = 2;
static const int flag_idx = 3;
static const int exp_idx = 4;
static const int cas_idx = 5;
static const int val_idx = 6;
static const int cpoint_idx = 7;

extern "C" {
    static void *restoreThreadMain(void *arg);
}

/**
 * A helper class to track the state of the restore manager
 */
class State {
public:
    State(const char *s) : name(s) {}
    const std::string &toString() const {
        return name;
    }

    static const State Uninitialized;
    static const State Initialized;
    static const State Starting;
    static const State Running;
    static const State Zombie;

private:
    const std::string name;
};

const State State::Uninitialized("uninitialized");
const State State::Initialized("initialized");
const State State::Starting("starting");
const State State::Running("running");
const State State::Zombie("zombie");

/**
 * Hehe.. Since we're applying all of the incremental backups in the
 * let's let the name reflect that ;-)
 *
 * The DecrementalRestorer is responsible for processing a single
 * incremental restore file and add all of it's content to epengine.
 *
 */
class DecrementalRestorer {
public:
    /**
     * Create a new instance of DecrementalRestorer and initialize
     * its member variable.
     * @param theEngine where to restore the data
     * @param dbname the name of the incremental database to restore
     */
    DecrementalRestorer(EventuallyPersistentEngine &theEngine,
                        const std::string &dbname) :
        db(NULL), statement(NULL), engine(theEngine),
        store(*engine.getEpStore()), file(dbname),
        expired(0), wrongVBucket(0), restored(0), skipped(0), busy(0), restore_cpoint(0)
    {
        // None needed
    }

    /**
     * Release all allocated resources. We might have the database in an
     * open state if an exception is thrown during restore.
     */
    ~DecrementalRestorer() {
        if (db != NULL) {
            (void)sqlite3_finalize(statement);
            (void)sqlite3_close(db);
        }
    }

    const std::string &getDbFile() const
    {
        return file;
    }

    uint32_t getNumBusy() const {
        return busy;
    }

    uint32_t getRestoreCheckpoint() const {
        return restore_cpoint;
    }

    uint32_t getNumRestored() const {
        return restored;
    }

    uint32_t getNumSkipped() const {
        return skipped;
    }

    uint32_t getNumWrongVBucket() const {
        return wrongVBucket;
    }

    uint32_t getNumExpired() const {
        return expired;
    }

    /**
     * Process this database file
     * @throw a string describing why an error occured
     */
    void process() throw (std::string) {
        if (sqlite3_open(file.c_str(), &db) !=  SQLITE_OK) {
            db = NULL;
            throw std::string("Failed to open database");
        }

        if (sqlite3_prepare_v2(db, query,
                               strlen(query),
                               &statement, NULL) != SQLITE_OK) {
            (void)sqlite3_finalize(statement);
            (void)sqlite3_close(db);
            db = NULL;
            throw std::string("Failed to prepare statement");
        }

        int rc;
        while ((rc = sqlite3_step(statement)) != SQLITE_DONE) {
            if (rc == SQLITE_ROW) {
                processEntry();
            } else if (rc == SQLITE_BUSY) {
                ++busy;
            } else {
                std::stringstream ss;
                ss << "sqlite error: " << sqlite3_errmsg(db);
                throw std::string(ss.str());
            }
        }

        (void)sqlite3_finalize(statement);
        (void)sqlite3_close(db);
        db = NULL;
    }

private:

    /**
     * callback to process the current item
     */
    void processEntry() throw(std::string) {
        uint32_t exptime = sqlite3_column_int(statement, exp_idx);
        if (exptime != 0 && exptime <  static_cast<int64_t>(ep_real_time())) {
            ++expired;
            return ;
        }
        std::string key((const char*)sqlite3_column_text(statement, key_idx),
                        sqlite3_column_bytes(statement, key_idx));

        enum queue_operation op = queue_op_set;
        if ((sqlite3_column_bytes(statement, op_idx) > 0) &&
            sqlite3_column_text(statement, op_idx)[0] == 'd') {
            op = queue_op_del;
        }

        value_t value(Blob::New((const char*)sqlite3_column_text(statement,
                                                                 val_idx),
                                sqlite3_column_bytes(statement, val_idx)));

        uint16_t vbid =  (uint16_t)sqlite3_column_int(statement,
                                                      vbucket_id_idx);
        if (!restore_cpoint) {
            restore_cpoint = (uint32_t)sqlite3_column_int(statement,
                                                            cpoint_idx);
        }

        int r = store.addUnlessThere(key, vbid, op, value,
                                     sqlite3_column_int(statement, flag_idx),
                                     sqlite3_column_int(statement, exp_idx),
                                     sqlite3_column_int(statement, cas_idx));
        if (r == 0) {
            ++restored;
        } else if (r == 1) {
            ++skipped;
        } else {
            ++wrongVBucket;
        }
    }

    sqlite3 *db;
    sqlite3_stmt *statement;
    EventuallyPersistentEngine &engine;
    EventuallyPersistentStore &store;
    const std::string file;
    uint32_t expired;
    uint32_t wrongVBucket;
    uint32_t restored;
    uint32_t skipped;
    uint32_t busy;
    uint32_t restore_cpoint;
};

class RestoreManagerImpl : public RestoreManager {
public:
    RestoreManagerImpl(EventuallyPersistentEngine &theEngine) :
        RestoreManager(theEngine),
        instance(NULL),
        expired(0),
        wrongVBucket(0),
        restored(0),
        skipped(0),
        busy(0),
        restore_cpoint(0),
        state(&State::Uninitialized)
    {
        // None needed
    }

    virtual void initialize(const std::string &config) throw(std::string)
    {
        if (access(config.c_str(), F_OK) != 0) {
            throw std::string("File not found");
        }

        LockHolder lh(mutex);
        errorMsg.resize(0);
        if (state != &State::Uninitialized) {
            // Only allow the zombie state, because then we can just reap it..
            if (state != &State::Zombie) {
                throw std::string("restorer isn't idle!");
            }
            // reap the zombie!
            reap_UNLOCKED();
        }

        assert(instance == NULL);
        instance = new DecrementalRestorer(engine, config);
        setState_UNLOCKED(State::Initialized);
    }

    virtual void start() throw (std::string)
    {
        LockHolder lh(mutex);
        if (instance == NULL) {
            lh.unlock();
            throw std::string("you need to call initialize before start");
        }

        if (state != &State::Initialized) {
            lh.unlock();
            throw std::string("Restore already running");
        }

        state = &State::Starting;
        int ret = pthread_create(&thread, NULL, restoreThreadMain, this);
        if (ret != 0) {
            state = &State::Uninitialized;
            collectResults();
            delete instance;
            instance = NULL;
            lh.unlock();
            std::stringstream ss;
            ss << "Failed to create restore thread: " << strerror(ret);
            throw ss.str();
        }
    }

    virtual void abort() throw (std::string)
    {
        LockHolder lh(mutex);
        terminate = true;
    }

    virtual void wait() throw (std::string)
    {
        LockHolder lh(mutex);
        if (state != &State::Initialized && state != &State::Uninitialized) {
            reap_UNLOCKED();
        }
    }

    virtual void stats(const void* cookie, ADD_STAT add_stat)
    {
        RestoreManager::stats(cookie, add_stat);
        addStat(cookie, "engine", "RestoreManagerImpl", add_stat);

        LockHolder lh(mutex);
        addStat(cookie, "state", state->toString(), add_stat);

        if (errorMsg.length() > 0) {
            addStat(cookie, "last_error", errorMsg, add_stat);
        }

        if (instance == NULL) {
            addStat(cookie, "restore_checkpoint", restore_cpoint, add_stat);
            addStat(cookie, "number_busy", busy, add_stat);
            addStat(cookie, "number_skipped", skipped, add_stat);
            addStat(cookie, "number_restored", restored, add_stat);
            addStat(cookie, "number_expired", expired, add_stat);
            addStat(cookie, "number_wrong_vbucket", wrongVBucket, add_stat);
        } else {
            addStat(cookie, "restore_checkpoint", restore_cpoint ? restore_cpoint :
                                        instance->getRestoreCheckpoint(), add_stat);
            addStat(cookie, "file", instance->getDbFile(), add_stat);
            addStat(cookie, "number_busy", instance->getNumBusy() + busy,
                    add_stat);
            addStat(cookie, "number_skipped", instance->getNumSkipped() + skipped,
                    add_stat);
            addStat(cookie, "number_restored", instance->getNumRestored() + restored,
                    add_stat);
            addStat(cookie, "number_expired",
                    instance->getNumExpired() + expired, add_stat);
            addStat(cookie, "number_wrong_vbucket",
                    instance->getNumWrongVBucket() + wrongVBucket, add_stat);
            addStat(cookie, "terminate", terminate, add_stat);
        }
    }

    virtual bool isRunning() {
        LockHolder lh(mutex);
        return (state == &State::Starting ||
                state == &State::Running);
    }

    virtual ~RestoreManagerImpl() {
        wait();
    }

    void *run(void) {
        ObjectRegistry::onSwitchThread(&engine);
        setState(State::Running);
        try {
            instance->process();
        } catch (std::string message) {
            LockHolder lh(mutex);
            errorMsg.assign(message);
        }
        setState(State::Zombie);
        return NULL;
    }

private:
    void collectResults() {
        skipped += instance->getNumSkipped();
        busy += instance->getNumBusy();
        restored += instance->getNumRestored();
        if (!restore_cpoint) {
            restore_cpoint = instance->getRestoreCheckpoint();
        }
    }

    void reap_UNLOCKED() throw (std::string) {
        if (instance != NULL) {
            void *rcode;
            int ret = pthread_join(thread, &rcode);
            if (ret != 0 && ret != ESRCH) {
                std::stringstream ss;
                ss << "Failed to join restore thread: " << strerror(ret);
                throw ss.str();
            }
            collectResults();
            delete instance;
            instance = NULL;
            setState_UNLOCKED(State::Uninitialized);
        }
    }

    void setState_UNLOCKED(const State &s) {
        state = &s;
    }

    void setState(const State &s) {
        LockHolder lh(mutex);
        setState_UNLOCKED(s);
    }

    // Access to the variables in here are all protected by a single mutex.
    // I know this doesn't scale much, but if you're having performance
    // problems you should stop calling stats all of the times ;-)
    Mutex mutex;
    DecrementalRestorer *instance;
    std::string errorMsg;
    uint32_t expired;
    uint32_t wrongVBucket;
    uint32_t restored;
    uint32_t skipped;
    uint32_t busy;
    uint32_t restore_cpoint;

    // should we abort or not?
    Atomic<bool> terminate;

    const State *state;
    // The thread running the backup
    pthread_t thread;
};

RestoreManager* create_restore_manager(EventuallyPersistentEngine &engine)
{
    return new RestoreManagerImpl(engine);
}

void destroy_restore_manager(RestoreManager *manager)
{
    delete manager;
}

static void *restoreThreadMain(void *arg)
{
    RestoreManagerImpl *instance;
    instance = reinterpret_cast<RestoreManagerImpl*>(arg);
    return instance->run();
}

<|endoftext|>


<commit_before>#include "xigua/function.hpp"

#include "xigua/evaluate.hpp"
#include "xigua/data.hpp"

namespace xig {

data method::call(std::vector<data> arguments, enviroment &enviroment,
                  std::vector<std::string> function_call_list) {

  if (should_evaluate_arguments()) {
    for (auto &item : arguments) {
      if (item.type() == data_type::process ||
          item.type() == data_type::symbol || item.type() == data_type::tuple ||
          item.type() == data_type::map)
        item = evaluate(enviroment, item, function_call_list);
    }
  }

  if (has_repeating_arguments()) {
    std::vector<data> new_args(arguments.begin(),
                               arguments.begin() + amount_of_arguments());
    data repeating_args(
        data_type::tuple,
        std::vector<data>(arguments.begin() + amount_of_arguments(),
                          arguments.end()));
    new_args.push_back(repeating_args);

    arguments = new_args;
  }

  return run(arguments, enviroment, function_call_list);
}

bool method::has_repeating_arguments() const { return false; }

bool method::should_evaluate_arguments() const { return true; }

bool method_set_comparator::operator()(const std::shared_ptr<method> &a,
                                       const std::shared_ptr<method> &b) {
  if (a->amount_of_arguments() < b->amount_of_arguments())
    return true;
  if (a->has_repeating_arguments() < b->has_repeating_arguments())
    return true;
  return false;
}

data function::call(std::vector<data> &args, enviroment &enviroment,
                    std::vector<std::string> function_call_list) {

  for (auto iterator = methods.rbegin(); iterator != methods.rend();
       iterator++) {
    if ((unsigned)(*iterator)->amount_of_arguments() == args.size())
      return (*iterator)->call(args, enviroment, function_call_list);
    else if ((unsigned)(*iterator)->amount_of_arguments() < args.size() &&
             (*iterator)->has_repeating_arguments())
      return (*iterator)->call(args, enviroment, function_call_list);
  }

    std::string error_message("Dosen't match a method overload in the specified function.\nAmount of Args Passed: ");
    error_message +=  std::to_string(args.size());
    error_message += "\nAmount of Args In Function: ";
    for (const auto &overload : methods) {
        error_message += std::to_string(overload->amount_of_arguments());
        if (overload->has_repeating_arguments())
            error_message += "r";
        if (overload->has_process_arguments())
            error_message += "p";
        error_message += " ";
    }
    throw error(error_type::invalid_arguments, error_message, function_call_list);

}
}
<commit_msg>Fixed function error message on master<commit_after>#include "xigua/function.hpp"

#include "xigua/evaluate.hpp"
#include "xigua/data.hpp"

namespace xig {

data method::call(std::vector<data> arguments, enviroment &enviroment,
                  std::vector<std::string> function_call_list) {

  if (should_evaluate_arguments()) {
    for (auto &item : arguments) {
      if (item.type() == data_type::process ||
          item.type() == data_type::symbol || item.type() == data_type::tuple ||
          item.type() == data_type::map)
        item = evaluate(enviroment, item, function_call_list);
    }
  }

  if (has_repeating_arguments()) {
    std::vector<data> new_args(arguments.begin(),
                               arguments.begin() + amount_of_arguments());
    data repeating_args(
        data_type::tuple,
        std::vector<data>(arguments.begin() + amount_of_arguments(),
                          arguments.end()));
    new_args.push_back(repeating_args);

    arguments = new_args;
  }

  return run(arguments, enviroment, function_call_list);
}

bool method::has_repeating_arguments() const { return false; }

bool method::should_evaluate_arguments() const { return true; }

bool method_set_comparator::operator()(const std::shared_ptr<method> &a,
                                       const std::shared_ptr<method> &b) {
  if (a->amount_of_arguments() < b->amount_of_arguments())
    return true;
  if (a->has_repeating_arguments() < b->has_repeating_arguments())
    return true;
  return false;
}

data function::call(std::vector<data> &args, enviroment &enviroment,
                    std::vector<std::string> function_call_list) {

  for (auto iterator = methods.rbegin(); iterator != methods.rend();
       iterator++) {
    if ((unsigned)(*iterator)->amount_of_arguments() == args.size())
      return (*iterator)->call(args, enviroment, function_call_list);
    else if ((unsigned)(*iterator)->amount_of_arguments() < args.size() &&
             (*iterator)->has_repeating_arguments())
      return (*iterator)->call(args, enviroment, function_call_list);
  }

    std::string error_message("Dosen't match a method overload in the specified function.\nAmount of Args Passed: ");
    error_message +=  std::to_string(args.size());
    error_message += "\nAmount of Args In Function: ";
    for (const auto &overload : methods) {
        error_message += std::to_string(overload->amount_of_arguments());
        if (overload->has_repeating_arguments())
            error_message += "r";
        error_message += " ";
    }
    throw error(error_type::invalid_arguments, error_message, function_call_list);

}
}
<|endoftext|>


<commit_before>// xml_file_loader.cc
// Implements file reader for an XML format
#include <algorithm>
#include <fstream>
#include <set>
#include <streambuf>

#include <boost/filesystem.hpp>

#include "blob.h"
#include "context.h"
#include "cyc_std.h"
#include "env.h"
#include "error.h"
#include "greedy_preconditioner.h"
#include "greedy_solver.h"
#include "logger.h"
#include "model.h"
#include "recorder.h"
#include "timer.h"
#include "xml_query_engine.h"

#include "xml_file_loader.h"

namespace cyclus {

namespace fs = boost::filesystem;

// - - - - - - - - - - - - - - - -   - - - - - - - - - - - - - - - - - -
void LoadStringstreamFromFile(std::stringstream& stream,
                              std::string file) {
  std::ifstream file_stream(file.c_str());
  if (!file_stream) {
    throw IOError("The file '" + file + "' could not be loaded.");
  }

  stream << file_stream.rdbuf();
  file_stream.close();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string BuildMasterSchema(std::string schema_path) {
  Timer ti;
  Recorder rec;
  Context ctx(&ti, &rec);

  std::stringstream schema("");
  LoadStringstreamFromFile(schema, schema_path);
  std::string master = schema.str();

  std::vector<std::string> names = Env::ListModules();
  std::map<std::string, std::string> subschemas;
  for (int i = 0; i < names.size(); ++i) {
    DynamicModule dyn(names[i]);
    Model* m = dyn.ConstructInstance(&ctx);
    subschemas[m->model_type()] += "<element name=\"" + names[i] + "\">\n";
    subschemas[m->model_type()] += m->schema() + "\n";
    subschemas[m->model_type()] += "</element>\n";
    ctx.DelModel(m);
    dyn.CloseLibrary();
  }

  // replace refs in master rng template file
  std::map<std::string, std::string>::iterator it;
  for (it = subschemas.begin(); it != subschemas.end(); ++it) {
    std::string search_str = std::string("@") + it->first + std::string("_REFS@");
    size_t pos = master.find(search_str);
    if (pos != std::string::npos) {
      master.replace(pos, search_str.size(), it->second);
    }
  }

  return master;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Composition::Ptr ReadRecipe(QueryEngine* qe) {
  bool atom_basis;
  std::string basis_str = qe->GetElementContent("basis");
  if (basis_str == "atom") {
    atom_basis = true;
  } else if (basis_str == "mass") {
    atom_basis = false;
  } else {
    throw IOError(basis_str + " basis is not 'mass' or 'atom'.");
  }

  double value;
  int key;
  std::string query = "nuclide";
  int nnucs = qe->NElementsMatchingQuery(query);
  CompMap v;
  for (int i = 0; i < nnucs; i++) {
    QueryEngine* nuclide = qe->QueryElement(query, i);
    key = strtol(nuclide->GetElementContent("id").c_str(), NULL, 10);
    value = strtod(nuclide->GetElementContent("comp").c_str(), NULL);
    v[key] = value;
    CLOG(LEV_DEBUG3) << "  Nuclide: " << key << " Value: " << v[key];
  }

  if (atom_basis) {
    return Composition::CreateFromAtom(v);
  } else {
    return Composition::CreateFromMass(v);
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
XMLFileLoader::XMLFileLoader(Context* ctx,
                             std::string schema_path,
                             const std::string load_filename) : ctx_(ctx) {
  schema_path_ = schema_path;
  file_ = load_filename;
  std::stringstream input;
  LoadStringstreamFromFile(input, file_);
  parser_ = boost::shared_ptr<XMLParser>(new XMLParser());
  parser_->Init(input);

  ctx_->NewDatum("InputFiles")
  ->AddVal("Data", Blob(input.str()))
  ->Record();

  schema_paths_["Region"] = "/*/region";
  schema_paths_["Inst"] = "/*/region/institution";
  schema_paths_["Facility"] = "/*/facility";
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
XMLFileLoader::~XMLFileLoader() {
  std::map<std::string, DynamicModule*>::iterator it;
  for (it = modules_.begin(); it != modules_.end(); it++) {
    it->second->CloseLibrary();
    delete it->second;
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::ApplySchema(const std::stringstream& schema) {
  parser_->Validate(schema);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string XMLFileLoader::master_schema() {
  return BuildMasterSchema(schema_path_);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadSim() {
  try {
    LoadDynamicModules();
  } catch (std::exception& e) {
    std::string msg = "Error loading dynamic modules: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
    std::stringstream ss(master_schema());
    parser_->Validate(ss);
  try {
    LoadSolver();
  } catch (std::exception& e) {
    std::string msg = "Error loading solver from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadControlParams();
  } catch (std::exception& e) {
    std::string msg = "Error loading control parameters from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadRecipes();
  } catch (std::exception& e) {
    std::string msg = "Error loading recipes from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadInitialAgents();
  } catch (std::exception& e) {
    std::string msg = "Error loading initial agents from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadSolver() {
  XMLQueryEngine xqe(*parser_);
  std::string query = "/*/commodity";

  std::map<std::string, double> commodity_order;
  std::string name;
  double order;
  int num_commods = xqe.NElementsMatchingQuery(query);
  for (int i = 0; i < num_commods; i++) {
    QueryEngine* qe = xqe.QueryElement(query, i);
    name = qe->GetElementContent("name");
    order = GetOptionalQuery<double>(qe, "solution_order", -1);
    commodity_order[name] = order;
  }

  ProcessCommodities(&commodity_order);

  // solver will delete conditioner
  GreedyPreconditioner* conditioner = new GreedyPreconditioner(
    commodity_order,
    GreedyPreconditioner::REVERSE);

  // context will delete solver
  bool exclusive_orders = false;
  GreedySolver* solver = new GreedySolver(exclusive_orders, conditioner);

  ctx_->solver(solver);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::ProcessCommodities(
  std::map<std::string, double>* commodity_order) {
  double max = std::max_element(
                 commodity_order->begin(),
                 commodity_order->end(),
                 SecondLT< std::pair<std::string, double> >())->second;
  if (max < 1) {
    max = 0;  // in case no orders are specified
  }

  std::map<std::string, double>::iterator it;
  for (it = commodity_order->begin();
       it != commodity_order->end();
       ++it) {
    if (it->second < 1) {
      it->second = max + 1;
    }
    CLOG(LEV_INFO1) << "Commodity ordering for " << it->first
                    << " is " << it->second;
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadRecipes() {
  XMLQueryEngine xqe(*parser_);

  std::string query = "/*/recipe";
  int num_recipes = xqe.NElementsMatchingQuery(query);
  for (int i = 0; i < num_recipes; i++) {
    QueryEngine* qe = xqe.QueryElement(query, i);
    std::string name = qe->GetElementContent("name");
    CLOG(LEV_DEBUG3) << "loading recipe: " << name;
    ctx_->AddRecipe(name, ReadRecipe(qe));
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadInitialAgents() {
  std::set<std::string> module_types;
  module_types.insert("Region");
  module_types.insert("Inst");
  module_types.insert("Facility");
  std::set<std::string>::iterator it;
  for (it = module_types.begin(); it != module_types.end(); it++) {
    XMLQueryEngine xqe(*parser_);
    int num_models = xqe.NElementsMatchingQuery(schema_paths_[*it]);
    for (int i = 0; i < num_models; i++) {
      QueryEngine* qe = xqe.QueryElement(schema_paths_[*it], i);
      QueryEngine* module_data = qe->QueryElement("model");
      std::string module_name = module_data->GetElementName();

      Model* model = modules_[module_name]->ConstructInstance(ctx_);
      model->SetModelImpl(module_name);
      model->InitFrom(qe);

      CLOG(LEV_DEBUG3) << "Module '" << model->name()
                       << "' has had its module members initialized:";
      CLOG(LEV_DEBUG3) << " * Type: " << model->model_type();
      CLOG(LEV_DEBUG3) << " * Implementation: " << model->ModelImpl() ;
      CLOG(LEV_DEBUG3) << " * ID: " << model->id();

      // register module
      ctx_->AddPrototype(model->name(), model);
      if (*it == "Region") {
        Model* clone = ctx_->CreateModel<Model>(model->name());
        clone->Build();
      }
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadDynamicModules() {
  std::string name;
  int nmods;
  std::map<std::string, std::string>::iterator m_it;
  XMLQueryEngine xqe(*parser_);
  for (m_it = schema_paths_.begin(); m_it != schema_paths_.end(); ++m_it) {
    nmods = xqe.NElementsMatchingQuery(m_it->second);
    for (int i = 0; i != nmods; i++) {
      QueryEngine* qe = xqe.QueryElement(m_it->second, i);
      name = qe->QueryElement("model")->GetElementName(0);
      if (modules_.find(name) == modules_.end()) {
        CLOG(LEV_DEBUG1) << "Loading module '" << name << "'.";
        DynamicModule* module = new DynamicModule(name);
        modules_[name] = module;
        CLOG(LEV_DEBUG1) << "Module '" << name << "' has been loaded.";
      }
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadControlParams() {
  XMLQueryEngine xqe(*parser_);
  std::string query = "/*/control";
  QueryEngine* qe = xqe.QueryElement(query);

  std::string handle;
  if (qe->NElementsMatchingQuery("simhandle") > 0) {
    handle = qe->GetElementContent("simhandle");
  }

  // get duration
  std::string dur_str = qe->GetElementContent("duration");
  int dur = strtol(dur_str.c_str(), NULL, 10);
  // get start month
  std::string m0_str = qe->GetElementContent("startmonth");
  int m0 = strtol(m0_str.c_str(), NULL, 10);
  // get start year
  std::string y0_str = qe->GetElementContent("startyear");
  int y0 = strtol(y0_str.c_str(), NULL, 10);
  // get decay interval
  std::string decay_str = qe->GetElementContent("decay");
  int dec = strtol(decay_str.c_str(), NULL, 10);

  ctx_->InitTime(dur, dec, m0, y0, handle);
}

} // namespace cyclus

<commit_msg>took qe out of for loop<commit_after>// xml_file_loader.cc
// Implements file reader for an XML format
#include <algorithm>
#include <fstream>
#include <set>
#include <streambuf>

#include <boost/filesystem.hpp>

#include "blob.h"
#include "context.h"
#include "cyc_std.h"
#include "env.h"
#include "error.h"
#include "greedy_preconditioner.h"
#include "greedy_solver.h"
#include "logger.h"
#include "model.h"
#include "recorder.h"
#include "timer.h"
#include "xml_query_engine.h"

#include "xml_file_loader.h"

namespace cyclus {

namespace fs = boost::filesystem;

// - - - - - - - - - - - - - - - -   - - - - - - - - - - - - - - - - - -
void LoadStringstreamFromFile(std::stringstream& stream,
                              std::string file) {
  std::ifstream file_stream(file.c_str());
  if (!file_stream) {
    throw IOError("The file '" + file + "' could not be loaded.");
  }

  stream << file_stream.rdbuf();
  file_stream.close();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string BuildMasterSchema(std::string schema_path) {
  Timer ti;
  Recorder rec;
  Context ctx(&ti, &rec);

  std::stringstream schema("");
  LoadStringstreamFromFile(schema, schema_path);
  std::string master = schema.str();

  std::vector<std::string> names = Env::ListModules();
  std::map<std::string, std::string> subschemas;
  for (int i = 0; i < names.size(); ++i) {
    DynamicModule dyn(names[i]);
    Model* m = dyn.ConstructInstance(&ctx);
    subschemas[m->model_type()] += "<element name=\"" + names[i] + "\">\n";
    subschemas[m->model_type()] += m->schema() + "\n";
    subschemas[m->model_type()] += "</element>\n";
    ctx.DelModel(m);
    dyn.CloseLibrary();
  }

  // replace refs in master rng template file
  std::map<std::string, std::string>::iterator it;
  for (it = subschemas.begin(); it != subschemas.end(); ++it) {
    std::string search_str = std::string("@") + it->first + std::string("_REFS@");
    size_t pos = master.find(search_str);
    if (pos != std::string::npos) {
      master.replace(pos, search_str.size(), it->second);
    }
  }

  return master;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Composition::Ptr ReadRecipe(QueryEngine* qe) {
  bool atom_basis;
  std::string basis_str = qe->GetElementContent("basis");
  if (basis_str == "atom") {
    atom_basis = true;
  } else if (basis_str == "mass") {
    atom_basis = false;
  } else {
    throw IOError(basis_str + " basis is not 'mass' or 'atom'.");
  }

  double value;
  int key;
  std::string query = "nuclide";
  int nnucs = qe->NElementsMatchingQuery(query);
  CompMap v;
  for (int i = 0; i < nnucs; i++) {
    QueryEngine* nuclide = qe->QueryElement(query, i);
    key = strtol(nuclide->GetElementContent("id").c_str(), NULL, 10);
    value = strtod(nuclide->GetElementContent("comp").c_str(), NULL);
    v[key] = value;
    CLOG(LEV_DEBUG3) << "  Nuclide: " << key << " Value: " << v[key];
  }

  if (atom_basis) {
    return Composition::CreateFromAtom(v);
  } else {
    return Composition::CreateFromMass(v);
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
XMLFileLoader::XMLFileLoader(Context* ctx,
                             std::string schema_path,
                             const std::string load_filename) : ctx_(ctx) {
  schema_path_ = schema_path;
  file_ = load_filename;
  std::stringstream input;
  LoadStringstreamFromFile(input, file_);
  parser_ = boost::shared_ptr<XMLParser>(new XMLParser());
  parser_->Init(input);

  ctx_->NewDatum("InputFiles")
  ->AddVal("Data", Blob(input.str()))
  ->Record();

  schema_paths_["Region"] = "/*/region";
  schema_paths_["Inst"] = "/*/region/institution";
  schema_paths_["Facility"] = "/*/facility";
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
XMLFileLoader::~XMLFileLoader() {
  std::map<std::string, DynamicModule*>::iterator it;
  for (it = modules_.begin(); it != modules_.end(); it++) {
    it->second->CloseLibrary();
    delete it->second;
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::ApplySchema(const std::stringstream& schema) {
  parser_->Validate(schema);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::string XMLFileLoader::master_schema() {
  return BuildMasterSchema(schema_path_);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadSim() {
  try {
    LoadDynamicModules();
  } catch (std::exception& e) {
    std::string msg = "Error loading dynamic modules: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
    std::stringstream ss(master_schema());
    parser_->Validate(ss);
  try {
    LoadSolver();
  } catch (std::exception& e) {
    std::string msg = "Error loading solver from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadControlParams();
  } catch (std::exception& e) {
    std::string msg = "Error loading control parameters from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadRecipes();
  } catch (std::exception& e) {
    std::string msg = "Error loading recipes from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
  try {
    LoadInitialAgents();
  } catch (std::exception& e) {
    std::string msg = "Error loading initial agents from xml file: ";
    msg += e.what();
    throw cyclus::Error(msg);
  }
};

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadSolver() {
  XMLQueryEngine xqe(*parser_);
  std::string query = "/*/commodity";

  std::map<std::string, double> commodity_order;
  std::string name;
  double order;
  int num_commods = xqe.NElementsMatchingQuery(query);
  for (int i = 0; i < num_commods; i++) {
    QueryEngine* qe = xqe.QueryElement(query, i);
    name = qe->GetElementContent("name");
    order = GetOptionalQuery<double>(qe, "solution_order", -1);
    commodity_order[name] = order;
  }

  ProcessCommodities(&commodity_order);

  // solver will delete conditioner
  GreedyPreconditioner* conditioner = new GreedyPreconditioner(
    commodity_order,
    GreedyPreconditioner::REVERSE);

  // context will delete solver
  bool exclusive_orders = false;
  GreedySolver* solver = new GreedySolver(exclusive_orders, conditioner);

  ctx_->solver(solver);
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::ProcessCommodities(
  std::map<std::string, double>* commodity_order) {
  double max = std::max_element(
                 commodity_order->begin(),
                 commodity_order->end(),
                 SecondLT< std::pair<std::string, double> >())->second;
  if (max < 1) {
    max = 0;  // in case no orders are specified
  }

  std::map<std::string, double>::iterator it;
  for (it = commodity_order->begin();
       it != commodity_order->end();
       ++it) {
    if (it->second < 1) {
      it->second = max + 1;
    }
    CLOG(LEV_INFO1) << "Commodity ordering for " << it->first
                    << " is " << it->second;
  }
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadRecipes() {
  XMLQueryEngine xqe(*parser_);

  std::string query = "/*/recipe";
  int num_recipes = xqe.NElementsMatchingQuery(query);
  for (int i = 0; i < num_recipes; i++) {
    QueryEngine* qe = xqe.QueryElement(query, i);
    std::string name = qe->GetElementContent("name");
    CLOG(LEV_DEBUG3) << "loading recipe: " << name;
    ctx_->AddRecipe(name, ReadRecipe(qe));
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadInitialAgents() {
  std::set<std::string> module_types;
  module_types.insert("Region");
  module_types.insert("Inst");
  module_types.insert("Facility");
  std::set<std::string>::iterator it;
  for (it = module_types.begin(); it != module_types.end(); it++) {
    XMLQueryEngine xqe(*parser_);
    int num_models = xqe.NElementsMatchingQuery(schema_paths_[*it]);
    for (int i = 0; i < num_models; i++) {
      QueryEngine* qe = xqe.QueryElement(schema_paths_[*it], i);
      QueryEngine* module_data = qe->QueryElement("model");
      std::string module_name = module_data->GetElementName();

      Model* model = modules_[module_name]->ConstructInstance(ctx_);
      model->SetModelImpl(module_name);
      model->InitFrom(qe);

      CLOG(LEV_DEBUG3) << "Module '" << model->name()
                       << "' has had its module members initialized:";
      CLOG(LEV_DEBUG3) << " * Type: " << model->model_type();
      CLOG(LEV_DEBUG3) << " * Implementation: " << model->ModelImpl() ;
      CLOG(LEV_DEBUG3) << " * ID: " << model->id();

      // register module
      ctx_->AddPrototype(model->name(), model);
      if (*it == "Region") {
        Model* clone = ctx_->CreateModel<Model>(model->name());
        clone->Build();
      }
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadDynamicModules() {
  std::string name;
  int nmods;
  QueryEngine* qe;
  std::map<std::string, std::string>::iterator m_it;
  XMLQueryEngine xqe(*parser_);
  for (m_it = schema_paths_.begin(); m_it != schema_paths_.end(); ++m_it) {
    nmods = xqe.NElementsMatchingQuery(m_it->second);
    for (int i = 0; i != nmods; i++) {
      qe = xqe.QueryElement(m_it->second, i);
      name = qe->QueryElement("model")->GetElementName(0);
      if (modules_.find(name) == modules_.end()) {
        CLOG(LEV_DEBUG1) << "Loading module '" << name << "'.";
        DynamicModule* module = new DynamicModule(name);
        modules_[name] = module;
        CLOG(LEV_DEBUG1) << "Module '" << name << "' has been loaded.";
      }
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void XMLFileLoader::LoadControlParams() {
  XMLQueryEngine xqe(*parser_);
  std::string query = "/*/control";
  QueryEngine* qe = xqe.QueryElement(query);

  std::string handle;
  if (qe->NElementsMatchingQuery("simhandle") > 0) {
    handle = qe->GetElementContent("simhandle");
  }

  // get duration
  std::string dur_str = qe->GetElementContent("duration");
  int dur = strtol(dur_str.c_str(), NULL, 10);
  // get start month
  std::string m0_str = qe->GetElementContent("startmonth");
  int m0 = strtol(m0_str.c_str(), NULL, 10);
  // get start year
  std::string y0_str = qe->GetElementContent("startyear");
  int y0 = strtol(y0_str.c_str(), NULL, 10);
  // get decay interval
  std::string decay_str = qe->GetElementContent("decay");
  int dec = strtol(decay_str.c_str(), NULL, 10);

  ctx_->InitTime(dur, dec, m0, y0, handle);
}

} // namespace cyclus

<|endoftext|>