| 1 | // Please use attached simplified file to reproduce the problem.
|
|---|
| 2 | // I used boost 1.59 with VS2015.
|
|---|
| 3 | // Reproduced in all 4 configurations (debug/release, x86/x64).
|
|---|
| 4 | // I'm not sure if it's a bug or limitation of multi_index, but commenting any line in fill_test_date() fixes the problem, so seems like a bug in some specific conditions.
|
|---|
| 5 | // If it's multi_index limitation, please mention this in description of modify/modify_key.
|
|---|
| 6 | //
|
|---|
| 7 | // The problem:
|
|---|
| 8 | // multi_index container has two keys ordered_unique and ordered_non_unique.
|
|---|
| 9 | // In calc_layer() I call modify() on first index and change the key of second index.
|
|---|
| 10 | // During calculation of new key for second index, other recursive calls to modify() may be performed.
|
|---|
| 11 | // All calls to modify() return true but in some cases second index is not correctly ordered in we see message "WRONG SORT ORDER BY LAYER".
|
|---|
| 12 |
|
|---|
| 13 | #include <set>
|
|---|
| 14 | #include <string>
|
|---|
| 15 | #include <boost/algorithm/string.hpp>
|
|---|
| 16 | #include <boost/multi_index_container.hpp>
|
|---|
| 17 | #include <boost/multi_index/member.hpp>
|
|---|
| 18 | #include <boost/multi_index/ordered_index.hpp>
|
|---|
| 19 |
|
|---|
| 20 | namespace mi = ::boost::multi_index;
|
|---|
| 21 |
|
|---|
| 22 | #define UNINITIALIZED -1
|
|---|
| 23 | #define CALCULATION_IN_PROGRESS -2
|
|---|
| 24 |
|
|---|
| 25 | struct node_info_t
|
|---|
| 26 | {
|
|---|
| 27 | node_info_t(const std::string& name)
|
|---|
| 28 | : name(name)
|
|---|
| 29 | , layer(UNINITIALIZED)
|
|---|
| 30 | {
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | std::string name;
|
|---|
| 34 | std::set<std::string> deps;
|
|---|
| 35 | int layer;
|
|---|
| 36 | };
|
|---|
| 37 |
|
|---|
| 38 | struct by_name {};
|
|---|
| 39 | struct by_layer {};
|
|---|
| 40 |
|
|---|
| 41 | typedef mi::multi_index_container<
|
|---|
| 42 | node_info_t,
|
|---|
| 43 | mi::indexed_by<
|
|---|
| 44 | mi::ordered_unique <mi::tag<by_name >, mi::member<node_info_t, std::string, &node_info_t::name> >,
|
|---|
| 45 | mi::ordered_non_unique<mi::tag<by_layer>, mi::member<node_info_t, int, &node_info_t::layer> >
|
|---|
| 46 | >
|
|---|
| 47 | > node_deps_map_t;
|
|---|
| 48 |
|
|---|
| 49 | typedef node_deps_map_t::index<by_name >::type index_by_name_t;
|
|---|
| 50 | typedef node_deps_map_t::index<by_layer>::type index_by_layer_t;
|
|---|
| 51 |
|
|---|
| 52 | node_deps_map_t deps_map;
|
|---|
| 53 |
|
|---|
| 54 | void add_node(const char* node_name, const char* node_deps_string)
|
|---|
| 55 | {
|
|---|
| 56 | auto res = deps_map.emplace(node_name);
|
|---|
| 57 | if (!res.second)
|
|---|
| 58 | {
|
|---|
| 59 | printf("Already inserted.\n");
|
|---|
| 60 | }
|
|---|
| 61 | else if (*node_deps_string)
|
|---|
| 62 | {
|
|---|
| 63 | deps_map.modify(res.first,
|
|---|
| 64 | [=](node_info_t& node_info)
|
|---|
| 65 | {
|
|---|
| 66 | boost::split(node_info.deps, node_deps_string, boost::is_any_of(" "), boost::algorithm::token_compress_on);
|
|---|
| 67 | });
|
|---|
| 68 | }
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | bool calc_layer(index_by_name_t::iterator it)
|
|---|
| 72 | {
|
|---|
| 73 | if (it->layer == CALCULATION_IN_PROGRESS)
|
|---|
| 74 | return printf("Circular reference.\n"), false;
|
|---|
| 75 | if (it->layer != UNINITIALIZED)
|
|---|
| 76 | return true;
|
|---|
| 77 |
|
|---|
| 78 | bool succeeded = deps_map.modify(it,
|
|---|
| 79 | [=](node_info_t& node_info)
|
|---|
| 80 | {
|
|---|
| 81 | if (node_info.deps.size() == 0)
|
|---|
| 82 | {
|
|---|
| 83 | node_info.layer = 0;
|
|---|
| 84 | }
|
|---|
| 85 | else
|
|---|
| 86 | {
|
|---|
| 87 | index_by_name_t& index_by_name = deps_map.get<by_name>();
|
|---|
| 88 | node_info.layer = CALCULATION_IN_PROGRESS;
|
|---|
| 89 | for (const std::string& dep_name : node_info.deps)
|
|---|
| 90 | {
|
|---|
| 91 | index_by_name_t::iterator dep_it = index_by_name.find(dep_name);
|
|---|
| 92 | if (dep_it == deps_map.end())
|
|---|
| 93 | {
|
|---|
| 94 | node_info.layer = UNINITIALIZED;
|
|---|
| 95 | printf("Unknown dependency.\n");
|
|---|
| 96 | return;
|
|---|
| 97 | }
|
|---|
| 98 | if (!calc_layer(dep_it))
|
|---|
| 99 | {
|
|---|
| 100 | node_info.layer = UNINITIALIZED;
|
|---|
| 101 | return;
|
|---|
| 102 | }
|
|---|
| 103 | node_info.layer = std::max(node_info.layer, dep_it->layer + 1);
|
|---|
| 104 | }
|
|---|
| 105 | }
|
|---|
| 106 | });
|
|---|
| 107 |
|
|---|
| 108 | if (!succeeded)
|
|---|
| 109 | return printf("modify() failed.\n"), false;
|
|---|
| 110 |
|
|---|
| 111 | return it->layer >= 0;
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | void fill_test_data()
|
|---|
| 115 | {
|
|---|
| 116 | add_node("m1", "");
|
|---|
| 117 | add_node("b1", "");
|
|---|
| 118 | add_node("o1", "");
|
|---|
| 119 | add_node("a3", "");
|
|---|
| 120 | add_node("t1", "");
|
|---|
| 121 | add_node("m2", "");
|
|---|
| 122 | add_node("c1", "b1");
|
|---|
| 123 | add_node("r1", "b1");
|
|---|
| 124 | add_node("t2", "b1");
|
|---|
| 125 | add_node("o2", "b1");
|
|---|
| 126 | add_node("h1", "b1");
|
|---|
| 127 | add_node("h2", "b1");
|
|---|
| 128 | add_node("d1", "b1");
|
|---|
| 129 | add_node("x1", "b1");
|
|---|
| 130 | add_node("h3", "b1");
|
|---|
| 131 | add_node("m3", "b1");
|
|---|
| 132 | add_node("l1", "b1 c1");
|
|---|
| 133 | add_node("w1", "b1 c1");
|
|---|
| 134 | add_node("g1", "b1 t2");
|
|---|
| 135 | add_node("s1", "b1 r1");
|
|---|
| 136 | add_node("s2", "b1 r1 t2 l1 d1 o2 m1");
|
|---|
| 137 | add_node("a6", "b1 s2");
|
|---|
| 138 | add_node("m4", "b1 s2 o2");
|
|---|
| 139 | add_node("a4", "b1 s2 o2 l1");
|
|---|
| 140 | add_node("w2", "b1 s2 c1 w1");
|
|---|
| 141 | add_node("s3", "b1 s2 o2 h2");
|
|---|
| 142 | add_node("s4", "b1 s2 o2 h2 t2");
|
|---|
| 143 | add_node("a5", "b1 s2 d1 r1 w1");
|
|---|
| 144 | add_node("g2", "b1 s2 r1 c1 l1");
|
|---|
| 145 | add_node("b2", "b1 s2 r1 d1 a5");
|
|---|
| 146 | add_node("s5", "b1 s2 r1");
|
|---|
| 147 | add_node("w3", "b1 x1");
|
|---|
| 148 | add_node("m5", "b1 s2");
|
|---|
| 149 | add_node("m6", "b1 s2 o2 m4");
|
|---|
| 150 | add_node("m7", "b1 s2");
|
|---|
| 151 | add_node("n1", "b1 s2 l1");
|
|---|
| 152 | add_node("p1", "b1 s2 l1 x1");
|
|---|
| 153 | add_node("s6", "b1 s2 l1 g1 t2");
|
|---|
| 154 | add_node("c2", "b1 s2 o2 m4");
|
|---|
| 155 | add_node("c3", "b1 s2 l1 o2 w1 m5");
|
|---|
| 156 | add_node("c4", "b1 s2 l1 g1 t2");
|
|---|
| 157 | add_node("d2", "b1 s2 l1 o2");
|
|---|
| 158 | add_node("a1", "b1 s2 t2 g1 a4 a6 t1 m2 a3 d1 s1 o1 a5 b2 w1 l1");
|
|---|
| 159 | add_node("a2", "b1 s2 t2 g1 a4 a6 t1 m2 r1 a3 d1 b2 s3 s4 o2 s1 o1 a5 w1 l1 c1");
|
|---|
| 160 | }
|
|---|
| 161 |
|
|---|
| 162 | int main(int, char*[])
|
|---|
| 163 | {
|
|---|
| 164 | fill_test_data();
|
|---|
| 165 |
|
|---|
| 166 | for (auto it = deps_map.begin(); it != deps_map.end(); ++it)
|
|---|
| 167 | {
|
|---|
| 168 | calc_layer(it);
|
|---|
| 169 | }
|
|---|
| 170 |
|
|---|
| 171 | index_by_layer_t& index_by_layer = deps_map.get<by_layer>();
|
|---|
| 172 | if (!std::is_sorted(index_by_layer.begin(), index_by_layer.end(),
|
|---|
| 173 | [](const node_info_t& l, const node_info_t& r) { return l.layer < r.layer; }))
|
|---|
| 174 | printf("\nWRONG SORT ORDER BY LAYER\n");
|
|---|
| 175 |
|
|---|
| 176 | return 0;
|
|---|
| 177 | }
|
|---|