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

org.hibernate.service.internal.ConcurrentServiceBinding Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2014, Red Hat Inc. or third-party contributors as
 * indicated by the @author tags or express copyright attribution
 * statements applied by the authors.  All third-party contributions are
 * distributed under license by Red Hat Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.hibernate.service.internal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Since Service lookup is a very hot operation and essentially it's a read only
 * data structure, to achieve threadsafety we can use immutability.
 * For our use case we just need reference equality, and the expectation is that a limited
 * number of elements will be contained in this custom collection (<32).
 * So the following structure is functionally equivalent to an Identity based ConcurrentMap,
 * but heavily tuned for reads, at cost of structural reorganization at writes.
 * The implementation is a binary tree basing the comparison order on the identityHashCode
 * of each key.
 *
 * @author Sanne Grinovero
 */
public class ConcurrentServiceBinding {

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static final Node EMPTY_LEAF = new Node( new Entry( 0, null, null ), null, null );

	@SuppressWarnings("unchecked")
	private volatile Node treeRoot = EMPTY_LEAF;

	@SuppressWarnings("unchecked")
	public synchronized void clear() {
		treeRoot = EMPTY_LEAF;
	}

	public synchronized void put(final K key, final V value) {
		final int code = hashKey( key );
		final Entry newEntry = new Entry( code, key, value );
		final ArrayList> list = convertToArrayList( treeRoot, key );
		list.add( newEntry );
		Collections.sort( list );
		final int size = list.size();
		@SuppressWarnings("unchecked")
		Entry[] array = list.toArray( new Entry[size] );
		treeRoot = treeFromRange( array, 0, size );
	}

	private Node treeFromRange(final Entry[] array, final int minInclusive, final int maxExclusive) {
		if ( minInclusive == maxExclusive ) {
			return null;
		}
		//find the midpoint, rounding down to avoid the exclusion range:
		int mid = ( minInclusive + maxExclusive ) / 2;
		//shift to the right to make sure we won't have left children with the same hash:
		while ( mid > minInclusive && array[mid].hash == array[mid-1].hash ) {
			mid--;
		}
		return new Node( array[mid], treeFromRange( array, minInclusive, mid ), treeFromRange( array, mid + 1, maxExclusive ) );
	}

	public V get(final K key) {
		final int hash = hashKey( key );
		final Node root = treeRoot;
		return root.get( key, hash );
	}

	protected int hashKey(final K key) {
		return System.identityHashCode( key );
	}

	public Iterable values() {
		@SuppressWarnings("rawtypes")
		ArrayList list = new ArrayList();
		treeRoot.collectAllValuesInto( list );
		return list;
	}

	private final ArrayList> convertToArrayList(final Node treeRoot, K exceptKey) {
		@SuppressWarnings("rawtypes")
		ArrayList> list = new ArrayList();
		if ( treeRoot != EMPTY_LEAF ) {
			treeRoot.collectAllEntriesInto( list, exceptKey );
		}
		return list;
	}

	private static final class Entry implements Comparable> {

		private final int hash;
		private final K key;
		private final V value;

		Entry(int keyHashCode, K key, V value) {
			this.hash = keyHashCode;
			this.key = key;
			this.value = value;
		}

		@Override
		public int compareTo(Entry o) {
			//Sorting by the identity hashcode
			//Note: this class has a natural ordering that is inconsistent with equals.
			return ( hash < o.hash ) ? -1 : ( (hash == o.hash) ? 0 : 1 );
		}

		@Override
		public boolean equals(Object obj) {
			//A ClassCastException is really not expected here,
			//as it's an internal private class,
			//so just let it happen as a form of assertion.
			final Entry other = (Entry)obj;
			//Reference equality on the key only!
			return other.key == this.key;
		}

		@Override
		public String toString() {
			return "<" + key + ", " + value + ">";
		}
	}

	private static final class Node {

		private final Entry entry;
		private final Node left;
		private final Node right;

		Node(Entry entry, Node left, Node right) {
			this.entry = entry;
			this.left = left;
			this.right = right;
		}

		public V get(final K key, final int hash) {
			if ( entry.key == key ) {
				return entry.value;
			}
			//Note that same-hashcode childs need to be on the right
			//as we don't test for equality, nor want to chase both
			//branches:
			else if ( hash < this.entry.hash ) {
				return left == null ? null : left.get( key, hash );
			}
			else {
				return right == null ? null : right.get( key, hash );
			}
		}

		public void collectAllEntriesInto(final List> list, final K exceptKey) {
			if ( entry != null && exceptKey != entry.key ) {
				list.add( entry );
			}
			if ( left != null ) {
				left.collectAllEntriesInto( list, exceptKey );
			}
			if ( right != null ) {
				right.collectAllEntriesInto( list, exceptKey );
			}
		}

		public void collectAllValuesInto(final List list) {
			if ( entry != null && entry.value != null ) {
				list.add( entry.value );
			}
			if ( left != null ) {
				left.collectAllValuesInto( list );
			}
			if ( right != null ) {
				right.collectAllValuesInto( list );
			}
		}

		/**
		 * Helper to visualize the tree via toString
		 */
		private void renderToStringBuilder(final StringBuilder sb, final int indent) {
			sb.append( entry );
			appendIndented( sb, indent, "L-> ", left );
			appendIndented( sb, indent, "R-> ", right );
		}

		private void appendIndented(final StringBuilder sb, final int indent, final String label, Node node) {
			if ( node == null ) {
				return;
			}
			sb.append( "\n" );
			for ( int i = 0; i < indent; i++ ) {
				sb.append( "\t" );
			}
			sb.append( label );
			node.renderToStringBuilder( sb, indent + 1 );
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			renderToStringBuilder( sb, 0 );
			return sb.toString();
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy