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

#include "chrome/browser/extensions/api/webview/webview_api.h"

#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/guestview/webview/webview_guest.h"
#include "chrome/common/extensions/api/webview.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/error_utils.h"

using extensions::api::tabs::InjectDetails;
namespace webview = extensions::api::webview;

namespace {
int MaskForKey(const char* key) {
  if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_APPCACHE;
  if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_COOKIES;
  if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
  if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
  if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
  if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_WEBSQL;
  return 0;
}

}  // namespace

WebviewClearDataFunction::WebviewClearDataFunction()
    : remove_mask_(0),
      bad_message_(false) {
};

WebviewClearDataFunction::~WebviewClearDataFunction() {
};

// Parses the |dataToRemove| argument to generate the remove mask. Sets
// |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool
// method) if 'dataToRemove' is not present.
uint32 WebviewClearDataFunction::GetRemovalMask() {
  base::DictionaryValue* data_to_remove;
  if (!args_->GetDictionary(2, &data_to_remove)) {
    bad_message_ = true;
    return 0;
  }

  uint32 remove_mask = 0;
  for (base::DictionaryValue::Iterator i(*data_to_remove);
       !i.IsAtEnd();
       i.Advance()) {
    bool selected = false;
    if (!i.value().GetAsBoolean(&selected)) {
      bad_message_ = true;
      return 0;
    }
    if (selected)
      remove_mask |= MaskForKey(i.key().c_str());
  }

  return remove_mask;
}

// TODO(lazyboy): Parameters in this extension function are similar (or a
// sub-set) to BrowsingDataRemoverFunction. How can we share this code?
bool WebviewClearDataFunction::RunImpl() {
  int instance_id = 0;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &instance_id));

  // Grab the initial |options| parameter, and parse out the arguments.
  base::DictionaryValue* options;
  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
  DCHECK(options);

  // If |ms_since_epoch| isn't set, default it to 0.
  double ms_since_epoch;
  if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
                          &ms_since_epoch)) {
    ms_since_epoch = 0;
  }

  // base::Time takes a double that represents seconds since epoch. JavaScript
  // gives developers milliseconds, so do a quick conversion before populating
  // the object. Also, Time::FromDoubleT converts double time 0 to empty Time
  // object. So we need to do special handling here.
  remove_since_ = (ms_since_epoch == 0) ?
      base::Time::UnixEpoch() :
      base::Time::FromDoubleT(ms_since_epoch / 1000.0);

  remove_mask_ = GetRemovalMask();
  if (bad_message_)
    return false;

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), instance_id);
  if (!guest)
    return false;

  AddRef();  // Balanced below or in WebviewClearDataFunction::Done().

  bool scheduled = false;
  if (remove_mask_) {
    scheduled = guest->ClearData(
        remove_since_,
        remove_mask_,
        base::Bind(&WebviewClearDataFunction::ClearDataDone,
                   this));
  }
  if (!remove_mask_ || !scheduled) {
    SendResponse(false);
    Release();  // Balanced above.
    return false;
  }

  // Will finish asynchronously.
  return true;
}

void WebviewClearDataFunction::ClearDataDone() {
  Release();  // Balanced in RunImpl().
  SendResponse(true);
}

WebviewExecuteCodeFunction::WebviewExecuteCodeFunction()
    : guest_instance_id_(0) {
}

WebviewExecuteCodeFunction::~WebviewExecuteCodeFunction() {
}

bool WebviewExecuteCodeFunction::Init() {
  if (details_.get())
    return true;

  if (!args_->GetInteger(0, &guest_instance_id_))
    return false;

  if (!guest_instance_id_)
    return false;

  base::DictionaryValue* details_value = NULL;
  if (!args_->GetDictionary(1, &details_value))
    return false;
  scoped_ptr<InjectDetails> details(new InjectDetails());
  if (!InjectDetails::Populate(*details_value, details.get()))
    return false;

  details_ = details.Pass();
  return true;
}

bool WebviewExecuteCodeFunction::ShouldInsertCSS() const {
  return false;
}

bool WebviewExecuteCodeFunction::CanExecuteScriptOnPage() {
  return true;
}

extensions::ScriptExecutor* WebviewExecuteCodeFunction::GetScriptExecutor() {
  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), guest_instance_id_);
  if (!guest)
    return NULL;

  return guest->script_executor();
}

bool WebviewExecuteCodeFunction::IsWebView() const {
  return true;
}

WebviewExecuteScriptFunction::WebviewExecuteScriptFunction() {
}

void WebviewExecuteScriptFunction::OnExecuteCodeFinished(
    const std::string& error,
    int32 on_page_id,
    const GURL& on_url,
    const base::ListValue& result) {
  content::RecordAction(content::UserMetricsAction("WebView.ExecuteScript"));
  if (error.empty())
    SetResult(result.DeepCopy());
  WebviewExecuteCodeFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
                                                    result);
}

WebviewInsertCSSFunction::WebviewInsertCSSFunction() {
}

bool WebviewInsertCSSFunction::ShouldInsertCSS() const {
  return true;
}

WebviewGoFunction::WebviewGoFunction() {
}

WebviewGoFunction::~WebviewGoFunction() {
}

