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

#include "chrome/test/ui/ui_test.h"

#include "base/test/test_timeouts.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/prefs/pref_value_store.h"
#include "chrome/browser/sync/signin_manager.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/json_pref_store.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/testing_pref_service.h"

class NewTabUITest : public UITest {
 public:
  NewTabUITest() {
    dom_automation_enabled_ = true;
    // Set home page to the empty string so that we can set the home page using
    // preferences.
    set_homepage("");

    // Setup the DEFAULT_THEME profile (has fake history entries).
    set_template_user_data(UITest::ComputeTypicalUserDataSource(
        UITestBase::DEFAULT_THEME));
  }
};

#if defined(OS_WIN)
// Bug 87200: Disable NTPHasThumbnails for Windows
#define MAYBE_NTPHasThumbnails DISABLED_NTPHasThumbnails
#elif defined(OS_LINUX)
// This test is flaky on Linux and CrOS: http://crbug/
#define MAYBE_NTPHasThumbnails FLAKY_NTPHasThumbnails
#else
#define MAYBE_NTPHasThumbnails NTPHasThumbnails
#endif
TEST_F(NewTabUITest, MAYBE_NTPHasThumbnails) {
  // Switch to the "new tab" tab, which should be any new tab after the
  // first (the first is about:blank).
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));

  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // TopSites should return at least 3 non-filler pages.
  // 8 - 3 = max 5 filler pages.
  ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"",
      L"window.domAutomationController.send("
      L"document.getElementsByClassName('filler').length <= 5)",
      TestTimeouts::action_max_timeout_ms()));
}

// Sometimes hangs: http://crbug.com/70157
TEST_F(NewTabUITest, DISABLED_NTPHasLoginName) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->SetStringPreference(prefs::kGoogleServicesUsername,
                                          "user@gmail.com"));
  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));

  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  std::wstring displayed_username;
  // The login span should be eventually populated and have the
  // correct value.
  ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"",
      L"window.domAutomationController.send("
      L"document.getElementById('login-username').innerText.length > 0)",
      TestTimeouts::action_max_timeout_ms()));

  ASSERT_TRUE(tab->ExecuteAndExtractString(
      L"",
      L"window.domAutomationController.send("
      L"document.getElementById('login-username').innerText)",
      &displayed_username));

  EXPECT_EQ(L"user@gmail.com", displayed_username);
}

// Bug 87200: Disable ChromeHangInNTP for Windows
#if defined(OS_WIN)
#define MAYBE_ChromeHangInNTP DISABLED_ChromeHangInNTP
#else
#define MAYBE_ChromeHangInNTP ChromeHangInNTP
#endif
// Loads chrome://hang/ into two NTP tabs, ensuring we don't crash.
// See http://crbug.com/59859.
TEST_F(NewTabUITest, MAYBE_ChromeHangInNTP) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // Navigate to chrome://hang/ to stall the process.
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));

  // Visit chrome://hang/ again in another NTP. Don't bother waiting for the
  // NTP to load, because it's hung.
  ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab2 = window->GetActiveTab();
  ASSERT_TRUE(tab2.get());
  ASSERT_TRUE(tab2->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));
}

// Allows testing NTP in process-per-tab mode.
class NewTabUIProcessPerTabTest : public NewTabUITest {
 public:
  NewTabUIProcessPerTabTest() : NewTabUITest() {}

 protected:
  virtual void SetUp() {
    launch_arguments_.AppendSwitch(switches::kProcessPerTab);
    UITest::SetUp();
  }
};

// Bug 87200: Disable NavBeforeNTPCommits for Windows
#if defined(OS_WIN)
#define MAYBE_NavBeforeNTPCommits DISABLED_NavBeforeNTPCommits
#else
#define MAYBE_NavBeforeNTPCommits NavBeforeNTPCommits
#endif
// Navigates away from NTP before it commits, in process-per-tab mode.
// Ensures that we don't load the normal page in the NTP process (and thus
// crash), as in http://crbug.com/69224.
TEST_F(NewTabUIProcessPerTabTest, MAYBE_NavBeforeNTPCommits) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // Navigate to chrome://hang/ to stall the process.
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));

  // Visit a normal URL in another NTP that hasn't committed.
  ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab2 = window->GetActiveTab();
  ASSERT_TRUE(tab2.get());
  ASSERT_TRUE(tab2->NavigateToURL(GURL("data:text/html,hello world")));
}

// Fails about ~5% of the time on all platforms. http://crbug.com/45001
TEST_F(NewTabUITest, FLAKY_ChromeInternalLoadsNTP) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Go to the "new tab page" using its old url, rather than chrome://newtab.
  scoped_refptr<TabProxy> tab = window->GetTab(0);
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL("chrome-internal:")));
  int load_time;
  ASSERT_TRUE(automation()->WaitForInitialNewTabUILoad(&load_time));

  // Ensure there are some thumbnails loaded in the page.
  int thumbnails_count = -1;
  ASSERT_TRUE(tab->ExecuteAndExtractInt(L"",
      L"window.domAutomationController.send("
      L"document.getElementsByClassName('thumbnail-container').length)",
      &thumbnails_count));
  EXPECT_GT(thumbnails_count, 0);
}

// Bug 87200: Disable UpdateUserPrefsVersion for Windows
#if defined(OS_WIN)
#define MAYBE_UpdateUserPrefsVersion DISABLED_UpdateUserPrefsVersion
#else
#define MAYBE_UpdateUserPrefsVersion UpdateUserPrefsVersion
#endif
TEST_F(NewTabUITest, MAYBE_UpdateUserPrefsVersion) {
  // PrefService with JSON user-pref file only, no enforced or advised prefs.
  scoped_ptr<PrefService> prefs(new TestingPrefService);

  // Does the migration
  NewTabUI::RegisterUserPrefs(prefs.get());

  ASSERT_EQ(NewTabUI::current_pref_version(),
            prefs->GetInteger(prefs::kNTPPrefVersion));

  // Reset the version
  prefs->ClearPref(prefs::kNTPPrefVersion);
  ASSERT_EQ(0, prefs->GetInteger(prefs::kNTPPrefVersion));

  bool migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get());
  ASSERT_TRUE(migrated);
  ASSERT_EQ(NewTabUI::current_pref_version(),
            prefs->GetInteger(prefs::kNTPPrefVersion));

  migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get());
  ASSERT_FALSE(migrated);
}
<commit_msg>Marking NewTabUITest.ChromeHangInNTP as flaky for Chrome OS.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/ui/ui_test.h"

#include "base/test/test_timeouts.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/prefs/pref_value_store.h"
#include "chrome/browser/sync/signin_manager.h"
#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/json_pref_store.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/testing_pref_service.h"

class NewTabUITest : public UITest {
 public:
  NewTabUITest() {
    dom_automation_enabled_ = true;
    // Set home page to the empty string so that we can set the home page using
    // preferences.
    set_homepage("");

    // Setup the DEFAULT_THEME profile (has fake history entries).
    set_template_user_data(UITest::ComputeTypicalUserDataSource(
        UITestBase::DEFAULT_THEME));
  }
};

#if defined(OS_WIN)
// Bug 87200: Disable NTPHasThumbnails for Windows
#define MAYBE_NTPHasThumbnails DISABLED_NTPHasThumbnails
#elif defined(OS_LINUX)
// This test is flaky on Linux and CrOS: http://crbug/
#define MAYBE_NTPHasThumbnails FLAKY_NTPHasThumbnails
#else
#define MAYBE_NTPHasThumbnails NTPHasThumbnails
#endif
TEST_F(NewTabUITest, MAYBE_NTPHasThumbnails) {
  // Switch to the "new tab" tab, which should be any new tab after the
  // first (the first is about:blank).
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));

  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // TopSites should return at least 3 non-filler pages.
  // 8 - 3 = max 5 filler pages.
  ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"",
      L"window.domAutomationController.send("
      L"document.getElementsByClassName('filler').length <= 5)",
      TestTimeouts::action_max_timeout_ms()));
}

// Sometimes hangs: http://crbug.com/70157
TEST_F(NewTabUITest, DISABLED_NTPHasLoginName) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  ASSERT_TRUE(window->SetStringPreference(prefs::kGoogleServicesUsername,
                                          "user@gmail.com"));
  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));

  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  std::wstring displayed_username;
  // The login span should be eventually populated and have the
  // correct value.
  ASSERT_TRUE(WaitUntilJavaScriptCondition(tab, L"",
      L"window.domAutomationController.send("
      L"document.getElementById('login-username').innerText.length > 0)",
      TestTimeouts::action_max_timeout_ms()));

  ASSERT_TRUE(tab->ExecuteAndExtractString(
      L"",
      L"window.domAutomationController.send("
      L"document.getElementById('login-username').innerText)",
      &displayed_username));

  EXPECT_EQ(L"user@gmail.com", displayed_username);
}

// Bug 87200: Disable ChromeHangInNTP for Windows
#if defined(OS_WIN)
#define MAYBE_ChromeHangInNTP DISABLED_ChromeHangInNTP
#elif defined(OS_CHROMEOS)
#define MAYBE_ChromeHangInNTP FLAKY_ChromeHangInNTP
#else
#define MAYBE_ChromeHangInNTP ChromeHangInNTP
#endif
// Loads chrome://hang/ into two NTP tabs, ensuring we don't crash.
// See http://crbug.com/59859.
TEST_F(NewTabUITest, MAYBE_ChromeHangInNTP) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // Navigate to chrome://hang/ to stall the process.
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));

  // Visit chrome://hang/ again in another NTP. Don't bother waiting for the
  // NTP to load, because it's hung.
  ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab2 = window->GetActiveTab();
  ASSERT_TRUE(tab2.get());
  ASSERT_TRUE(tab2->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));
}

// Allows testing NTP in process-per-tab mode.
class NewTabUIProcessPerTabTest : public NewTabUITest {
 public:
  NewTabUIProcessPerTabTest() : NewTabUITest() {}

 protected:
  virtual void SetUp() {
    launch_arguments_.AppendSwitch(switches::kProcessPerTab);
    UITest::SetUp();
  }
};

