/* * Copyright (c) 2016, Kasra Faghihi, All rights reserved. * * 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 3.0 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. */ package com.offbynull.coroutines.user; import java.io.Serializable; import java.util.LinkedList; import java.util.ListIterator; /** * Do not use -- for internal use only. * <p> * Holds on to the state of "synchronized" locks (MONITORENTER/MONITOREXIT) within a method frame. * @author Kasra Faghihi */ public final class LockState implements Serializable { private static final long serialVersionUID = 3L; // We use a linkedlist to make sure that we retain the order of monitors as they come in. Otherwise we're going to deal with deadlock // issues if we have code structured with double locks. For example, imagine the following scenario... // // Method 1: // synchronized(a) { // synchronized(b) { // continuation.suspend(); // } // } // // Method 2 (same as method1): // synchronized(a) { // synchronized(b) { // continuation.suspend(); // } // } // // // If we weren't ordered, we may end up coming back after a suspend and re-locking the monitors in the wrong order. For example, // we may lock b before we lock a. That has the potential for a deadlock because another thread could execute the locking logic // correctly (first a and then b). Dual locking without retaining the same order = a deadlock waiting to happen. // // Long story short: it's vital that we keep the order which locks happen private LinkedList monitors = new LinkedList(); /** * Do not use -- for internal use only. * <p> * Should be called after a MONITORENTER instruction has been executed. Tracks the object that MONITORENTER was used on. * @param monitor object the MONITORENTER instruction was used on */ public void enter(Object monitor) { if (monitor == null) { throw new NullPointerException(); } monitors.add(monitor); } /** * Do not use -- for internal use only. * <p> * Should be called after a MONITOREXIT instruction has been executed. Untracks the object that MONITOREXIT was used on. * @param monitor object the MONITOREXIT instruction was used on */ public void exit(Object monitor) { if (monitor == null) { throw new NullPointerException(); } // remove last ListIterator it = monitors.listIterator(monitors.size()); while (it.hasPrevious()) { Object prev = it.previous(); if (monitor == prev) { // Never use equals() to test equality. We always need to make sure that the objects are the same, we // don't care if they're the objects are logically equivalent it.remove(); return; } } throw new IllegalArgumentException(); // not found } /** * Dumps monitors out as an array. Order is retained. * @return monitors */ public Object[] toArray() { return monitors.toArray(); } }