/*
 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.svm.core.heap;

import com.oracle.svm.core.annotate.NeverInline;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalObject;
import com.oracle.svm.core.util.VMError;

Motivation: Open an instance of this class to detect attempts to allocate in the Heap. Design: There shouldn't be tests for a locked heap on the allocation fast-path, so the key is that creating one of these sets top to end in the young space, so allocation attempts fail over to the slow-path, and there can be a test for a locked heap on the slow path. TODO: When SVM becomes multi-threaded, this should only prevent allocation on a particular thread. That could be done by setting the thread's TLAB to null, which should force it on to the slow-path to allocation a new TLAB.
/** * Motivation: Open an instance of this class to detect attempts to allocate in the Heap. * * Design: There shouldn't be tests for a locked heap on the allocation fast-path, so the key is * that creating one of these sets top to end in the young space, so allocation attempts fail over * to the slow-path, and there can be a test for a locked heap on the slow path. * * TODO: When SVM becomes multi-threaded, this should only prevent allocation on a particular * thread. That could be done by setting the thread's TLAB to null, which should force it on to the * slow-path to allocation a new TLAB. */
public class NoAllocationVerifier implements AutoCloseable { public static final String ERROR_MSG = "Attempt to allocate while allocation was explicitly disabled using a NoAllocationVerifier";
A guard to place before an allocation, giving the call site and the allocation type.
/** A guard to place before an allocation, giving the call site and the allocation type. */
public static void exit(final String callSite, final String typeName) { Log.log().string("[NoAllocationVerifier detected disallowed allocation: ").string(callSite).string(": ").string(typeName).newline(); if (openVerifiers.get() != null) { Log.log().string("[NoAllocationVerifier stack: "); for (NoAllocationVerifier rest = openVerifiers.get(); rest != null; rest = rest.next) { Log.log().newline().string(" ").string(" reason: ").string(rest.reason).newline(); } Log.log().string("]").newline(); } Log.log().string("]").newline(); throw VMError.shouldNotReachHere(ERROR_MSG); } private static final FastThreadLocalObject<NoAllocationVerifier> openVerifiers = FastThreadLocalFactory.createObject(NoAllocationVerifier.class);
Create an opened instance. Usage is:
    try (NoAllocationVerifier verifier = NoAllocationVerifier.factory()) {
        ....
    }
/** * Create an opened instance. * * Usage is: * * <pre> * try (NoAllocationVerifier verifier = NoAllocationVerifier.factory()) { * .... * } * </pre> */
public static NoAllocationVerifier factory(String reason) { return NoAllocationVerifier.factory(reason, true); }
Create an instance that can be opened lazily. Usage is:
    NoAllocationVerifier instance = NoAllocationVerifier.factory(false);
    ....
    try(NoAllocationVerifier resource = instance.open()) {
        ....
    }
/** * Create an instance that can be opened lazily. * * Usage is: * * <pre> * NoAllocationVerifier instance = NoAllocationVerifier.factory(false); * .... * try(NoAllocationVerifier resource = instance.open()) { * .... * } * </pre> */
public static NoAllocationVerifier factory(String reason, boolean open) { NoAllocationVerifier result = new NoAllocationVerifier(reason); if (open) { result.open(); } return result; }
Returns true if there is an open NoAllocationVerifier, i.e., returns true if no allocation is allowed in this thread.
/** * Returns true if there is an open NoAllocationVerifier, i.e., returns true if no allocation is * allowed in this thread. */
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static boolean isActive() { return openVerifiers.get() != null; } private String reason; private boolean isOpen; private NoAllocationVerifier next; protected NoAllocationVerifier(String reason) { this.reason = reason; isOpen = false; next = null; } @NeverInline("Access of fields also used by allocation fast path, so must not be inlined in a method that performs allocation") public NoAllocationVerifier open() { VMError.guarantee(!isOpen, "NoAllocationVerifier already open"); if (!isActive()) { /* No other verifier open yet, so suspend allocation. */ Heap.getHeap().suspendAllocation(); } /* Push to linked list of open verifiers. */ isOpen = true; next = openVerifiers.get(); openVerifiers.set(this); return this; } @Override @NeverInline("Access of fields also used by allocation fast path, so must not be inlined in a method that performs allocation") public void close() { VMError.guarantee(isOpen, "NoAllocationVerifier not open"); openVerifiers.set(next); next = null; isOpen = false; if (!isActive()) { /* No other verifier open anymore, so resume allocation. */ Heap.getHeap().resumeAllocation(); } } }