Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This fixes the core compilation issues with MSVC12 #2411

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
87 changes: 83 additions & 4 deletions hpx/lcos/local/composable_guard.hpp
Expand Up @@ -2,6 +2,81 @@
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// How do guards work:
// Pythonesque pseudocode
//
// class guard:
// task # an atomic pointer to a guard_task
//
// class guard_task:
// run # a function pointer of some kind
// next # an atomic pointer to another guard task
//
// def run_guarded(g,func):
// n = new guard_task
// n.run = func
// t = g.task.exchange(n)
// if t == nullptr:
// run_task(n)
// else:
// zero = nullptr
// if t.next.compare_exchange_strong(zero,n):
// pass
// else:
// run_task(n)
// delete t
//
// def run_task(t):
// t.run() // call the task
// zero = nullptr
// if t.next.compare_exchange_strong(zero,t):
// pass
// else:
// run_task(zero)
// delete t
//
// Consider cases. Thread A, B, and C on guard g.
// Case 1:
// Thread A runs on guard g, gets t == nullptr and runs to completion.
// Thread B starts, gets t != null, compare_exchange_strong fails,
// it runs to completion and deletes t
// Thread C starts, gets t != null, compare_exchange_strong fails,
// it runs to completion and deletes t
//
// Case 2:
// Thread A runs on guard g, gets t == nullptr,
// but before it completes, thread B starts.
// Thread B runs on guard g, gets t != nullptr,
// compare_exchange_strong succeeds. It does nothing further.
// Thread A resumes and finishes, compare_exchange_strong fails,
// it runs B's task to completion.
// Thread C starts, gets t != null, compare_exchange_strong fails,
// it runs to completion and deletes t
//
// Case 3:
// Thread A runs on guard g, gets t == nullptr,
// but before it completes, thread B starts.
// Thread B runs on guard g, gets t != nullptr,
// compare_exchange_strong succeeds, It does nothing further.
// Thread C runs on guard g, gets t != nullptr,
// compare_exchange_strong succeeds, It does nothing further.
// Thread A resumes and finishes, compare_exchange_strong fails,
// it runs B's task to completion.
// Thread B does compare_exchange_strong fails,
// it runs C's task to completion.
//
// def destructor guard:
// t = g.load()
// if t == nullptr:
// pass
// else:
// zero = nullptr
// if t.next.compare_exchange_strong(zero,empty):
// pass
// else:
// delete t

#ifndef HPX_LCOS_LOCAL_COMPOSABLE_GUARD_HPP
#define HPX_LCOS_LOCAL_COMPOSABLE_GUARD_HPP

Expand All @@ -10,6 +85,7 @@
#include <hpx/util/deferred_call.hpp>
#include <hpx/util/unique_function.hpp>
#include <hpx/util_fwd.hpp>
#include <hpx/lcos/local/packaged_task.hpp>

#include <boost/atomic.hpp>

Expand Down Expand Up @@ -62,9 +138,7 @@ namespace hpx { namespace lcos { namespace local
detail::guard_atomic task;

guard() : task(nullptr) {}
~guard() {
detail::free(task.load());
}
HPX_API_EXPORT ~guard();
};

class guard_set : public detail::debug_object
Expand All @@ -78,15 +152,20 @@ namespace hpx { namespace lcos { namespace local

public:
guard_set() : guards(), sorted(true) {}
~guard_set() {}
~guard_set() {}

std::shared_ptr<guard> get(std::size_t i) { return guards[i]; }

void add(std::shared_ptr<guard> const& guard_ptr) {
HPX_ASSERT(guard_ptr.get() != nullptr);
guards.push_back(guard_ptr);
sorted = false;
}

std::size_t size() {
return guards.size();
}

friend HPX_API_EXPORT void run_guarded(
guard_set& guards, detail::guard_function task);
};
Expand Down
47 changes: 36 additions & 11 deletions src/lcos/local/composable_guard.cpp
Expand Up @@ -3,14 +3,14 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <hpx/lcos/local/composable_guard.hpp>

#include <hpx/config.hpp>
#include <hpx/apply.hpp>
#include <hpx/util/assert.hpp>
#include <hpx/util/bind.hpp>
#include <hpx/util/function.hpp>

#include <hpx/lcos/local/composable_guard.hpp>

#include <boost/atomic.hpp>

#include <algorithm>
Expand All @@ -24,6 +24,8 @@ namespace hpx { namespace lcos { namespace local
static void run_composable(detail::guard_task* task);
static void run_async(detail::guard_task* task);

static void nothing() {}

namespace detail
{
// A link in the list of tasks attached
Expand All @@ -35,9 +37,9 @@ namespace hpx { namespace lcos { namespace local
bool const single_guard;

guard_task()
: next(nullptr), run(nullptr), single_guard(true) {}
: next(nullptr), run(nothing), single_guard(true) {}
guard_task(bool sg)
: next(nullptr), run(nullptr), single_guard(sg) {}
: next(nullptr), run(nothing), single_guard(sg) {}
};

void free(guard_task* task)
Expand All @@ -62,20 +64,25 @@ namespace hpx { namespace lcos { namespace local
{
guard_set gs;
detail::guard_function task;
std::size_t n;
detail::guard_task** stages;

stage_data(detail::guard_function task_,
std::vector<std::shared_ptr<guard> >& guards)
: task(std::move(task_))
, stages(new detail::guard_task*[guards.size()])
: gs()
, task(std::move(task_))
, n(guards.size())
, stages(new detail::guard_task*[n])
{
std::size_t const n = guards.size();
for (std::size_t i=0; i<n; i++) {
stages[i] = new detail::guard_task(false);
}
}

~stage_data() {
if(stages == nullptr)
abort();
HPX_ASSERT(n == gs.size());
delete[] stages;
stages = nullptr;
}
Expand All @@ -90,7 +97,7 @@ namespace hpx { namespace lcos { namespace local
prev->check();
detail::guard_task* zero = nullptr;
if (!prev->next.compare_exchange_strong(zero, task)) {
run_async(task);
run_composable(task);
free(prev);
}
} else {
Expand All @@ -116,7 +123,7 @@ namespace hpx { namespace lcos { namespace local
zero = nullptr;
if (!lt->next.compare_exchange_strong(zero, lt)) {
HPX_ASSERT(zero != lt);
run_async(zero);
run_composable(zero);
free(lt);
}
}
Expand Down Expand Up @@ -190,22 +197,40 @@ namespace hpx { namespace lcos { namespace local
HPX_ASSERT(task != nullptr);
task->check();
if (!task->next.compare_exchange_strong(zero, task)) {
HPX_ASSERT(task->next.load()!=nullptr);
run_async(zero);
HPX_ASSERT(zero != nullptr);
run_composable(zero);
free(task);
}
}
};

using hpx::lcos::local::detail::guard_task;
guard_task *empty = new guard_task;

static void run_composable(detail::guard_task* task)
{
if(task == empty)
return;
HPX_ASSERT(task != nullptr);
task->check();
if (task->single_guard) {
run_composable_cleanup rcc(task);
task->run();
} else {
task->run();
// Note that by this point in the execution
// the task data structure has probably
// been deleted.
}
}

guard::~guard() {
guard_task *zero = nullptr;
guard_task *current = task.load();
if(current == nullptr)
return;
if(!current->next.compare_exchange_strong(zero,empty)) {
free(zero);
}
}
}}}