// Bug 87200: Disable NavBeforeNTPCommits for Windows
#if defined(OS_WIN)
#define MAYBE_NavBeforeNTPCommits DISABLED_NavBeforeNTPCommits
#else
#define MAYBE_NavBeforeNTPCommits NavBeforeNTPCommits
#endif
// Navigates away from NTP before it commits, in process-per-tab mode.
// Ensures that we don't load the normal page in the NTP process (and thus
// crash), as in http://crbug.com/69224.
TEST_F(NewTabUIProcessPerTabTest, MAYBE_NavBeforeNTPCommits) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Bring up a new tab page.
  ASSERT_TRUE(window->RunCommand(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab = window->GetActiveTab();
  ASSERT_TRUE(tab.get());

  // Navigate to chrome://hang/ to stall the process.
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kChromeUIHangURL)));

  // Visit a normal URL in another NTP that hasn't committed.
  ASSERT_TRUE(window->RunCommandAsync(IDC_NEW_TAB));
  scoped_refptr<TabProxy> tab2 = window->GetActiveTab();
  ASSERT_TRUE(tab2.get());
  ASSERT_TRUE(tab2->NavigateToURL(GURL("data:text/html,hello world")));
}

// Fails about ~5% of the time on all platforms. http://crbug.com/45001
TEST_F(NewTabUITest, FLAKY_ChromeInternalLoadsNTP) {
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(window.get());

  // Go to the "new tab page" using its old url, rather than chrome://newtab.
  scoped_refptr<TabProxy> tab = window->GetTab(0);
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURLAsync(GURL("chrome-internal:")));
  int load_time;
  ASSERT_TRUE(automation()->WaitForInitialNewTabUILoad(&load_time));

  // Ensure there are some thumbnails loaded in the page.
  int thumbnails_count = -1;
  ASSERT_TRUE(tab->ExecuteAndExtractInt(L"",
      L"window.domAutomationController.send("
      L"document.getElementsByClassName('thumbnail-container').length)",
      &thumbnails_count));
  EXPECT_GT(thumbnails_count, 0);
}

// Bug 87200: Disable UpdateUserPrefsVersion for Windows
#if defined(OS_WIN)
#define MAYBE_UpdateUserPrefsVersion DISABLED_UpdateUserPrefsVersion
#else
#define MAYBE_UpdateUserPrefsVersion UpdateUserPrefsVersion
#endif
TEST_F(NewTabUITest, MAYBE_UpdateUserPrefsVersion) {
  // PrefService with JSON user-pref file only, no enforced or advised prefs.
  scoped_ptr<PrefService> prefs(new TestingPrefService);

  // Does the migration
  NewTabUI::RegisterUserPrefs(prefs.get());

  ASSERT_EQ(NewTabUI::current_pref_version(),
            prefs->GetInteger(prefs::kNTPPrefVersion));

  // Reset the version
  prefs->ClearPref(prefs::kNTPPrefVersion);
  ASSERT_EQ(0, prefs->GetInteger(prefs::kNTPPrefVersion));

  bool migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get());
  ASSERT_TRUE(migrated);
  ASSERT_EQ(NewTabUI::current_pref_version(),
            prefs->GetInteger(prefs::kNTPPrefVersion));

  migrated = NewTabUI::UpdateUserPrefsVersion(prefs.get());
  ASSERT_FALSE(migrated);
}
<|endoftext|>


<commit_before>#include "Network.hpp"
#include "Server.hpp"
#include "Proximity.hpp"
#include "CoordinateSegmentation.hpp"
#include "Message.hpp"
#include "ServerIDMap.hpp"
#include "ServerMessageQueue.hpp"
#include "Statistics.hpp"
#include "Options.hpp"
#include "ObjectMessageQueue.hpp"
#include "LoadMonitor.hpp"
#include "ForwarderUtilityClasses.hpp"
#include "Forwarder.hpp"

#include "ObjectSegmentation.hpp"

#include "ObjectConnection.hpp"

#include "Random.hpp"

#include <iostream>
#include <iomanip>

namespace CBR
{

Server::Server(ServerID id, Forwarder* forwarder, LocationService* loc_service, CoordinateSegmentation* cseg, Proximity* prox, ObjectMessageQueue* omq, ServerMessageQueue* smq, LoadMonitor* lm, Trace* trace,ObjectSegmentation* oseg)
    : mID(id),
      mLocationService(loc_service),
      mCSeg(cseg),
      mProximity(prox),
      mCurrentTime(Time::null()),
      mTrace(trace),
      mOSeg(oseg),
      mForwarder(forwarder)
  {
      mForwarder->registerMessageRecipient(MESSAGE_TYPE_MIGRATE, this);
      mForwarder->registerObjectMessageRecipient(OBJECT_PORT_SESSION, this);

    mForwarder->initialize(trace,cseg,oseg,loc_service,omq,smq,lm,&mCurrentTime, mProximity);    //run initialization for forwarder

  }

Server::~Server()
{
    mForwarder->unregisterObjectMessageRecipient(OBJECT_PORT_SESSION, this);
    mForwarder->unregisterMessageRecipient(MESSAGE_TYPE_MIGRATE, this);

    for(ObjectConnectionMap::iterator it = mObjects.begin(); it != mObjects.end(); it++) {
        UUID obj_id = it->first;

        // Stop any proximity queries for this object
        mProximity->removeQuery(obj_id);

        mLocationService->removeLocalObject(obj_id);

        // Stop Forwarder from delivering via this Object's
        // connection, destroy said connection
        ObjectConnection* migrated_conn = mForwarder->removeObjectConnection(obj_id);
        mClosingConnections.insert(migrated_conn);

        // FIXME there's probably quite a bit more cleanup to do here
    }
    mObjects.clear();
}

const ServerID& Server::id() const {
    return mID;
}

void Server::networkTick(const Time&t)
{
  mForwarder->tick(t);

  // Tick closing object connections, deleting them when they are
  ObjectConnectionSet persistingConnections;
  for(ObjectConnectionSet::iterator it = mClosingConnections.begin(); it != mClosingConnections.end(); it++) {
      ObjectConnection* conn = *it;
      conn->tick(t);
      if (conn->empty())
          delete conn;
      else
          persistingConnections.insert(conn);
  }
  mClosingConnections.swap(persistingConnections);
}

void Server::handleOpenConnection(ObjectConnection* conn) {
    mConnectingObjects[conn->id()] = conn;
}

bool Server::receiveObjectHostMessage(const std::string& msg) {
    // All messages from object host to space server are object messages
    // They are either to another object or to the space (UUID 0)
    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    bool parse_success = obj_msg->ParseFromArray((void*)&msg[0], msg.size());
    assert(parse_success);

    // The object could be migrating and we get outdated packets.  Currently this can
    // happen because we need to maintain the connection long enough to deliver the init migration
    // message.  Currently we just discard these messages, but we may need to account for this.
    // NOTE that we check connecting objects as well since we need to get past this point to deliver
    // Session::Connect messages.
    if (mObjects.find(obj_msg->source_object()) == mObjects.end() &&
        mConnectingObjects.find(obj_msg->source_object()) == mConnectingObjects.end())
    {
        //printf("Warning: Server got message from object after migration started.\n");
        return true;
    }

    return mForwarder->routeObjectHostMessage(obj_msg);
}

// Handle Session messages from an object
void Server::handleSessionMessage(ObjectConnection* conn, const std::string& session_payload) {
    CBR::Protocol::Session::Container session_msg;
    bool parse_success = session_msg.ParseFromArray((void*)&session_payload[0], session_payload.size());
    assert(parse_success);

    // Connect messages
    if (session_msg.has_connect())
        handleConnect(conn, session_msg.connect());

    // InitiateMigration messages
    assert(!session_msg.has_init_migration());

    // Migrate messages
    if (session_msg.has_migrate())
        handleMigrate(conn, session_msg.migrate());
}

// Handle Connect message from object
void Server::handleConnect(ObjectConnection* conn, const CBR::Protocol::Session::Connect& connect_msg) {
    UUID obj_id = conn->id();
    assert(obj_id == connect_msg.object());

    mObjects[obj_id] = conn;

    // If the connection is in the connecting list, remove it
    mConnectingObjects.erase(obj_id);

    // Add object as local object to LocationService
    TimedMotionVector3f loc( connect_msg.loc().t(), MotionVector3f(connect_msg.loc().position(), connect_msg.loc().velocity()) );
    mLocationService->addLocalObject(obj_id, loc, connect_msg.bounds());
    // Register proximity query
    mProximity->addQuery(obj_id, SolidAngle(connect_msg.query_angle()));
    // Allow the forwarder to send to ship messages to this connection
    mForwarder->addObjectConnection(obj_id, conn);
}

// Handle Migrate message from object
void Server::handleMigrate(ObjectConnection* conn, const CBR::Protocol::Session::Migrate& migrate_msg) {
    assert(conn->id() == migrate_msg.object());
    mObjectsAwaitingMigration[migrate_msg.object()] = conn;

    // If the connection is in the connecting list, remove it
    mConnectingObjects.erase(migrate_msg.object());

    // Try to handle this migration if all info is available
    handleMigration(migrate_msg.object());
}

void Server::receiveMessage(Message* msg) {
    MigrateMessage* migrate_msg = dynamic_cast<MigrateMessage*>(msg);
    if (migrate_msg != NULL) {
        const UUID obj_id = migrate_msg->contents.object();
        mObjectMigrations[obj_id] = new CBR::Protocol::Migration::MigrationMessage( migrate_msg->contents );
        // Try to handle this migration if all the info is available
        handleMigration(obj_id);
    }

    delete msg;
}

void Server::receiveMessage(const CBR::Protocol::Object::ObjectMessage& msg) {
    switch( msg.dest_port() ) {
      case OBJECT_PORT_SESSION:
          {
              ObjectConnection* conn = NULL;

              {
                  ObjectConnectionMap::iterator it = mObjects.find(msg.source_object());
                  if(it != mObjects.end())
                      conn = it->second;
              }

              {
                  ObjectConnectionMap::iterator it = mConnectingObjects.find(msg.source_object());
                  if (it != mObjects.end())
                      conn = it->second;
              }

              handleSessionMessage(conn, msg.payload());
          }
        break;
      default:
        printf("Received message for unknown port: %d\n", msg.dest_port());
        break;
    }
}

void Server::handleMigration(const UUID& obj_id) {
    // Try to find the info in both lists -- the connection and migration information
    ObjectConnectionMap::iterator obj_map_it = mObjectsAwaitingMigration.find(obj_id);
    if (obj_map_it == mObjectsAwaitingMigration.end())
        return;

    ObjectMigrationMap::iterator migration_map_it = mObjectMigrations.find(obj_id);
    if (migration_map_it == mObjectMigrations.end())
        return;

    // Get the data from the two maps
    ObjectConnection* obj_conn = obj_map_it->second;
    CBR::Protocol::Migration::MigrationMessage* migrate_msg = migration_map_it->second;

    // Extract the migration message data
    TimedMotionVector3f obj_loc(
        migrate_msg->loc().t(),
        MotionVector3f( migrate_msg->loc().position(), migrate_msg->loc().velocity() )
    );
    BoundingSphere3f obj_bounds( migrate_msg->bounds() );
    SolidAngle obj_query_angle( migrate_msg->query_angle() );
    std::vector<UUID> obj_subs;
    for(uint32 i = 0; i < migrate_msg->subscriber_size(); i++)
        obj_subs.push_back(migrate_msg->subscriber(i));

    obj_conn->handleMigrateMessage(obj_id, obj_query_angle, obj_subs);

    // Move from list waiting for migration message to active objects
    mObjects[obj_id] = obj_conn;

    // Update LOC to indicate we have this object locally
    mLocationService->addLocalObject(obj_id, obj_loc, obj_bounds);

    //update our oseg to show that we know that we have this object now.
    mOSeg->addObject(obj_id, mID);

    //We also send an oseg message to the server that the object was formerly hosted on.  This is an acknwoledge message that says, we're handling the object now...that's going to be the server with the origin tag affixed.
    ServerID idOSegAckTo = (ServerID)migrate_msg->source_server();
    Message* oseg_ack_msg;
    //              mOSeg->generateAcknowledgeMessage(obj_id, idOSegAckTo,oseg_ack_msg);
    oseg_ack_msg = mOSeg->generateAcknowledgeMessage(obj_id, idOSegAckTo);

    if (oseg_ack_msg != NULL)
        mForwarder->route(oseg_ack_msg, (dynamic_cast <OSegMigrateMessage*>(oseg_ack_msg))->getMessageDestination(),false);

    // Finally, subscribe the object for proximity queries
    mProximity->addQuery(obj_id, obj_query_angle);

    // Allow the forwarder to deliver to this connection
    mForwarder->addObjectConnection(obj_id, obj_conn);

    // Clean out the two records from the migration maps
    mObjectsAwaitingMigration.erase(obj_map_it);
    mObjectMigrations.erase(migration_map_it);
}

void Server::tick(const Time& t)
{
  mCurrentTime = t;

  // Update object locations
  mLocationService->tick(t);

  // Check proximity updates
  proximityTick(t);
  networkTick(t);
  // Check for object migrations
  checkObjectMigrations();
}

void Server::proximityTick(const Time& t)
{
    // If we have global introduction, then we can just ignore proximity evaluation.
    if (GetOption(OBJECT_GLOBAL)->as<bool>() == true)
        return;

  // Check for proximity updates
  std::queue<ProximityEventInfo> proximity_events;
  mProximity->evaluate(t, proximity_events);

  while(!proximity_events.empty())
  {
    ProximityEventInfo& evt = proximity_events.front();
    CBR::Protocol::Prox::ProximityResults prox_results;
    prox_results.set_t(t);
    if (evt.type() == ProximityEventInfo::Entered) {
        CBR::Protocol::Prox::IObjectAddition addition = prox_results.add_addition();
        addition.set_object( evt.object() );

        CBR::Protocol::Prox::ITimedMotionVector motion = addition.mutable_location();
        TimedMotionVector3f loc = mLocationService->location(evt.object());
        motion.set_t(loc.updateTime());
        motion.set_position(loc.position());
        motion.set_velocity(loc.velocity());

        addition.set_bounds( mLocationService->bounds(evt.object()) );
    }
    else {
        CBR::Protocol::Prox::IObjectRemoval removal = prox_results.add_removal();
        removal.set_object( evt.object() );
    }

    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    obj_msg->set_source_object(UUID::null());
    obj_msg->set_source_port(OBJECT_PORT_PROXIMITY);
    obj_msg->set_dest_object(evt.query());
    obj_msg->set_dest_port(OBJECT_PORT_PROXIMITY);
    obj_msg->set_unique(GenerateUniqueID(id()));
    obj_msg->set_payload( serializePBJMessage(prox_results) );

    mForwarder->route(obj_msg);

    proximity_events.pop();
  }
}

void Server::checkObjectMigrations()
{
    // * check for objects crossing server boundaries
    // * wrap up state and send message to other server
    //     to reinstantiate the object there
    // * delete object on this side

    std::vector<UUID> migrated_objects;
    for(ObjectConnectionMap::iterator it = mObjects.begin(); it != mObjects.end(); it++)
    {
        const UUID& obj_id = it->first;
        ObjectConnection* obj_conn = it->second;

        Vector3f obj_pos = mLocationService->currentPosition(obj_id);
	ServerID new_server_id = lookup(obj_pos);

        if (new_server_id != mID)
        {
            // FIXME While we're working on the transition to a separate object host
            // we do 2 things when we detect a server boundary crossing:
            // 1. Generate a message which is sent to the object host telling it to
            //    transition to the new server
            // 2. Generate the migrate message which contains the current state of
            //    the object which will be reconstituted on the other space server.

            // Part 1
            CBR::Protocol::Session::Container session_msg;
            CBR::Protocol::Session::IInitiateMigration init_migration_msg = session_msg.mutable_init_migration();
            init_migration_msg.set_new_server( (uint64)new_server_id );
            CBR::Protocol::Object::ObjectMessage* init_migr_obj_msg = new CBR::Protocol::Object::ObjectMessage();
            init_migr_obj_msg->set_source_object(UUID::null());
            init_migr_obj_msg->set_source_port(OBJECT_PORT_SESSION);
            init_migr_obj_msg->set_dest_object(obj_id);
            init_migr_obj_msg->set_dest_port(OBJECT_PORT_SESSION);
            init_migr_obj_msg->set_unique(GenerateUniqueID(id()));
            init_migr_obj_msg->set_payload( serializePBJMessage(session_msg) );
            obj_conn->deliver(*init_migr_obj_msg); // Note that we can't go through the forwarder since it will stop delivering to this object connection right after this
            delete init_migr_obj_msg;
            //printf("Routed init migration msg to %s\n", obj_id.toString().c_str());

            // Part 2

          //bftm
          //          mOSeg->migrateObject(obj_id,new_server_id);
          //

            // Send out the migrate message
            MigrateMessage* migrate_msg = new MigrateMessage(id());
            migrate_msg->contents.set_source_server(this->id());
            obj_conn->fillMigrateMessage(migrate_msg);
/*
	    printf("migrating object %s due to position %s \n",
		   obj_id.readableHexData().c_str(),
		   obj_pos.toString().c_str());
*/
            mForwarder->route( migrate_msg , new_server_id);

            // Stop any proximity queries for this object
            mProximity->removeQuery(obj_id);

            // Stop tracking the object locally
            mLocationService->removeLocalObject(obj_id);

            // Stop Forwarder from delivering via this Object's
            // connection, destroy said connection
            ObjectConnection* migrated_conn = mForwarder->removeObjectConnection(obj_id);
            mClosingConnections.insert(migrated_conn);

	    migrated_objects.push_back(obj_id);
        }
    }

    for (std::vector<UUID>::iterator it = migrated_objects.begin(); it != migrated_objects.end(); it++){
        ObjectConnectionMap::iterator obj_map_it = mObjects.find(*it);
        mObjects.erase(obj_map_it);
    }
}

ServerID Server::lookup(const Vector3f& pos)
{
  ServerID sid = mCSeg->lookup(pos);
  return sid;
}

ServerID Server::lookup(const UUID& obj_id)
{
  Vector3f pos = mLocationService->currentPosition(obj_id);
  ServerID sid = mCSeg->lookup(pos);
  return sid;
}

} // namespace CBR
<commit_msg>Actually servicing object connections so they can function would be a good thing.<commit_after>#include "Network.hpp"
#include "Server.hpp"
#include "Proximity.hpp"
#include "CoordinateSegmentation.hpp"
#include "Message.hpp"
#include "ServerIDMap.hpp"
#include "ServerMessageQueue.hpp"
#include "Statistics.hpp"
#include "Options.hpp"
#include "ObjectMessageQueue.hpp"
#include "LoadMonitor.hpp"
#include "ForwarderUtilityClasses.hpp"
#include "Forwarder.hpp"

#include "ObjectSegmentation.hpp"

#include "ObjectConnection.hpp"

#include "Random.hpp"

#include <iostream>
#include <iomanip>

