/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package com.github.geophile.erdo.apiimpl; import com.github.geophile.erdo.Cursor; import com.github.geophile.erdo.map.diskmap.tree.TreePosition; import com.github.geophile.erdo.util.IdentitySet; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.logging.Level; import java.util.logging.Logger; // Tracks TreePositions used to implement some operation. Any TreePositions left by the end of the operation // are reclaimed (returned to the pool) by destroyRemainingTreePositions. Usually, the operation in question // is a Cursor operation, e.g. next. But there are exceptions, e.g. recovery and consolidation. In these // cases, the context is null. public class TreePositionTracker { public static void registerTreePosition(Cursor context, TreePosition treePosition) { if (LOG.isLoggable(Level.FINEST)) { LOG.log(Level.FINEST, "Register TreePosition {0} -> {1})", new Object[]{context, treePosition}); } IdentityHashMap<Cursor, IdentitySet<TreePosition>> threadTreePositions = TREE_POSITIONS.get(); if (threadTreePositions == null) { threadTreePositions = new IdentityHashMap<>(); TREE_POSITIONS.set(threadTreePositions); } IdentitySet<TreePosition> contextTreePositions = threadTreePositions.get(context); if (contextTreePositions == null) { contextTreePositions = new IdentitySet<>(); threadTreePositions.put(context, contextTreePositions); } TreePosition replaced = contextTreePositions.add(treePosition); assert replaced == null : context; } public static void unregisterTreePosition(Cursor context, TreePosition treePosition) { if (LOG.isLoggable(Level.FINEST)) { LOG.log(Level.FINEST, "Unregister TreePosition {0} -> {1}", new Object[]{context, treePosition}); } IdentityHashMap<Cursor, IdentitySet<TreePosition>> threadTreePositions = TREE_POSITIONS.get(); assert threadTreePositions != null : treePosition; IdentitySet<TreePosition> contextTreePositions = threadTreePositions.get(context); assert contextTreePositions != null : context; TreePosition removed = contextTreePositions.remove(treePosition); assert removed == treePosition : String.format("context: %s, treePosition: %s, removed: %s", context, treePosition, removed); if (contextTreePositions.isEmpty()) { threadTreePositions.remove(context); } } public static void destroyRemainingTreePositions(Cursor context) { IdentityHashMap<Cursor, IdentitySet<TreePosition>> threadTreePositions = TREE_POSITIONS.get(); if (threadTreePositions != null) { IdentitySet<TreePosition> contextTreePositions = threadTreePositions.get(context); if (contextTreePositions != null) { // Need a copy because TreePosition.destroyRecordReference() removes an element // from contextTreePositions. for (TreePosition treePosition : new ArrayList<>(contextTreePositions.values())) { treePosition.destroyRecordReference(); if (LOG.isLoggable(Level.FINEST)) { LOG.log(Level.FINEST, "Released TreePosition {0} -> {1}", new Object[]{context, treePosition}); } } } } } // Class state private static final Logger LOG = Logger.getLogger(TreePositionTracker.class.getName()); private static final ThreadLocal<IdentityHashMap<Cursor, IdentitySet<TreePosition>>> TREE_POSITIONS = new ThreadLocal<>(); }