// 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