namespace CBR
{

Server::Server(ServerID id, Forwarder* forwarder, LocationService* loc_service, CoordinateSegmentation* cseg, Proximity* prox, ObjectMessageQueue* omq, ServerMessageQueue* smq, LoadMonitor* lm, Trace* trace,ObjectSegmentation* oseg)
    : mID(id),
      mLocationService(loc_service),
      mCSeg(cseg),
      mProximity(prox),
      mCurrentTime(Time::null()),
      mTrace(trace),
      mOSeg(oseg),
      mForwarder(forwarder)
  {
      mForwarder->registerMessageRecipient(MESSAGE_TYPE_MIGRATE, this);
      mForwarder->registerObjectMessageRecipient(OBJECT_PORT_SESSION, this);

    mForwarder->initialize(trace,cseg,oseg,loc_service,omq,smq,lm,&mCurrentTime, mProximity);    //run initialization for forwarder

  }

Server::~Server()
{
    mForwarder->unregisterObjectMessageRecipient(OBJECT_PORT_SESSION, this);
    mForwarder->unregisterMessageRecipient(MESSAGE_TYPE_MIGRATE, this);

    for(ObjectConnectionMap::iterator it = mObjects.begin(); it != mObjects.end(); it++) {
        UUID obj_id = it->first;

        // Stop any proximity queries for this object
        mProximity->removeQuery(obj_id);

        mLocationService->removeLocalObject(obj_id);

        // Stop Forwarder from delivering via this Object's
        // connection, destroy said connection
        ObjectConnection* migrated_conn = mForwarder->removeObjectConnection(obj_id);
        mClosingConnections.insert(migrated_conn);

        // FIXME there's probably quite a bit more cleanup to do here
    }
    mObjects.clear();
}

const ServerID& Server::id() const {
    return mID;
}

void Server::networkTick(const Time&t)
{
  mForwarder->tick(t);

  // Tick all active connections
  for(ObjectConnectionMap::iterator it = mObjects.begin(); it != mObjects.end(); it++) {
      ObjectConnection* conn = it->second;
      conn->tick(t);
  }

  // Tick closing object connections, deleting them when they are
  ObjectConnectionSet persistingConnections;
  for(ObjectConnectionSet::iterator it = mClosingConnections.begin(); it != mClosingConnections.end(); it++) {
      ObjectConnection* conn = *it;
      conn->tick(t);
      if (conn->empty())
          delete conn;
      else
          persistingConnections.insert(conn);
  }
  mClosingConnections.swap(persistingConnections);
}

void Server::handleOpenConnection(ObjectConnection* conn) {
    mConnectingObjects[conn->id()] = conn;
}

bool Server::receiveObjectHostMessage(const std::string& msg) {
    // All messages from object host to space server are object messages
    // They are either to another object or to the space (UUID 0)
    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    bool parse_success = obj_msg->ParseFromArray((void*)&msg[0], msg.size());
    assert(parse_success);

    // The object could be migrating and we get outdated packets.  Currently this can
    // happen because we need to maintain the connection long enough to deliver the init migration
    // message.  Currently we just discard these messages, but we may need to account for this.
    // NOTE that we check connecting objects as well since we need to get past this point to deliver
    // Session::Connect messages.
    if (mObjects.find(obj_msg->source_object()) == mObjects.end() &&
        mConnectingObjects.find(obj_msg->source_object()) == mConnectingObjects.end())
    {
        //printf("Warning: Server got message from object after migration started.\n");
        return true;
    }

    return mForwarder->routeObjectHostMessage(obj_msg);
}

// Handle Session messages from an object
void Server::handleSessionMessage(ObjectConnection* conn, const std::string& session_payload) {
    CBR::Protocol::Session::Container session_msg;
    bool parse_success = session_msg.ParseFromArray((void*)&session_payload[0], session_payload.size());
    assert(parse_success);

    // Connect messages
    if (session_msg.has_connect())
        handleConnect(conn, session_msg.connect());

    // InitiateMigration messages
    assert(!session_msg.has_init_migration());

    // Migrate messages
    if (session_msg.has_migrate())
        handleMigrate(conn, session_msg.migrate());
}

// Handle Connect message from object
void Server::handleConnect(ObjectConnection* conn, const CBR::Protocol::Session::Connect& connect_msg) {
    UUID obj_id = conn->id();
    assert(obj_id == connect_msg.object());

    mObjects[obj_id] = conn;

    // If the connection is in the connecting list, remove it
    mConnectingObjects.erase(obj_id);

    // Add object as local object to LocationService
    TimedMotionVector3f loc( connect_msg.loc().t(), MotionVector3f(connect_msg.loc().position(), connect_msg.loc().velocity()) );
    mLocationService->addLocalObject(obj_id, loc, connect_msg.bounds());
    //update our oseg to show that we know that we have this object now.
    mOSeg->addObject(obj_id, mID);
    // Register proximity query
    mProximity->addQuery(obj_id, SolidAngle(connect_msg.query_angle()));
    // Allow the forwarder to send to ship messages to this connection
    mForwarder->addObjectConnection(obj_id, conn);
}

// Handle Migrate message from object
void Server::handleMigrate(ObjectConnection* conn, const CBR::Protocol::Session::Migrate& migrate_msg) {
    assert(conn->id() == migrate_msg.object());
    mObjectsAwaitingMigration[migrate_msg.object()] = conn;

    // If the connection is in the connecting list, remove it
    mConnectingObjects.erase(migrate_msg.object());

    // Try to handle this migration if all info is available
    handleMigration(migrate_msg.object());
}

void Server::receiveMessage(Message* msg) {
    MigrateMessage* migrate_msg = dynamic_cast<MigrateMessage*>(msg);
    if (migrate_msg != NULL) {
        const UUID obj_id = migrate_msg->contents.object();
        mObjectMigrations[obj_id] = new CBR::Protocol::Migration::MigrationMessage( migrate_msg->contents );
        // Try to handle this migration if all the info is available
        handleMigration(obj_id);
    }

    delete msg;
}

void Server::receiveMessage(const CBR::Protocol::Object::ObjectMessage& msg) {
    switch( msg.dest_port() ) {
      case OBJECT_PORT_SESSION:
          {
              ObjectConnection* conn = NULL;

              {
                  ObjectConnectionMap::iterator it = mObjects.find(msg.source_object());
                  if(it != mObjects.end())
                      conn = it->second;
              }

              {
                  ObjectConnectionMap::iterator it = mConnectingObjects.find(msg.source_object());
                  if (it != mObjects.end())
                      conn = it->second;
              }

              handleSessionMessage(conn, msg.payload());
          }
        break;
      default:
        printf("Received message for unknown port: %d\n", msg.dest_port());
        break;
    }
}

void Server::handleMigration(const UUID& obj_id) {
    // Try to find the info in both lists -- the connection and migration information
    ObjectConnectionMap::iterator obj_map_it = mObjectsAwaitingMigration.find(obj_id);
    if (obj_map_it == mObjectsAwaitingMigration.end())
        return;

    ObjectMigrationMap::iterator migration_map_it = mObjectMigrations.find(obj_id);
    if (migration_map_it == mObjectMigrations.end())
        return;

    // Get the data from the two maps
    ObjectConnection* obj_conn = obj_map_it->second;
    CBR::Protocol::Migration::MigrationMessage* migrate_msg = migration_map_it->second;

    // Extract the migration message data
    TimedMotionVector3f obj_loc(
        migrate_msg->loc().t(),
        MotionVector3f( migrate_msg->loc().position(), migrate_msg->loc().velocity() )
    );
    BoundingSphere3f obj_bounds( migrate_msg->bounds() );
    SolidAngle obj_query_angle( migrate_msg->query_angle() );
    std::vector<UUID> obj_subs;
    for(uint32 i = 0; i < migrate_msg->subscriber_size(); i++)
        obj_subs.push_back(migrate_msg->subscriber(i));

    obj_conn->handleMigrateMessage(obj_id, obj_query_angle, obj_subs);

    // Move from list waiting for migration message to active objects
    mObjects[obj_id] = obj_conn;

    // Update LOC to indicate we have this object locally
    mLocationService->addLocalObject(obj_id, obj_loc, obj_bounds);

    //update our oseg to show that we know that we have this object now.
    mOSeg->addObject(obj_id, mID);

    //We also send an oseg message to the server that the object was formerly hosted on.  This is an acknwoledge message that says, we're handling the object now...that's going to be the server with the origin tag affixed.
    ServerID idOSegAckTo = (ServerID)migrate_msg->source_server();
    Message* oseg_ack_msg;
    //              mOSeg->generateAcknowledgeMessage(obj_id, idOSegAckTo,oseg_ack_msg);
    oseg_ack_msg = mOSeg->generateAcknowledgeMessage(obj_id, idOSegAckTo);

    if (oseg_ack_msg != NULL)
        mForwarder->route(oseg_ack_msg, (dynamic_cast <OSegMigrateMessage*>(oseg_ack_msg))->getMessageDestination(),false);

    // Finally, subscribe the object for proximity queries
    mProximity->addQuery(obj_id, obj_query_angle);

    // Allow the forwarder to deliver to this connection
    mForwarder->addObjectConnection(obj_id, obj_conn);

    // Clean out the two records from the migration maps
    mObjectsAwaitingMigration.erase(obj_map_it);
    mObjectMigrations.erase(migration_map_it);
}

void Server::tick(const Time& t)
{
  mCurrentTime = t;

  // Update object locations
  mLocationService->tick(t);

  // Check proximity updates
  proximityTick(t);
  networkTick(t);
  // Check for object migrations
  checkObjectMigrations();
}

void Server::proximityTick(const Time& t)
{
    // If we have global introduction, then we can just ignore proximity evaluation.
    if (GetOption(OBJECT_GLOBAL)->as<bool>() == true)
        return;

  // Check for proximity updates
  std::queue<ProximityEventInfo> proximity_events;
  mProximity->evaluate(t, proximity_events);

  while(!proximity_events.empty())
  {
    ProximityEventInfo& evt = proximity_events.front();
    CBR::Protocol::Prox::ProximityResults prox_results;
    prox_results.set_t(t);
    if (evt.type() == ProximityEventInfo::Entered) {
        CBR::Protocol::Prox::IObjectAddition addition = prox_results.add_addition();
        addition.set_object( evt.object() );

        CBR::Protocol::Prox::ITimedMotionVector motion = addition.mutable_location();
        TimedMotionVector3f loc = mLocationService->location(evt.object());
        motion.set_t(loc.updateTime());
        motion.set_position(loc.position());
        motion.set_velocity(loc.velocity());

        addition.set_bounds( mLocationService->bounds(evt.object()) );
    }
    else {
        CBR::Protocol::Prox::IObjectRemoval removal = prox_results.add_removal();
        removal.set_object( evt.object() );
    }

    CBR::Protocol::Object::ObjectMessage* obj_msg = new CBR::Protocol::Object::ObjectMessage();
    obj_msg->set_source_object(UUID::null());
    obj_msg->set_source_port(OBJECT_PORT_PROXIMITY);
    obj_msg->set_dest_object(evt.query());
    obj_msg->set_dest_port(OBJECT_PORT_PROXIMITY);
    obj_msg->set_unique(GenerateUniqueID(id()));
    obj_msg->set_payload( serializePBJMessage(prox_results) );

    mForwarder->route(obj_msg);

    proximity_events.pop();
  }
}

void Server::checkObjectMigrations()
{
    // * check for objects crossing server boundaries
    // * wrap up state and send message to other server
    //     to reinstantiate the object there
    // * delete object on this side

    std::vector<UUID> migrated_objects;
    for(ObjectConnectionMap::iterator it = mObjects.begin(); it != mObjects.end(); it++)
    {
        const UUID& obj_id = it->first;
        ObjectConnection* obj_conn = it->second;

        Vector3f obj_pos = mLocationService->currentPosition(obj_id);
	ServerID new_server_id = lookup(obj_pos);

        if (new_server_id != mID)
        {
            // FIXME While we're working on the transition to a separate object host
            // we do 2 things when we detect a server boundary crossing:
            // 1. Generate a message which is sent to the object host telling it to
            //    transition to the new server
            // 2. Generate the migrate message which contains the current state of
            //    the object which will be reconstituted on the other space server.

            // Part 1
            CBR::Protocol::Session::Container session_msg;
            CBR::Protocol::Session::IInitiateMigration init_migration_msg = session_msg.mutable_init_migration();
            init_migration_msg.set_new_server( (uint64)new_server_id );
            CBR::Protocol::Object::ObjectMessage* init_migr_obj_msg = new CBR::Protocol::Object::ObjectMessage();
            init_migr_obj_msg->set_source_object(UUID::null());
            init_migr_obj_msg->set_source_port(OBJECT_PORT_SESSION);
            init_migr_obj_msg->set_dest_object(obj_id);
            init_migr_obj_msg->set_dest_port(OBJECT_PORT_SESSION);
            init_migr_obj_msg->set_unique(GenerateUniqueID(id()));
            init_migr_obj_msg->set_payload( serializePBJMessage(session_msg) );
            obj_conn->deliver(*init_migr_obj_msg); // Note that we can't go through the forwarder since it will stop delivering to this object connection right after this
            delete init_migr_obj_msg;
            //printf("Routed init migration msg to %s\n", obj_id.toString().c_str());

            // Part 2

          //bftm
          //          mOSeg->migrateObject(obj_id,new_server_id);
          //

            // Send out the migrate message
            MigrateMessage* migrate_msg = new MigrateMessage(id());
            migrate_msg->contents.set_source_server(this->id());
            obj_conn->fillMigrateMessage(migrate_msg);
/*
	    printf("migrating object %s due to position %s \n",
		   obj_id.readableHexData().c_str(),
		   obj_pos.toString().c_str());
*/
            mForwarder->route( migrate_msg , new_server_id);

            // Stop any proximity queries for this object
            mProximity->removeQuery(obj_id);

            // Stop tracking the object locally
            mLocationService->removeLocalObject(obj_id);

            // Stop Forwarder from delivering via this Object's
            // connection, destroy said connection
            ObjectConnection* migrated_conn = mForwarder->removeObjectConnection(obj_id);
            mClosingConnections.insert(migrated_conn);

	    migrated_objects.push_back(obj_id);
        }
    }

    for (std::vector<UUID>::iterator it = migrated_objects.begin(); it != migrated_objects.end(); it++){
        ObjectConnectionMap::iterator obj_map_it = mObjects.find(*it);
        mObjects.erase(obj_map_it);
    }
}

ServerID Server::lookup(const Vector3f& pos)
{
  ServerID sid = mCSeg->lookup(pos);
  return sid;
}

ServerID Server::lookup(const UUID& obj_id)
{
  Vector3f pos = mLocationService->currentPosition(obj_id);
  ServerID sid = mCSeg->lookup(pos);
  return sid;
}

} // namespace CBR
<|endoftext|>


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

