All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.kernel.impl.util.collection.ConcurrentBag Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

The newest version!
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.kernel.impl.util.collection;

import static org.neo4j.memory.HeapEstimator.shallowSizeOfInstance;

import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.neo4j.memory.MemoryTracker;

/**
 * A specialized concurrent collection used in parallel runtime.
 * 

* The collection exposes only two methods, {@link #add(Object)} and {@link #iterator()}, it doesn't keep track of its size nor does it automatically track memory. * However, memory tracking can easily be added by the client using {@link #SIZE_OF_NODE} to compute the size of each added item. The imagined use-case for * this collection, and what it has been optimised for, is when you have multiple threads adding to the collection and then a single reader reading the accumulated * result. The implementation is based on the LockFreeQueue in "The ART of MULTIPROCESSOR PROGRAMMING" (Herlihy, Luchangco, Shavi & Spear) and it is based * around having quicker threads help slower threads *

* Other approaches have been tested but all with inferior performance, including * *

  • java.util.concurrent.ConcurrentLinkedQueue
  • *
  • A bag implementation based on a HeapTrackingConcurrentHashSet with referential equality
  • *
  • A bag implementation using ThreadLocal so that each thread adds to a local list.
  • * * @param the type of the values to add. */ public final class ConcurrentBag { @SuppressWarnings("rawtypes") private static final AtomicReferenceFieldUpdater UPDATE_TAIL = AtomicReferenceFieldUpdater.newUpdater(ConcurrentBag.class, Node.class, "tail"); private static final AtomicReferenceFieldUpdater UPDATE_NODE = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next"); public static final long SIZE_OF_NODE = shallowSizeOfInstance(Node.class); static final long SIZE_OF_BAG = shallowSizeOfInstance(ConcurrentBag.class) + SIZE_OF_NODE; private final Node head; private volatile Node tail; /** * Allocate a new ConcurrentBag */ public static ConcurrentBag newBag(MemoryTracker memoryTracker) { memoryTracker.allocateHeap(SIZE_OF_BAG); return new ConcurrentBag<>(); } public ConcurrentBag() { Node node = new Node(null); this.head = node; this.tail = node; } public void add(T value) { Node node = new Node(value); while (true) { Node last = tail; Node next = last.next; if (last == tail) { if (next == null) { if (UPDATE_NODE.compareAndSet(last, null, node)) { // NOTE: if this CAS operation fails we can still return successfully since it means // another thread "helped out" (see NOTE below). UPDATE_TAIL.compareAndSet(this, last, node); return; } } else { // NOTE: some other thread appended its node but has not yet updated tail, // let's be helpful and set it for them. UPDATE_TAIL.compareAndSet(this, last, next); } } } } public Iterator iterator() { return new Iterator<>() { private Node current = head; @Override public boolean hasNext() { return current.next != null; } @SuppressWarnings("unchecked") @Override public T next() { current = current.next; return (T) current.value; } }; } static class Node { private final Object value; volatile Node next; Node(Object value) { this.value = value; this.next = null; } boolean compareAndSetNext(Node newNode) { return UPDATE_NODE.compareAndSet(this, null, newNode); } } }




    © 2015 - 2025 Weber Informatics LLC | Privacy Policy