/* concurrentset.vala * * Copyright (C) 2012 Maciej Piechotka * * This library 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 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Maciej Piechotka */ /** * A skip-linked list. This implementation is based on * [[http://www.cse.yorku.ca/~ruppert/Mikhail.pdf|Mikhail Fomitchev Master Thesis]]. * * Many threads are allowed to operate on the same structure as well as modification * of structure during iteration is allowed. However the change may not be immidiatly * visible to other threads. */ public class Gee.ConcurrentSet : AbstractSortedSet { public ConcurrentSet (owned CompareDataFunc? compare_func = null) { if (compare_func == null) { compare_func = Functions.get_compare_func_for (typeof (G)); } _cmp = compare_func; } ~ConcurrentSet () { HazardPointer.Context ctx = new HazardPointer.Context (); _head = null; } public override int size { get { return GLib.AtomicInt.get (ref _size); } } public override bool read_only { get { return false; } } public override Gee.Iterator iterator () { return new Iterator (this, _head); } public override bool contains (G key) { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _head; return Tower.search (_cmp, key, ref prev, null); } public override bool add (G key) { HazardPointer.Context ctx = new HazardPointer.Context (); Rand *rnd = rand.get (); if (rnd == null) { rand.set (rnd = new Rand ()); } uint32 rand_int = rnd->int_range (0, int32.MAX); uint8 height = 1 + (uint8)GLib.Bit.nth_lsf (~rand_int, -1); TowerIter prev = TowerIter(); prev._iter[height - 1] = _head; if (Tower.search (_cmp, key, ref prev._iter[height - 1], null, height - 1)) { return false; } for (int i = height - 2; i >= 0; i--) { prev._iter[i] = prev._iter[height - 1]; } Tower? result = Tower.insert (_cmp, ref prev, key, height - 1); if (result != null) { GLib.AtomicInt.inc (ref _size); } return result != null; } public override bool remove (G item) { HazardPointer.Context ctx = new HazardPointer.Context (); TowerIter prev = TowerIter(); for (int i = 0; i < _MAX_HEIGHT; i++) { prev._iter[i] = _head; } bool result = Tower.remove_key (_cmp, ref prev, item); if (result) { GLib.AtomicInt.dec_and_test (ref _size); } return result; } public override void clear () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? first; while ((first = _head.get_next (0)) != null) { remove (first._data); } } public override G first () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? prev = null; Tower curr = _head; if (Tower.proceed (_cmp, ref prev, ref curr, 0)) { return curr._data; } else { return null; } } public override G last () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? prev = null; Tower curr = _head; bool found = false; for (int i = _MAX_HEIGHT; i >= 0; i--) { while (Tower.proceed (_cmp, ref prev, ref curr, 0)) { found = true; } } if (!found) { return null; } return curr._data; } public override Gee.Iterator? iterator_at (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); TowerIter prev = TowerIter (); TowerIter curr; for (int i = 0; i < _MAX_HEIGHT; i++) { prev._iter[i] = _head; } if (!Tower.search_from_bookmark (_cmp, element, ref prev, out curr)) { return null; } return new Iterator.point_at (this, ref prev, curr._iter[0]); } public override G? lower (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _head; Tower.search (_cmp, element, ref prev); if (prev == _head) { return null; } return prev._data; } public override G? higher (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _head; Tower? next; if (Tower.search (_cmp, element, ref prev, out next)) { if (!Tower.proceed (_cmp, ref prev, ref next, 0)) { return null; } } if (next == null) { return null; } return next._data; } public override G? floor (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _head; Tower? next; if (Tower.search (_cmp, element, ref prev, out next)) { return next._data; } else if (prev != _head) { return prev._data; } else { return null; } } public override G? ceil (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _head; Tower? next; Tower.search (_cmp, element, ref prev, out next); if (next == null) { return null; } return next._data; } public override SortedSet head_set (G before) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (new Range.head (this, before)); } public override SortedSet tail_set (G after) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (new Range.tail (this, after)); } public override SortedSet sub_set (G from, G to) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (new Range (this, from, to)); } private unowned G max (G a, G b, out bool changed = null) { changed = _cmp (a, b) < 0; return changed ? b : a; } private unowned G min (G a, G b, out bool changed = null) { changed = _cmp (a, b) > 0; return changed ? b : a; } #if DEBUG public void dump () { for (int i = _MAX_HEIGHT - 1; i >= 0; i--) { bool printed = false; Tower? curr = _head; State state; while ((curr = curr.get_succ (out state, (uint8)i)) != null) { if (!printed) { stderr.printf("Level %d\n", i); printed = true; } stderr.printf(" Node %p%s - %s\n", curr, state == State.NONE ? "" : state == State.MARKED ? " (MARKED)" : " (FLAGGED)", (string)curr._data); assert (curr._height > i); } } } #endif private int _size = 0; private Tower _head = new Tower.head (); private CompareDataFunc? _cmp; private static const int _MAX_HEIGHT = 31; private static Private rand = new Private((ptr) => { Rand *rnd = (Rand *)ptr; delete rnd; }); private class Iterator : Object, Traversable, Gee.Iterator { public Iterator (ConcurrentSet cset, Tower head) { _curr = head; _set = cset; assert (_curr != null); } public Iterator.point_at (ConcurrentSet cset, ref TowerIter prev, Tower curr) { _curr = curr; _set = cset; _prev = prev; assert (_curr != null); } public new bool foreach (ForallFunc f) { assert (_curr != null); HazardPointer.Context ctx = new HazardPointer.Context (); if (_prev._iter[0] != null && !_removed) { if (!f (_curr._data)) { assert (_curr != null); return false; } } Tower new_prev = _prev._iter[0]; Tower? new_curr = _curr; while (Tower.proceed (_set._cmp, ref new_prev, ref new_curr, 0)) { assert (_curr != null); if (!_removed) { //FIXME: Help mark/delete on the way _prev._iter[0] = new_prev; int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { _prev._iter[i] = _prev._iter[0]; } } _curr = new_curr; _removed = false; if (!f (_curr._data)) { assert (_curr != null); return false; } } assert (_curr != null); return true; } public bool next () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? new_prev = _prev._iter[0]; Tower? new_curr = _curr; bool success = Tower.proceed (_set._cmp, ref new_prev, ref new_curr, 0); if (success) { if (!_removed) { //FIXME: Help mark/delete on the way _prev._iter[0] = (owned)new_prev; int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { _prev._iter[i] = _prev._iter[0]; } } _curr = (owned)new_curr; _removed = false; } assert (_curr != null); return success; } public bool has_next () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower prev = _prev._iter[0]; Tower? curr = _curr; return Tower.proceed (_set._cmp, ref prev, ref curr, 0); } public new G get () { assert (valid); return _curr._data; } public void remove () { HazardPointer.Context ctx = new HazardPointer.Context (); assert (valid); if (Tower.remove (_set._cmp, ref _prev, _curr)) { AtomicInt.dec_and_test (ref _set._size); } _removed = true; } public bool valid { get { return _prev._iter[0] != null && !_removed; } } public bool read_only { get { return true; } } private bool _removed = false; private ConcurrentSet _set; private TowerIter _prev; private Tower _curr; } private class SubSet : AbstractSortedSet { public override int size { get { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? curr; Range.improve_bookmark (_range, out curr); if (curr != null) { int acc = 1; Tower? prev = HazardPointer.get_pointer> (&_range._bookmark._iter[0]); while (Range.proceed (_range, ref prev, ref curr, 0)) { acc++; } return acc; } else { return 0; } } } public bool is_empty { get { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? curr; Range.improve_bookmark (_range, out curr); return curr != null; } } public override bool read_only { get { return false; } } public SubSet (Range range) { _range = range; } public override Gee.Iterator iterator () { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubIterator (_range); } public override bool contains (G item) { HazardPointer.Context ctx = new HazardPointer.Context (); if (!Range.inside (_range, item)) { return false; } TowerIter prev; Range.improve_bookmark (_range, null, out prev); return Tower.search_from_bookmark (_range._set._cmp, item, ref prev); } public override bool add (G key) { HazardPointer.Context ctx = new HazardPointer.Context (); if (!Range.inside (_range, key)) { return false; } TowerIter prev; Range.improve_bookmark (_range, null, out prev); Rand *rnd = ConcurrentSet.rand.get (); if (rnd == null) { rand.set (rnd = new Rand ()); } uint32 rand_int = rnd->int_range (0, int32.MAX); uint8 height = 1 + (uint8)GLib.Bit.nth_lsf (~rand_int, -1); if (Tower.search_from_bookmark (_range._set._cmp, key, ref prev, null, height - 1)) { return false; } for (int i = height - 2; i >= 0; i--) { prev._iter[i] = prev._iter[height - 1]; } Tower? result = Tower.insert (_range._set._cmp, ref prev, key, height - 1); if (result != null) { GLib.AtomicInt.inc (ref _range._set._size); } return result != null; } public override bool remove (G key) { HazardPointer.Context ctx = new HazardPointer.Context (); if (!Range.inside (_range, key)) { return false; } TowerIter prev; Range.improve_bookmark (_range, null, out prev); // FIXME: Use full bookmark bool result = Tower.remove_key (_range._set._cmp, ref prev, key); if (result) { GLib.AtomicInt.dec_and_test (ref _range._set._size); } return result; } public override void clear () { HazardPointer.Context ctx = new HazardPointer.Context (); TowerIter prev; Tower? first; Range.improve_bookmark (_range, out first, out prev); while (first != null) { Tower.remove (_range._set._cmp, ref prev, first); Range.improve_bookmark (_range, out first, out prev); } } public override G? first () { HazardPointer.Context ctx = new HazardPointer.Context (); Tower? first; Range.improve_bookmark (_range, out first); if (first == null) { return null; } return first._data; } public override G? last () { HazardPointer.Context ctx = new HazardPointer.Context (); TowerIter prev; Range.improve_bookmark (_range, null, out prev); Tower? curr = null; for (int i = _MAX_HEIGHT - 1; i >= 0; i--) { if (curr == null) { curr = prev._iter[i].get_next ((uint8)i); if (curr == null || !Range.inside (_range, curr._data)) { curr = null; continue; } } bool improved = false; while (Range.proceed (_range, ref prev._iter[i], ref curr, (uint8)i)) { improved = true; } if (improved && i > 0) { prev._iter[i - 1] = prev._iter[i]; } } if (curr == null) { return null; } return curr._data; } public override Gee.Iterator? iterator_at (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); if (!Range.inside (_range, element)) { return null; } TowerIter prev; TowerIter next; Range.improve_bookmark (_range, null, out prev); if (!Tower.search_from_bookmark (_range._set._cmp, element, ref prev, out next)) { return null; } return new SubIterator.point_at (_range, ref prev, next._iter[0]); } public override G? lower (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); switch (Range.cmp (_range, element)) { case Range.Position.BEFORE: case Range.Position.EMPTY: return null; case Range.Position.INSIDE: TowerIter prev; Range.improve_bookmark (_range, null, out prev); Tower.search_from_bookmark (_range._set._cmp, element, ref prev); if (prev._iter[0] == _range._set._head || !Range.inside (_range, prev._iter[0]._data)) { return null; } return prev._iter[0]._data; case Range.Position.AFTER: return last (); default: assert_not_reached (); } } public override G? higher (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); switch (Range.cmp (_range, element)) { case Range.Position.BEFORE: return first (); case Range.Position.INSIDE: TowerIter prev; TowerIter curr; Range.improve_bookmark (_range, null, out prev); if (Tower.search_from_bookmark (_range._set._cmp, element, ref prev, out curr)) { if (!Tower.proceed (_range._set._cmp, ref prev._iter[0], ref curr._iter[0], 0)) { return null; } } if (curr._iter[0] == null || !Range.inside(_range, curr._iter[0]._data)) { return null; } return curr._iter[0]._data; case Range.Position.AFTER: case Range.Position.EMPTY: return null; default: assert_not_reached (); } } public override G? floor (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); switch (Range.cmp (_range, element)) { case Range.Position.BEFORE: case Range.Position.EMPTY: return null; case Range.Position.INSIDE: TowerIter curr; TowerIter prev; Range.improve_bookmark (_range, null, out prev); if (!Tower.search_from_bookmark (_range._set._cmp, element, ref prev, out curr)) { curr._iter[0] = (owned)prev._iter[0]; } if (curr._iter[0] == null || curr._iter[0].is_head () || !Range.inside(_range, curr._iter[0]._data)) { return null; } return curr._iter[0]._data; case Range.Position.AFTER: return last (); default: assert_not_reached (); } } public override G? ceil (G element) { HazardPointer.Context ctx = new HazardPointer.Context (); switch (Range.cmp (_range, element)) { case Range.Position.BEFORE: return first (); case Range.Position.INSIDE: TowerIter curr; TowerIter prev; Range.improve_bookmark (_range, null, out prev); Tower.search_from_bookmark (_range._set._cmp, element, ref prev, out curr); if (curr._iter[0] == null || !Range.inside(_range, curr._iter[0]._data)) { return null; } return curr._iter[0]._data; case Range.Position.AFTER: case Range.Position.EMPTY: return null; default: assert_not_reached (); } } public override SortedSet head_set (G before) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (Range.cut_tail (_range, before)); } public override SortedSet tail_set (G after) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (Range.cut_head (_range, after)); } public override SortedSet sub_set (G from, G to) { HazardPointer.Context ctx = new HazardPointer.Context (); return new SubSet (Range.cut (_range, from, to)); } private Range _range; } private class SubIterator : Object, Traversable, Gee.Iterator { public SubIterator (Range range) { Range.improve_bookmark (range); _range = range; } public SubIterator.point_at (Range range, ref TowerIter prev, owned Tower curr) { Range.improve_bookmark (range); _range = range; _prev = prev; _curr = curr; } public new bool foreach (ForallFunc f) { assert (_curr != null); HazardPointer.Context ctx = new HazardPointer.Context (); if (!begin ()) { return true; } if (_prev._iter[0] != null && !_removed) { if (!f (_curr._data)) { assert (_curr != null); return false; } } Tower new_prev = _prev._iter[0]; Tower? new_curr = _curr; while (Range.proceed (_range, ref new_prev, ref new_curr, 0)) { assert (_curr != null); if (!f (_curr._data)) { assert (_curr != null); return false; } if (!_removed) { //FIXME: Help mark/delete on the way _prev._iter[0] = (owned)new_prev; int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { _prev._iter[i] = _prev._iter[0]; } } _curr = (owned)new_curr; _removed = false; } assert (_curr != null); return true; } public bool next () { HazardPointer.Context ctx = new HazardPointer.Context (); if (_prev._iter[0] == null) { return begin (); } else { Tower new_prev = _prev._iter[0]; Tower new_curr = _curr; if (Range.proceed (_range, ref new_prev, ref new_curr, 0)) { if (!_removed) { //FIXME: Help mark/delete on the way _prev._iter[0] = (owned)new_prev; int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { _prev._iter[i] = _prev._iter[0]; } } _curr = (owned)new_curr; _removed = false; return true; } else { return false; } } } public bool has_next () { HazardPointer.Context ctx = new HazardPointer.Context (); if (_prev._iter[0] == null) { Tower next; Range.improve_bookmark (_range, out next); if (next != null && Range.beyond (_range, next)) { next = null; } return next != null; } else { Tower new_prev = _prev._iter[0]; Tower new_curr = _curr; return Range.proceed (_range, ref new_prev, ref new_curr, 0); } } public new G get () { assert (valid); return _curr._data; } public void remove () { HazardPointer.Context ctx = new HazardPointer.Context (); assert (valid); if (Tower.remove (_range._set._cmp, ref _prev, _curr)) { AtomicInt.dec_and_test (ref _range._set._size); } _removed = true; } public bool valid { get { bool is_valid = _prev._iter[0] != null && !_removed; assert (!is_valid || _curr != null); return is_valid; } } public bool read_only { get { return false; } } private bool begin () { if (_prev._iter[0] != null) { return true; } Range.improve_bookmark (_range, out _curr, out _prev); if (_curr == null) { for (int i = 0; i < _MAX_HEIGHT; i++) { _prev._iter[i] = null; } } return _curr != null; } private Range _range; private TowerIter _prev; private Tower? _curr = null; private bool _removed = false; } private class Range { public G? _start; public G? _end; public RangeType _type; public TowerIter _bookmark; public ConcurrentSet _set; public Range (ConcurrentSet cset, G start, G end) { _start = start; _end = end; if (cset._cmp(start, end) < 0) { _type = RangeType.BOUNDED; for (int i = 0; i < _MAX_HEIGHT; i++) { _bookmark._iter[i] = cset._head; } } else { _type = RangeType.EMPTY; } _set = cset; } public Range.head (ConcurrentSet cset, G end) { _end = end; _type = RangeType.HEAD; for (int i = 0; i < _MAX_HEIGHT; i++) { _bookmark._iter[i] = cset._head; } _set = cset; } public Range.tail (ConcurrentSet cset, G start) { _start = start; _type = RangeType.TAIL; for (int i = 0; i < _MAX_HEIGHT; i++) { _bookmark._iter[i] = cset._head; } _set = cset; } public Range.empty (ConcurrentSet cset) { _type = RangeType.EMPTY; _set = cset; } private void copy_bookmark (Range range) { for (int i = 0; i < _MAX_HEIGHT; i++) { _bookmark._iter[i] = HazardPointer.get_pointer> (&range._bookmark._iter[i]); } } public static Range cut_head (Range from, G start) { Range result = new Range.empty (from._set); switch (from._type) { case RangeType.HEAD: if (from._set._cmp (start, from._end) < 0) { result._start = start; result._end = from._end; result._type = RangeType.BOUNDED; } else { result._type = RangeType.EMPTY; } break; case RangeType.TAIL: result._start = from._set.max (from._start, start); result._type = RangeType.TAIL; break; case RangeType.BOUNDED: if (from._set._cmp (from._start, start) < 0) { result._start = from._set.max (from._start, start); result._end = from._end; result._type = RangeType.BOUNDED; } else { result._type = RangeType.EMPTY; } break; case RangeType.EMPTY: result._type = RangeType.EMPTY; break; default: assert_not_reached (); } if (result._type != RangeType.EMPTY) { improve_bookmark (from); result.copy_bookmark (from); improve_bookmark (result); } return result; } public static Range cut_tail (Range from, G end) { Range result = new Range.empty (from._set); switch (from._type) { case RangeType.HEAD: result._end = from._set.min (from._end, end); result._type = RangeType.HEAD; break; case RangeType.TAIL: if (from._set._cmp (from._start, end) < 0) { result._start = from._start; result._end = end; result._type = RangeType.BOUNDED; } else { result._type = RangeType.EMPTY; } break; case RangeType.BOUNDED: if (from._set._cmp (from._start, end) < 0) { result._start = from._start; result._end = from._set.min (from._end, end); result._type = RangeType.BOUNDED; } else { result._type = RangeType.EMPTY; } break; case RangeType.EMPTY: result._type = RangeType.EMPTY; break; default: assert_not_reached (); } if (result._type != RangeType.EMPTY) { improve_bookmark (from); result.copy_bookmark (from); improve_bookmark (result); } return result; } public static Range cut (Range from, G start, G end) { Range result = new Range.empty (from._set); result._type = RangeType.BOUNDED; switch (from._type) { case RangeType.HEAD: end = from._set.min (from._end, end); break; case RangeType.TAIL: start = from._set.max (from._start, start); break; case RangeType.BOUNDED: start = from._set.max (from._start, start); end = from._set.min (from._end, end); break; case RangeType.EMPTY: result._type = RangeType.EMPTY; break; default: assert_not_reached (); } if (result._type != RangeType.EMPTY && from._set._cmp (start, end) < 0) { result._start = start; result._end = end; result._type = RangeType.BOUNDED; } else { result._type = RangeType.EMPTY; } if (result._type != RangeType.EMPTY) { improve_bookmark (from); result.copy_bookmark (from); improve_bookmark (result); } return result; } public static void improve_bookmark (Range range, out Tower? out_curr = null, out TowerIter prev = null) { prev = TowerIter(); switch (range._type) { case RangeType.HEAD: if (&out_curr != null) { out_curr = HazardPointer.get_pointer> (&range._bookmark._iter[0]); if (&prev != null) { prev._iter[0] = (owned)out_curr; out_curr = prev._iter[0].get_next (0); } else { out_curr = out_curr.get_next (0); } } if (&prev != null) { for (int i = &out_curr != null ? 1 : 0; i < _MAX_HEIGHT; i++) { prev._iter[i] = HazardPointer.get_pointer> (&range._bookmark._iter[i]); } } break; case RangeType.EMPTY: out_curr = null; break; case RangeType.TAIL: case RangeType.BOUNDED: Tower? last_best = null; for (int i = _MAX_HEIGHT - 1; i >= 0; i--) { Tower curr = HazardPointer.get_pointer> (&range._bookmark._iter[i]); Tower curr_old = curr; assert (curr != null); Tower.backtrace (ref curr, (uint8)i); if (last_best != null && last_best != curr && Tower.compare (range._set._cmp, curr, last_best) < 0) { curr = last_best; } if (curr != curr_old) { if (!HazardPointer.compare_and_exchange_pointer> (&range._bookmark._iter[i], curr_old, curr)) { curr = HazardPointer.get_pointer> (&range._bookmark._iter[i]); } } Tower next = curr.get_next ((uint8)i); if (&out_curr != null && i == 0) { out_curr = next; } while (next != null && Tower.compare_data (range._set._cmp, next, range._start) < 0) { Tower.proceed (range._set._cmp, ref curr, ref next, (uint8)i, true); if (&curr != null && i == 0 && next != null) { out_curr = next; } if (Tower.compare_data (range._set._cmp, curr, range._start) < 0) { if (!HazardPointer.compare_and_exchange_pointer> (&range._bookmark._iter[i], curr_old, curr)) { curr = HazardPointer.get_pointer> (&range._bookmark._iter[i]); } curr_old = curr; } else { break; } } if (&prev != null) { prev._iter[i] = curr; } last_best = (owned)curr; } break; default: assert_not_reached (); } if (&out_curr != null && out_curr != null && Range.beyond (range, out_curr)) { out_curr = null; } #if DEBUG stderr.printf("Bookmark after:\n"); for (int i = _MAX_HEIGHT - 1; i >= 0; i--) { stderr.printf(" Level %d:\n", i); Tower? t = HazardPointer.get_pointer> (&range._bookmark._iter[i]); assert (t == null || t.get_height () > i); while (t != null) { stderr.printf(" %s\n", t.is_head () ? "<>" : t._data); t = t.get_next ((uint8)i); assert (t == null || t.get_height () > i); } } #endif } public static bool proceed (Range range, ref Tower? prev, ref Tower curr, uint8 level) { switch (range._type) { case RangeType.EMPTY: return false; case RangeType.TAIL: return Tower.proceed (range._set._cmp, ref prev, ref curr, level); case RangeType.HEAD: case RangeType.BOUNDED: Tower? tmp_prev = prev; Tower? tmp_curr = curr; if (!Tower.proceed (range._set._cmp, ref tmp_prev, ref tmp_curr, level)) { return false; } else if (Tower.compare_data (range._set._cmp, tmp_curr, range._end) >= 0) { return false; } else { prev = (owned)tmp_prev; curr = (owned)tmp_curr; return true; } default: assert_not_reached (); } } public static bool inside (Range range, G val) { switch (range._type) { case RangeType.HEAD: return range._set._cmp (val, range._end) < 0; case RangeType.TAIL: return range._set._cmp (val, range._start) >= 0; case RangeType.BOUNDED: return range._set._cmp (val, range._start) >= 0 && range._set._cmp (val, range._end) < 0; case RangeType.EMPTY: return false; default: assert_not_reached (); } } public static bool beyond (Range range, Tower tw) { switch (range._type) { case RangeType.EMPTY: return true; case RangeType.TAIL: return false; case RangeType.HEAD: case RangeType.BOUNDED: return Tower.compare_data (range._set._cmp, tw, range._end) >= 0; default: assert_not_reached (); } } public static int cmp (Range range, G val) { switch (range._type) { case RangeType.HEAD: return range._set._cmp (val, range._end) < 0 ? Position.INSIDE : Position.AFTER; case RangeType.TAIL: return range._set._cmp (val, range._start) >= 0 ? Position.INSIDE : Position.BEFORE; case RangeType.BOUNDED: return range._set._cmp (val, range._start) >= 0 ? (range._set._cmp (val, range._end) < 0 ? Position.INSIDE : Position.AFTER) : Position.BEFORE; case RangeType.EMPTY: return Position.EMPTY; default: assert_not_reached (); } } enum Position { BEFORE = -1, INSIDE = 0, AFTER = 1, EMPTY } } public enum RangeType { HEAD, TAIL, BOUNDED, EMPTY } private class Tower { public inline Tower (G data, uint8 height) { _nodes = new TowerNode[height]; _data = data; _height = 0; AtomicPointer.set (&_nodes[0]._backlink, null); // FIXME: This should be memory barrier } public inline Tower.head () { _nodes = new TowerNode[_MAX_HEIGHT]; _height = -1; #if DEBUG _data = (G)"<>"; #endif } inline ~Tower () { int height = get_height(); for (uint8 i = 0; i < height; i++) { set_succ (null, State.NONE, i); set_backlink (null, i); } _nodes = null; } public static inline bool search (CompareDataFunc? cmp, G key, ref Tower prev, out Tower? next = null, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) { assert (from_level >= to_level); bool res = false; next = null; for (int i = from_level; i >= to_level; i--) { res = search_helper (cmp, key, ref prev, out next, (uint8)i); } return res; } public static inline bool search_from_bookmark (CompareDataFunc? cmp, G key, ref TowerIter prev, out TowerIter next = null, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) { assert (from_level >= to_level); bool res = false; for (int i = from_level; i >= to_level; i--) { unowned Tower tmp_prev = prev._iter[i]; // Should be treated as NULL-like value Tower tmp_next; res = search_helper (cmp, key, ref prev._iter[i], out tmp_next, (uint8)i); if (&next != null) { next._iter[i] = (owned)tmp_next; } if (prev._iter[i] != tmp_prev) { prev._iter[i] = prev._iter[i]; if (i > to_level && compare (cmp, prev._iter[i - 1], prev._iter[i]) <= 0) { prev._iter[i - 1] = prev._iter[i]; } } } return res; } private static inline bool search_helper (CompareDataFunc? cmp, G key, ref Tower? prev, out Tower? next, uint8 level) { next = prev.get_next (level); while (next != null && compare_data (cmp, next, key) < 0 && proceed (cmp, ref prev, ref next, level, true)); return next != null && cmp(key, next._data) == 0; } public static inline Tower? insert (CompareDataFunc? cmp, ref TowerIter prev, G key, uint8 chosen_level) { return insert_helper (cmp, ref prev, key, chosen_level, chosen_level); } private static inline Tower? insert_helper (CompareDataFunc? cmp, ref TowerIter prev, G key, uint8 chosen_level, uint8 level) { Tower? new_tower; Tower? next; if (search_helper (cmp, key, ref prev._iter[level], out next, level)) { return null; } if (level > 0) { if (compare (cmp, prev._iter[level], prev._iter[level - 1]) >= 0) { prev._iter[level - 1] = prev._iter[level]; } new_tower = insert_helper (cmp, ref prev, key, chosen_level, level - 1); } else { new_tower = new Tower (key, chosen_level + 1); } if (new_tower == null) { return null; } while (true) { State prev_state; Tower? prev_next = prev._iter[level].get_succ (out prev_state, level); if (prev_state == State.FLAGGED) { prev_next.help_flagged (prev._iter[level], level); } else { new_tower.set_succ (next, State.NONE, level); bool result = prev._iter[level].compare_and_exchange (next, State.NONE, new_tower, State.NONE, level); if (result) break; prev_next = prev._iter[level].get_succ (out prev_state, level); if (prev_state == State.FLAGGED) { prev_next.help_flagged (prev._iter[level], level); } backtrace (ref prev._iter[level], level); } if (search_helper (cmp, key, ref prev._iter[level], null, level)) { return null; } } GLib.AtomicInt.inc (ref new_tower._height); if (new_tower.get_state (0) == State.MARKED) { remove_level (cmp, ref prev._iter[level], new_tower, level); return null; } return new_tower; } public static inline bool remove_key (CompareDataFunc? cmp, ref TowerIter prev, G key, uint8 from_level = (uint8)_MAX_HEIGHT - 1) { for (int i = from_level; i >= 1; i--) { Tower next; search_helper (cmp, key, ref prev._iter[i], out next, (uint8)i); if (compare (cmp, prev._iter[i - 1], prev._iter[i]) < 0) { prev._iter[i - 1] = prev._iter[i]; } } Tower? curr; if (search_helper (cmp, key, ref prev._iter[0], out curr, 0)) { return remove (cmp, ref prev, curr); } else { return false; } } public static inline bool remove (CompareDataFunc? cmp, ref TowerIter prev, Tower curr) { bool removed = remove_level (cmp, ref prev._iter[0], curr, 0); for (int i = 1; i < _MAX_HEIGHT; i++) { remove_level (cmp, ref prev._iter[i], curr, (uint8)i); } return removed; } private static inline bool remove_level (CompareDataFunc? cmp, ref Tower prev, Tower curr, uint8 level) { bool status; bool flagged = curr.try_flag (cmp, ref prev, out status, level); if (status) { curr.help_flagged (prev, level); } return flagged; } public static inline bool proceed (CompareDataFunc? cmp, ref Tower? arg_prev, ref Tower arg_curr, uint8 level, bool force = false) { Tower curr = arg_curr; Tower? next = curr.get_next (level); if (next != null) { while (next != null && next.get_state (0) == State.MARKED) { bool status; next.try_flag (cmp, ref curr, out status, level); if (status) { next.help_flagged (curr, level); } next = curr.get_next (level); } } bool success = next != null; if (success || force) { arg_prev = (owned)curr; arg_curr = (owned)next; } return success; } public inline void help_marked (Tower prev_tower, uint8 level) { prev_tower.compare_and_exchange (this, State.FLAGGED, get_next (level), State.NONE, level); } public inline void help_flagged (Tower prev, uint8 level) { set_backlink (prev, level); if (get_state (level) != State.MARKED) try_mark (level); help_marked (prev, level); } public inline void try_mark (uint8 level) { do { Tower? next_tower = get_next (level); bool result = compare_and_exchange (next_tower, State.NONE, next_tower, State.MARKED, level); if (!result) { State state; next_tower = get_succ (out state, level); if (state == State.FLAGGED) help_flagged (next_tower, level); } } while (get_state (level) != State.MARKED); } public inline bool try_flag (CompareDataFunc? cmp, ref Tower prev_tower, out bool status, uint8 level) { while (true) { if (prev_tower.compare_succ (this, State.FLAGGED, level)) { status = true; return false; } bool result = prev_tower.compare_and_exchange (this, State.NONE, this, State.FLAGGED, level); if (result) { status = true; return true; } State result_state; Tower? result_tower = prev_tower.get_succ (out result_state, level); if (result_tower == this && result_state == State.FLAGGED) { status = true; return false; } backtrace (ref prev_tower, level); if (!search_helper (cmp, _data, ref prev_tower, null, level)) { status = false; return false; } } } public static inline void backtrace (ref Tower? curr, uint8 level) { while (curr.get_state (level) == State.MARKED) curr = curr.get_backlink (level); } public inline bool compare_and_exchange (Tower? old_tower, State old_state, Tower? new_tower, State new_state, uint8 level) { return HazardPointer.compare_and_exchange_pointer?> (&_nodes[level]._succ, old_tower, new_tower, 3, (size_t)old_state, (size_t)new_state); } public inline bool compare_succ (Tower? next, State state, uint8 level) { size_t cur = (size_t)AtomicPointer.get (&_nodes[level]._succ); return cur == ((size_t)next | (size_t)state); } public inline Tower? get_next (uint8 level) { return get_succ (null, level); } public inline State get_state (uint8 level) { return (State)((size_t)AtomicPointer.get (&_nodes[level]._succ) & 3); } public inline Tower? get_succ (out State state, uint8 level) { size_t rstate; Tower? succ = HazardPointer.get_pointer> (&_nodes[level]._succ, 3, out rstate); state = (State)rstate; return (owned)succ; } public inline void set_succ (Tower? next, State state, uint8 level) { HazardPointer.set_pointer> (&_nodes[level]._succ, next, 3, (size_t)state); } public inline Tower? get_backlink (uint8 level) { return HazardPointer.get_pointer> (&_nodes[level]._backlink); } public inline void set_backlink (Tower? backlink, uint8 level) { HazardPointer.compare_and_exchange_pointer?> (&_nodes[level]._backlink, null, backlink); } public inline int get_height () { int height = GLib.AtomicInt.get (ref _height); return height != -1 ? height : _MAX_HEIGHT; } public inline bool is_head () { int height = GLib.AtomicInt.get (ref _height); return height == -1; } public inline static int compare (CompareDataFunc? cmp, Tower a, Tower b) { bool ah = GLib.AtomicInt.get (ref a._height) == -1; bool bh = GLib.AtomicInt.get (ref b._height) == -1; return ah ? (bh ? 0 : -1) : (bh ? 1 : cmp(a._data, b._data)); } public inline static int compare_data (CompareDataFunc? cmp, Tower a, G b) { bool ah = GLib.AtomicInt.get (ref a._height) == -1; return ah ? -1 : cmp(a._data, b); } [NoArrayLength] public TowerNode[] _nodes; public G _data; public int _height; } private struct TowerNode { public Tower *_succ; public Tower *_backlink; } private struct TowerIter { [NoArrayLength] public Tower? _iter[31 /*_MAX_HEIGHT*/]; } private enum State { NONE = 0, MARKED = 1, FLAGGED = 2 } }