// A test that exercises Chrome Frame's DLL Redirctor update code. This test
// generates a new version of CF from the one already in the build folder and
// then loads them both into the current process to verify the handoff.

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/path_service.h"
#include "base/scoped_native_library.h"
#include "base/scoped_temp_dir.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
#include "base/version.h"
#include "base/win/scoped_comptr.h"
#include "chrome/installer/test/alternate_version_generator.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
#include "chrome_frame/chrome_tab.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
const wchar_t kSharedMemoryPrefix[] = L"ChromeFrameVersionBeacon_";
const uint32 kSharedMemoryBytes = 128;
}

class DllRedirectorLoadingTest : public testing::Test {
 public:
  // This sets up the following directory structure:
  //  TEMP\<version X>\npchrome_frame.dll
  //      \<version X+1>\npchrome_frame.dll
  //
  // This structure emulates enough of the directory structure of a Chrome
  // install to test upgrades.
  static void SetUpTestCase() {
    // First ensure that we can find the built Chrome Frame DLL.
    FilePath build_chrome_frame_dll = GetChromeFrameBuildPath();
    ASSERT_TRUE(file_util::PathExists(build_chrome_frame_dll));

    // Then grab its version.
    scoped_ptr<FileVersionInfo> original_version_info(
        FileVersionInfo::CreateFileVersionInfo(build_chrome_frame_dll));
    ASSERT_TRUE(original_version_info != NULL);
    original_version_.reset(Version::GetVersionFromString(
        WideToASCII(original_version_info->file_version())));
    ASSERT_TRUE(original_version_ != NULL);

    // Make a place for us to run the test from.
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    // Make a versioned dir for the original chrome frame dll to run under.
    FilePath original_version_dir(
        temp_dir_.path().AppendASCII(original_version_->GetString()));
    ASSERT_TRUE(file_util::CreateDirectory(original_version_dir));

    // Now move the original DLL that we will operate on into a named-version
    // folder.
    original_chrome_frame_dll_ =
        original_version_dir.Append(build_chrome_frame_dll.BaseName());
    ASSERT_TRUE(file_util::CopyFile(build_chrome_frame_dll,
                                    original_chrome_frame_dll_));
    ASSERT_TRUE(file_util::PathExists(original_chrome_frame_dll_));

    // Temporary location for the new Chrome Frame DLL.
    FilePath temporary_new_chrome_frame_dll(
        temp_dir_.path().Append(build_chrome_frame_dll.BaseName()));

    // Generate the version-bumped Chrome Frame DLL to a temporary path.
    ASSERT_TRUE(
        upgrade_test::GenerateAlternatePEFileVersion(
            original_chrome_frame_dll_,
            temporary_new_chrome_frame_dll,
            upgrade_test::NEXT_VERSION));

    // Ensure it got created and grab its version.
    scoped_ptr<FileVersionInfo> new_version_info(
        FileVersionInfo::CreateFileVersionInfo(temporary_new_chrome_frame_dll));
    ASSERT_TRUE(new_version_info != NULL);
    new_version_.reset(Version::GetVersionFromString(
        WideToASCII(new_version_info->file_version())));
    ASSERT_TRUE(new_version_ != NULL);

    // Make sure the new version is larger than the old.
    ASSERT_EQ(new_version_->CompareTo(*original_version_.get()), 1);

    // Now move the new Chrome Frame dll to its final resting place:
    FilePath new_version_dir(
        temp_dir_.path().AppendASCII(new_version_->GetString()));
    ASSERT_TRUE(file_util::CreateDirectory(new_version_dir));
    new_chrome_frame_dll_ =
        new_version_dir.Append(build_chrome_frame_dll.BaseName());
    ASSERT_TRUE(file_util::Move(temporary_new_chrome_frame_dll,
                                new_chrome_frame_dll_));
    ASSERT_TRUE(file_util::PathExists(new_chrome_frame_dll_));
  }

  static void TearDownTestCase() {
    if (!temp_dir_.Delete()) {
      // The temp_dir cleanup has been observed to fail in some cases. It looks
      // like something is holding on to the Chrome Frame DLLs after they have
      // been explicitly unloaded. At least schedule them for cleanup on reboot.
      ScheduleDirectoryForDeletion(temp_dir_.path().value().c_str());
    }
  }

 protected:
  static FilePath original_chrome_frame_dll_;
  static FilePath new_chrome_frame_dll_;
  static scoped_ptr<Version> original_version_;
  static scoped_ptr<Version> new_version_;

  static ScopedTempDir temp_dir_;
};  // class DllRedirectorLoadingTest

FilePath DllRedirectorLoadingTest::original_chrome_frame_dll_;
FilePath DllRedirectorLoadingTest::new_chrome_frame_dll_;
scoped_ptr<Version> DllRedirectorLoadingTest::original_version_;
scoped_ptr<Version> DllRedirectorLoadingTest::new_version_;
ScopedTempDir DllRedirectorLoadingTest::temp_dir_;

