// Copyright 2012, Matthias Andreas Benkard.
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program. If not, see
// .
#pragma once
#ifndef CELLS_IMPL_HPP
#define CELLS_IMPL_HPP
#include "cells.hpp"
#include "dynvars.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#ifndef thread_local
//#define thread_local __thread
#define thread_local
#endif
namespace cells {
using std::cout;
using std::cerr;
using std::endl;
using namespace dynvars;
struct dag_node {
dag_node(std::shared_ptr item_) : item(item_) { }
std::shared_ptr item;
std::unordered_set incoming_edges;
std::unordered_set outgoing_edges;
};
struct transaction {
std::unordered_map, std::shared_ptr> dag;
};
static thread_local dynvar>> current_dependencies;
static thread_local dynvar current_transaction;
inline void observer::clear_dependencies() {
for (auto const& dep : dependencies) {
dep->remove_dependent(this);
}
dependencies = {};
}
inline void observer::add_dependent(std::shared_ptr dependent) {
dependents.push_front(dependent);
}
inline void observer::remove_dependent(observer* dependent) {
dependents.remove_if([&](std::weak_ptr const& other) -> bool {
std::shared_ptr other2 = other.lock();
// note: this should also work for empty other
return (other2.get() == dependent);
});
}
inline void observer::reset_dependencies(std::forward_list> const& newdeps) {
clear_dependencies();
dependencies = newdeps;
for (auto const& dep : newdeps) {
dep->add_dependent(shared_from_this());
}
}
inline void observer::mark() {
if (!current_transaction) {
with_transaction([&]() { this->mark(); });
return;
}
std::shared_ptr self = shared_from_this();
if (current_transaction->dag.find(self) ==
current_transaction->dag.end()) {
std::unordered_set incoming_edges;
std::unordered_set outgoing_edges;
std::shared_ptr node = std::make_shared(self);
current_transaction->dag[self] = std::shared_ptr(node);
for (std::weak_ptr x : dependents) {
std::shared_ptr px = x.lock();
if (px) {
px->mark();
std::shared_ptr xn = current_transaction->dag[px];
xn->incoming_edges.insert(node.get());
node->outgoing_edges.insert(xn.get());
}
}
}
}
inline observer::~observer() {
clear_dependencies();
}
template
cell::cell() {
}
template
void cell::update() {
T oldval = current_value;
with>, void>
(current_dependencies,
std::forward_list>(),
[=]{
current_value = recompute(current_value);
reset_dependencies(*current_dependencies);
});
}
template
T& cell::get() {
if (current_dependencies) {
current_dependencies->push_front(shared_from_this());
return current_value;
} else {
return current_value;
}
}
template
cell::~cell() {
}
template
T formula_cell::recompute(T old) {
return alt_formula(old);
}
template
T formula_cell::init() {
return formula();
}
template
void formula_cell::reset(T value) {
this->alt_formula = ([=](T) { return value; });
this->formula = ([=]() { return value; });
this->mark();
}
template
void formula_cell::reset(std::function formula) {
this->formula = formula;
this->alt_formula = ([=](T old) { return formula(); });
this->mark();
}
template
void formula_cell::reset(std::function formula,
std::function alt_formula) {
this->formula = formula;
this->alt_formula = alt_formula;
this->mark();
}
template
formula_cell::~formula_cell() {
}
static void with_transaction(std::function thunk) {
using std::cout;
using std::cerr;
using std::endl;
with(current_transaction, transaction(), [&]() -> void {
//cerr << "; begin transaction." << endl;
thunk();
//cerr << "; number of affected nodes: " << current_transaction->dag.size() << endl;
std::deque> nodes;
// topological sort
std::forward_list independent_nodes;
auto left = current_transaction->dag.size();
for (auto const& o_and_n : current_transaction->dag) {
auto node = o_and_n.second;
if (node->incoming_edges.size() == 0) {
independent_nodes.push_front(node.get());
}
}
while (!independent_nodes.empty()) {
left--;
auto node = independent_nodes.front();
independent_nodes.pop_front();
nodes.push_back(node->item);
// Or we can do away with the nodes list and just do:
// node->item->update()
// which makes transactions non-transactional but improves
// performance.
for (dag_node* other : node->outgoing_edges) {
other->incoming_edges.erase(node);
if (other->incoming_edges.size() == 0) {
independent_nodes.push_front(other);
}
}
}
if (left != 0) {
throw std::logic_error("Cell cycle detected");
}
for (auto const& node : nodes) {
node->update();
}
});
}
}
#endif //CELLS_IMPL_HPP