/*
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.grizzly.http2;
import java.util.concurrent.locks.ReentrantReadWriteLock;
N-ary tree node implementation to support HTTP/2 stream hierarchies.
/**
* N-ary tree node implementation to support HTTP/2 stream hierarchies.
*/
public abstract class Node {
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
protected static final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
protected static final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
protected final int id;
protected Node next;
protected Node prev;
protected Node parent;
protected Node firstChild;
protected boolean exclusive;
// ----------------------------------------------------------- Constructors
protected Node(final int id) {
this.id = id;
}
// ------------------------------------------------------ Protected Methods
Mark this Node
as exclusive. Any siblings will be migrated to the children list. /**
* Mark this {@link Node} as exclusive. Any siblings will be migrated to the children list.
*/
protected void exclusive() {
writeLock.lock();
try {
final Node p = parent;
p.detach(id);
p.addChild(this, true);
} finally {
writeLock.unlock();
}
}
Add a sibling to this Node
. /**
* Add a sibling to this {@link Node}.
*/
protected void addSibling(final Node sibling) {
writeLock.lock();
try {
sibling.next = this;
this.prev = sibling;
sibling.parent = this.parent;
parent.firstChild = sibling;
} finally {
writeLock.unlock();
}
}
Add a child to this Node
. /**
* Add a child to this {@link Node}.
*/
protected void addChild(final Node n) {
addChild(n, false);
}
Add a new child. If the child is marked as exclusive, any other children will be moved to become the children of this
exclusive child.
/**
* Add a new child. If the child is marked as exclusive, any other children will be moved to become the children of this
* exclusive child.
*/
protected void addChild(final Node nodeBeingAddedAsChild, final boolean exclusive) {
writeLock.lock();
try {
if (exclusive) {
nodeBeingAddedAsChild.exclusive = true;
if (nodeBeingAddedAsChild.firstChild != null && firstChild != null) {
Node tail = firstChild;
while (tail.next != null) {
tail = tail.next;
}
tail.next = nodeBeingAddedAsChild.firstChild;
nodeBeingAddedAsChild.firstChild.prev = tail;
nodeBeingAddedAsChild.firstChild = firstChild;
} else if (nodeBeingAddedAsChild.firstChild == null && firstChild != null) {
nodeBeingAddedAsChild.firstChild = firstChild;
}
firstChild = null;
if (nodeBeingAddedAsChild.firstChild != null) {
Node t = nodeBeingAddedAsChild.firstChild;
do {
t.parent = nodeBeingAddedAsChild;
} while ((t = t.next) != null);
}
}
if (firstChild == null) {
firstChild = nodeBeingAddedAsChild;
firstChild.parent = this;
} else {
firstChild.addSibling(nodeBeingAddedAsChild);
}
} finally {
writeLock.unlock();
}
}
Detail this Node
from the tree maintaining any children. /**
* Detail this {@link Node} from the tree maintaining any children.
*/
protected Node detach(final int id) {
return remove(id, true);
}
/**
* Remove this {@link Node} from the tree. Any children will be moved up as a child of the remove {@link Node}'s parent.
*/
protected Node remove(final int id) {
return remove(id, false);
}
Top down search from this Node
and any children (recursively) returning the node with a matching id
.
/**
* Top down search from this {@link Node} and any children (recursively) returning the node with a matching
* <code>id</code>.
*/
protected Node find(final int id) {
if (this.id == id) {
return this;
}
readLock.lock();
try {
if (firstChild != null) {
Node n = firstChild;
do {
if (n.id == id) {
return n;
}
Node result = n.find(id);
if (result != null) {
return result;
}
} while ((n = n.next) != null);
}
return null;
} finally {
readLock.unlock();
}
}
// -------------------------------------------------------- Private Methods
private boolean isFirstSibling() {
return next != null && prev == null;
}
private boolean isLastSibling() {
return next == null && prev != null;
}
private boolean hasSiblings() {
return next != null || prev != null;
}
private Node remove(final int id, final boolean retainChildren) {
final Node n = find(id);
if (n != null) {
writeLock.lock();
try {
// remove this node from sibling pointer chains
if (n.hasSiblings()) {
final Node left = n.prev;
final Node right = n.next;
if (n.isFirstSibling()) {
right.parent.firstChild = right;
right.prev = null;
} else if (n.isLastSibling()) {
left.next = null;
} else {
// Middle child!
left.next = right;
right.prev = left;
}
}
// re-parent the children to this node's parent and
// push these children to the front of the child new parent child list
if (!retainChildren) {
final Node np = n.parent;
if (n.firstChild != null) {
Node t = n.firstChild;
Node last = null;
do {
t.parent = np;
// quick look ahead to see if this node will be the last
if (t.next == null) {
last = t;
}
} while ((t = t.next) != null);
// 'push' the current child to the 'end' of children of the removed node
last.next = np.firstChild;
np.firstChild.prev = last;
// Set the new pointer to the new first child.
np.firstChild = n.firstChild;
}
}
// clear pointers and return
n.parent = null;
n.next = null;
n.prev = null;
if (!retainChildren) {
n.firstChild = null;
}
return n;
} finally {
writeLock.unlock();
}
}
return null;
}
}