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

com.sleepycat.je.latch.LatchSupport Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.latch;

import static com.sleepycat.je.EnvironmentFailureException.unexpectedState;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.sleepycat.je.EnvironmentFailureException;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.utilint.DatabaseUtil;
import com.sleepycat.je.utilint.LoggerUtils;

/**
 * Supports latch debugging.
 *
 * In JE test mode (when the JE_TEST system property is set), TRACK_LATCHES
 * will be true and related debugging methods may be used to check the number
 * of Btree latches held.
 *
 * CAPTURE_OWNER is also set to true if the system property
 * JE_CAPTURE_LATCH_OWNER is defined to true.  This will capture a stack trace
 * when a latch is acquired exclusively, and the stack trace will be included
 * in all error messages.  Capturing the stack trace is expensive so this is
 * off by default for unit testing.
 */
public class LatchSupport {

    public static final boolean TRACK_LATCHES = DatabaseUtil.TEST;

    static final boolean CAPTURE_OWNER =
        Boolean.getBoolean("JE_CAPTURE_LATCH_OWNER");

    /*
     * Indicates whether to use tryLock() with a timeout, instead of a simple
     * lock() that waits forever and is uninterruptible.  We would like to
     * always use timeouts and interruptible latches, but these are new
     * features and this boolean allows reverting to the old behavior.
     */
    static final boolean INTERRUPTIBLE_WITH_TIMEOUT = true;

    /* Used for Btree latches. */
    public final static LatchTable btreeLatchTable =
        TRACK_LATCHES ? (new LatchTable()) : null;

    /* Used for all other latches. */
    public final static LatchTable otherLatchTable =
        TRACK_LATCHES ? (new LatchTable()) : null;

    public static void expectBtreeLatchesHeld(final int expectNLatches) {
        expectBtreeLatchesHeld(expectNLatches, "");
    }

    /* Used for SizeOf. */
    public static final LatchContext DUMMY_LATCH_CONTEXT = new LatchContext() {
        @Override
        public int getLatchTimeoutMs() {
            return 0;
        }
        @Override
        public String getLatchName() {
            return null;
        }
        @Override
        public LatchTable getLatchTable() {
            return null;
        }
        @Override
        public EnvironmentImpl getEnvImplForFatalException() {
            return null;
        }
    };

    public static void expectBtreeLatchesHeld(final int expectNLatches,
                                              final String msg) {
        final int nHeld = btreeLatchTable.nLatchesHeld();
        if (nHeld == expectNLatches) {
            return;
        }
        throw unexpectedState(String.format(
            "Expected %d Btree latches held but got %d. %s\nLatch table: %s\n",
            expectNLatches, nHeld, msg, btreeLatchesHeldToString()));
    }

    public static int nBtreeLatchesHeld() {
        return btreeLatchTable.nLatchesHeld();
    }

    public static void dumpBtreeLatchesHeld() {
        System.out.println(btreeLatchesHeldToString());
    }

    public static String btreeLatchesHeldToString() {
        return btreeLatchTable.latchesHeldToString();
    }

    /**
     * Should be called when closing the environment, so that residual latches
     * don't impact another environment that is opened
     */
    public static void clear() {
        if (TRACK_LATCHES) {
            btreeLatchTable.clear();
            otherLatchTable.clear();
        }
    }

    /**
     * Record debug info when a latch is acquired.
     */
    static void trackAcquire(final Latch latch, final LatchContext context) {

        final LatchTable latchTable = context.getLatchTable();
        if (latchTable == null) {
            return;
        }
        if (!latchTable.add(latch)) {
            throw unexpectedState(
                "Latch already held." + latch.debugString());
        }
    }

    /**
     * Record debug info when a latch is released.
     */
    static void trackRelease(final Latch latch, final LatchContext context) {

        final LatchTable latchTable = context.getLatchTable();
        if (latchTable == null) {
            return;
        }
        if (!latchTable.remove(latch)) {
            throw unexpectedState(
                "Latch not held." + latch.debugString());
        }
    }

    static String toString(final Latch latch,
                           final LatchContext context,
                           final OwnerInfo lastOwnerInfo) {
        final StringBuilder builder = new StringBuilder();
        builder.append(context.getLatchName()).
            append(" exclusiveOwner: ").
            append(latch.getExclusiveOwner());
        if (lastOwnerInfo != null) {
            lastOwnerInfo.toString(builder);
        }
        return builder.toString();
    }

    static String debugString(final Latch latch,
                              final LatchContext context,
                              final OwnerInfo lastOwnerInfo) {

        final StringBuilder builder = new StringBuilder(500);
        builder.append(context.getLatchName());
        builder.append(" currentThread: ");
        builder.append(Thread.currentThread());
        builder.append(" currentTime: ");
        builder.append(System.currentTimeMillis());

        if (TRACK_LATCHES) {
            final LatchTable latchTable = context.getLatchTable();
            if (latchTable != null) {
                builder.append(" allLatchesHeld: (");
                builder.append(latchTable.latchesHeldToString());
                builder.append(")");
            }
        }

        builder.append(" exclusiveOwner: ");
        final Thread ownerThread = latch.getExclusiveOwner();
        if (ownerThread != null) {
            builder.append(ownerThread);
            if (lastOwnerInfo != null) {
                lastOwnerInfo.toString(builder);
            }
        } else {
            builder.append("-none-");
        }

        return builder.toString();
    }

    static EnvironmentFailureException handleTimeout(
        final Latch latch,
        final LatchContext context) {

        final EnvironmentImpl envImpl = context.getEnvImplForFatalException();
        final Logger logger = envImpl.getLogger();
        final String msg = latch.debugString();

        LoggerUtils.logMsg(
            logger, envImpl, Level.SEVERE,
            "Thread dump follows for latch timeout: " + msg);

        LoggerUtils.fullThreadDump(logger, envImpl, Level.SEVERE);

        return unexpectedState(
            envImpl, "Latch timeout. " + msg);

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy