aboutsummaryrefslogtreecommitdiffstats
path: root/vespajlib/src/main/java/com/yahoo/yolean/concurrent/ThreadRobustList.java
blob: f6d8b68416c18ead517923e1d7ba6668a13c1f6a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.yolean.concurrent;

import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * <p>This class implements a thread-safe, lock-free list of Objects that supports multiple readers and a single writer.
 * Because there are no locks or other memory barriers involved, there exists no <i>happens-before</i> relationship
 * among calls to either methods of the <code>ThreadRobustList</code>. This means that there are no guarantees as to when
 * (or even if) an item {@link #add(Object)}ed becomes visible through {@link #iterator()}. If visibility is required,
 * either use explicit synchronization between reader and writer thread, or move to a different concurrent collection
 * (e.g. <code>CopyOnWriteArrayList</code>).</p>
 * <p>Because it is lock-free, the <code>ThreadRobustList</code> has minimal overhead to both reading and writing. The
 * iterator offered by this class always observes the list in a consistent state, and it never throws a
 * <code>ConcurrentModificationException</code>.</p>
 * <p>The <code>ThreadRobustList</code> does not permit adding <code>null</code> items.</p>
 * <p>The usage of <code>ThreadRobustList</code> has no memory consistency effects. </p>
 *
 * @author Steinar Knutsen
 * @author bratseth
 * @since 5.1.15
 */
public class ThreadRobustList<T> implements Iterable<T> {

    private Object[] items;
    private int next = 0;

    /**
     * <p>Constructs a new instance of this class with an initial capacity of <code>10</code>.</p>
     */
    public ThreadRobustList() {
        this(10);
    }

    /**
     * <p>Constructs a new instance of this class with a given initial capacity.</p>
     *
     * @param initialCapacity the initial capacity of this list
     */
    public ThreadRobustList(int initialCapacity) {
        items = new Object[initialCapacity];
    }

    /**
     * <p>Returns whether or not this list is empty.</p>
     *
     * @return <code>true</code> if this list has zero items
     */
    public boolean isEmpty() {
        return next == 0;
    }

    /**
     * <p>Adds an item to this list. As opposed to <code>CopyOnWriteArrayList</code>, items added to this list may become
     * visible to iterators created <em>before</em> a call to this method.</p>
     *
     * @param item the item to add
     * @throws NullPointerException if <code>item</code> is <code>null</code>
     */
    public void add(T item) {
        if (item == null) {
            throw new NullPointerException();
        }
        Object[] workItems = items;
        if (next >= items.length) {
            workItems = Arrays.copyOf(workItems, 20 + items.length * 2);
            workItems[next++] = item;
            items = workItems;
        } else {
            workItems[next++] = item;
        }
    }

    /**
     * <p>Returns an iterator over the items in this list. As opposed to <code>CopyOnWriteArrayList</code>, this iterator
     * may see items added to the <code>ThreadRobustList</code> even if they occur <em>after</em> a call to this method.</p>
     * <p>The returned iterator does not support <code>remove()</code>.</p>
     *
     * @return an iterator over this list
     */
    @Override
    public Iterator<T> iterator() {
        return new ThreadRobustIterator<>(items);
    }

    private static class ThreadRobustIterator<T> implements Iterator<T> {

        final Object[] items;
        int nextIndex = 0;

        ThreadRobustIterator(Object[] items) {
            this.items = items;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

        @SuppressWarnings("unchecked")
        @Override
        public T next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return (T)items[nextIndex++];
        }

        @Override
        public boolean hasNext() {
            if (nextIndex >= items.length) {
                return false;
            }
            if (items[nextIndex] == null) {
                return false;
            }
            return true;
        }
    }
}