From: Maciej Piechotka Date: Sun, 2 Dec 2012 10:58:09 +0000 (+0000) Subject: Make ConcurrentSet implement SortedSet X-Git-Tag: 0.9.91~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f197d88265c1ba5adc30193902f065e4be74d0f3;p=platform%2Fupstream%2Flibgee.git Make ConcurrentSet implement SortedSet --- diff --git a/gee/concurrentset.vala b/gee/concurrentset.vala index 62d937d..854340f 100644 --- a/gee/concurrentset.vala +++ b/gee/concurrentset.vala @@ -28,7 +28,7 @@ * of structure during iteration is allowed. However the change may not be immidiatly * visible to other threads. */ -public class Gee.ConcurrentSet : AbstractSet { +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)); @@ -63,10 +63,14 @@ public class Gee.ConcurrentSet : AbstractSet { } uint32 rand_int = rnd->int_range (0, int32.MAX); uint8 height = 1 + (uint8)GLib.Bit.nth_lsf (~rand_int, -1); - Tower prev = _head; - if (Tower.search (_cmp, key, ref prev, null, height - 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); @@ -76,7 +80,11 @@ public class Gee.ConcurrentSet : AbstractSet { public override bool remove (G item) { HazardPointer.Context ctx = new HazardPointer.Context (); - bool result = Tower.remove_key (_cmp, _head, item); + 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); } @@ -91,6 +99,119 @@ public class Gee.ConcurrentSet : AbstractSet { } } + 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--) { @@ -103,6 +224,7 @@ public class Gee.ConcurrentSet : AbstractSet { 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); } } } @@ -124,34 +246,32 @@ public class Gee.ConcurrentSet : AbstractSet { assert (_curr != null); } - public Iterator.pointing (ConcurrentSet cset, Tower[] prev, Tower curr) { - for (int i = 0; i < _MAX_HEIGHT; i++) { - _prev[i] = prev[i]; - } + 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[0] != null && !_removed) { + if (_prev._iter[0] != null && !_removed) { if (!f (_curr._data)) { assert (_curr != null); return false; } } - Tower new_prev = _prev[0]; + 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[0] = new_prev; - int prev_height = GLib.AtomicInt.get(ref _prev[0]._height); + _prev._iter[0] = new_prev; + int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { - _prev[i] = _prev[0]; + _prev._iter[i] = _prev._iter[0]; } } _curr = new_curr; @@ -167,16 +287,16 @@ public class Gee.ConcurrentSet : AbstractSet { public bool next () { HazardPointer.Context ctx = new HazardPointer.Context (); - Tower new_prev = _prev[0]; + 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[0] = (owned)new_prev; - int prev_height = GLib.AtomicInt.get(ref _prev[0]._height); + _prev._iter[0] = (owned)new_prev; + int prev_height = _prev._iter[0].get_height(); for (int i = 1; i < prev_height; i++) { - _prev[i] = _prev[0]; + _prev._iter[i] = _prev._iter[0]; } } _curr = (owned)new_curr; @@ -188,7 +308,7 @@ public class Gee.ConcurrentSet : AbstractSet { public bool has_next () { HazardPointer.Context ctx = new HazardPointer.Context (); - Tower prev = _prev[0]; + Tower prev = _prev._iter[0]; Tower? curr = _curr; return Tower.proceed (_set._cmp, ref prev, ref curr, 0); } @@ -201,22 +321,746 @@ public class Gee.ConcurrentSet : AbstractSet { public void remove () { HazardPointer.Context ctx = new HazardPointer.Context (); assert (valid); - if (Tower.remove (_set._cmp, _prev, _curr)) { + if (Tower.remove (_set._cmp, ref _prev, _curr)) { AtomicInt.dec_and_test (ref _set._size); } _removed = true; } - public bool valid { get { return _prev[0] != null && !_removed; } } + 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 Tower? _prev[31 /*_MAX_HEIGHT*/]; + 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]; @@ -227,18 +1071,22 @@ public class Gee.ConcurrentSet : AbstractSet { public inline Tower.head () { _nodes = new TowerNode[_MAX_HEIGHT]; - _height = _MAX_HEIGHT; + _height = -1; +#if DEBUG + _data = (G)"<>"; +#endif } inline ~Tower () { - for (uint i = 0; i < _height; i++) { - HazardPointer>.set_pointer (&_nodes[i]._succ, null); - HazardPointer>.set_pointer (&_nodes[i]._backlink, null); + uint8 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, uint8 to_level = 0, uint8 from_level = (uint8)_MAX_HEIGHT - 1) { + 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; @@ -248,25 +1096,47 @@ public class Gee.ConcurrentSet : AbstractSet { 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 && cmp(key, next._data) < 0 && proceed (cmp, ref prev, ref next, level, true)); + 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 Tower prev, G key, uint8 chosen_level) { + 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 Tower prev, G key, uint8 chosen_level, uint8 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, out next, level)) { + if (search_helper (cmp, key, ref prev._iter[level], out next, level)) { return null; } if (level > 0) { - Tower prev_down = prev; - new_tower = insert_helper (cmp, ref prev_down, key, chosen_level, level - 1); + 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); } @@ -275,53 +1145,52 @@ public class Gee.ConcurrentSet : AbstractSet { } while (true) { State prev_state; - Tower? prev_next = prev.get_succ (out prev_state, level); + Tower? prev_next = prev._iter[level].get_succ (out prev_state, level); if (prev_state == State.FLAGGED) { - prev_next.help_flagged (prev, level); + prev_next.help_flagged (prev._iter[level], level); } else { new_tower.set_succ (next, State.NONE, level); - bool result = prev.compare_and_exchange (next, State.NONE, new_tower, 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.get_succ (out prev_state, level); + prev_next = prev._iter[level].get_succ (out prev_state, level); if (prev_state == State.FLAGGED) { - prev_next.help_flagged (prev, level); + prev_next.help_flagged (prev._iter[level], level); } - backtrace (ref prev, level); + backtrace (ref prev._iter[level], level); } - if (search_helper (cmp, key, ref prev, null, 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, new_tower, level); + remove_level (cmp, ref prev._iter[level], new_tower, level); return null; } return new_tower; } - public static inline bool remove_key (CompareDataFunc? cmp, Tower arg_prev, G key, uint8 from_level = (uint8)_MAX_HEIGHT - 1) { - Tower prev[31 /*_MAX_HEIGHT*/]; - prev[from_level] = arg_prev; + 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[i], out next, (uint8)i); - prev[i - 1] = prev[i]; + 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[0], out curr, 0)) { - return remove (cmp, prev, 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, Tower[] prev, Tower curr) { - assert (prev.length >= AtomicInt.get (ref curr._height)); - bool removed = remove_level (cmp, ref prev[0], curr, 0); - for (int i = 1; i < prev.length; i++) { - remove_level (cmp, ref prev[i], curr, (uint8)i); + 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; } @@ -339,7 +1208,7 @@ public class Gee.ConcurrentSet : AbstractSet { Tower curr = arg_curr; Tower? next = curr.get_next (level); if (next != null) { - while (next.get_state (0) == State.MARKED) { + while (next != null && next.get_state (0) == State.MARKED) { bool status; next.try_flag (cmp, ref curr, out status, level); if (status) { @@ -446,6 +1315,27 @@ public class Gee.ConcurrentSet : AbstractSet { 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; @@ -457,6 +1347,12 @@ public class Gee.ConcurrentSet : AbstractSet { public Tower *_backlink; } + private struct TowerIter { + [NoArrayLength] + public Tower? _iter[31 /*_MAX_HEIGHT*/]; + } + + private enum State { NONE = 0, MARKED = 1, diff --git a/tests/testconcurrentset.vala b/tests/testconcurrentset.vala index 39fcfcf..0b23c4e 100644 --- a/tests/testconcurrentset.vala +++ b/tests/testconcurrentset.vala @@ -22,9 +22,9 @@ using Gee; -public class ConcurrentSetTests : SetTests { +public class ConcurrentSetTests : SortedSetTests { public ConcurrentSetTests () { - base ("ConcurrentSet"); + base ("ConcurrentSet", false); } public override void set_up () { diff --git a/tests/testsortedset.vala b/tests/testsortedset.vala index fdd6e44..00c9bd2 100644 --- a/tests/testsortedset.vala +++ b/tests/testsortedset.vala @@ -24,8 +24,9 @@ using GLib; using Gee; public abstract class SortedSetTests : SetTests { - public SortedSetTests (string name) { + public SortedSetTests (string name, bool strict = true) { base (name); + this.strict = strict; add_test ("[SortedSet] first", test_first); add_test ("[SortedSet] last", test_last); add_test ("[SortedSet] ordering", test_ordering); @@ -34,10 +35,10 @@ public abstract class SortedSetTests : SetTests { add_test ("[SortedSet] higher", test_higher); add_test ("[SortedSet] floor", test_floor); add_test ("[SortedSet] ceil", test_ceil); - get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.HEAD).get_suite ()); - get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.TAIL).get_suite ()); - get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.SUB).get_suite ()); - get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.EMPTY).get_suite ()); + get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.HEAD, strict).get_suite ()); + get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.TAIL, strict).get_suite ()); + get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.SUB, strict).get_suite ()); + get_suite ().add_suite (new SubSetTests (this, SubSetTests.Type.EMPTY, strict).get_suite ()); } public void test_ordering () { @@ -90,12 +91,14 @@ public abstract class SortedSetTests : SetTests { public void test_first () { var test_set = test_collection as SortedSet; - if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | - TestTrapFlags.SILENCE_STDERR)) { - test_set.first (); - Posix.exit (0); + if (strict) { + if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | + TestTrapFlags.SILENCE_STDERR)) { + test_set.first (); + Posix.exit (0); + } + Test.trap_assert_failed (); } - Test.trap_assert_failed (); assert (test_set.add ("one")); assert (test_set.add ("two")); @@ -110,12 +113,14 @@ public abstract class SortedSetTests : SetTests { public void test_last () { var test_set = test_collection as SortedSet; - if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | - TestTrapFlags.SILENCE_STDERR)) { - test_set.last (); - Posix.exit (0); + if (strict) { + if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | + TestTrapFlags.SILENCE_STDERR)) { + test_set.last (); + Posix.exit (0); + } + Test.trap_assert_failed (); } - Test.trap_assert_failed (); assert (test_set.add ("one")); assert (test_set.add ("two")); @@ -267,10 +272,11 @@ public abstract class SortedSetTests : SetTests { } private Type type; - public SubSetTests (SortedSetTests test, Type type) { + public SubSetTests (SortedSetTests test, Type type, bool strict) { base ("%s Subset".printf (type.to_string ())); this.test = test; this.type = type; + this.strict = strict; add_test ("[Collection] size", test_size); add_test ("[Collection] contains", test_contains); add_test ("[Collection] add", test_add); @@ -580,18 +586,20 @@ public abstract class SortedSetTests : SetTests { assert (subset.last () == "six"); break; case Type.EMPTY: - if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | - TestTrapFlags.SILENCE_STDERR)) { - subset.first (); - Posix.exit (0); + if (strict) { + if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | + TestTrapFlags.SILENCE_STDERR)) { + subset.first (); + Posix.exit (0); + } + Test.trap_assert_failed (); + if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | + TestTrapFlags.SILENCE_STDERR)) { + subset.last (); + Posix.exit (0); + } + Test.trap_assert_failed (); } - Test.trap_assert_failed (); - if (Test.trap_fork (0, TestTrapFlags.SILENCE_STDOUT | - TestTrapFlags.SILENCE_STDERR)) { - subset.last (); - Posix.exit (0); - } - Test.trap_assert_failed (); break; default: assert_not_reached (); @@ -842,6 +850,10 @@ public abstract class SortedSetTests : SetTests { assert_not_reached (); } } + + private bool strict; } + + private bool strict; }