TEST_F(DllRedirectorLoadingTest, TestDllRedirection) {
  struct TestData {
    FilePath first_dll;
    FilePath second_dll;
    Version* expected_beacon_version;
  } test_data[] = {
      {
          original_chrome_frame_dll_,
          new_chrome_frame_dll_,
          original_version_.get()
      },
      {
          new_chrome_frame_dll_,
          original_chrome_frame_dll_,
          new_version_.get()
      }
  };

  for (int i = 0; i < arraysize(test_data); ++i) {
    // First load the original dll into our test process.
    base::ScopedNativeLibrary original_library(test_data[i].first_dll);

    ASSERT_TRUE(original_library.is_valid());

    // Now query the original dll for its DllGetClassObject method and use that
    // to get the class factory for a known CLSID.
    LPFNGETCLASSOBJECT original_dgco_ptr =
        reinterpret_cast<LPFNGETCLASSOBJECT>(
            original_library.GetFunctionPointer("DllGetClassObject"));

    ASSERT_TRUE(original_dgco_ptr != NULL);

    base::win::ScopedComPtr<IClassFactory> original_class_factory;
    HRESULT hr = original_dgco_ptr(
        CLSID_ChromeFrame,
        IID_IClassFactory,
        reinterpret_cast<void**>(original_class_factory.Receive()));

    ASSERT_HRESULT_SUCCEEDED(hr);

    // Now load the new dll into our test process.
    base::ScopedNativeLibrary new_library(test_data[i].second_dll);

    ASSERT_TRUE(new_library.is_valid());

    // Now query the new dll for its DllGetClassObject method and use that
    // to get the class factory for a known CLSID.
    LPFNGETCLASSOBJECT new_dgco_ptr =
        reinterpret_cast<LPFNGETCLASSOBJECT>(
            new_library.GetFunctionPointer("DllGetClassObject"));

    ASSERT_TRUE(new_dgco_ptr != NULL);

    base::win::ScopedComPtr<IClassFactory> new_class_factory;
    hr = new_dgco_ptr(CLSID_ChromeFrame,
                      IID_IClassFactory,
                      reinterpret_cast<void**>(new_class_factory.Receive()));

    ASSERT_HRESULT_SUCCEEDED(hr);

    base::win::ScopedComPtr<IUnknown> qi_test;
    EXPECT_HRESULT_SUCCEEDED(original_class_factory.QueryInterface(IID_IUnknown,
          reinterpret_cast<void**>(qi_test.Receive())));

    EXPECT_TRUE(new_class_factory.IsSameObject(qi_test));

    // Check the version beacon.
    std::wstring beacon_name(kSharedMemoryPrefix);
    beacon_name += GetHostProcessName(false /* without extension */);
    base::SharedMemory beacon(beacon_name);

    EXPECT_TRUE(beacon.Open(WideToASCII(beacon_name), true /* read_only */));
    EXPECT_TRUE(beacon.Map(0));
    EXPECT_TRUE(beacon.memory());

    char buffer[kSharedMemoryBytes] = {0};
    memcpy(buffer, beacon.memory(), kSharedMemoryBytes - 1);
    scoped_ptr<Version> beacon_version(Version::GetVersionFromString(buffer));
    ASSERT_TRUE(beacon_version != NULL);

    EXPECT_EQ(0,
              beacon_version->CompareTo(*test_data[i].expected_beacon_version));
  }
}
<commit_msg>Disable DllRedirectorLoadingTest.TestDllRedirection for component builds.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// A test that exercises Chrome Frame's DLL Redirctor update code. This test
// generates a new version of CF from the one already in the build folder and
// then loads them both into the current process to verify the handoff.

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/path_service.h"
#include "base/scoped_native_library.h"
#include "base/scoped_temp_dir.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
#include "base/version.h"
#include "base/win/scoped_comptr.h"
#include "chrome/installer/test/alternate_version_generator.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
#include "chrome_frame/chrome_tab.h"
#include "chrome_frame/test_utils.h"
#include "chrome_frame/utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
const wchar_t kSharedMemoryPrefix[] = L"ChromeFrameVersionBeacon_";
const uint32 kSharedMemoryBytes = 128;
}

class DllRedirectorLoadingTest : public testing::Test {
 public:
  // This sets up the following directory structure:
  //  TEMP\<version X>\npchrome_frame.dll
  //      \<version X+1>\npchrome_frame.dll
  //
  // This structure emulates enough of the directory structure of a Chrome
  // install to test upgrades.
  static void SetUpTestCase() {
    // First ensure that we can find the built Chrome Frame DLL.
    FilePath build_chrome_frame_dll = GetChromeFrameBuildPath();
    ASSERT_TRUE(file_util::PathExists(build_chrome_frame_dll));

    // Then grab its version.
    scoped_ptr<FileVersionInfo> original_version_info(
        FileVersionInfo::CreateFileVersionInfo(build_chrome_frame_dll));
    ASSERT_TRUE(original_version_info != NULL);
    original_version_.reset(Version::GetVersionFromString(
        WideToASCII(original_version_info->file_version())));
    ASSERT_TRUE(original_version_ != NULL);

    // Make a place for us to run the test from.
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());

    // Make a versioned dir for the original chrome frame dll to run under.
    FilePath original_version_dir(
        temp_dir_.path().AppendASCII(original_version_->GetString()));
    ASSERT_TRUE(file_util::CreateDirectory(original_version_dir));

    // Now move the original DLL that we will operate on into a named-version
    // folder.
    original_chrome_frame_dll_ =
        original_version_dir.Append(build_chrome_frame_dll.BaseName());
    ASSERT_TRUE(file_util::CopyFile(build_chrome_frame_dll,
                                    original_chrome_frame_dll_));
    ASSERT_TRUE(file_util::PathExists(original_chrome_frame_dll_));

    // Temporary location for the new Chrome Frame DLL.
    FilePath temporary_new_chrome_frame_dll(
        temp_dir_.path().Append(build_chrome_frame_dll.BaseName()));

    // Generate the version-bumped Chrome Frame DLL to a temporary path.
    ASSERT_TRUE(
        upgrade_test::GenerateAlternatePEFileVersion(
            original_chrome_frame_dll_,
            temporary_new_chrome_frame_dll,
            upgrade_test::NEXT_VERSION));

    // Ensure it got created and grab its version.
    scoped_ptr<FileVersionInfo> new_version_info(
        FileVersionInfo::CreateFileVersionInfo(temporary_new_chrome_frame_dll));
    ASSERT_TRUE(new_version_info != NULL);
    new_version_.reset(Version::GetVersionFromString(
        WideToASCII(new_version_info->file_version())));
    ASSERT_TRUE(new_version_ != NULL);

    // Make sure the new version is larger than the old.
    ASSERT_EQ(new_version_->CompareTo(*original_version_.get()), 1);

    // Now move the new Chrome Frame dll to its final resting place:
    FilePath new_version_dir(
        temp_dir_.path().AppendASCII(new_version_->GetString()));
    ASSERT_TRUE(file_util::CreateDirectory(new_version_dir));
    new_chrome_frame_dll_ =
        new_version_dir.Append(build_chrome_frame_dll.BaseName());
    ASSERT_TRUE(file_util::Move(temporary_new_chrome_frame_dll,
                                new_chrome_frame_dll_));
    ASSERT_TRUE(file_util::PathExists(new_chrome_frame_dll_));
  }

  static void TearDownTestCase() {
    if (!temp_dir_.Delete()) {
      // The temp_dir cleanup has been observed to fail in some cases. It looks
      // like something is holding on to the Chrome Frame DLLs after they have
      // been explicitly unloaded. At least schedule them for cleanup on reboot.
      ScheduleDirectoryForDeletion(temp_dir_.path().value().c_str());
    }
  }

 protected:
  static FilePath original_chrome_frame_dll_;
  static FilePath new_chrome_frame_dll_;
  static scoped_ptr<Version> original_version_;
  static scoped_ptr<Version> new_version_;

  static ScopedTempDir temp_dir_;
};  // class DllRedirectorLoadingTest

FilePath DllRedirectorLoadingTest::original_chrome_frame_dll_;
FilePath DllRedirectorLoadingTest::new_chrome_frame_dll_;
scoped_ptr<Version> DllRedirectorLoadingTest::original_version_;
scoped_ptr<Version> DllRedirectorLoadingTest::new_version_;
ScopedTempDir DllRedirectorLoadingTest::temp_dir_;

#if defined(COMPONENT_BUILD)
// Disabling since npchrome_frame.dll's DllMain can't handle being loaded into
// a process that has already started services in base.dll such as logging; see
// http://crbug.com/110492.
#define MAYBE_TestDllRedirection DISABLED_TestDllRedirection
#else
#define MAYBE_TestDllRedirection TestDllRedirection
#endif

TEST_F(DllRedirectorLoadingTest, MAYBE_TestDllRedirection) {
  struct TestData {
    FilePath first_dll;
    FilePath second_dll;
    Version* expected_beacon_version;
  } test_data[] = {
      {
          original_chrome_frame_dll_,
          new_chrome_frame_dll_,
          original_version_.get()
      },
      {
          new_chrome_frame_dll_,
          original_chrome_frame_dll_,
          new_version_.get()
      }
  };

  for (int i = 0; i < arraysize(test_data); ++i) {
    // First load the original dll into our test process.
    base::ScopedNativeLibrary original_library(test_data[i].first_dll);

    ASSERT_TRUE(original_library.is_valid());

    // Now query the original dll for its DllGetClassObject method and use that
    // to get the class factory for a known CLSID.
    LPFNGETCLASSOBJECT original_dgco_ptr =
        reinterpret_cast<LPFNGETCLASSOBJECT>(
            original_library.GetFunctionPointer("DllGetClassObject"));

    ASSERT_TRUE(original_dgco_ptr != NULL);

    base::win::ScopedComPtr<IClassFactory> original_class_factory;
    HRESULT hr = original_dgco_ptr(
        CLSID_ChromeFrame,
        IID_IClassFactory,
        reinterpret_cast<void**>(original_class_factory.Receive()));

    ASSERT_HRESULT_SUCCEEDED(hr);

    // Now load the new dll into our test process.
    base::ScopedNativeLibrary new_library(test_data[i].second_dll);

    ASSERT_TRUE(new_library.is_valid());

    // Now query the new dll for its DllGetClassObject method and use that
    // to get the class factory for a known CLSID.
    LPFNGETCLASSOBJECT new_dgco_ptr =
        reinterpret_cast<LPFNGETCLASSOBJECT>(
            new_library.GetFunctionPointer("DllGetClassObject"));

    ASSERT_TRUE(new_dgco_ptr != NULL);

    base::win::ScopedComPtr<IClassFactory> new_class_factory;
    hr = new_dgco_ptr(CLSID_ChromeFrame,
                      IID_IClassFactory,
                      reinterpret_cast<void**>(new_class_factory.Receive()));

    ASSERT_HRESULT_SUCCEEDED(hr);

    base::win::ScopedComPtr<IUnknown> qi_test;
    EXPECT_HRESULT_SUCCEEDED(original_class_factory.QueryInterface(IID_IUnknown,
          reinterpret_cast<void**>(qi_test.Receive())));

    EXPECT_TRUE(new_class_factory.IsSameObject(qi_test));

    // Check the version beacon.
    std::wstring beacon_name(kSharedMemoryPrefix);
    beacon_name += GetHostProcessName(false /* without extension */);
    base::SharedMemory beacon(beacon_name);

    EXPECT_TRUE(beacon.Open(WideToASCII(beacon_name), true /* read_only */));
    EXPECT_TRUE(beacon.Map(0));
    EXPECT_TRUE(beacon.memory());

    char buffer[kSharedMemoryBytes] = {0};
    memcpy(buffer, beacon.memory(), kSharedMemoryBytes - 1);
    scoped_ptr<Version> beacon_version(Version::GetVersionFromString(buffer));
    ASSERT_TRUE(beacon_version != NULL);

    EXPECT_EQ(0,
              beacon_version->CompareTo(*test_data[i].expected_beacon_version));
  }
}
<|endoftext|>


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

