Issue 13311: The call to receiveOffer at the end of ActionActivation::fire could can cause an infinite recursion (fuml-ftf) Source: Model Driven Solutions (Mr. Ed Seidewitz, ed-s(at)modeldriven.com) Nature: Uncategorized Issue Severity: Summary: Specification: Semantics of a Foundation Subset for Executable UML Models, FTF – Beta 1 (ptc/08-11-03) Section: 8.6.2.2.1 (ActionActivation) Summary: At the end of ActionActivation::fire, a check is made to see if the action should fire again. The intent is that this should happen if the action has input pins and there are still tokens left on one or more pins after the previous firing (because there were more tokens on the pins than the multiplicity upper bound). Currently, this check is made by first checking that the action has input pins and then calling receiveOffer, so that the isReady test can check for inputs. However, if the action input pins, but they are all optional (i.e., multiplicity lower bound of 0), and no incoming control flows, then the action will be ready to fire whether or not there are any inputs on the pins. This will result in an infinite recursion of the action continually firing. Proposed resolution: Replace: // Activate the action again, if prerequisites are still satisfied (i.e., when tokens have been left on input pins). Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0) { this.receiveOffer(); } with: // Activate the action again, if tokens have been left on input pins and the action has no incoming control flows. Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0 & this.node.incoming.size() == 0) { boolean fireAgain = true; InputPinList inputPins = ((Action)(this.node)).input; int j = 1; while (fireAgain & j <= inputPins.size()) { PinActivation inputPinActivation = this.getPinActivation(inputPins.getValue(j-1)); fireAgain = inputPinActivation.isReady() & inputPinActivation.countUnofferedTokens() > 0; j = j + 1; } if (fireAgain) { this.fire(new TokenList()); } } (Note: This code does not check for incoming control tokens, but, rather, only fires the action again if it has no incoming control flows. If an action has incoming control flows and inputs left on its input pins, then an offer on a control flow will trigger the action to fire again.) Resolution: Change the code as proposed. Revised Text: In the method for ActionActivation::doAction, replace: // Activate the action again, if prerequisites are still // satisfied (i.e., when tokens have been left on input // pins). Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0) { this.receiveOffer(); } with: // Activate the action again, if tokens have been left on input pins and the action has no incoming control flows. Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0 & this.node.incoming.size() == 0) { boolean fireAgain = true; InputPinList inputPins = ((Action)(this.node)).input; int j = 1; while (fireAgain & j <= inputPins.size()) { PinActivation inputPinActivation = this.getPinActivation(inputPins.getValue(j-1)); fireAgain = inputPinActivation.isReady() & inputPinActivation.countUnofferedTokens() > 0; j = j + 1; } if (fireAgain) { this.fire(new TokenList()); } } Actions taken: January 20, 2009: received issue July 23, 2010: closed issue Discussion: End of Annotations:===== ubject: Issue: The call to receiveOffer at the end of ActionActivation::fire could can cause an infinite recursion Date: Tue, 20 Jan 2009 15:51:11 -0500 X-MS-Has-Attach: X-MS-TNEF-Correlator: Thread-Topic: Issue: The call to receiveOffer at the end of ActionActivation::fire could can cause an infinite recursion thread-index: Acl7QNMsl+g75ig/SFeNCTuaOEYnnw== From: "Ed Seidewitz" To: Cc: Specification: Semantics of a Foundation Subset for Executable UML Models, FTF . Beta 1 (ptc/08-11-03) Section: 8.6.2.2.1 (ActionActivation) Summary: At the end of ActionActivation::fire, a check is made to see if the action should fire again. The intent is that this should happen if the action has input pins and there are still tokens left on one or more pins after the previous firing (because there were more tokens on the pins than the multiplicity upper bound). Currently, this check is made by first checking that the action has input pins and then calling receiveOffer, so that the isReady test can check for inputs. However, if the action input pins, but they are all optional (i.e., multiplicity lower bound of 0), and no incoming control flows, then the action will be ready to fire whether or not there are any inputs on the pins. This will result in an infinite recursion of the action continually firing. Proposed resolution: Replace: // Activate the action again, if prerequisites are still satisfied (i.e., when tokens have been left on input pins). Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0) { this.receiveOffer(); } with: // Activate the action again, if tokens have been left on input pins and the action has no incoming control flows. Debug.println("[fire] Checking if " + this.node.name + " should fire again..."); if (((Action)(this.node)).input.size() > 0 & this.node.incoming.size() == 0) { boolean fireAgain = true; InputPinList inputPins = ((Action)(this.node)).input; int j = 1; while (fireAgain & j <= inputPins.size()) { PinActivation inputPinActivation = this.getPinActivation(inputPins.getValue(j-1)); fireAgain = inputPinActivation.isReady() & inputPinActivation.countUnofferedTokens() > 0; j = j + 1; } if (fireAgain) { this.fire(new TokenList()); } } (Note: This code does not check for incoming control tokens, but, rather, only fires the action again if it has no incoming control flows. If an action has incoming control flows and inputs left on its input pins, then an offer on a control flow will trigger the action to fire again.)