bool WebviewGoFunction::RunImpl() {
  scoped_ptr<webview::Go::Params> params(webview::Go::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Go(params->relative_index);
  return true;
}

WebviewReloadFunction::WebviewReloadFunction() {
}

WebviewReloadFunction::~WebviewReloadFunction() {
}

bool WebviewReloadFunction::RunImpl() {
  scoped_ptr<webview::Reload::Params> params(
      webview::Reload::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Reload();
  return true;
}

WebviewSetPermissionFunction::WebviewSetPermissionFunction() {
}

WebviewSetPermissionFunction::~WebviewSetPermissionFunction() {
}

bool WebviewSetPermissionFunction::RunImpl() {
  scoped_ptr<webview::SetPermission::Params> params(
      webview::SetPermission::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  EXTENSION_FUNCTION_VALIDATE(
      guest->SetPermission(params->request_id,
                           params->should_allow,
                           params->user_input));
  return true;
}

WebviewStopFunction::WebviewStopFunction() {
}

WebviewStopFunction::~WebviewStopFunction() {
}

bool WebviewStopFunction::RunImpl() {
  scoped_ptr<webview::Stop::Params> params(
      webview::Stop::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Stop();
  return true;
}

WebviewTerminateFunction::WebviewTerminateFunction() {
}

WebviewTerminateFunction::~WebviewTerminateFunction() {
}

bool WebviewTerminateFunction::RunImpl() {
  scoped_ptr<webview::Terminate::Params> params(
      webview::Terminate::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Terminate();
  return true;
}
<commit_msg><webview>: Add some more UMA tracking.<commit_after>// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/webview/webview_api.h"

#include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/guestview/webview/webview_guest.h"
#include "chrome/common/extensions/api/webview.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/error_utils.h"

using extensions::api::tabs::InjectDetails;
namespace webview = extensions::api::webview;

namespace {
int MaskForKey(const char* key) {
  if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_APPCACHE;
  if (strcmp(key, extension_browsing_data_api_constants::kCookiesKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_COOKIES;
  if (strcmp(key, extension_browsing_data_api_constants::kFileSystemsKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_FILE_SYSTEMS;
  if (strcmp(key, extension_browsing_data_api_constants::kIndexedDBKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_INDEXEDDB;
  if (strcmp(key, extension_browsing_data_api_constants::kLocalStorageKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE;
  if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0)
    return content::StoragePartition::REMOVE_DATA_MASK_WEBSQL;
  return 0;
}

}  // namespace

WebviewClearDataFunction::WebviewClearDataFunction()
    : remove_mask_(0),
      bad_message_(false) {
};

WebviewClearDataFunction::~WebviewClearDataFunction() {
};

// Parses the |dataToRemove| argument to generate the remove mask. Sets
// |bad_message_| (like EXTENSION_FUNCTION_VALIDATE would if this were a bool
// method) if 'dataToRemove' is not present.
uint32 WebviewClearDataFunction::GetRemovalMask() {
  base::DictionaryValue* data_to_remove;
  if (!args_->GetDictionary(2, &data_to_remove)) {
    bad_message_ = true;
    return 0;
  }

  uint32 remove_mask = 0;
  for (base::DictionaryValue::Iterator i(*data_to_remove);
       !i.IsAtEnd();
       i.Advance()) {
    bool selected = false;
    if (!i.value().GetAsBoolean(&selected)) {
      bad_message_ = true;
      return 0;
    }
    if (selected)
      remove_mask |= MaskForKey(i.key().c_str());
  }

  return remove_mask;
}

// TODO(lazyboy): Parameters in this extension function are similar (or a
// sub-set) to BrowsingDataRemoverFunction. How can we share this code?
bool WebviewClearDataFunction::RunImpl() {
  content::RecordAction(content::UserMetricsAction("WebView.ClearData"));
  int instance_id = 0;
  EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &instance_id));

  // Grab the initial |options| parameter, and parse out the arguments.
  base::DictionaryValue* options;
  EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
  DCHECK(options);

  // If |ms_since_epoch| isn't set, default it to 0.
  double ms_since_epoch;
  if (!options->GetDouble(extension_browsing_data_api_constants::kSinceKey,
                          &ms_since_epoch)) {
    ms_since_epoch = 0;
  }

  // base::Time takes a double that represents seconds since epoch. JavaScript
  // gives developers milliseconds, so do a quick conversion before populating
  // the object. Also, Time::FromDoubleT converts double time 0 to empty Time
  // object. So we need to do special handling here.
  remove_since_ = (ms_since_epoch == 0) ?
      base::Time::UnixEpoch() :
      base::Time::FromDoubleT(ms_since_epoch / 1000.0);

  remove_mask_ = GetRemovalMask();
  if (bad_message_)
    return false;

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), instance_id);
  if (!guest)
    return false;

  AddRef();  // Balanced below or in WebviewClearDataFunction::Done().

  bool scheduled = false;
  if (remove_mask_) {
    scheduled = guest->ClearData(
        remove_since_,
        remove_mask_,
        base::Bind(&WebviewClearDataFunction::ClearDataDone,
                   this));
  }
  if (!remove_mask_ || !scheduled) {
    SendResponse(false);
    Release();  // Balanced above.
    return false;
  }

  // Will finish asynchronously.
  return true;
}

void WebviewClearDataFunction::ClearDataDone() {
  Release();  // Balanced in RunImpl().
  SendResponse(true);
}

WebviewExecuteCodeFunction::WebviewExecuteCodeFunction()
    : guest_instance_id_(0) {
}

WebviewExecuteCodeFunction::~WebviewExecuteCodeFunction() {
}

bool WebviewExecuteCodeFunction::Init() {
  if (details_.get())
    return true;

  if (!args_->GetInteger(0, &guest_instance_id_))
    return false;

  if (!guest_instance_id_)
    return false;

  base::DictionaryValue* details_value = NULL;
  if (!args_->GetDictionary(1, &details_value))
    return false;
  scoped_ptr<InjectDetails> details(new InjectDetails());
  if (!InjectDetails::Populate(*details_value, details.get()))
    return false;

  details_ = details.Pass();
  return true;
}

bool WebviewExecuteCodeFunction::ShouldInsertCSS() const {
  return false;
}

bool WebviewExecuteCodeFunction::CanExecuteScriptOnPage() {
  return true;
}

extensions::ScriptExecutor* WebviewExecuteCodeFunction::GetScriptExecutor() {
  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), guest_instance_id_);
  if (!guest)
    return NULL;

  return guest->script_executor();
}

bool WebviewExecuteCodeFunction::IsWebView() const {
  return true;
}

WebviewExecuteScriptFunction::WebviewExecuteScriptFunction() {
}

void WebviewExecuteScriptFunction::OnExecuteCodeFinished(
    const std::string& error,
    int32 on_page_id,
    const GURL& on_url,
    const base::ListValue& result) {
  content::RecordAction(content::UserMetricsAction("WebView.ExecuteScript"));
  if (error.empty())
    SetResult(result.DeepCopy());
  WebviewExecuteCodeFunction::OnExecuteCodeFinished(error, on_page_id, on_url,
                                                    result);
}

WebviewInsertCSSFunction::WebviewInsertCSSFunction() {
}

bool WebviewInsertCSSFunction::ShouldInsertCSS() const {
  return true;
}

WebviewGoFunction::WebviewGoFunction() {
}

WebviewGoFunction::~WebviewGoFunction() {
}

bool WebviewGoFunction::RunImpl() {
  content::RecordAction(content::UserMetricsAction("WebView.Go"));
  scoped_ptr<webview::Go::Params> params(webview::Go::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Go(params->relative_index);
  return true;
}

WebviewReloadFunction::WebviewReloadFunction() {
}

WebviewReloadFunction::~WebviewReloadFunction() {
}

bool WebviewReloadFunction::RunImpl() {
  content::RecordAction(content::UserMetricsAction("WebView.Reload"));
  scoped_ptr<webview::Reload::Params> params(
      webview::Reload::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Reload();
  return true;
}

WebviewSetPermissionFunction::WebviewSetPermissionFunction() {
}

WebviewSetPermissionFunction::~WebviewSetPermissionFunction() {
}

bool WebviewSetPermissionFunction::RunImpl() {
  scoped_ptr<webview::SetPermission::Params> params(
      webview::SetPermission::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  EXTENSION_FUNCTION_VALIDATE(
      guest->SetPermission(params->request_id,
                           params->should_allow,
                           params->user_input));
  return true;
}

WebviewStopFunction::WebviewStopFunction() {
}

WebviewStopFunction::~WebviewStopFunction() {
}

bool WebviewStopFunction::RunImpl() {
  content::RecordAction(content::UserMetricsAction("WebView.Stop"));
  scoped_ptr<webview::Stop::Params> params(
      webview::Stop::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Stop();
  return true;
}

WebviewTerminateFunction::WebviewTerminateFunction() {
}

WebviewTerminateFunction::~WebviewTerminateFunction() {
}

bool WebviewTerminateFunction::RunImpl() {
  content::RecordAction(content::UserMetricsAction("WebView.Terminate"));
  scoped_ptr<webview::Terminate::Params> params(
      webview::Terminate::Params::Create(*args_));
  EXTENSION_FUNCTION_VALIDATE(params.get());

  WebViewGuest* guest = WebViewGuest::From(
      render_view_host()->GetProcess()->GetID(), params->instance_id);
  if (!guest)
    return false;

  guest->Terminate();
  return true;
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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 "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/common/chrome_switches.h"

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Popup) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);

  ASSERT_TRUE(RunExtensionTest("popup")) << message_;
}
<commit_msg>Mark ExtensionApiTest.Popup as FLAKY, it is still flaky.<commit_after>// Copyright (c) 2009 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 "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/common/chrome_switches.h"

// Flaky, http://crbug.com/46601.
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FLAKY_Popup) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);

  ASSERT_TRUE(RunExtensionTest("popup")) << message_;
}
<|endoftext|>


<commit_before>#include "../src/DependenceGraph.h"
#include <assert.h>
#include <cstdarg>
#include <cstdio>

#include "../src/LLVMDependenceGraph.h"

using namespace dg;

class TestDG : public DependenceGraph<int>
{
};

static unsigned int id = 0;

class TestNode : public DGNode<int>
{
public:
    TestNode() :DGNode<int>(++id), dfs_visited(0) {};

    unsigned int dfs_visited;
};

static void dump_to_dot(const DGNode<int> *n, FILE *f)
{
    for (auto I = n->control_begin(), E = n->control_end();
         I != E; ++I)
        fprintf(f, "\tNODE%p -> NODE%p;\n", n, *I);
    for (auto I = n->dependence_begin(), E = n->dependence_end();
         I != E; ++I)
        fprintf(f, "\tNODE%p -> NODE%p [color=red];\n", n, *I);
}

template<typename Key>
void print_to_dot(DependenceGraph<Key> *dg,
                  const char *file = "last_test.dot",
                  const char *description = NULL)
{
    // we have stdio included, do not use streams for such
    // easy task
    FILE *out = fopen(file, "w");
    if (!out) {
        fprintf(stderr, "Failed opening file %s\n", file);
        return;
    }

    fprintf(out, "digraph \"%s\" {\n",
            description ? description : "DependencyGraph");

    // if we have entry node, use it as a root
    // otherwise just dump the graph somehow
    if (dg->getEntry()) {
        dg->DFS(dg->getEntry(), dump_to_dot, out);
    } else {
        for (auto I = dg->begin(), E = dg->end(); I != E; ++I) {
            dump_to_dot(I->second, out);
        }
    }

    fprintf(out, "}\n");
    fclose(out);
}

/* return true when expr is violated and false when
 * it is OK */
static bool check(int expr, const char *func, int line, const char *fmt, ...)
{
    va_list args;

    if (expr)
        return false;

    fprintf(stderr, "%s:%d - ", func, line);

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);

    fputc('\n', stderr);

    return true;
}

#define chck_init() bool __chck_ret = false;
// return false when some check failed
#define chck_ret() return __chck_ret;
#define chck_dump(d)\
    do { if (__chck_ret) \
        {print_to_dot((d)); }\
    } while(0)
#define chck(expr, ...)    \
    do { __chck_ret |= check((expr), __func__, __LINE__, __VA_ARGS__); } while(0)

static bool constructors_test(void)
{
    chck_init();

    TestDG d;

    chck(d.getEntry() == NULL, "BUG: garbage in entry");
    chck(d.getSize() == 0, "BUG: garbage in nodes_num");

    TestNode n;

    chck(n.getSubgraph() == NULL, "BUG: garbage in subgraph");
    chck(n.getParameters() == NULL, "BUG: garbage in parameters");

    chck_dump(&d);
    chck_ret();
}

static bool add_test1(void)
{
    chck_init();

    TestDG d;
    TestNode n1, n2;

    chck(n1.addControlEdge(&n2), "adding C edge claims it is there");
    chck(n2.addDependenceEdge(&n1), "adding D edge claims it is there");

    d.addNode(&n1);
    d.addNode(&n2);

    d.setEntry(&n1);
    chck(d.getEntry() == &n1, "BUG: Entry setter");

    int n = 0;
    /*
    for (auto I = d.begin(), E = d.end(); I != E; ++I) {
        ++n;
        chck(*I == &n1 || *I == &n2, "Got some garbage in nodes");
    }
    */

    chck(n == 2, "BUG: adding nodes to graph, got %d instead of 2", n);

    int nn = 0;
    for (auto ni = n1.control_begin(), ne = n1.control_end(); ni != ne; ++ni){
        chck(*ni == &n2, "got wrong control edge");
        ++nn;
    }

    chck(nn == 1, "bug: adding control edges, has %d instead of 1", nn);

    nn = 0;
    for (auto ni = n2.dependence_begin(), ne = n2.dependence_end();
         ni != ne; ++ni) {
        chck(*ni == &n1, "got wrong control edge");
        ++nn;
    }

    chck(nn == 1, "BUG: adding dep edges, has %d instead of 1", nn);
    chck(d.getSize() == 2, "BUG: wrong nodes num");

    // adding the same node should not increase number of nodes
    chck(!d.addNode(&n1), "should get false when adding same node");
    chck(d.getSize() == 2, "BUG: wrong nodes num (2)");
    chck(!d.addNode(&n2), "should get false when adding same node (2)");
    chck(d.getSize() == 2, "BUG: wrong nodes num (2)");

    // don't trust just the counter
    n = 0;
    /*
    for (auto I = d.begin(), E = d.end(); I != E; ++I)
        ++n;
    */

    chck(n == 2, "BUG: wrong number of nodes in graph", n);

    // we're not a multi-graph, each edge is there only once
    // try add multiple edges
    chck(!n1.addControlEdge(&n2),
         "adding multiple C edge claims it is not there");
    chck(!n2.addDependenceEdge(&n1),
         "adding multiple D edge claims it is not there");

    nn = 0;
    for (auto ni = n1.control_begin(), ne = n1.control_end(); ni != ne; ++ni){
        chck(*ni == &n2, "got wrong control edge (2)");
        ++nn;
    }

    chck(nn == 1, "bug: adding control edges, has %d instead of 1 (2)", nn);

    nn = 0;
    for (auto ni = n2.dependence_begin(), ne = n2.dependence_end();
         ni != ne; ++ni) {
        chck(*ni == &n1, "got wrong control edge (2) ");
        ++nn;
    }

    chck(nn == 1, "bug: adding dependence edges, has %d instead of 1 (2)", nn);

    chck_dump(&d);
    chck_ret();
}

int main(int argc, char *argv[])
{
    bool ret = false;

    ret |= constructors_test();
    ret |= add_test1();

    return ret;
}
<commit_msg>tests: fix iterators over graph<commit_after>#include "../src/DependenceGraph.h"
#include <assert.h>
#include <cstdarg>
#include <cstdio>

#include "../src/LLVMDependenceGraph.h"

using namespace dg;

class TestDG : public DependenceGraph<int>
{
};

static unsigned int id = 0;

class TestNode : public DGNode<int>
{
public:
    TestNode() :DGNode<int>(++id), dfs_visited(0) {};

    unsigned int dfs_visited;
};

static void dump_to_dot(const DGNode<int> *n, FILE *f)
{
    for (auto I = n->control_begin(), E = n->control_end();
         I != E; ++I)
        fprintf(f, "\tNODE%p -> NODE%p;\n", n, *I);
    for (auto I = n->dependence_begin(), E = n->dependence_end();
         I != E; ++I)
        fprintf(f, "\tNODE%p -> NODE%p [color=red];\n", n, *I);
}

template<typename Key>
void print_to_dot(DependenceGraph<Key> *dg,
                  const char *file = "last_test.dot",
                  const char *description = NULL)
{
    // we have stdio included, do not use streams for such
    // easy task
    FILE *out = fopen(file, "w");
    if (!out) {
        fprintf(stderr, "Failed opening file %s\n", file);
        return;
    }

    fprintf(out, "digraph \"%s\" {\n",
            description ? description : "DependencyGraph");

    // if we have entry node, use it as a root
    // otherwise just dump the graph somehow
    if (dg->getEntry()) {
        dg->DFS(dg->getEntry(), dump_to_dot, out);
    } else {
        for (auto I = dg->begin(), E = dg->end(); I != E; ++I) {
            dump_to_dot(I->second, out);
        }
    }

    fprintf(out, "}\n");
    fclose(out);
}

/* return true when expr is violated and false when
 * it is OK */
static bool check(int expr, const char *func, int line, const char *fmt, ...)
{
    va_list args;

    if (expr)
        return false;

    fprintf(stderr, "%s:%d - ", func, line);

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);

    fputc('\n', stderr);

    return true;
}

#define chck_init() bool __chck_ret = false;
// return false when some check failed
#define chck_ret() return __chck_ret;
#define chck_dump(d)\
    do { if (__chck_ret) \
        {print_to_dot((d)); }\
    } while(0)
#define chck(expr, ...)    \
    do { __chck_ret |= check((expr), __func__, __LINE__, __VA_ARGS__); } while(0)

static bool constructors_test(void)
{
    chck_init();

    TestDG d;

    chck(d.getEntry() == NULL, "BUG: garbage in entry");
    chck(d.getSize() == 0, "BUG: garbage in nodes_num");

    TestNode n;

    chck(n.getSubgraph() == NULL, "BUG: garbage in subgraph");
    chck(n.getParameters() == NULL, "BUG: garbage in parameters");

    chck_dump(&d);
    chck_ret();
}

static bool add_test1(void)
{
    chck_init();

    TestDG d;
    TestNode n1, n2;

    chck(n1.addControlEdge(&n2), "adding C edge claims it is there");
    chck(n2.addDependenceEdge(&n1), "adding D edge claims it is there");

    d.addNode(&n1);
    d.addNode(&n2);

    d.setEntry(&n1);
    chck(d.getEntry() == &n1, "BUG: Entry setter");

    int n = 0;
    for (auto I = d.begin(), E = d.end(); I != E; ++I) {
        ++n;
        chck((*I).second == &n1 || (*I).second == &n2, "Got some garbage in nodes");
    }

    chck(n == 2, "BUG: adding nodes to graph, got %d instead of 2", n);

    int nn = 0;
    for (auto ni = n1.control_begin(), ne = n1.control_end(); ni != ne; ++ni){
        chck(*ni == &n2, "got wrong control edge");
        ++nn;
    }

    chck(nn == 1, "bug: adding control edges, has %d instead of 1", nn);

    nn = 0;
    for (auto ni = n2.dependence_begin(), ne = n2.dependence_end();
         ni != ne; ++ni) {
        chck(*ni == &n1, "got wrong control edge");
        ++nn;
    }

    chck(nn == 1, "BUG: adding dep edges, has %d instead of 1", nn);
    chck(d.getSize() == 2, "BUG: wrong nodes num");

    // adding the same node should not increase number of nodes
    chck(!d.addNode(&n1), "should get false when adding same node");
    chck(d.getSize() == 2, "BUG: wrong nodes num (2)");
    chck(!d.addNode(&n2), "should get false when adding same node (2)");
    chck(d.getSize() == 2, "BUG: wrong nodes num (2)");

    // don't trust just the counter
    n = 0;
    for (auto I = d.begin(), E = d.end(); I != E; ++I)
        ++n;

    chck(n == 2, "BUG: wrong number of nodes in graph", n);

    // we're not a multi-graph, each edge is there only once
    // try add multiple edges
    chck(!n1.addControlEdge(&n2),
         "adding multiple C edge claims it is not there");
    chck(!n2.addDependenceEdge(&n1),
         "adding multiple D edge claims it is not there");

    nn = 0;
    for (auto ni = n1.control_begin(), ne = n1.control_end(); ni != ne; ++ni){
        chck(*ni == &n2, "got wrong control edge (2)");
        ++nn;
    }

    chck(nn == 1, "bug: adding control edges, has %d instead of 1 (2)", nn);

    nn = 0;
    for (auto ni = n2.dependence_begin(), ne = n2.dependence_end();
         ni != ne; ++ni) {
        chck(*ni == &n1, "got wrong control edge (2) ");
        ++nn;
    }

    chck(nn == 1, "bug: adding dependence edges, has %d instead of 1 (2)", nn);

    chck_dump(&d);
    chck_ret();
}

int main(int argc, char *argv[])
{
    bool ret = false;

    ret |= constructors_test();
    ret |= add_test1();

    return ret;
}
<|endoftext|>


<commit_before>/** binding_contexts.cc                                              -*- C++ -*-
    Jeremy Barnes, 14 March 2015

    This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

    Contexts in which to execute scoped SQL expressions.
*/

#include "binding_contexts.h"
#include "http/http_exception.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

/*****************************************************************************/
/* READ THROUGH BINDING CONTEXT                                              */
/*****************************************************************************/

BoundSqlExpression
ReadThroughBindingContext::
rebind(BoundSqlExpression expr)
{
    auto outerExec = expr.exec;

    // Call the exec function with the context pivoted to the output context
    expr.exec = [=] (const SqlRowScope & context,
                     ExpressionValue & storage,
                     const VariableFilter & filter)
        -> const ExpressionValue &
        {
            auto & row = static_cast<const RowContext &>(context);
            return outerExec(row.outer, storage, filter);
        };

    return expr;
}

BoundFunction
ReadThroughBindingContext::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{
#if 0
   // Rebind the function parameters to the outer
    std::vector<BoundSqlExpression> outerArgs;
    for (auto & arg: args) {
        if (arg.metadata.isConstant)  //don't rebind constant expression since they don't need to access the row
            outerArgs.emplace_back(std::move(arg));
        else
            outerArgs.emplace_back(std::move(rebind(arg)));
    }
#endif
    // Get the outer function
    auto outerFunction = outer.doGetFunction(tableName, functionName, args);

    BoundFunction result = outerFunction;

    if (!outerFunction)
        return result;

    // Call it with the outer context
    result.exec = [=] (const std::vector<BoundSqlExpression> & args,
                       const SqlRowScope & context)
        {
            auto & row = static_cast<const RowContext &>(context);

            //cerr << "rebinding to apply function " << functionName
            //<< ": context type is "
            //<< ML::type_name(context) << " outer type is "
            //<< ML::type_name(row.outer) << endl;

            return outerFunction(args, row.outer);
        };

    return result;
}

VariableGetter
ReadThroughBindingContext::
doGetVariable(const Utf8String & tableName,
              const Utf8String & variableName)
{
    auto outerImpl = outer.doGetVariable(tableName, variableName);

    return {[=] (const SqlRowScope & context,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {
                auto & row = static_cast<const RowContext &>(context);
                cerr << "getting variable " << variableName
                     << " from outer: context type is "
                     << ML::type_name(context) << " outer type is "
                     << ML::type_name(row.outer) << endl;
                return outerImpl(row.outer, storage, filter);
            },
            outerImpl.info};
}

GetAllColumnsOutput
ReadThroughBindingContext::
doGetAllColumns(const Utf8String & tableName,
                std::function<Utf8String (const Utf8String &)> keep)
{
    GetAllColumnsOutput result = outer.doGetAllColumns(tableName, keep);
    auto outerFn = result.exec;
    result.exec = [=] (const SqlRowScope & scope)
        {
            auto & row = static_cast<const RowContext &>(scope);
            return outerFn(row.outer);
        };
    return result;
}

VariableGetter
ReadThroughBindingContext::
doGetBoundParameter(const Utf8String & paramName)
{
    auto outerImpl = outer.doGetBoundParameter(paramName);

    return {[=] (const SqlRowScope & context,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {
                auto & row = static_cast<const RowContext &>(context);
                return outerImpl(row.outer, storage, filter);
            },
            outerImpl.info};
}

std::shared_ptr<Function>
ReadThroughBindingContext::
doGetFunctionEntity(const Utf8String & functionName)
{
    return outer.doGetFunctionEntity(functionName);
}

std::shared_ptr<Dataset>
ReadThroughBindingContext::
doGetDataset(const Utf8String & datasetName)
{
    return outer.doGetDataset(datasetName);
}

std::shared_ptr<Dataset>
ReadThroughBindingContext::
doGetDatasetFromConfig(const Any & datasetConfig)
{
    return outer.doGetDatasetFromConfig(datasetConfig);
}

/*****************************************************************************/
/* COLUMN EXPRESSION BINDING CONTEXT                                         */
/*****************************************************************************/

BoundFunction
ColumnExpressionBindingContext::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{

    if (functionName == "columnName") {
        return {[=] (const std::vector<BoundSqlExpression> & args,
                     const SqlRowScope & context)
                {
                    auto & col = static_cast<const ColumnContext &>(context);
                    return ExpressionValue(col.columnName.toUtf8String(),
                                           Date::negativeInfinity());
                },
                std::make_shared<Utf8StringValueInfo>()};
    }

    auto fn = outer.doGetColumnFunction(functionName);

    if (fn)
    {
         return {[=] (const std::vector<BoundSqlExpression> & args,
                 const SqlRowScope & context)
            {
                auto & col = static_cast<const ColumnContext &>(context);

                // consider changing the signature of the column function
                // to let them evaluate their args as a potential speed improvement
                std::vector<ExpressionValue> evaluatedArgs;
                evaluatedArgs.reserve(args.size());
                for (auto & arg: args)
                    evaluatedArgs.emplace_back(std::move(arg(context)));

                return fn(col.columnName, evaluatedArgs);
            },
            std::make_shared<Utf8StringValueInfo>()};
    }

    auto sqlfn = SqlBindingScope::doGetFunction(tableName, functionName, args);

    if (sqlfn)
        return sqlfn;

    throw HttpReturnException(400, "Unknown function " + functionName + " in column expression");

}

/*****************************************************************************/
/* SQL EXPRESSION WHEN SCOPE                                                 */
/*****************************************************************************/

BoundFunction
SqlExpressionWhenScope::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{
    if (functionName == "timestamp") {
        isTupleDependent = true;
        return  {[=] (const std::vector<BoundSqlExpression> & args,
                      const SqlRowScope & scope)
                {
                    auto & row = static_cast<const RowScope &>(scope);
                    return ExpressionValue(row.ts, row.ts);
                },
                std::make_shared<TimestampValueInfo>()};
    }

    return ReadThroughBindingContext::doGetFunction(tableName, functionName,
                                                    args);
}

/*****************************************************************************/
/* SQL EXPRESSION PARAM SCOPE                                                */
/*****************************************************************************/

VariableGetter
SqlExpressionParamScope::
doGetBoundParameter(const Utf8String & paramName)
{
    return {[=] (const SqlRowScope & scope,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {

                auto & row = static_cast<const RowScope &>(scope);
                return row.params(paramName);
            },
            std::make_shared<AnyValueInfo>() };
}

} // namespace MLDB
} // namespace Datacratic
<commit_msg>[MLDB-1235] commented out more code in readthroughbindingcontext<commit_after>/** binding_contexts.cc                                              -*- C++ -*-
    Jeremy Barnes, 14 March 2015

    This file is part of MLDB. Copyright 2015 Datacratic. All rights reserved.

    Contexts in which to execute scoped SQL expressions.
*/

#include "binding_contexts.h"
#include "http/http_exception.h"

using namespace std;

namespace Datacratic {
namespace MLDB {

/*****************************************************************************/
/* READ THROUGH BINDING CONTEXT                                              */
/*****************************************************************************/

BoundSqlExpression
ReadThroughBindingContext::
rebind(BoundSqlExpression expr)
{
    auto outerExec = expr.exec;

    // Call the exec function with the context pivoted to the output context
    expr.exec = [=] (const SqlRowScope & context,
                     ExpressionValue & storage,
                     const VariableFilter & filter)
        -> const ExpressionValue &
        {
            auto & row = static_cast<const RowContext &>(context);
            return outerExec(row.outer, storage, filter);
        };

    return expr;
}

BoundFunction
ReadThroughBindingContext::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{
#if 0
   // Rebind the function parameters to the outer
    std::vector<BoundSqlExpression> outerArgs;
    for (auto & arg: args) {
        if (arg.metadata.isConstant)  //don't rebind constant expression since they don't need to access the row
            outerArgs.emplace_back(std::move(arg));
        else
            outerArgs.emplace_back(std::move(rebind(arg)));
    }
#endif
    // Get the outer function
    return outer.doGetFunction(tableName, functionName, args);

#if 0
    BoundFunction result = outerFunction;

    if (!outerFunction)
        return result;

    // Call it with the outer context
    result.exec = [=] (const std::vector<BoundSqlExpression> & args,
                       const SqlRowScope & context)
        {
            auto & row = static_cast<const RowContext &>(context);

            //cerr << "rebinding to apply function " << functionName
            //<< ": context type is "
            //<< ML::type_name(context) << " outer type is "
            //<< ML::type_name(row.outer) << endl;

            return outerFunction(args, row.outer);
        };

    return result;
#endif
}

VariableGetter
ReadThroughBindingContext::
doGetVariable(const Utf8String & tableName,
              const Utf8String & variableName)
{
    auto outerImpl = outer.doGetVariable(tableName, variableName);

    return {[=] (const SqlRowScope & context,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {
                auto & row = static_cast<const RowContext &>(context);
                cerr << "getting variable " << variableName
                     << " from outer: context type is "
                     << ML::type_name(context) << " outer type is "
                     << ML::type_name(row.outer) << endl;
                return outerImpl(row.outer, storage, filter);
            },
            outerImpl.info};
}

GetAllColumnsOutput
ReadThroughBindingContext::
doGetAllColumns(const Utf8String & tableName,
                std::function<Utf8String (const Utf8String &)> keep)
{
    GetAllColumnsOutput result = outer.doGetAllColumns(tableName, keep);
    auto outerFn = result.exec;
    result.exec = [=] (const SqlRowScope & scope)
        {
            auto & row = static_cast<const RowContext &>(scope);
            return outerFn(row.outer);
        };
    return result;
}

VariableGetter
ReadThroughBindingContext::
doGetBoundParameter(const Utf8String & paramName)
{
    auto outerImpl = outer.doGetBoundParameter(paramName);

    return {[=] (const SqlRowScope & context,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {
                auto & row = static_cast<const RowContext &>(context);
                return outerImpl(row.outer, storage, filter);
            },
            outerImpl.info};
}

std::shared_ptr<Function>
ReadThroughBindingContext::
doGetFunctionEntity(const Utf8String & functionName)
{
    return outer.doGetFunctionEntity(functionName);
}

std::shared_ptr<Dataset>
ReadThroughBindingContext::
doGetDataset(const Utf8String & datasetName)
{
    return outer.doGetDataset(datasetName);
}

std::shared_ptr<Dataset>
ReadThroughBindingContext::
doGetDatasetFromConfig(const Any & datasetConfig)
{
    return outer.doGetDatasetFromConfig(datasetConfig);
}

/*****************************************************************************/
/* COLUMN EXPRESSION BINDING CONTEXT                                         */
/*****************************************************************************/

BoundFunction
ColumnExpressionBindingContext::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{

    if (functionName == "columnName") {
        return {[=] (const std::vector<BoundSqlExpression> & args,
                     const SqlRowScope & context)
                {
                    auto & col = static_cast<const ColumnContext &>(context);
                    return ExpressionValue(col.columnName.toUtf8String(),
                                           Date::negativeInfinity());
                },
                std::make_shared<Utf8StringValueInfo>()};
    }

    auto fn = outer.doGetColumnFunction(functionName);

    if (fn)
    {
         return {[=] (const std::vector<BoundSqlExpression> & args,
                 const SqlRowScope & context)
            {
                auto & col = static_cast<const ColumnContext &>(context);

                // consider changing the signature of the column function
                // to let them evaluate their args as a potential speed improvement
                std::vector<ExpressionValue> evaluatedArgs;
                evaluatedArgs.reserve(args.size());
                for (auto & arg: args)
                    evaluatedArgs.emplace_back(std::move(arg(context)));

                return fn(col.columnName, evaluatedArgs);
            },
            std::make_shared<Utf8StringValueInfo>()};
    }

    auto sqlfn = SqlBindingScope::doGetFunction(tableName, functionName, args);

    if (sqlfn)
        return sqlfn;

    throw HttpReturnException(400, "Unknown function " + functionName + " in column expression");

}

/*****************************************************************************/
/* SQL EXPRESSION WHEN SCOPE                                                 */
/*****************************************************************************/

BoundFunction
SqlExpressionWhenScope::
doGetFunction(const Utf8String & tableName,
              const Utf8String & functionName,
              const std::vector<std::shared_ptr<SqlExpression> > & args)
{
    if (functionName == "timestamp") {
        isTupleDependent = true;
        return  {[=] (const std::vector<BoundSqlExpression> & args,
                      const SqlRowScope & scope)
                {
                    auto & row = static_cast<const RowScope &>(scope);
                    return ExpressionValue(row.ts, row.ts);
                },
                std::make_shared<TimestampValueInfo>()};
    }

    return ReadThroughBindingContext::doGetFunction(tableName, functionName,
                                                    args);
}

/*****************************************************************************/
/* SQL EXPRESSION PARAM SCOPE                                                */
/*****************************************************************************/

VariableGetter
SqlExpressionParamScope::
doGetBoundParameter(const Utf8String & paramName)
{
    return {[=] (const SqlRowScope & scope,
                 ExpressionValue & storage,
                 const VariableFilter & filter) -> const ExpressionValue &
            {

                auto & row = static_cast<const RowScope &>(scope);
                return row.params(paramName);
            },
            std::make_shared<AnyValueInfo>() };
}

} // namespace MLDB
} // namespace Datacratic
<|endoftext|>


<commit_before>/***********************************************************************************
**
** Target.cpp
**
** Copyright (C) August 2016 Hotride
**
************************************************************************************
*/
//----------------------------------------------------------------------------------
#include "Target.h"
#include "Game objects/GameWorld.h"
#include "Network/Packets.h"
#include "OrionUO.h"
#include "Managers/MapManager.h"
//----------------------------------------------------------------------------------
CTarget g_Target;
//----------------------------------------------------------------------------------
CTarget::CTarget()
: m_Type(0), m_Targeting(false), m_MultiGraphic(0), m_Multi(NULL), m_CursorID(0)
{
	//
	memset(m_Data, 0, sizeof(m_Data));
	memset(m_LastData, 0, sizeof(m_LastData));
}
//----------------------------------------------------------------------------------
void CTarget::Reset()
{
	//
	memset(m_Data, 0, sizeof(m_Data));
	memset(m_LastData, 0, sizeof(m_LastData));

	if (m_Multi != NULL)
	{
		delete m_Multi;
		m_Multi = NULL;
	}

	m_Type = 0;
	m_CursorType = 0;
	m_CursorID = 0;
	m_Targeting = false;
	m_MultiGraphic = 0;
}
//----------------------------------------------------------------------------------
void CTarget::SetData(WISP_DATASTREAM::CDataReader &reader)
{
	//
	memcpy(&m_Data[0], reader.Start, reader.Size);

	//
	m_Type = reader.ReadUInt8();
	m_CursorID = reader.ReadUInt32BE();
	m_CursorType = reader.ReadUInt8();
	m_Targeting = true;
	m_MultiGraphic = false;
}
//----------------------------------------------------------------------------------
void CTarget::SetMultiData(WISP_DATASTREAM::CDataReader &reader)
{
	//
	m_Type = 1;
	m_CursorType = 0;
	m_Targeting = true;
	m_CursorID = reader.ReadUInt32BE(1);

	//
	memset(&m_Data[0], 0, 19);
	m_Data[0] = 0x6C;
	m_Data[1] = 1; //
	memcpy(m_Data + 2, reader.Start + 2, 4); // ID  (ID )

	reader.ResetPtr();
	m_MultiGraphic = reader.ReadUInt16BE(18);
}
//----------------------------------------------------------------------------------
void CTarget::SendTargetObject(const uint &serial)
{
	if (!m_Targeting)
		return; //     -

	if (serial != g_PlayerSerial)
	{
		g_LastTargetObject = serial;

		if (serial < 0x40000000)
			CPacketStatusRequest(serial).Send();
	}

	//  ,    ,  -
	pack32(m_Data + 7, serial);
	m_Data[1] = 0;

	CGameObject *obj = (g_World != NULL ? g_World->FindWorldObject(serial) : NULL);

	if (obj != NULL)
	{
		pack16(m_Data + 11, obj->X);
		pack16(m_Data + 13, obj->Y);
		m_Data[15] = 0xFF;
		m_Data[16] = obj->Z;
		pack16(m_Data + 17, obj->Graphic);
	}
	else
	{
		pack32(m_Data + 11, 0);
		pack32(m_Data + 15, 0);
	}

	//  LastTarget
	memcpy(m_LastData, m_Data, sizeof(m_Data));

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendTargetTile(const ushort &tileID, const short &x, const short &y, char z)
{
	if (!m_Targeting)
		return; //     -

	m_Data[1] = 1;

	//    ,   ,
	pack32(m_Data + 7, 0);
	pack16(m_Data + 11, x);
	pack16(m_Data + 13, y);
	m_Data[15] = 0xFF;

	if (m_MultiGraphic != 0)
	{
		int grZ = 0;
		int stZ = 0;
		g_MapManager->GetMapZ(x, y, grZ, stZ);
		z = grZ;
	}

	m_Data[16] = z;
	pack16(m_Data + 17, tileID);

	//  LastTarget
	memcpy(m_LastData, m_Data, sizeof(m_Data));

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendCancelTarget()
{
	if (!m_Targeting)
		return; //     -

	//
	pack32(m_Data + 7, 0);
	pack32(m_Data + 11, 0xFFFFFFFF);
	pack32(m_Data + 15, 0);

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendLastTarget()
{
	if (!m_Targeting)
		return; //     -

	//
	memcpy(m_Data, m_LastData, sizeof(m_Data));
	m_Data[0] = 0x6C;
	m_Data[1] = m_Type;
	m_Data[6] = m_CursorType;
	pack32(m_Data + 2, m_CursorID);

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendTarget()
{
	g_Orion.Send(m_Data, sizeof(m_Data));

	//
	memset(m_Data, 0, sizeof(m_Data));
	m_Targeting = false;
	m_MultiGraphic = 0;
}
//----------------------------------------------------------------------------------
void CTarget::UnloadMulti()
{
	if (m_Multi != NULL)
	{
		delete m_Multi;
		m_Multi = NULL;
	}
}
//----------------------------------------------------------------------------------
void CTarget::LoadMulti(const int &x, const int &y, const char &z)
{
	CIndexMulti &index = g_Orion.m_MultiDataIndex[m_MultiGraphic];

	if (index.Address != NULL)
	{
		int count = (int)index.Count;

		IFOR(j, 0, count)
		{
			PMULTI_BLOCK pmb = (PMULTI_BLOCK)(index.Address + (j * sizeof(MULTI_BLOCK)));

			CMultiObject *mo = new CMultiObject(pmb->ID, x + pmb->X, y + pmb->Y, z + (char)pmb->Z, 2);
			g_MapManager->AddRender(mo);
			AddMultiObject(mo);
		}
	}
}
//----------------------------------------------------------------------------------
void CTarget::AddMultiObject(CMultiObject *obj)
{
	if (m_Multi == NULL)
	{
		m_Multi = new CMulti(obj->X, obj->Y);
		m_Multi->m_Next = NULL;
		m_Multi->m_Items = obj;
		obj->m_Next = NULL;
		obj->m_Prev = NULL;
	}
	else
	{
		CMulti *multi = GetMultiAtXY(obj->X, obj->Y);

		if (multi != NULL)
		{
			QFOR(multiobj, multi->m_Items, CMultiObject*)
			{
				if (obj->Z < multiobj->Z)
				{
					if (multiobj->m_Prev == NULL)
					{
						obj->m_Prev = NULL;
						obj->m_Next = multiobj;
						multiobj->m_Prev = obj;
						multi->m_Items = obj;
					}
					else
					{
						obj->m_Next = multiobj->m_Next;
						multiobj->m_Next = obj;
						obj->m_Prev = multiobj;
					}

					return;
				}

				if (multiobj->m_Next == NULL)
				{
					multiobj->m_Next = obj;
					obj->m_Prev = multiobj;
					obj->m_Next = NULL;

					return;
				}
			}

			//   - -
		}
		else
		{
			CMulti *newmulti = new CMulti(obj->X, obj->Y);
			newmulti->m_Next = NULL;
			newmulti->m_Items = obj;
			obj->m_Next = NULL;
			obj->m_Prev = NULL;

			multi = m_Multi;

			while (multi != NULL)
			{
				if (multi->m_Next == NULL)
				{
					multi->m_Next = newmulti;
					break;
				}

				multi = (CMulti*)multi->m_Next;
			}
		}
	}
}
//----------------------------------------------------------------------------------
CMulti *CTarget::GetMultiAtXY(const short &x, const short &y)
{
	CMulti *multi = m_Multi;

	while (multi != NULL)
	{
		if (multi->X == x && multi->Y == y)
			return multi;

		multi = (CMulti*)multi->m_Next;
	}

	return multi;
}
//----------------------------------------------------------------------------------
<commit_msg>Поправил невозможность записи таргета на себя в LastTarget.<commit_after>/***********************************************************************************
**
** Target.cpp
**
** Copyright (C) August 2016 Hotride
**
************************************************************************************
*/
//----------------------------------------------------------------------------------
#include "Target.h"
#include "Game objects/GameWorld.h"
#include "Network/Packets.h"
#include "OrionUO.h"
#include "Managers/MapManager.h"
//----------------------------------------------------------------------------------
CTarget g_Target;
//----------------------------------------------------------------------------------
CTarget::CTarget()
: m_Type(0), m_Targeting(false), m_MultiGraphic(0), m_Multi(NULL), m_CursorID(0)
{
	//
	memset(m_Data, 0, sizeof(m_Data));
	memset(m_LastData, 0, sizeof(m_LastData));
}
//----------------------------------------------------------------------------------
void CTarget::Reset()
{
	//
	memset(m_Data, 0, sizeof(m_Data));
	memset(m_LastData, 0, sizeof(m_LastData));

	if (m_Multi != NULL)
	{
		delete m_Multi;
		m_Multi = NULL;
	}

	m_Type = 0;
	m_CursorType = 0;
	m_CursorID = 0;
	m_Targeting = false;
	m_MultiGraphic = 0;
}
//----------------------------------------------------------------------------------
void CTarget::SetData(WISP_DATASTREAM::CDataReader &reader)
{
	//
	memcpy(&m_Data[0], reader.Start, reader.Size);

	//
	m_Type = reader.ReadUInt8();
	m_CursorID = reader.ReadUInt32BE();
	m_CursorType = reader.ReadUInt8();
	m_Targeting = true;
	m_MultiGraphic = false;
}
//----------------------------------------------------------------------------------
void CTarget::SetMultiData(WISP_DATASTREAM::CDataReader &reader)
{
	//
	m_Type = 1;
	m_CursorType = 0;
	m_Targeting = true;
	m_CursorID = reader.ReadUInt32BE(1);

	//
	memset(&m_Data[0], 0, 19);
	m_Data[0] = 0x6C;
	m_Data[1] = 1; //
	memcpy(m_Data + 2, reader.Start + 2, 4); // ID  (ID )

	reader.ResetPtr();
	m_MultiGraphic = reader.ReadUInt16BE(18);
}
//----------------------------------------------------------------------------------
void CTarget::SendTargetObject(const uint &serial)
{
	if (!m_Targeting)
		return; //     -

	//  ,    ,  -
	pack32(m_Data + 7, serial);
	m_Data[1] = 0;

	CGameObject *obj = (g_World != NULL ? g_World->FindWorldObject(serial) : NULL);

	if (obj != NULL)
	{
		pack16(m_Data + 11, obj->X);
		pack16(m_Data + 13, obj->Y);
		m_Data[15] = 0xFF;
		m_Data[16] = obj->Z;
		pack16(m_Data + 17, obj->Graphic);
	}
	else
	{
		pack32(m_Data + 11, 0);
		pack32(m_Data + 15, 0);
	}

	if (serial != g_PlayerSerial)
	{
		g_LastTargetObject = serial;

		//  LastTarget
		memcpy(m_LastData, m_Data, sizeof(m_Data));

		if (serial < 0x40000000)
			CPacketStatusRequest(serial).Send();
	}

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendTargetTile(const ushort &tileID, const short &x, const short &y, char z)
{
	if (!m_Targeting)
		return; //     -

	m_Data[1] = 1;

	//    ,   ,
	pack32(m_Data + 7, 0);
	pack16(m_Data + 11, x);
	pack16(m_Data + 13, y);
	m_Data[15] = 0xFF;

	if (m_MultiGraphic != 0)
	{
		int grZ = 0;
		int stZ = 0;
		g_MapManager->GetMapZ(x, y, grZ, stZ);
		z = grZ;
	}

	m_Data[16] = z;
	pack16(m_Data + 17, tileID);

	//  LastTarget
	memcpy(m_LastData, m_Data, sizeof(m_Data));

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendCancelTarget()
{
	if (!m_Targeting)
		return; //     -

	//
	pack32(m_Data + 7, 0);
	pack32(m_Data + 11, 0xFFFFFFFF);
	pack32(m_Data + 15, 0);

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendLastTarget()
{
	if (!m_Targeting)
		return; //     -

	//
	memcpy(m_Data, m_LastData, sizeof(m_Data));
	m_Data[0] = 0x6C;
	m_Data[1] = m_Type;
	m_Data[6] = m_CursorType;
	pack32(m_Data + 2, m_CursorID);

	SendTarget();
}
//----------------------------------------------------------------------------------
void CTarget::SendTarget()
{
	g_Orion.Send(m_Data, sizeof(m_Data));

	//
	memset(m_Data, 0, sizeof(m_Data));
	m_Targeting = false;
	m_MultiGraphic = 0;
}
//----------------------------------------------------------------------------------
void CTarget::UnloadMulti()
{
	if (m_Multi != NULL)
	{
		delete m_Multi;
		m_Multi = NULL;
	}
}
//----------------------------------------------------------------------------------
void CTarget::LoadMulti(const int &x, const int &y, const char &z)
{
	CIndexMulti &index = g_Orion.m_MultiDataIndex[m_MultiGraphic];

	if (index.Address != NULL)
	{
		int count = (int)index.Count;

		IFOR(j, 0, count)
		{
			PMULTI_BLOCK pmb = (PMULTI_BLOCK)(index.Address + (j * sizeof(MULTI_BLOCK)));

			CMultiObject *mo = new CMultiObject(pmb->ID, x + pmb->X, y + pmb->Y, z + (char)pmb->Z, 2);
			g_MapManager->AddRender(mo);
			AddMultiObject(mo);
		}
	}
}
//----------------------------------------------------------------------------------
void CTarget::AddMultiObject(CMultiObject *obj)
{
	if (m_Multi == NULL)
	{
		m_Multi = new CMulti(obj->X, obj->Y);
		m_Multi->m_Next = NULL;
		m_Multi->m_Items = obj;
		obj->m_Next = NULL;
		obj->m_Prev = NULL;
	}
	else
	{
		CMulti *multi = GetMultiAtXY(obj->X, obj->Y);

		if (multi != NULL)
		{
			QFOR(multiobj, multi->m_Items, CMultiObject*)
			{
				if (obj->Z < multiobj->Z)
				{
					if (multiobj->m_Prev == NULL)
					{
						obj->m_Prev = NULL;
						obj->m_Next = multiobj;
						multiobj->m_Prev = obj;
						multi->m_Items = obj;
					}
					else
					{
						obj->m_Next = multiobj->m_Next;
						multiobj->m_Next = obj;
						obj->m_Prev = multiobj;
					}

					return;
				}

				if (multiobj->m_Next == NULL)
				{
					multiobj->m_Next = obj;
					obj->m_Prev = multiobj;
					obj->m_Next = NULL;

					return;
				}
			}

			//   - -
		}
		else
		{
			CMulti *newmulti = new CMulti(obj->X, obj->Y);
			newmulti->m_Next = NULL;
			newmulti->m_Items = obj;
			obj->m_Next = NULL;
			obj->m_Prev = NULL;

			multi = m_Multi;

			while (multi != NULL)
			{
				if (multi->m_Next == NULL)
				{
					multi->m_Next = newmulti;
					break;
				}

				multi = (CMulti*)multi->m_Next;
			}
		}
	}
}
//----------------------------------------------------------------------------------
CMulti *CTarget::GetMultiAtXY(const short &x, const short &y)
{
	CMulti *multi = m_Multi;

	while (multi != NULL)
	{
		if (multi->X == x && multi->Y == y)
			return multi;

		multi = (CMulti*)multi->m_Next;
	}

	return multi;
}
//----------------------------------------------------------------------------------
<|endoftext|>


<commit_before>#include "arguments.hpp"
#include "call_frame.hpp"
#include "on_stack.hpp"
#include "memory.hpp"
#include "metrics.hpp"
#include "raise_reason.hpp"
#include "thread_phase.hpp"

#include "class/array.hpp"
#include "class/class.hpp"
#include "class/exception.hpp"
#include "class/fiber.hpp"
#include "class/lookup_table.hpp"
#include "class/native_method.hpp"
#include "class/object.hpp"
#include "class/thread.hpp"
#include "class/thread_state.hpp"

#include "memory/gc.hpp"

#include "dtrace/dtrace.h"

#include "logger.hpp"

#include "missing/gettid.h"

#include <ostream>
#include <regex>
#include <string>

namespace rubinius {
  std::atomic<uint32_t> Fiber::fiber_ids_;

  void Fiber::bootstrap(STATE) {
    GO(fiber).set(state->memory()->new_class<Class, Fiber>(state, "Fiber"));

    fiber_ids_.store(0);
  }

  bool Fiber::root_p() {
    return vm()->fiber()->vm() == vm()->thread()->vm();
  }

  bool Fiber::canceled_p() {
    return !root_p() && vm()->thread_state()->raise_reason() == cFiberCancel;
  }

  void Fiber::unpack_arguments(STATE, Arguments& args) {
    switch(args.total()) {
      case 0:
        state->vm()->thread()->fiber_value(state, cNil);
        break;
      case 1:
        state->vm()->thread()->fiber_value(state, args.get_argument(0));
        break;
      default:
        state->vm()->thread()->fiber_value(state,  args.as_array(state));
        break;
    }
  }

  void Fiber::start(STATE, Arguments& args) {
    state->vm()->thread()->fiber_value(state, args.as_array(state));

    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, stack_size()->to_native());
    pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);

    int status = pthread_create(&vm()->os_thread(), &attrs,
        Fiber::run, (void*)vm());

    pthread_attr_destroy(&attrs);

    if(status != 0) {
      char buf[RBX_STRERROR_BUFSIZE];
      char* err = RBX_STRERROR(status, buf, RBX_STRERROR_BUFSIZE);
      Exception::raise_fiber_error(state, err);
    }

    // Wait for Fiber thread to start up and pause.
    while(!vm()->suspended_p()) {
      ; // spin wait
    }
  }

  void Fiber::restart(STATE) {
    UnmanagedPhase unmanaged(state);

    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(!vm()->suspended_p()) {
        std::ostringstream msg;
        msg << "attempt to restart non-suspended ("
          << vm()->fiber_transition_flag() << ") fiber";
        Exception::raise_fiber_error(state, msg.str().c_str());
      }

      state->vm()->set_suspending();

      restart_context(state->vm());
      wakeup();

      while(vm()->suspended_p()) {
        std::lock_guard<std::mutex> guard(vm()->fiber_wait_mutex());
        vm()->fiber_wait_condition().notify_one();
      }
    }

    while(!vm()->running_p()) {
      ; // spin wait
    }
  }

  void Fiber::cancel(STATE) {
    if(!vm()->zombie_p()) {
      vm()->thread_state()->raise_fiber_cancel();

      wakeup();

      while(vm()->suspended_p()) {
        std::lock_guard<std::mutex> guard(vm()->fiber_wait_mutex());
        vm()->fiber_wait_condition().notify_one();
      }
    }
  }

  void Fiber::suspend_and_continue(STATE) {
    UnmanagedPhase unmanaged(state);

    {
      std::unique_lock<std::mutex> lk(vm()->fiber_wait_mutex());

      vm()->set_suspended();
      while(!wakeup_p()) {
        vm()->fiber_wait_condition().wait(lk);
      }
      clear_wakeup();
      vm()->set_resuming();
    }

    if(!vm()->canceled_p()) {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      vm()->set_running();

      while(!restart_context()->suspended_p()) {
        ; // spin wait
      }

      state->vm()->thread()->current_fiber(state, this);
    }
  }

  Object* Fiber::return_value(STATE) {
    if(vm()->thread_state()->raise_reason() == cNone) {
      return state->vm()->thread()->fiber_value();
    } else if(canceled_p()) {
      return NULL;
    } else {
      invoke_context()->thread_state()->set_state(vm()->thread_state());
      return NULL;
    }
  }

  void* Fiber::run(void* ptr) {
    VM* vm = reinterpret_cast<VM*>(ptr);
    State state_obj(vm), *state = &state_obj;

    vm->set_stack_bounds(vm->fiber()->stack_size()->to_native());
    vm->set_current_thread();
    vm->set_start_time();

    RUBINIUS_THREAD_START(
        const_cast<RBX_DTRACE_CHAR_P>(
          vm->name().c_str()), vm->fiber()->fiber_id()->to_native(), 0);

    vm->fiber()->pid(state, Fixnum::from(gettid()));

    if(state->shared().config.machine_thread_log_lifetime.value) {
      logger::write("fiber: run: %s, %d, %#x",
          vm->name().c_str(), vm->fiber()->pid()->to_native(),
          (intptr_t)pthread_self());
    }

    NativeMethod::init_thread(state);

    vm->fiber()->suspend_and_continue(state);

    Object* value = vm->fiber()->block()->send(state, G(sym_call),
        as<Array>(vm->thread()->fiber_value()), vm->fiber()->block());
    vm->set_call_frame(NULL);

    if(value) {
      vm->thread()->fiber_value(state, value);
    } else {
      vm->thread()->fiber_value(state, cNil);
    }

    if(vm->thread_state()->raise_reason() != cFiberCancel) {
      if(vm->fiber()->status() == eTransfer) {
        // restart the root Fiber
        vm->thread()->fiber()->invoke_context(vm);
        vm->thread()->fiber()->restart(state);
      } else {
        vm->fiber()->invoke_context()->fiber()->restart(state);
      }
    }

    {
      std::lock_guard<std::mutex> guard(vm->fiber_wait_mutex());

      vm->fiber()->status(eDead);
      vm->set_suspended();
    }

    vm->unmanaged_phase(state);

    state->shared().report_profile(state);

    NativeMethod::cleanup_thread(state);

    if(state->shared().config.machine_fiber_log_lifetime.value) {
      logger::write("fiber: exit: %s %fs", vm->name().c_str(), vm->run_time());
    }

    vm->set_zombie(state);

    RUBINIUS_THREAD_STOP(
        const_cast<RBX_DTRACE_CHAR_P>(
          vm->name().c_str()), vm->fiber()->fiber_id()->to_native(), 0);

    return 0;
  }

  Fiber* Fiber::current(STATE) {
    return state->vm()->fiber();
  }

  /* This creates the pseudo-Fiber for the Thread. This provides a uniform
   * means of expressing things like Fiber.current.
   */
  Fiber* Fiber::create(STATE, VM* vm) {
    Fiber* fiber = state->memory()->new_object_pinned<Fiber>(state, G(fiber));

    vm->set_fiber(fiber);
    vm->set_running();

    fiber->vm(vm);
    fiber->pid(vm->thread()->pid());
    fiber->stack_size(vm->thread()->stack_size());
    fiber->thread_name(state, String::create(state, vm->name().c_str()));
    fiber->fiber_id(Fixnum::from(0));
    fiber->status(eRunning);
    fiber->invoke_context(vm);

    return fiber;
  }

  Fiber* Fiber::create(STATE, Object* self, Object* stack_size, Object* block) {
    Fiber* fiber = state->memory()->new_object_pinned<Fiber>(state, as<Class>(self));
    fiber->block(state, block);

    std::ostringstream name;
    name << "fiber." << fiber->fiber_id()->to_native();

    {
      BlockPhase blocking(state);
      fiber->vm(state->vm()->thread_nexus()->new_vm(&state->shared(), name.str().c_str()));
    }

    fiber->vm()->set_kind(memory::ManagedThread::eFiber);
    fiber->vm()->set_suspending();

    if(Fixnum* size = try_as<Fixnum>(stack_size)) {
      fiber->vm()->validate_stack_size(state, size->to_native());
      fiber->stack_size(state, size);
    }

    fiber->vm()->set_thread(state->vm()->thread());
    fiber->vm()->set_fiber(fiber);

    state->memory()->native_finalizer(state, fiber,
        (memory::FinalizerFunction)&Fiber::finalize);

    if(state->shared().config.machine_fiber_log_lifetime.value) {
      const std::regex& filter = state->shared().config.machine_fiber_log_filter();

      if(CallFrame* call_frame = state->vm()->get_filtered_frame(state, filter)) {
        std::ostringstream source;

        source << call_frame->file(state)->cpp_str(state).c_str()
          << ":" << call_frame->line(state);

        logger::write("fiber: new: %s, %d, %s",
            fiber->thread_name()->c_str(state),
            fiber->fiber_id()->to_native(), source.str().c_str());

        fiber->source(state, String::create(state, source.str().c_str()));
      } else {
        logger::write("fiber: new: %s, %d",
            fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
      }
    }

    state->vm()->metrics().system.fibers_created++;

    return fiber;
  }

  String* Fiber::status(STATE) {
    switch(status()) {
      case eCreated:
        return String::create(state, "created");
        break;
      case eRunning:
        return String::create(state, "run");
        break;
      case eYielding:
        return String::create(state, "yield");
        break;
      case eTransfer:
        return String::create(state, "transfer");
        break;
      case eDead:
        return String::create(state, "dead");
        break;
    }

    return String::create(state, "unknown");
  }

  Object* Fiber::resume(STATE, Arguments& args) {
    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(state->vm()->thread() != thread()) {
        Exception::raise_fiber_error(state, "attempt to resume fiber across threads");
      } else if(status() == eTransfer) {
        Exception::raise_fiber_error(state, "attempt to resume transfered fiber");
      } else if(status() == eRunning) {
        Exception::raise_fiber_error(state, "attempt to resume running fiber");
      } else if(status() == eDead) {
        Exception::raise_fiber_error(state, "attempt to resume dead fiber");
      } else if(root_p()) {
        Exception::raise_fiber_error(state, "attempt to resume root fiber");
      }

      unpack_arguments(state, args);
      invoke_context(state->vm());

      if(status() == eCreated) {
        start(state, args);
      }

      status(eRunning);
    }

    // Being cooperative...
    restart(state);

    // Through the worm hole...
    state->vm()->fiber()->suspend_and_continue(state);

    // We're back...
    return return_value(state);
  }

  Object* Fiber::transfer(STATE, Arguments& args) {
    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(state->vm()->thread() != thread()) {
        Exception::raise_fiber_error(state, "attempt to transfer fiber across threads");
      } else if(status() == eDead) {
        Exception::raise_fiber_error(state, "attempt to transfer to dead fiber");
      } else if(state->vm()->fiber() == this) {
        // This should arguably be a FiberError
        return args.as_array(state);
      }

      unpack_arguments(state, args);
      invoke_context(state->vm());

      if(status() == eCreated) {
        start(state, args);
      }

      status(eTransfer);
    }

    // Being cooperative...
    restart(state);

    // Through the worm hole...
    state->vm()->fiber()->suspend_and_continue(state);

    // We're back...
    return return_value(state);
  }

  Object* Fiber::dispose(STATE) {
    cancel(state);

    return this;
  }

  Object* Fiber::s_yield(STATE, Arguments& args) {
    Fiber* fiber = state->vm()->fiber();
    ThreadState* thread_state = state->vm()->thread_state()->state_as_object(state);
    OnStack<2> os(state, fiber, thread_state);

    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(fiber->root_p()) {
        Exception::raise_fiber_error(state, "can't yield from root fiber");
      } else if(fiber->status() == eTransfer) {
        Exception::raise_fiber_error(state, "can't yield from transferred fiber");
      }

      fiber->unpack_arguments(state, args);
      fiber->status(eYielding);
    }

    // Being cooperative...
    state->vm()->thread_state()->clear_raise();
    fiber->invoke_context()->fiber()->restart(state);

    // Through the worm hole...
    fiber->suspend_and_continue(state);

    if(fiber->canceled_p()) return NULL;

    if(!thread_state->nil_p()) {
      state->vm()->thread_state()->set_state(state, thread_state);
    }

    // We're back...
    return state->vm()->thread()->fiber_value();
  }

  Array* Fiber::s_list(STATE) {
    return state->shared().vm_fibers(state);
  }

  Fiber* Fiber::s_main(STATE) {
    return state->vm()->thread()->fiber();
  }

  Fixnum* Fiber::s_count(STATE) {
    return state->shared().vm_fibers_count(state);
  }

  void Fiber::finalize(STATE, Fiber* fiber) {
    if(state->shared().config.machine_fiber_log_finalizer.value) {
      logger::write("fiber: finalizer: %s, %d",
          fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
    }

    if(fiber->vm()) {
      if(!state->shared().halting_p()) {
        if(!fiber->vm()->zombie_p()) {
          fiber->cancel(state);
        }
      }

      if(fiber->vm()->zombie_p()) {
        VM::discard(state, fiber->vm());
        fiber->vm(NULL);
      } else {
        logger::write("fiber: finalizer: fiber not completed: %s, %d",
            fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
      }
    }
  }
}
<commit_msg>Guard access to Fiber's Thread. Fixes #3772.<commit_after>#include "arguments.hpp"
#include "call_frame.hpp"
#include "on_stack.hpp"
#include "memory.hpp"
#include "metrics.hpp"
#include "raise_reason.hpp"
#include "thread_phase.hpp"

#include "class/array.hpp"
#include "class/class.hpp"
#include "class/exception.hpp"
#include "class/fiber.hpp"
#include "class/lookup_table.hpp"
#include "class/native_method.hpp"
#include "class/object.hpp"
#include "class/thread.hpp"
#include "class/thread_state.hpp"

#include "memory/gc.hpp"

#include "dtrace/dtrace.h"

#include "logger.hpp"

#include "missing/gettid.h"

#include <ostream>
#include <regex>
#include <string>

namespace rubinius {
  std::atomic<uint32_t> Fiber::fiber_ids_;

  void Fiber::bootstrap(STATE) {
    GO(fiber).set(state->memory()->new_class<Class, Fiber>(state, "Fiber"));

    fiber_ids_.store(0);
  }

  bool Fiber::root_p() {
    return vm()->fiber()->vm() == vm()->thread()->vm();
  }

  bool Fiber::canceled_p() {
    return !root_p() && vm()->thread_state()->raise_reason() == cFiberCancel;
  }

  void Fiber::unpack_arguments(STATE, Arguments& args) {
    switch(args.total()) {
      case 0:
        state->vm()->thread()->fiber_value(state, cNil);
        break;
      case 1:
        state->vm()->thread()->fiber_value(state, args.get_argument(0));
        break;
      default:
        state->vm()->thread()->fiber_value(state,  args.as_array(state));
        break;
    }
  }

  void Fiber::start(STATE, Arguments& args) {
    state->vm()->thread()->fiber_value(state, args.as_array(state));

    pthread_attr_t attrs;
    pthread_attr_init(&attrs);
    pthread_attr_setstacksize(&attrs, stack_size()->to_native());
    pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);

    int status = pthread_create(&vm()->os_thread(), &attrs,
        Fiber::run, (void*)vm());

    pthread_attr_destroy(&attrs);

    if(status != 0) {
      char buf[RBX_STRERROR_BUFSIZE];
      char* err = RBX_STRERROR(status, buf, RBX_STRERROR_BUFSIZE);
      Exception::raise_fiber_error(state, err);
    }

    // Wait for Fiber thread to start up and pause.
    while(!vm()->suspended_p()) {
      ; // spin wait
    }
  }

  void Fiber::restart(STATE) {
    UnmanagedPhase unmanaged(state);

    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(!vm()->suspended_p()) {
        std::ostringstream msg;
        msg << "attempt to restart non-suspended ("
          << vm()->fiber_transition_flag() << ") fiber";
        Exception::raise_fiber_error(state, msg.str().c_str());
      }

      state->vm()->set_suspending();

      restart_context(state->vm());
      wakeup();

      while(vm()->suspended_p()) {
        std::lock_guard<std::mutex> guard(vm()->fiber_wait_mutex());
        vm()->fiber_wait_condition().notify_one();
      }
    }

    while(!vm()->running_p()) {
      ; // spin wait
    }
  }

  void Fiber::cancel(STATE) {
    if(!vm()->zombie_p()) {
      vm()->thread_state()->raise_fiber_cancel();

      wakeup();

      while(vm()->suspended_p()) {
        std::lock_guard<std::mutex> guard(vm()->fiber_wait_mutex());
        vm()->fiber_wait_condition().notify_one();
      }
    }
  }

  void Fiber::suspend_and_continue(STATE) {
    UnmanagedPhase unmanaged(state);

    {
      std::unique_lock<std::mutex> lk(vm()->fiber_wait_mutex());

      vm()->set_suspended();
      while(!wakeup_p()) {
        vm()->fiber_wait_condition().wait(lk);
      }
      clear_wakeup();
      vm()->set_resuming();
    }

    if(!vm()->canceled_p()) {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      vm()->set_running();

      while(!restart_context()->suspended_p()) {
        ; // spin wait
      }

      state->vm()->thread()->current_fiber(state, this);
    }
  }

  Object* Fiber::return_value(STATE) {
    if(vm()->thread()->nil_p()) {
      return nullptr;
    } else if(vm()->thread_state()->raise_reason() == cNone) {
      return state->vm()->thread()->fiber_value();
    } else if(canceled_p()) {
      return nullptr;
    } else {
      invoke_context()->thread_state()->set_state(vm()->thread_state());
      return nullptr;
    }
  }

  void* Fiber::run(void* ptr) {
    VM* vm = reinterpret_cast<VM*>(ptr);
    State state_obj(vm), *state = &state_obj;

    vm->set_stack_bounds(vm->fiber()->stack_size()->to_native());
    vm->set_current_thread();
    vm->set_start_time();

    RUBINIUS_THREAD_START(
        const_cast<RBX_DTRACE_CHAR_P>(
          vm->name().c_str()), vm->fiber()->fiber_id()->to_native(), 0);

    vm->fiber()->pid(state, Fixnum::from(gettid()));

    if(state->shared().config.machine_thread_log_lifetime.value) {
      logger::write("fiber: run: %s, %d, %#x",
          vm->name().c_str(), vm->fiber()->pid()->to_native(),
          (intptr_t)pthread_self());
    }

    NativeMethod::init_thread(state);

    vm->fiber()->suspend_and_continue(state);

    Object* value = vm->fiber()->block()->send(state, G(sym_call),
        as<Array>(vm->thread()->fiber_value()), vm->fiber()->block());
    vm->set_call_frame(nullptr);

    if(value) {
      vm->thread()->fiber_value(state, value);
    } else {
      vm->thread()->fiber_value(state, cNil);
    }

    if(vm->thread_state()->raise_reason() != cFiberCancel) {
      if(vm->fiber()->status() == eTransfer) {
        // restart the root Fiber
        vm->thread()->fiber()->invoke_context(vm);
        vm->thread()->fiber()->restart(state);
      } else {
        vm->fiber()->invoke_context()->fiber()->restart(state);
      }
    }

    {
      std::lock_guard<std::mutex> guard(vm->fiber_wait_mutex());

      vm->fiber()->status(eDead);
      vm->set_suspended();
    }

    vm->unmanaged_phase(state);

    state->shared().report_profile(state);

    NativeMethod::cleanup_thread(state);

    if(state->shared().config.machine_fiber_log_lifetime.value) {
      logger::write("fiber: exit: %s %fs", vm->name().c_str(), vm->run_time());
    }

    vm->set_zombie(state);

    RUBINIUS_THREAD_STOP(
        const_cast<RBX_DTRACE_CHAR_P>(
          vm->name().c_str()), vm->fiber()->fiber_id()->to_native(), 0);

    return 0;
  }

  Fiber* Fiber::current(STATE) {
    return state->vm()->fiber();
  }

  /* This creates the pseudo-Fiber for the Thread. This provides a uniform
   * means of expressing things like Fiber.current.
   */
  Fiber* Fiber::create(STATE, VM* vm) {
    Fiber* fiber = state->memory()->new_object_pinned<Fiber>(state, G(fiber));

    vm->set_fiber(fiber);
    vm->set_running();

    fiber->vm(vm);
    fiber->pid(vm->thread()->pid());
    fiber->stack_size(vm->thread()->stack_size());
    fiber->thread_name(state, String::create(state, vm->name().c_str()));
    fiber->fiber_id(Fixnum::from(0));
    fiber->status(eRunning);
    fiber->invoke_context(vm);

    return fiber;
  }

  Fiber* Fiber::create(STATE, Object* self, Object* stack_size, Object* block) {
    Fiber* fiber = state->memory()->new_object_pinned<Fiber>(state, as<Class>(self));
    fiber->block(state, block);

    std::ostringstream name;
    name << "fiber." << fiber->fiber_id()->to_native();

    {
      BlockPhase blocking(state);
      fiber->vm(state->vm()->thread_nexus()->new_vm(&state->shared(), name.str().c_str()));
    }

    fiber->vm()->set_kind(memory::ManagedThread::eFiber);
    fiber->vm()->set_suspending();

    if(Fixnum* size = try_as<Fixnum>(stack_size)) {
      fiber->vm()->validate_stack_size(state, size->to_native());
      fiber->stack_size(state, size);
    }

    fiber->vm()->set_thread(state->vm()->thread());
    fiber->vm()->set_fiber(fiber);

    state->memory()->native_finalizer(state, fiber,
        (memory::FinalizerFunction)&Fiber::finalize);

    if(state->shared().config.machine_fiber_log_lifetime.value) {
      const std::regex& filter = state->shared().config.machine_fiber_log_filter();

      if(CallFrame* call_frame = state->vm()->get_filtered_frame(state, filter)) {
        std::ostringstream source;

        source << call_frame->file(state)->cpp_str(state).c_str()
          << ":" << call_frame->line(state);

        logger::write("fiber: new: %s, %d, %s",
            fiber->thread_name()->c_str(state),
            fiber->fiber_id()->to_native(), source.str().c_str());

        fiber->source(state, String::create(state, source.str().c_str()));
      } else {
        logger::write("fiber: new: %s, %d",
            fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
      }
    }

    state->vm()->metrics().system.fibers_created++;

    return fiber;
  }

  String* Fiber::status(STATE) {
    switch(status()) {
      case eCreated:
        return String::create(state, "created");
        break;
      case eRunning:
        return String::create(state, "run");
        break;
      case eYielding:
        return String::create(state, "yield");
        break;
      case eTransfer:
        return String::create(state, "transfer");
        break;
      case eDead:
        return String::create(state, "dead");
        break;
    }

    return String::create(state, "unknown");
  }

  Object* Fiber::resume(STATE, Arguments& args) {
    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(state->vm()->thread() != thread()) {
        Exception::raise_fiber_error(state, "attempt to resume fiber across threads");
      } else if(status() == eTransfer) {
        Exception::raise_fiber_error(state, "attempt to resume transfered fiber");
      } else if(status() == eRunning) {
        Exception::raise_fiber_error(state, "attempt to resume running fiber");
      } else if(status() == eDead) {
        Exception::raise_fiber_error(state, "attempt to resume dead fiber");
      } else if(root_p()) {
        Exception::raise_fiber_error(state, "attempt to resume root fiber");
      }

      unpack_arguments(state, args);
      invoke_context(state->vm());

      if(status() == eCreated) {
        start(state, args);
      }

      status(eRunning);
    }

    // Being cooperative...
    restart(state);

    // Through the worm hole...
    state->vm()->fiber()->suspend_and_continue(state);

    // We're back...
    return return_value(state);
  }

  Object* Fiber::transfer(STATE, Arguments& args) {
    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(state->vm()->thread() != thread()) {
        Exception::raise_fiber_error(state, "attempt to transfer fiber across threads");
      } else if(status() == eDead) {
        Exception::raise_fiber_error(state, "attempt to transfer to dead fiber");
      } else if(state->vm()->fiber() == this) {
        // This should arguably be a FiberError
        return args.as_array(state);
      }

      unpack_arguments(state, args);
      invoke_context(state->vm());

      if(status() == eCreated) {
        start(state, args);
      }

      status(eTransfer);
    }

    // Being cooperative...
    restart(state);

    // Through the worm hole...
    state->vm()->fiber()->suspend_and_continue(state);

    // We're back...
    return return_value(state);
  }

  Object* Fiber::dispose(STATE) {
    cancel(state);

    return this;
  }

  Object* Fiber::s_yield(STATE, Arguments& args) {
    Fiber* fiber = state->vm()->fiber();
    ThreadState* thread_state = state->vm()->thread_state()->state_as_object(state);
    OnStack<2> os(state, fiber, thread_state);

    {
      std::lock_guard<std::mutex> guard(state->vm()->thread()->fiber_mutex());

      if(fiber->root_p()) {
        Exception::raise_fiber_error(state, "can't yield from root fiber");
      } else if(fiber->status() == eTransfer) {
        Exception::raise_fiber_error(state, "can't yield from transferred fiber");
      }

      fiber->unpack_arguments(state, args);
      fiber->status(eYielding);
    }

    // Being cooperative...
    state->vm()->thread_state()->clear_raise();
    fiber->invoke_context()->fiber()->restart(state);

    // Through the worm hole...
    fiber->suspend_and_continue(state);

    if(fiber->canceled_p()) return nullptr;

    if(!thread_state->nil_p()) {
      state->vm()->thread_state()->set_state(state, thread_state);
    }

    // We're back...
    return state->vm()->thread()->fiber_value();
  }

  Array* Fiber::s_list(STATE) {
    return state->shared().vm_fibers(state);
  }

  Fiber* Fiber::s_main(STATE) {
    return state->vm()->thread()->fiber();
  }

  Fixnum* Fiber::s_count(STATE) {
    return state->shared().vm_fibers_count(state);
  }

  void Fiber::finalize(STATE, Fiber* fiber) {
    if(state->shared().config.machine_fiber_log_finalizer.value) {
      logger::write("fiber: finalizer: %s, %d",
          fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
    }

    if(fiber->vm()) {
      if(!state->shared().halting_p()) {
        if(!fiber->vm()->zombie_p()) {
          fiber->cancel(state);
        }
      }

      if(fiber->vm()->zombie_p()) {
        VM::discard(state, fiber->vm());
        fiber->vm(nullptr);
      } else {
        logger::write("fiber: finalizer: fiber not completed: %s, %d",
            fiber->thread_name()->c_str(state), fiber->fiber_id()->to_native());
      }
    }
  }
}
<|endoftext|>


<commit_before>//  VERSION_STRING 1_1
//  madthreading version.hh configuration header file  ---------------------//

//  (C) Copyright Jonathan Madsen 2015.

#ifndef madthreading_VERSION_HH_
#define madthreading_VERSION_HH_

//
//  Caution, this is the only madthreading header that is guarenteed
//  to change with every release, including this header
//  will cause a recompile every time a new madthreading version is
//  released.
//
//  madthreading_VERSION % 100 is the patch level
//  madthreading_VERSION / 100 % 1000 is the minor version
//  madthreading_VERSION / 100000 is the major version

#define madthreading_VERSION 010100

//
//  madthreading_LIB_VERSION must be defined to be the same as
//  madthreading_VERSION but as a *string* in the form "x_y[_z]" where x is
//  the major version number, y is the minor version number, and z is the patch
//  level if not 0.

#define madthreading_LIB_VERSION "1_1"

#endif
<commit_msg>Updated version header<commit_after>//  VERSION_STRING 2_0_1
//  madthreading version.hh configuration header file  ---------------------//

//  (C) Copyright Jonathan Madsen 2015.

#ifndef madthreading_VERSION_HH_
#define madthreading_VERSION_HH_

//
//  Caution, this is the only madthreading header that is guarenteed
//  to change with every release, including this header
//  will cause a recompile every time a new madthreading version is
//  released.
//
//  madthreading_VERSION % 100 is the patch level
//  madthreading_VERSION / 100 % 1000 is the minor version
//  madthreading_VERSION / 100000 is the major version

#define madthreading_VERSION 020001

//
//  madthreading_LIB_VERSION must be defined to be the same as
//  madthreading_VERSION but as a *string* in the form "x_y[_z]" where x is
//  the major version number, y is the minor version number, and z is the patch
//  level if not 0.

#define madthreading_LIB_VERSION "2_0_1"

#endif
<|endoftext|>