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

com.vmlens.trace.agent.bootstrap.callback.getState.ConcurrentHashMapComputeIfAbsentOnly Maven / Gradle / Ivy

There is a newer version: 1.1.5
Show newest version
package com.vmlens.trace.agent.bootstrap.callback.getState;

import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReferenceArray;

/*
 * 		
 * 
 * 
 * 
     nur getOrCreate, kein remove kein update keine iteratoren
     
     
     
     State modell:
			null -> Filled
			null -> MovedNull
			

			
			
		current
		prevoius array
		
		wenn prevoius gefüllt resize im gange.
		
		resize prozess:
				prevous array auf current
				current neu erzeugen
				erstmal alle null auf resize in previuos
				previuos muss wert behalten offen ob das benötigt wird für existierende werte
	
		bei resize darauf achten dass keine werte doppelt oder nirgends vorhanden sind
		updaten immer Filled

		erstmal Filled -> Move dann null -> Filled in neuer map
		offen update unterstützen?

		für resize misses und maximale länge bei miss zählen
		
		
		resize:
			prevoius = current
			current neu erzeugen
			in previous null werte füllen
			übertragen
			
			resize unter syncblock
			als erstes prüfen ob sich länge geändert hat (resize lief schon)
			
		https://en.wikipedia.org/wiki/Open_addressing
		
		
 * 	WithoutRemove
 * 
 * 
 */

public class ConcurrentHashMapComputeIfAbsentOnly {

	
	/**
	 * The largest possible table capacity. This value must be exactly 1<<30 to stay
	 * within Java array allocation and indexing bounds for power of two table
	 * sizes, and is further required because the top two bits of 32bit hash fields
	 * are used for control purposes.
	 */
	
	private static final int MAXIMUM_CAPACITY = 1 << 30;
	private static final Object MOVED_NULL_KEY = new Object();
	private static final KeyValue MOVED_KEY_VALUE = new KeyValue(MOVED_NULL_KEY, null);
	private final Object LOCK = new Object();
//	private static final sun.misc.Unsafe U;
//	private static final long ABASE;
//	private static final int ASHIFT;
//
//	static {
//		try {
//			Constructor unsafeConstructor = sun.misc.Unsafe.class.getDeclaredConstructor();
//			unsafeConstructor.setAccessible(true);
//			U = unsafeConstructor.newInstance();
//
//			Class ak = KeyValue[].class;
//			ABASE = U.arrayBaseOffset(ak);
//			int scale = U.arrayIndexScale(ak);
//			if ((scale & (scale - 1)) != 0)
//				throw new Error("data type scale not a power of two");
//			ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
//		} catch (Exception e) {
//			throw new Error(e);
//		}
//	}

	private static final KeyValue tabAt(AtomicReferenceArray  tab, int i) {
		return    tab.get(i);  //(KeyValue) U.getObjectVolatile(tab, ((long) i << ASHIFT) + ABASE);
	}

	private static final boolean casTabAt(AtomicReferenceArray  tab, int i, KeyValue newValue) {
		// tab[i] = newValue; //U.compareAndSwapObject(tab, ((long) i << ASHIFT) + ABASE, null, newValue);
	     return tab.compareAndSet(i, null, newValue);
	}

	/**
	 * Returns a power of two table size for the given desired capacity. See Hackers
	 * Delight, sec 3.2
	 */
	private static final int tableSizeFor(int c) {
		int n = c - 1;
		n |= n >>> 1;
		n |= n >>> 2;
		n |= n >>> 4;
		n |= n >>> 8;
		n |= n >>> 16;
		return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
	}
	
	
	/*
	 * package visibility for tests
	 */
	
	volatile AtomicReferenceArray currentArray;
	private volatile boolean resizeRunning;

	public ConcurrentHashMapComputeIfAbsentOnly(int size) {
		super();
		int mod2Size = tableSizeFor(size);
		currentArray = new AtomicReferenceArray(mod2Size); 
	}

	private Object insertDuringResize(Object key, FunctionForJDK7 compute) {
		synchronized (LOCK) {
			while (resizeRunning) {
				try {
					LOCK.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

		return computeIfAbsent(key, compute);
	}
	

	
	

	private void resize(int checkedLength) {
		/*
		 * 
		 * check if current was already changed if not resize flag = true and all null
		 * fields sperren
		 * 
		 * neues array anlegen und rüberkopieren wenn abgeschlossen neu setzen resize
		 * flag neu setzen alle warten threads benachrichtigen
		 * 
		 * 
		 * 
		 */

		synchronized (LOCK) {

			if (currentArray.length() > checkedLength) {
				return;
			}

			resizeRunning = true;

			for (int i = 0; i < currentArray.length(); i++) {
				if (tabAt(currentArray, i) == null) {
					casTabAt(currentArray, i, MOVED_KEY_VALUE);
				}
			}

			AtomicReferenceArray newArray = new AtomicReferenceArray
			(2 * currentArray.length());

			for (int i = 0; i < currentArray.length(); i++) {
				KeyValue current = tabAt(currentArray, i);

				if (current != MOVED_KEY_VALUE) {
					int hashCode = current.key.hashCode();
					int index = (newArray.length() - 1) & hashCode;

					while (newArray.get(index) != null) {
						index++;
						
						if( index== newArray.length() )
						{
							index = 0;
						}
						
					}

					newArray.set(index, current);

				}

			}

			currentArray = newArray;
			resizeRunning = false;

			LOCK.notifyAll();

		}
	}

	public Object computeIfAbsent(Object key, FunctionForJDK7 compute) {
		AtomicReferenceArray local = currentArray;
		int hashCode = key.hashCode();
		int index = (local.length() - 1) & hashCode;
		int start = index;

		KeyValue created = null;
		KeyValue current = tabAt(local, index);
		
		if (current != null) {
			if (current.key == key) {
				return current.value;
			} else if (current.key == MOVED_NULL_KEY) {
				return insertDuringResize(key, compute);
			}

		}

		while (true) {	
			if (current == null)  {
				if (created == null) {
					created = new KeyValue(key, compute.apply(key));
				}

				if (casTabAt(local, index, created)) {
					if ( ( (index - start) << 3 ) > local.length()) {
						resize(local.length());
					}
					return created.value;
				}

				current = tabAt(local, index);

				if (current.key == key) {
					return current.value;
				} else if (current.key == MOVED_NULL_KEY) {
					return insertDuringResize(key, compute);
				}

			}

			index++;
			
			if( index == currentArray.length() )
			{
				index = 0;
			}
			
			if( index == start )
			{
				resize(local.length());

				return computeIfAbsent(key, compute);
			}
			
			
			current = tabAt(local, index);
			if (current != null) {
				if (current.key.equals(key)) {
					return current.value;
				} else if (current.key == MOVED_NULL_KEY) {
					return insertDuringResize(key, compute);
				}

			}
			
			

		}

	

	}
	
	
	
	
	
	
//	
//	public Object get(Object key) {
//		KeyValue[] local = currentArray;
//		int hashCode = key.hashCode();
//		int index = (local.length - 1) & hashCode;
//		int start = index;
//
//		KeyValue created = null;
//		KeyValue current = tabAt(local, index);
//		
//		if (current != null) {
//			if (current.key.equals(key)) {
//				return current.value;
//			} else if (current.key == MOVED_NULL_KEY) {
//				return getDuringResize(key);
//			}
//
//		}
//
//		while (true) {	
//			if (current == null)  {
//				return null;
//			}
//
//			index++;
//			
//			if( index == currentArray.length )
//			{
//				index = 0;
//			}
//			
//			if( index == start )
//			{
//				return null;
//			}
//			
//			
//			current = tabAt(local, index);
//			if (current != null) {
//				if (current.key.equals(key)) {
//					return current.value;
//				} else if (current.key == MOVED_NULL_KEY) {
//					return getDuringResize(key);
//				}
//
//			}
//			
//			
//
//		}
//
//	
//
//	}
//	
//	
//	
//	
//	
	
	
	
	
	
	
	
	
	
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy