Package org.jnode.vm.scheduler

Source Code of org.jnode.vm.scheduler.MonitorManager

/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.scheduler;

import org.jnode.annotation.Internal;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.Uninterruptible;
import org.jnode.util.NumberUtils;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VmMagic;
import org.jnode.vm.classmgr.ObjectFlags;
import org.jnode.vm.classmgr.ObjectLayout;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Word;

/**
* @author epr
*/
@MagicPermission
@Uninterruptible
public final class MonitorManager {

    /**
     * A fast implementation of the monitorEnter opcode. This implementation is
     * based on a thin-lock, present if the status word of the header of each
     * object.
     *
     * @param object
     */
    public static void monitorEnter(Object object) {
        if (object == null) {
            throw new NullPointerException();
        }

        // Prepare
        final Word tid = Word.fromIntZeroExtend(VmMagic.currentProcessor().getCurrentThread().getId());
        final Address objectPtr = ObjectReference.fromObject(object).toAddress();
        final Address statusPtr = objectPtr.add(ObjectLayout.FLAGS_SLOT * Address.size());

        for (;;) {
            // attempt fast path: object is not locked.
            final Word oldlockword = statusPtr.prepareWord();

            final Word statusFlags = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.STATUS_FLAGS_MASK));
            if (statusPtr.attempt(statusFlags, statusFlags.or(tid))) {
                // fast path succeeded, the object was not locked and
                // has been locked by the current thread.
                return;
            }

            // object is locked or has an inflated lock.
            if (!oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)).isZero()) {
                // slow path 2: high bit of lock word is set --> inflated lock
                final Monitor m = getMonitor(oldlockword);
                m.enter();
                return;
            } else if (oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).EQ(tid)) {
                // Current thread owns the thinlock
                final Word counter = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK));
                if (counter.EQ(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK))) {
                    // thin lock entry counter == max, so we need to inflate
                    // ourselves.
                    installInflatedLock(object, null).enter();
                    return;
                } else {
                    // not-quite-so-fast path: locked by current thread.
                    // increment counter.
                    // Try to update lock, it may be inflated by some other
                    // thread, so
                    // be cautious
                    final Word newlockword;
                    newlockword = oldlockword.add(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_INC));
                    // Try to update lock, it may be inflated by some other
                    // thread, so
                    // be cautious
                    if (statusPtr.attempt(oldlockword, newlockword)) {
                        return;
                    }
                }
            } else {
                // Another thread owns the lock
                // thin lock owned by another thread.
                int ownerId = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).toInt();
                VmThread thread = VmMagic.currentProcessor().getScheduler().getThreadById(ownerId);
                if (thread == null) {
                    //the owner of the lock was destroyed              
                    //aquire the lock in fast fashion
                    statusPtr.store(statusFlags.or(tid));
                    return;
                } else {
                    // install an inflated lock.
                    installInflatedLock(object, thread).enter();
                }
                return;
            }

            Unsafe.debug("@monitorEnter loop@");
        }
    }

    /**
     * Monitorexit runtime routine. Checks for thin lock usage, otherwise falls
     * back to inflated locks.
     *
     * @param object
     */
    public static void monitorExit(Object object) {
        if (object == null) {
            throw new NullPointerException();
        }

        // Prepare
        final Word tid = Word.fromIntZeroExtend(VmMagic.currentProcessor().getCurrentThread().getId());
        final Address objectPtr = ObjectReference.fromObject(object).toAddress();
        final Address statusPtr = objectPtr.add(ObjectLayout.FLAGS_SLOT * Address.size());

        for (;;) {
            final Word oldlockword = statusPtr.prepareWord();

            // Unsafe.debug(" exit:");
            // proc.getArchitecture().getStackReader().debugStackTrace();
            // Unsafe.debug(oldlockword); Unsafe.debug(tid); Unsafe.debug('}');

            // if not inflated and tid matches, this contains status flags and
            // counter
            if (!oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)).isZero()) {
                // inflated lock
                final Monitor m = getMonitor(oldlockword);
                m.exit();
                return;
            } else if (oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).EQ(tid)) {
                // owned by current thread
                final Word counter = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK));
                final Word newlockword;
                if (counter.isZero()) {
                    // Count is zero, clear tid field
                    newlockword = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.STATUS_FLAGS_MASK));
                } else {
                    // count is non-zero, decrement count
                    newlockword = oldlockword.sub(Word.fromIntSignExtend(ObjectFlags.LOCK_COUNT_INC));
                }
                if (statusPtr.attempt(oldlockword, newlockword)) {
                    return;
                }
            } else {
                // lock not owned by us!
                String exMsg = "Lock not owned by us:";
                Unsafe.debug(exMsg);
                Unsafe.debug(object.getClass().getName());
                // Extra debug info
                Unsafe.debug("\n");
                Unsafe.debug(oldlockword);
                Unsafe.debug(objectPtr);
                Unsafe.debug(statusPtr);
                Unsafe.debug(oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK)));
                Unsafe.debug(tid);
                Unsafe.debug(statusPtr.prepareWord());
                VmMagic.currentProcessor().getArchitecture().getStackReader().debugStackTrace();
                Unsafe.die("Please report this problem with the above values to epr@jnode.org");
                throw new IllegalMonitorStateException(exMsg);
            }
        }
    }

    /**
     * Wait on the given object, until interrupted or notified
     *
     * @param object
     * @param timeout
     * @throws InterruptedException
     */
    public static void wait(Object object, long timeout) throws InterruptedException {
        // Test interrupted state of current thread
        if (VmThread.currentThread().isInterrupted(true)) {
            throw new InterruptedException();
        }

        getOwnedInflatedMonitor(object).Wait(timeout);
    }

    /**
     * Notify the first thread waiting on the given object
     *
     * @param object
     */
    public static void notify(Object object) {
        getOwnedInflatedMonitor(object).Notify(false);
    }

    /**
     * Notify all threads waiting on the given object
     *
     * @param object
     */
    public static void notifyAll(Object object) {
        getOwnedInflatedMonitor(object).Notify(true);
    }

    /**
     * Gets the inflated monitor of the given object. The object must have been
     * locked by the current thread, otherwise an IllegalMonitorStateException
     * is thrown.
     *
     * @param object
     * @return The monitor
     * @throws IllegalMonitorStateException
     * @throw IllegalMonitorStateException
     */
    private static Monitor getOwnedInflatedMonitor(Object object)
        throws IllegalMonitorStateException {
        final Monitor m = installInflatedLock(object, null);
        if (!m.isOwner(VmMagic.currentProcessor().getCurrentThread())) {
            // lock not owned by us!
            String exMsg = "Object not locked by current thread";
            Unsafe.debug(exMsg);
            throw new IllegalMonitorStateException(exMsg);
        }
        return m;
    }

    /**
     * Make sure that object K has an inflated monitor.
     *
     * @param k
     */
    static void setupInflatedLock(Object k) {
        installInflatedLock(k, null);
    }

    /**
     * Installs an inflated lock on the given object. Uses a spin-loop to wait
     * until the object is unlocked or inflated.
     *
     * @param k the object for which the inflated lock is installed
     * @param thread
     * @return the Monitor object representing the lock
     */
    private static Monitor installInflatedLock(Object k, VmThread thread) {
        Monitor m = null;
        Word monAddr = null;

        final Address kPtr = ObjectReference.fromObject(k).toAddress();
        final Address statusPtr = kPtr.add(ObjectLayout.FLAGS_SLOT * Address.size());

        for (;;) {
            final Word oldlockword = statusPtr.prepareWord();
            if (!oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)).isZero()) {
                // inflated by another thread, use that one.
                return getMonitor(oldlockword);
            }

            if (m == null) {
                m = new Monitor(VmMagic.currentProcessor().getCurrentThread(), 1);
                monAddr = ObjectReference.fromObject(m).toAddress().toWord();
                if (!monAddr.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED
                    | ObjectFlags.STATUS_FLAGS_MASK)).isZero()) {
                    throw new InternalError("Monitor object has address that conflicts with header flags 0x"
                        + NumberUtils.hex(monAddr.toInt()));
                }
            }

            // Put entry count & owner in monitor
            int lockcount = 1 + oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_COUNT_MASK)).
                rshl(ObjectFlags.LOCK_COUNT_SHIFT).toInt();
            int ownerId = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).toInt();
            if (thread == null) {
                thread = VmMagic.currentProcessor().getScheduler().getThreadById(ownerId);
            }
            m.initialize(thread, lockcount);

            final Word statusFlags = oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.STATUS_FLAGS_MASK));
            final Word newlockword = monAddr.or(statusFlags).or(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED));
            if (statusPtr.attempt(oldlockword, newlockword)) {
                // successfully obtained inflated lock.
                return m;
            }
        }
    }

    /**
     * Get the Monitor object associated with this lockword.
     *
     * @param lockword
     * @return The monitor
     */
    private static Monitor getMonitor(Word lockword) {
        final Address address = lockword.and(Word.fromIntZeroExtend(
            ~(ObjectFlags.LOCK_EXPANDED | ObjectFlags.STATUS_FLAGS_MASK))).toAddress();

        if (address.isZero()) {
            String exMsg = "Inflated monitor is null????";
            Unsafe.debug(exMsg);
            throw new IllegalMonitorStateException(exMsg);
        }
        return (Monitor) address.toObjectReference().toObject();
    }

    /**
     * Gets the inflated monitor of an object (if any).
     *
     * @param object
     * @return The inflated monitor of the given object, or null if the given
     *         object has no inflated monitor.
     */
    @Internal
    public static Monitor getInflatedMonitor(Object object) {
        final Word oldlockword = ObjectReference.fromObject(object).toAddress().
            add(ObjectLayout.FLAGS_SLOT * Address.size()).loadWord();

        if (!oldlockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)).isZero()) {
            return getMonitor(oldlockword);
        } else {
            return null;
        }
    }

    /**
     * Checks whether the current thread holds the monitor on a given object.
     * This allows you to do <code>assert Thread.holdsLock(obj)</code>.
     *
     * @param obj the object to test lock ownership on.
     * @return true if the current thread is currently synchronized on obj
     * @throws NullPointerException if obj is null
     * @since 1.4
     */
    public static boolean holdsLock(Object obj) {
        final VmThread current = VmThread.currentThread();

        final Word lockword = ObjectReference.fromObject(obj).toAddress().
            add(ObjectLayout.FLAGS_SLOT * Address.size()).prepareWord();

        if (!lockword.and(Word.fromIntZeroExtend(ObjectFlags.LOCK_EXPANDED)).isZero()) {
            return getMonitor(lockword).isOwner(current);
        } else {
            final Word tid = Word.fromIntZeroExtend(current.getId());
            return lockword.and(Word.fromIntZeroExtend(ObjectFlags.THREAD_ID_MASK)).EQ(tid);
        }
    }

    /**
     * Make sure the given thread id does fit into the space reserved for it by
     * the thinlock stuff.
     *
     * @param tid
     */
    static void testThreadId(int tid) {
        if ((tid & ~ObjectFlags.THREAD_ID_MASK) != 0) {
            throw new InternalError("Invalid thread id " + tid);
        }
        if ((tid & ObjectFlags.THREAD_ID_MASK) == 0) {
            throw new InternalError("Invalid thread id " + tid);
        }
    }
}
TOP

Related Classes of org.jnode.vm.scheduler.MonitorManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.