/* * JBoss, Home of Professional Open Source * Copyright 2008-10 Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.synchronization; import org.jboss.byteman.rule.exception.ExecuteException; /** * class used to manage rule rendezvous operations */ public class Rendezvous { public Rendezvous(int expected) { this(expected, false); } public Rendezvous(int expected, boolean rejoinable) { this.expected = expected; this.rejoinable = rejoinable; this.needsRemove = false; this.isDeleted = false; this.counter = new Counter(); } /** * enter this rendezvous. n.b. this must be called synchronized on the rendezvous object * in question * @param millis how long to wait (wait forever if 0) * @return the index in arrival order from 0 to expected of the calling thread or -1 if * either the rendezvous has completed and is not restartable or the rendezvous has been deleted */ public int rendezvous(long millis) { Counter currentCounter = counter; // too late the rendezvous has expired if (isDeleted || (currentCounter.arrived == expected)) { return -1; } // n.b. getting here implies !currentCounter.isPoisoned int index = currentCounter.arrived++; if (currentCounter.arrived < expected) { long target_time=System.currentTimeMillis() + millis; // make sure we don't return before the rendezvous has actually happened while (currentCounter.arrived < expected) { try { if(millis <= 0) { this.wait(); } else { long wait_time=target_time - System.currentTimeMillis(); if(wait_time > 0) { this.wait(wait_time); } else { throw new ExecuteException("timeout occurred in rendezvous"); } } } catch (InterruptedException e) { // do nothing } // isPoisoned may have changed because a delete happened while we were waiting if (currentCounter.isPoisoned) { return -1; } } } else { if (rejoinable) { // create a new counter for the next rendezvous -- this allows the current threads // to complete without counting them back out counter = new Counter(); } else { // tag the rendezvous to indicate that it has been deleted and needs ot be removed. the // first thread emerging from a call to rendezvous must make sure it gets removed from // the rendezvous map. isDeleted = true; needsRemove = true; } this.notifyAll(); } return index; } /** * delete this rendezvous causing any waiting threads to return -1 form the rendezvous call. n.b. this * must be called synchronized on the rendezvous object in question * @return false if a delete has already been requested otherwise true */ public boolean delete() { if (isDeleted) { return false; } isDeleted = true; needsRemove = true; // if any threads arrived then make sure they are *all* poisoned if (counter.arrived > 0 && counter.arrived < expected) { counter.isPoisoned = true; this.notifyAll(); } return true; } public int getExpected() { return expected; } /** * the number of threads which are expected to arrive at this rendezvous */ private int expected; /** * the current counter for this rendezvous */ private Counter counter; /** * true if this rendezvous can be repeatedly joined, false it it is a one-off meeting */ private boolean rejoinable; /** * true if a rendezvous was deleted while a rendezbvous was in progress but had not completed */ private boolean isDeleted; /** * true if a non-restartable rendezvous has completed and has not been removed from the rendezvous map */ private boolean needsRemove; /** * retrieve the number of threads waiting at the rendezvous or -1 if the rendezvous has * been deleted * @return number of threads waiting or -1 */ public int getArrived() { if (isDeleted) { return -1; } return counter.arrived; } /** * check if the rendezvous has completed but has not yet been removed * @return the above */ public boolean needsRemove() { return needsRemove; } /** * mark a completed rendezvous to indicate that it has been removed */ public void setRemoved() { needsRemove = false; } /** * class encapsulating state for a specific rendezvous */ public class Counter { /** * count of the number of threads actually arrived at this rendezvous */ public int arrived; /** * true if this */ public boolean isPoisoned; public Counter() { arrived = 0; isPoisoned = false; } } }