/** * Create a guarded communication with a send communication. This constructor allows actors which * are not CSPActors access to CSP functionality by providing their own * ConditionalBranchController. * * @param guard The guard for the guarded communication statement represented by this object. * @param port The IOPort containing the channel (and thus receiver) that this branch will try to * rendezvous with. * @param channel The channel in the IOPort that this branch is trying to rendezvous with. * @param branchID The identification number assigned to this branch upon creation by the * CSPActor. * @param token The token this branch is trying to send. * @param controller The controller that this branch uses. * @exception IllegalActionException If the channel has more than one receiver or if the receiver * is not of type CSPReceiver. */ public ConditionalSend( boolean guard, IOPort port, int channel, int branchID, Token token, ConditionalBranchController controller) throws IllegalActionException { super(guard, port, branchID, controller); _port = port; _channel = channel; try { port.workspace().getReadAccess(); if (!port.isOutput()) { throw new IllegalActionException( port, "ConditionalSend: " + "tokens only sent from an output port."); } if (channel >= port.getWidth() || channel < 0) { throw new IllegalActionException(port, "ConditionalSend: " + "channel index out of range."); } Receiver[][] receivers = port.getRemoteReceivers(); if (receivers == null || receivers[channel] == null) { throw new IllegalActionException( port, "ConditionalSend: " + "Trying to rendezvous with null receiver"); } if (!(receivers[channel][0] instanceof CSPReceiver)) { throw new IllegalActionException( port, "ConditionalSend: " + "channel " + channel + " does not have a receiver " + "of type CSPReceiver."); } _setReceivers(receivers[channel]); } finally { port.workspace().doneReading(); } _setToken(token); }
/** * Return the director that created this receiver. If this receiver is an inside receiver of an * output port of an opaque composite actor, then the director will be the local director of the * container of its port. Otherwise, it's the executive director of the container of its port.Note * that the director returned is guaranteed to be non-null. This method is read synchronized on * the workspace. * * @return An instance of DEDirector. * @exception IllegalActionException If there is no container port, or if the port has no * container actor, or if the actor has no director, or if the director is not an instance of * DEDirector. */ private DEDirector _getDirector() throws IllegalActionException { IOPort port = getContainer(); if (port != null) { if (_directorVersion == port.workspace().getVersion()) { return _director; } // Cache is invalid. Reconstruct it. try { port.workspace().getReadAccess(); Actor actor = (Actor) port.getContainer(); if (actor != null) { Director dir; if (!port.isInput() && (actor instanceof CompositeActor) && ((CompositeActor) actor).isOpaque()) { dir = actor.getDirector(); } else { dir = actor.getExecutiveDirector(); } if (dir != null) { if (dir instanceof DEDirector) { _director = (DEDirector) dir; _directorVersion = port.workspace().getVersion(); return _director; } else { throw new IllegalActionException(getContainer(), "Does not have a DEDirector."); } } } } finally { port.workspace().doneReading(); } } throw new IllegalActionException( getContainer(), "Does not have a IOPort as the container of the receiver."); }
/** * Put a token on the queue contained in this receiver. If the queue is full, then suspend the * calling thread (blocking write) and inform the director of the same. Resume the process on * detecting room in the queue. If a termination is requested, then initiate the termination of * the calling process by throwing a TerminateProcessException. On detecting a room in the queue, * put a token in the queue. Check whether any process is blocked on a read from this receiver. If * a process is indeed blocked, then unblock the process, and inform the director of the same. * * @param token The token to be put in the receiver, or null to not put anything. * @exception NoRoomException If during initialization, capacity cannot be increased enough to * accommodate initial tokens. */ public void put(Token token) throws NoRoomException { IOPort port = getContainer(); if (port == null || token == null) { return; // Nothing to do. } Workspace workspace = port.workspace(); while (!_terminate) { int depth = 0; try { // NOTE: Avoid acquiring read access on the workspace // while holding the lock on the director because if // some other process is trying to acquire write access, // the request for read access will be deferred. Nameable container = getContainer().getContainer(); Manager manager = ((Actor) container).getManager(); // NOTE: This used to synchronize on this, but since it calls // director methods that are synchronized on the director, // this can cause deadlock. synchronized (_director) { // Need to check this again after acquiring the lock. // Otherwise, could end up calling wait() below _after_ // notification has occurred. if (_terminate) { break; } // If we are in the initialization phase, then we may have // to increase the queue capacity before proceeding. This // may be needed to support PublisherPorts that produce // initial tokens (or, I suppose, any actor that produces // initial tokens during initialize()?). if (!super.hasRoom()) { if (container instanceof Actor) { if (manager.getState().equals(Manager.INITIALIZING)) { try { _queue.setCapacity(_queue.getCapacity() + 1); } catch (IllegalActionException e) { throw new NoRoomException( getContainer(), "Failed to increase queue capacity enough to accommodate initial tokens"); } } } } // Try to write. if (super.hasRoom()) { super.put(token); // If any thread is blocked on a get(), then it will become // unblocked. Notify the director now so that there isn't a // spurious deadlock detection. if (_readPending != null) { _director.threadUnblocked(_readPending, this, PNDirector.READ_BLOCKED); _readPending = null; } // Normally, the _writePending reference will have // been cleared by the read that unblocked this write. // However, it might be that the director increased the // buffer size, which would also have the affect of unblocking // this write. Hence, we clear it here if it is set. if (_writePending != null) { _director.threadUnblocked(_writePending, this, PNDirector.WRITE_BLOCKED); _writePending = null; } break; } // Wait to try again. _writePending = Thread.currentThread(); _director.threadBlocked(_writePending, this, PNDirector.WRITE_BLOCKED); // NOTE: We cannot use workspace.wait(Object) here without // introducing a race condition, because we have to release // the lock on the _director before calling workspace.wait(_director). depth = workspace.releaseReadPermission(); _director.wait(); } // release lock on _director before reacquiring read permissions. } catch (InterruptedException e) { _terminate = true; } finally { if (depth > 0) { workspace.reacquireReadPermission(depth); } } } if (_terminate) { throw new TerminateProcessException("Process terminated."); } }