0

In an UML activity diagram, join nodes synchronise concurrent flows by regulating the flow of tokens. The UML specifications foresees that tokens can be rejected. Section 15.3.3.4 Join Nodes says:

If any tokens are offered to the outgoing ActivityEdge of a JoinNode, they shall be accepted by the target or rejected for traversal over the edge (e.g., due to a failed guard) before any more tokens are offered to the outgoing edge. If tokens are rejected for traversal, they shall no longer be offered to the outgoing edge.

But it does not clearly determine what can reject token. For example selection behavior and weight property of outgoing edge and also acceptance of target node (includes it's precondition) can force a token to wait before traversing. Which ones can reject traversing?

Also above quote says:

... before any more tokens are offered to the outgoing edge

Does it mean when a token is rejected for traversal, it immediately will be withdrawn (and destroyed) ? Or may it wait until the next token is prepared and when that next token be offered, older one will be destroyed?

For example in below diagram, what will happen if left JoinNode receives tokens from branches b1 and b2 and creates token b but token a be not created yet? Does b wait for a? Or it will be withdrawn? And if it waits, what will happen if left join node creates another token before a is offered to the right JoinNode?

diagram

2

The quote mentions not just rejected but rejected for traversal over the edge. This corresponds to the case that a token is not allowed to go through an edge, and not to the case where a target may not accept it.

In which case is it rejected for traversal ?

Unfortunately, unlike the terms offered and accepted which are well defined, the term rejected is not explicitly defined and need to be interpreted.

To my knowledge it can happen only in the case of a guard, as described in 15.2.3.3:

An ActivityEdge may have a guard, which is a ValueSpecification that is evaluated for each token offered to the edge. An offer shall only pass along an ActivityEdge if the guard for the edge evaluates to true for the offered token.

In which case is it not rejected for traversal ?

For weight, there is no rejected traversal. The tokens are accumulated in the source until the threshold is reached. In case a guard blocks a token, it's not the traversal that fails but the offer:

If the guard fails for any of the tokens, and this reduces the number of tokens that can be offered to the target to less than the weight, then all the tokens fail to be offered.

For selection there's also no rejection of a traversal, but a delay on acceptance (15.2.3.4) :

selection Behavior is used while offering tokens to the target node, it may be run many times on the same token before the token is accepted by the target node.

Additional remarks on 15.3.3.4 and resolution of your scenario

The following statement tells us that in case of a rejected traversal, the token is definitively withdrawn:

If tokens are rejected for traversal, they shall no longer be offered to the outgoing edge.

The following statement tells us that unless a token is rejected for traversal, it will wait until it is accepted by the target (the acceptance doesn't need to be immediate):

They shall be accepted by the target or rejected for traversal over the edge

Consequence: In your scenario, b will wait for a and they will both together be consumed to generate an output token of the right join node.

And finally, this sentence explains that in case of successful traversal, the tokens are sent sequentially, one by one:

before any more tokens are offered to the outgoing edge

Consequence: In your scenario, as long as b will wait for an a, no new b will be offered by the left join.

Edit: Isn't there an issue with the UML spec here ?

The "one by one" rule doesn't seem fully consistent IMHO:

  • The rule is written about the offering part on the output, not the consumption part of the input. This makes the behavior in case of a weight on the output extremely ambiguous: I'd avoid it.
  • The rule seems to be written with control tokens in mind. It is not clear to me what happens if 2 different object tokens are on the input (or 2 replicated objects without the CombinedDuplicates): I'd expect all of them to pass the join. But what hapens if one of it gets rejected: would the other continue its way alone ?

Similarly, IMHO the rejection on traversal logic seems not to cover all the cases. For example, what if the output edge goes to a decision node (so it is not rejected), but none of the subsequent guarded branch can accept it ? Shall we understand that the token is rejected (implicitly the decision input would be guarded by the combination of all possible output), or shall we consider it's consumed and the token is waiting in the decision node ?

So I think there is still some work to do for the standard committee in this area.

  • By "as long as b will wait for an a, no new b will be offered by the left join.", do you mean "join does not accept tokens when it's previously produced token does not traverse outgoing edge"? Note that a control token cannot hold tokens. If it is true, then it means we cannot use non default weight and selection for outgoing edges of joinnode. Is this OK? Although it seems if offering new token by left joinnode when b is waited for a causes withdrawing b, then consequence will again be "we cannot use non default weight and selection for outgoing edges of joinnode". – hasanghaforian Oct 2 '18 at 3:18
  • @hasanghaforian I think indeed there are some open issues with the standard in this regard. I've edited the answer to add a special section about this. – Christophe Oct 2 '18 at 6:15
  • There's also a place where they talk about "withdrawal of tokens" which is pretty much confusing. Actually they meant there that the token is consumed elsewhere. Even standards are flaky. – qwerty_so Oct 2 '18 at 14:33
  • @Christophe I added an answer to my question, can you see that and write your opinion in about that please? – hasanghaforian Oct 3 '18 at 6:29
  • @ThomasKilian I added an answer to my question, can you see that and write your opinion in about that please? – hasanghaforian Oct 3 '18 at 6:30
1

I think the problem with your picture is that you are trying to do something superficial. The edge b is absolutely superfluous. Actually what you would do is to directly join the tokens in one place:

enter image description here

Tokens will wait in Action and the ones sending b1 and b2.

You question is a sophism since the facts are wrong. Like

The village barber shaves all men in the village that don't shave themselves. Does he shave himself or not?

If you think a moment you find that the thesis is just not right. So any discussion around it is meaningless.

  • OK. But that was for simplifying describing my question. Really I want to know does a produced token wait? Also is it possible to use weight on outgoing edge of JoinNode? – hasanghaforian Oct 3 '18 at 8:13
  • See my addition above. Regarding weights you should post a new question. – qwerty_so Oct 3 '18 at 9:00
0

I read Christophe's answer and related parts of specification multiple times until now and describe what I understand.

At first I attempt to second part of my question:

Does it mean when a token is rejected for traversal, it immediately will be withdrawn (and destroyed) ? Or may it wait until the next token is prepared and when that next token be offered, older one will be destroyed?

As Christophe said, this statement can be interpreted that "a token can wait":

they shall be accepted by the target or rejected for traversal over the edge (e.g., due to a failed guard) before any more tokens are offered to the outgoing edge.

But there is a question: "Where can arrived token wait?"

  • not in JoinNode (specification says only FinalNode of control nodes can hold a token)
  • not in outgoing edge (specification says only outgoing edge of ForkNode can hold tokens)

So I conclude: "When a token offers to outgoing edge of JoinNode, it must immediately traverse to downstream node of edge or it will be rejected (and shall no longer be offered to the outgoing edge)." Thus in diagram of question, b does not wait for a.

In about first part of my question, I think when a token cannot wait, using non default weight will be irrational and a token may be rejected by either of guard or selection or precondition of target node.

  • Interesting. I'm very tempted to agree on the weight issue. However, I'm still not sure about the requirement for an immediate traversal: the join can consume incoming tokens, and without storing a token, wait to generate the new token. This could explain why the standard explicitly mentions the one by one rule. – Christophe Oct 3 '18 at 6:53
  • @Christophe Does Activity diagrams tell us about time? – hasanghaforian Oct 3 '18 at 7:06
  • @Christophe OK, so we can think it can wait until newer token be produced. But if newer be produced, older one will be removed/withdrawn/destroyed. Are you agree? – hasanghaforian Oct 3 '18 at 7:08
  • Not in case of a timing issue. But let me reformulate in a better way the discrepancy between consumed/generated, because it's misleading: the incoming tokens are not pushed and received, but offered and accepted. So if new tokens are offered in b1 and b2 but the join is still delayed in offering the output token, then the incoming tokens will wait in their source activity. So there will never be a rejection due to timing. Same principle as with petri nets – Christophe Oct 3 '18 at 7:37
  • By the way, this reasoning reinforces the suspicion of incompatibility with weight on an output. If weight is needed on the output, the safe approach would be to use an intermediary receiving activity before the weight. – Christophe Oct 3 '18 at 7:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.