// ---
// Author: Hrishikesh Amur

#include <assert.h>
#include <pthread.h>
#include <stdint.h>
#include <deque>
#include "Slaves.h"

namespace cbt {
    Slave::Slave(CompressTree* tree) :
            tree_(tree),
            inputComplete_(false),
            queueEmpty_(true),
            askForCompletionNotice_(false) {
        pthread_mutex_init(&queueMutex_, NULL);
        pthread_cond_init(&queueHasWork_, NULL);

        pthread_mutex_init(&completionMutex_, NULL);
        pthread_cond_init(&complete_, NULL);
    }

    bool Slave::empty() const {
        return queueEmpty_;
    }

    bool Slave::wakeup() {
        pthread_mutex_lock(&queueMutex_);
        pthread_cond_signal(&queueHasWork_);
        pthread_mutex_unlock(&queueMutex_);
        return true;
    }

    void Slave::sendCompletionNotice() {
        pthread_mutex_lock(&completionMutex_);
        if (askForCompletionNotice_) {
            pthread_cond_signal(&complete_);
            askForCompletionNotice_ = false;
        }
        pthread_mutex_unlock(&completionMutex_);
    }

    void Slave::waitUntilCompletionNoticeReceived() {
        pthread_mutex_lock(&queueMutex_);
        if (!empty()) {
            pthread_mutex_lock(&completionMutex_);
            askForCompletionNotice_ = true;
            pthread_mutex_unlock(&queueMutex_);

            pthread_cond_wait(&complete_, &completionMutex_);
            pthread_mutex_unlock(&completionMutex_);
        } else {
            pthread_mutex_unlock(&queueMutex_);
        }
    }

    void* Slave::callHelper(void* context) {
        return static_cast<Slave*>(context)->work();
    }

    void Slave::printElements() const {
        for (uint32_t i = 0; i < nodes_.size(); ++i) {
            fprintf(stderr, "%d, ", nodes_[i]->id());
        }
        fprintf(stderr, "\n");
    }

    void Slave::startThreads() {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_create(&thread_, &attr, callHelper,
                reinterpret_cast<void*>(this));
    }

    void Slave::stopThreads() {
        void* status;
        setInputComplete(true);
        wakeup();
        pthread_join(thread_, &status);
    }

    Emptier::Emptier(CompressTree* tree) :
            Slave(tree) {
    }

    Emptier::~Emptier() {
    }

    void* Emptier::work() {
        bool rootFlag = false;
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (queue_.empty() && !inputComplete_) {
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "emptier sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "emptier fingered\n");
#endif
            while (!queue_.empty()) {
                Node* n = queue_.pop();
                pthread_mutex_unlock(&queueMutex_);
                pthread_mutex_lock(&n->queuedForEmptyMutex_);
                n->queuedForEmptying_ = false;
                pthread_mutex_unlock(&n->queuedForEmptyMutex_);
                if (n->isRoot())
                    rootFlag = true;
#ifdef CT_NODE_DEBUG
                fprintf(stderr, "emptier: emptying node: %d (size: %u)\t",
                        n->id_, n->buffer_.numElements());
                fprintf(stderr, "remaining: ");
                queue_.printElements();
#endif
                if (n->isRoot()) {
                    n->aggregateSortedBuffer();
                } else {
                    n->aggregateMergedBuffer();
                }
                n->emptyBuffer();
                if (n->isLeaf())
                    tree_->handleFullLeaves();
                if (rootFlag) {
                    // do the split and create new root
                    rootFlag = false;

                    pthread_mutex_lock(&tree_->rootNodeAvailableMutex_);
                    pthread_cond_signal(&tree_->rootNodeAvailableForWriting_);
                    pthread_mutex_unlock(&tree_->rootNodeAvailableMutex_);
                }
                pthread_mutex_lock(&queueMutex_);
            }
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Emptier quitting: %ld\n", queue_.size());
#endif
        pthread_exit(NULL);
    }

    void Emptier::addNode(Node* node) {
        pthread_mutex_lock(&queueMutex_);
        if (node) {
            queue_.insert(node, node->level());
            queueEmpty_ = false;

            std::deque<Node*> depNodes;
            uint32_t prio = node->level();
            depNodes.push_back(node);
            while (!depNodes.empty()) {
                Node* t = depNodes.front();
                for (uint32_t i = 0; i < t->children_.size(); i++) {
                    if (t->children_[i]->queuedForEmptying_) {
                        queue_.insert(t->children_[i], ++prio);
                        depNodes.push_back(t->children_[i]);
                    }
                }
                depNodes.pop_front();
            }
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Node %d (size: %u) added to to-empty list: ",
                node->id_, node->buffer_.numElements());
        queue_.printElements();
#endif
        }
        pthread_mutex_unlock(&queueMutex_);
    }

    Compressor::Compressor(CompressTree* tree) :
            Slave(tree) {
    }

    Compressor::~Compressor() {
    }

    void* Compressor::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "compressor sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "compressor fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                n->performCompressAction();
                pthread_mutex_lock(&queueMutex_);
            }
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Compressor quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Compressor::addNode(Node* node) {
        Buffer::CompressionAction act = node->getCompressAction();
        if (act == Buffer::COMPRESS) {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_back(node);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "adding node %d to compress: ", node->id_);
            printElements();
#endif
#ifdef ENABLE_PAGING
            /* Put in a request for paging out. This is necessary to do
             * right away because of the following case: if a page-in request
             * arrives when the node is on the compression queue waiting to
             * be compressed, the page-in request could simply get discarded
             * since there is no page-out request (yet). This leads to a case
             * where a decompression later assumes that the page-in has
             * completed */
            node->scheduleBufferPageAction(Buffer::PAGE_OUT);
#endif
        } else {
#ifdef ENABLE_PAGING
            node->scheduleBufferPageAction(Buffer::PAGE_IN);
#endif
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_front(node);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "adding node %d (size: %u) to decompress: ",
                    node->id_, node->buffer_.numElements());
            printElements();
#endif
        }
        queueEmpty_ = false;
        pthread_mutex_unlock(&queueMutex_);
    }

    Sorter::Sorter(CompressTree* tree) :
            Slave(tree) {
    }

    Sorter::~Sorter() {
    }

    void* Sorter::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "sorter sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "sorter fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                n->waitForCompressAction(Buffer::DECOMPRESS);
#ifdef CT_NODE_DEBUG
                fprintf(stderr, "sorter: sorting node: %d (size: %u)\t",
                        n->id_, n->buffer_.numElements());
                fprintf(stderr, "remaining: ");
                for (int i = 0; i < nodes_.size(); ++i)
                    fprintf(stderr, "%d, ", nodes_[i]->id_);
                fprintf(stderr, "\n");
#endif
                if (n->isRoot()) {
                    n->sortBuffer();
                } else {
                    n->mergeBuffer();
                }
                tree_->emptier_->addNode(n);
                tree_->emptier_->wakeup();
                pthread_mutex_lock(&queueMutex_);
            }
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Sorter quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Sorter::addNode(Node* node) {
        pthread_mutex_lock(&queueMutex_);
        if (node) {
            // Set node as queued for emptying
            pthread_mutex_lock(&node->queuedForEmptyMutex_);
            node->queuedForEmptying_ = true;
            pthread_mutex_unlock(&node->queuedForEmptyMutex_);
            nodes_.push_back(node);
            queueEmpty_ = false;
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "Node %d added to to-sort list (size: %u)\n",
                    node->id_, node->buffer_.numElements());
#endif
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Node %d added to to-sort list (size: %u)\n",
                node->id_, node->buffer_.numElements());
        for (int i = 0; i < nodes_.size(); ++i)
            fprintf(stderr, "%d, ", nodes_[i]->id_);
        fprintf(stderr, "\n");
#endif
    }

#ifdef ENABLE_PAGING
    Pager::Pager(CompressTree* tree) :
            Slave(tree) {
    }

    Pager::~Pager() {
    }

    void* Pager::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "pager sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "pager fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                bool suc = n->performPageAction();
                pthread_mutex_lock(&queueMutex_);
                if (!suc)
                    nodes_.push_back(n);
            }
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Pager quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Pager::addNode(Node* node) {
        Buffer::PageAction act = node->getPageAction();
        if (act == Buffer::PAGE_OUT) {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_back(node);
        } else {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_front(node);
        }
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Node %d added to page list: ", node->id_);
        printElements();
#endif
        queueEmpty_ = false;
        pthread_mutex_unlock(&queueMutex_);
    }
#endif  // ENABLE_PAGING

#ifdef ENABLE_COUNTERS
    Monitor::Monitor(CompressTree* tree) :
            Slave(tree),
            numElements(0),
            numMerged(0),
            actr(0),
            bctr(0),
            cctr(0) {
    }

    Monitor::~Monitor() {
    }

    void* Monitor::work() {
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (!inputComplete_) {
            sleep(1);
            elctr.push_back(numElements);
        }
        uint64_t tot = 0;
        for (uint32_t i = 0; i < elctr.size(); ++i) {
            tot += elctr[i];
        }
        fprintf(stderr, "Avg. number of elements: %f\n",
                static_cast<float>(tot) / elctr.size());
        fprintf(stderr, "A: %lu, B:%lu\n", numElements, numMerged);
        elctr.clear();
    }

    void Monitor::addNode(Node* n) {
        return;
    }
#endif
}
<commit_msg>Fix cppcheck complaint<commit_after>// Copyright (C) 2012 Georgia Institute of Technology
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// ---
// Author: Hrishikesh Amur

#include <assert.h>
#include <pthread.h>
#include <stdint.h>
#include <deque>
#include "Slaves.h"

namespace cbt {
    Slave::Slave(CompressTree* tree) :
            tree_(tree),
            inputComplete_(false),
            queueEmpty_(true),
            askForCompletionNotice_(false) {
        pthread_mutex_init(&queueMutex_, NULL);
        pthread_cond_init(&queueHasWork_, NULL);

        pthread_mutex_init(&completionMutex_, NULL);
        pthread_cond_init(&complete_, NULL);
    }

    bool Slave::empty() const {
        return queueEmpty_;
    }

    bool Slave::wakeup() {
        pthread_mutex_lock(&queueMutex_);
        pthread_cond_signal(&queueHasWork_);
        pthread_mutex_unlock(&queueMutex_);
        return true;
    }

    void Slave::sendCompletionNotice() {
        pthread_mutex_lock(&completionMutex_);
        if (askForCompletionNotice_) {
            pthread_cond_signal(&complete_);
            askForCompletionNotice_ = false;
        }
        pthread_mutex_unlock(&completionMutex_);
    }

    void Slave::waitUntilCompletionNoticeReceived() {
        pthread_mutex_lock(&queueMutex_);
        if (!empty()) {
            pthread_mutex_lock(&completionMutex_);
            askForCompletionNotice_ = true;
            pthread_mutex_unlock(&queueMutex_);

            pthread_cond_wait(&complete_, &completionMutex_);
            pthread_mutex_unlock(&completionMutex_);
        } else {
            pthread_mutex_unlock(&queueMutex_);
        }
    }

    void* Slave::callHelper(void* context) {
        return static_cast<Slave*>(context)->work();
    }

    void Slave::printElements() const {
        for (uint32_t i = 0; i < nodes_.size(); ++i) {
            fprintf(stderr, "%d, ", nodes_[i]->id());
        }
        fprintf(stderr, "\n");
    }

    void Slave::startThreads() {
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_create(&thread_, &attr, callHelper,
                reinterpret_cast<void*>(this));
    }

    void Slave::stopThreads() {
        void* status;
        setInputComplete(true);
        wakeup();
        pthread_join(thread_, &status);
    }

    Emptier::Emptier(CompressTree* tree) :
            Slave(tree) {
    }

    Emptier::~Emptier() {
    }

    void* Emptier::work() {
        bool rootFlag = false;
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (queue_.empty() && !inputComplete_) {
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "emptier sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "emptier fingered\n");
#endif
            while (!queue_.empty()) {
                Node* n = queue_.pop();
                pthread_mutex_unlock(&queueMutex_);
                pthread_mutex_lock(&n->queuedForEmptyMutex_);
                n->queuedForEmptying_ = false;
                pthread_mutex_unlock(&n->queuedForEmptyMutex_);
                if (n->isRoot())
                    rootFlag = true;
#ifdef CT_NODE_DEBUG
                fprintf(stderr, "emptier: emptying node: %d (size: %u)\t",
                        n->id_, n->buffer_.numElements());
                fprintf(stderr, "remaining: ");
                queue_.printElements();
#endif
                if (n->isRoot()) {
                    n->aggregateSortedBuffer();
                } else {
                    n->aggregateMergedBuffer();
                }
                n->emptyBuffer();
                if (n->isLeaf())
                    tree_->handleFullLeaves();
                if (rootFlag) {
                    // do the split and create new root
                    rootFlag = false;

                    pthread_mutex_lock(&tree_->rootNodeAvailableMutex_);
                    pthread_cond_signal(&tree_->rootNodeAvailableForWriting_);
                    pthread_mutex_unlock(&tree_->rootNodeAvailableMutex_);
                }
                pthread_mutex_lock(&queueMutex_);
            }
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Emptier quitting: %ld\n", queue_.size());
#endif
        pthread_exit(NULL);
    }

    void Emptier::addNode(Node* node) {
        pthread_mutex_lock(&queueMutex_);
        if (node) {
            queue_.insert(node, node->level());
            queueEmpty_ = false;

            std::deque<Node*> depNodes;
            uint32_t prio = node->level();
            depNodes.push_back(node);
            while (!depNodes.empty()) {
                Node* t = depNodes.front();
                for (uint32_t i = 0; i < t->children_.size(); i++) {
                    if (t->children_[i]->queuedForEmptying_) {
                        queue_.insert(t->children_[i], ++prio);
                        depNodes.push_back(t->children_[i]);
                    }
                }
                depNodes.pop_front();
            }
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Node %d (size: %u) added to to-empty list: ",
                node->id_, node->buffer_.numElements());
        queue_.printElements();
#endif
        }
        pthread_mutex_unlock(&queueMutex_);
    }

    Compressor::Compressor(CompressTree* tree) :
            Slave(tree) {
    }

    Compressor::~Compressor() {
    }

    void* Compressor::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "compressor sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "compressor fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                n->performCompressAction();
                pthread_mutex_lock(&queueMutex_);
            }
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Compressor quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Compressor::addNode(Node* node) {
        Buffer::CompressionAction act = node->getCompressAction();
        if (act == Buffer::COMPRESS) {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_back(node);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "adding node %d to compress: ", node->id_);
            printElements();
#endif
#ifdef ENABLE_PAGING
            /* Put in a request for paging out. This is necessary to do
             * right away because of the following case: if a page-in request
             * arrives when the node is on the compression queue waiting to
             * be compressed, the page-in request could simply get discarded
             * since there is no page-out request (yet). This leads to a case
             * where a decompression later assumes that the page-in has
             * completed */
            node->scheduleBufferPageAction(Buffer::PAGE_OUT);
#endif
        } else {
#ifdef ENABLE_PAGING
            node->scheduleBufferPageAction(Buffer::PAGE_IN);
#endif
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_front(node);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "adding node %d (size: %u) to decompress: ",
                    node->id_, node->buffer_.numElements());
            printElements();
#endif
        }
        queueEmpty_ = false;
        pthread_mutex_unlock(&queueMutex_);
    }

    Sorter::Sorter(CompressTree* tree) :
            Slave(tree) {
    }

    Sorter::~Sorter() {
    }

    void* Sorter::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "sorter sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "sorter fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                n->waitForCompressAction(Buffer::DECOMPRESS);
#ifdef CT_NODE_DEBUG
                fprintf(stderr, "sorter: sorting node: %d (size: %u)\t",
                        n->id_, n->buffer_.numElements());
                fprintf(stderr, "remaining: ");
                for (int i = 0; i < nodes_.size(); ++i)
                    fprintf(stderr, "%d, ", nodes_[i]->id_);
                fprintf(stderr, "\n");
#endif
                if (n->isRoot()) {
                    n->sortBuffer();
                } else {
                    n->mergeBuffer();
                }
                tree_->emptier_->addNode(n);
                tree_->emptier_->wakeup();
                pthread_mutex_lock(&queueMutex_);
            }
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Sorter quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Sorter::addNode(Node* node) {
        pthread_mutex_lock(&queueMutex_);
        if (node) {
            // Set node as queued for emptying
            pthread_mutex_lock(&node->queuedForEmptyMutex_);
            node->queuedForEmptying_ = true;
            pthread_mutex_unlock(&node->queuedForEmptyMutex_);
            nodes_.push_back(node);
            queueEmpty_ = false;
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "Node %d added to to-sort list (size: %u)\n",
                    node->id_, node->buffer_.numElements());
            fprintf(stderr, "Node %d added to to-sort list (size: %u)\n",
                    node->id_, node->buffer_.numElements());
#endif
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        for (int i = 0; i < nodes_.size(); ++i)
            fprintf(stderr, "%d, ", nodes_[i]->id_);
        fprintf(stderr, "\n");
#endif
    }

#ifdef ENABLE_PAGING
    Pager::Pager(CompressTree* tree) :
            Slave(tree) {
    }

    Pager::~Pager() {
    }

    void* Pager::work() {
        pthread_mutex_lock(&queueMutex_);
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (nodes_.empty() && !inputComplete_) {
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "pager sleeping\n");
#endif
            queueEmpty_ = true;
            pthread_cond_wait(&queueHasWork_, &queueMutex_);
#ifdef CT_NODE_DEBUG
            fprintf(stderr, "pager fingered\n");
#endif
            while (!nodes_.empty()) {
                Node* n = nodes_.front();
                nodes_.pop_front();
                pthread_mutex_unlock(&queueMutex_);
                bool suc = n->performPageAction();
                pthread_mutex_lock(&queueMutex_);
                if (!suc)
                    nodes_.push_back(n);
            }
            /* check if anybody wants a notification when list is empty */
            sendCompletionNotice();
        }
        pthread_mutex_unlock(&queueMutex_);
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Pager quitting: %ld\n", nodes_.size());
#endif
        pthread_exit(NULL);
    }

    void Pager::addNode(Node* node) {
        Buffer::PageAction act = node->getPageAction();
        if (act == Buffer::PAGE_OUT) {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_back(node);
        } else {
            pthread_mutex_lock(&queueMutex_);
            nodes_.push_front(node);
        }
#ifdef CT_NODE_DEBUG
        fprintf(stderr, "Node %d added to page list: ", node->id_);
        printElements();
#endif
        queueEmpty_ = false;
        pthread_mutex_unlock(&queueMutex_);
    }
#endif  // ENABLE_PAGING

#ifdef ENABLE_COUNTERS
    Monitor::Monitor(CompressTree* tree) :
            Slave(tree),
            numElements(0),
            numMerged(0),
            actr(0),
            bctr(0),
            cctr(0) {
    }

    Monitor::~Monitor() {
    }

    void* Monitor::work() {
        pthread_barrier_wait(&tree_->threadsBarrier_);
        while (!inputComplete_) {
            sleep(1);
            elctr.push_back(numElements);
        }
        uint64_t tot = 0;
        for (uint32_t i = 0; i < elctr.size(); ++i) {
            tot += elctr[i];
        }
        fprintf(stderr, "Avg. number of elements: %f\n",
                static_cast<float>(tot) / elctr.size());
        fprintf(stderr, "A: %lu, B:%lu\n", numElements, numMerged);
        elctr.clear();
    }

    void Monitor::addNode(Node* n) {
        return;
    }
#endif
}
<|endoftext|>