/* * $Id$ * * Copyright (C) 2003-2015 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.isolate; import java.io.IOException; import java.io.InterruptedIOException; import java.util.LinkedList; import java.util.Queue; import javax.isolate.ClosedLinkException; import javax.isolate.Link; import javax.isolate.LinkMessage; /** * Shared implementation of javax.isolate.Link * * @author Ewout Prangsma (epr@users.sourceforge.net) */ public final class VmLink { private final VmIsolateLocal<LinkImpl> linkHolder = new VmIsolateLocal<LinkImpl>(); private final Queue<LinkMessageImpl> messages = new LinkedList<LinkMessageImpl>(); private boolean closed = false; private VmIsolate sender; private VmIsolate receiver; /** * Create a new data link between the given isolates. * * @param sender * @param receiver * @return the new Link */ public static Link newLink(VmIsolate sender, VmIsolate receiver) { if (sender == receiver) { throw new IllegalArgumentException("sender == receiver"); } VmLink vmLink = new VmLink(sender, receiver); return vmLink.asLink(); } public static VmLink fromLink(Link link) { return ((LinkImpl) link).getImpl(); } /** * @param sender * @param receiver */ VmLink(VmIsolate sender, VmIsolate receiver) { this.sender = sender; this.receiver = receiver; } /** * Gets this shared link as Link instance. * * @return the Link instance */ public final Link asLink() { final LinkImpl link = linkHolder.get(); if (link == null) { linkHolder.set(new LinkImpl(this)); return linkHolder.get(); } else { return link; } } /** * Close this link. */ final void close() { if (!this.closed) { final VmIsolate current = VmIsolate.currentIsolate(); if ((current != receiver) && (current != sender)) { throw new IllegalStateException( "Only sender or receiver can close this link"); } this.closed = true; synchronized (this) { notifyAll(); } } } /** * Is this link currently open. * * @return {@code true} if the link is open, otherwise {@code false}. */ final boolean isOpen() { return !closed; } /** * @return the receiver */ final VmIsolate getReceiver() { return receiver; } /** * @return the sender */ final VmIsolate getSender() { return sender; } /** * Receives a copy of a message sent on this Link. * <p/> * The current thread will block in this method until a sender is available. * When the sender and receiver rendezvous, the message will then be * transferred. If multiple threads invoke this method on the same object, * only one thread will receive any message at rendezvous and the other * threads will contend for subsequent access to the rendezvous point. A * normal return indicates that the message was received successfully. If an * exception occured on the sender side, no rendezvous will occur, and no * object will be transferred; the receiver will wait for the next * successful send. If an exception occurs on the receive, the sender will * see a successful transfer. * <p/> * This method never returns null. * <p/> * If the sending isolate becomes terminated after this method is invoked * but before it returns, the link will be closed, a ClosedLinkException * will be thrown, any subsequent attempts to use receive() will result in a * ClosedLinkException, and any Isolate objects corresponding to the receive * side of the link will reflect a terminated state. * <p/> * If invoked on a closed Link, this method will throw a * ClosedLinkException. * <p/> * If close() is invoked on the link while a thread is blocked in receive(), * receive() will throw a ClosedLinkException. No message will have been * transferred in that case. * <p/> * If Thread.interrupt() is invoked on a thread that has not yet completed * an invocation of this method, the results are undefined. An * InterruptedIOException may or may not be thrown; if not, control will * return from the method with a valid LinkMessage object. However, even if * InterruptedIOException is thrown, rendezvous and data transfer from the * sending isolate may have occurred. In particular, undetected message loss * between sender and receiver may occur. * <p/> * If a failure occurs due to the object being transferred between isolates * an IOException may be thrown in the receiver. For example, if a message * containing a large buffer is sent and the receiver has insufficient heap * memory for the buffer or if construction of a link in the receiver * isolate fails, an IOException will be thrown. The sender will see a * successful transfer in these cases. * <p/> * If the current isolate is not a receiver on this Link an * IllegalStateException will be thrown. The receiver will not rendezvous * with a sender in this case. * * @return */ final LinkMessage receive() throws ClosedLinkException, IllegalStateException, InterruptedIOException, IOException { if (VmIsolate.currentIsolate() != receiver) { // Current isolate is not the receiver throw new IllegalStateException(); } if (this.closed) { throw new ClosedLinkException(); } final LinkMessageImpl message; synchronized (this) { while (messages.isEmpty()) { if (this.closed) { throw new ClosedLinkException(); } try { wait(); } catch (InterruptedException ex) { throw new InterruptedIOException(); } } message = messages.poll(); } message.notifyReceived(); return message.cloneMessage(); } /** * Sends the given message on this Link. * <p/> * The current thread will block in this method until a receiver is * available. When the sender and receiver rendezvous, the message will then * be transferred. A normal return indicates that the message was * transferred. If an exception occurs on the sender side, no rendezvous * will occur and no object will be transferred. But if an exception occurs * on the receive(), the sender will see a successful transfer. * <p/> * If the receiving isolate becomes terminated after this method is invoked * but before it returns, the link will be closed, a ClosedLinkException * will be thrown, any subsequent attempts to use send() will result in a * ClosedLinkException, and any Isolate objects corresponding to the receive * side of the link will reflect a terminated state. * <p/> * If invoked on a closed link, this method will throw a * ClosedLinkException. * <p/> * If close() is invoked on this Link while a thread is blocked in send(), * send() will throw a ClosedLinkException. No message will have been * transferred in that case. * <p/> * If Thread.interrupt() is invoked on a thread that has not yet completed * an invocation of this method, the results are undefined. An * InterruptedIOException may or may not be thrown; however, even if not, * control will return from the method. Rendezvous and data transfer to the * receiving isolate may or may not have occurred. In particular, undetected * message loss between sender and receiver may occur. * <p/> * If a failure occurs during the transfer an IOException may be thrown in * the sender and the object will not be sent. A transfer may succeed from * the sender's point of view, but cause an independent IOException in the * receiver. For example, if a message containing a large buffer is sent and * the receiver has insufficient heap memory for the buffer or if * construction of a link in the receiver isolate fails, an IOException will * be thrown in the receiver after the transfer completes. * <p/> * A ClosedLinkException will be thrown if the given LinkMessage contains a * closed Link, Socket, or ServerSocket. No object will be transferred in * this case. * <p/> * If the current isolate is not a sender on this Link, an * UnsupportedOperationException will be thrown. No object will be sent in * this case. * * @param message * @throws ClosedLinkException * @throws InterruptedIOException * @throws IOException */ final void send(LinkMessage message) throws ClosedLinkException, InterruptedIOException, IOException { if (VmIsolate.currentIsolate() != sender) { // Current isolate is not the sender for this message throw new UnsupportedOperationException(); } if (this.closed) { throw new ClosedLinkException(); } final LinkMessageImpl messageImpl = (LinkMessageImpl) message; synchronized (this) { if (this.closed) { throw new ClosedLinkException(); } // Send message messages.add(messageImpl); notifyAll(); } // Wait for the message to be picked up by the receiver try { messageImpl.waitUntilReceived(); } catch (InterruptedException ex) { throw new InterruptedIOException(); } } /** * This method is used to send status messages. These are sent * without blocking and are queued in the link for the receiver * to read at its leisure. If the link is closed when this * method is called, the message is quietly dropped. * * @param message the status message to be sent. */ public final synchronized void sendStatus(LinkMessage message) { if (!this.closed) { // Send message messages.add((LinkMessageImpl) message); notifyAll(); } } }