// 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(observer* item_) : item(item_) { }
observer* item;
std::unordered_set incoming_edges;
std::unordered_set outgoing_edges;
};
struct transaction {
std::unordered_map dag;
};
static thread_local dynvar> current_dependencies;
static thread_local dynvar current_transaction;
inline void observer::clear_dependencies() {
for (auto const& dep : dependencies) {
std::shared_ptr sdep = dep.lock();
if (sdep) {
(*sdep)->remove_dependent(this);
}
}
dependencies = {};
}
inline void observer::add_dependent(observer* dependent) {
dependents.push_front(dependent->self);
}
inline void observer::remove_dependent(observer* dependent) {
dependents.remove_if([&](std::weak_ptr const& other) -> bool {
std::shared_ptr other2 = other.lock();
return (!other2 || *other2 == dependent);
});
}
inline void observer::reset_dependencies(std::forward_list const& newdeps) {
clear_dependencies();
for (auto const& dep : newdeps) {
dependencies.push_front(dep->self);
dep->add_dependent(this);
}
}
inline void observer::mark() {
if (!current_transaction) {
with_transaction([&]() { this->mark(); });
return;
}
if (current_transaction->dag.find(this) ==
current_transaction->dag.end()) {
std::unordered_set incoming_edges;
std::unordered_set outgoing_edges;
dag_node* node = new dag_node(this);
current_transaction->dag[this] = node;
for (auto e = dependents.cbegin(); e != dependents.end(); e++) {
std::weak_ptr const& x = *e;
std::shared_ptr px = x.lock();
if (px) {
(*px)->mark();
dag_node* xn = current_transaction->dag[*px];
xn->incoming_edges.insert(node);
node->outgoing_edges.insert(xn);
} else {
// Purge dead nodes.
dependents.erase(e);
}
}
}
}
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(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);
}
}
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