3

I am running several subtasks in parallel with Ant.

This is the simplified content of build.xml:

<target name="parallelOperations">
    <var name="port.number" value="9006"/>
    <for list="a,b,c,d,e" param="letter" parallel="true">
        <sequential>
            <echo>Letter @{letter}</echo>
            <math result="port.number" operand1="${port.number}" operation="+" operand2="1" datatype="int"/>
            <echo>${port.number}</echo>
        </sequential>
    </for>
</target>

This is the main result:

 [echo] Letter c
 [echo] Letter b
 [echo] Letter a
 [echo] Letter d
 [echo] Letter e
 [echo] 9007
 [echo] 9007
 [echo] 9007
 [echo] 9007
 [echo] 9007

What happens here is for each element of the list, it prints the content of the element and the result of plus operation. The problem here is the math operation is not thread-save, means that every operation accesses the variable $port.number concurrently, adds one value, and then assigns the value.

Is there anyway to do it thread-safe with Ant? What I try to do is per each subtask which runs in parallel, get an unique port number. If there is any other way to do it, it could be also good solution.

Mark O'Connor
  • 76,015
  • 10
  • 139
  • 185
jiahao
  • 3,373
  • 2
  • 35
  • 36

2 Answers2

3

This is from the documentation of parallel:

The primary use case for <parallel> is to run external programs such as an application server, and the JUnit or TestNG test suites at the same time. Anyone trying to run large Ant task sequences in parallel, such as javadoc and javac at the same time, is implicitly taking on the task of identifying and fixing all concurrency bugs the tasks that they run.

Therefore synchronizing the operation is up to the user. There are potentially two solutions:

  1. Either use the waitfor task to make each thread to wait for a certain condition to be true (e.g. a property is set).

  2. Implement your own synchronization task. This link describes a way to write a task that can take an id and locks on it.

The first solution is more simple and is generally intended for this kind of synchronization.

M A
  • 71,713
  • 13
  • 134
  • 174
  • waitfor was not enough for me. The implementation of Stefan Frank of synchronisation linked on the question 2 is the right one. Thanks for it! – jiahao Jan 14 '15 at 07:53
1

I am going to answer my own question posting the specific solution, just in case someone could need it.

This is the working solution:

<target name="ParallelOperations">
<var name="port.number" value="9006"/>
<for list="a,b,c,d" param="letter" parallel="true">
    <sequential>
        <echo>Letter @{letter}</echo>
        <!-- Synchronize the port number -->
        <synchronized id="port.number.lock">
            <echo>@{letter} new port ${port.number}</echo>
            <var name="@{letter}.port.number" value="${port.number}" />
            <math result="port.number" operand1="${port.number}" operation="+" operand2="1" datatype="int"/>
        </synchronized>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        <echo>@{letter} ${@{letter}.port.number}</echo>
        </sequential>
</for>

And this is the result:

ParallelOperations:
     [echo] Letter d
     [echo] Letter a
     [echo] Letter c
     [echo] Letter b
     [echo] c new port 9006
     [echo] c 9006
     [echo] b new port 9007
     [echo] c 9006
     [echo] c 9006
     [echo] b 9007
     [echo] c 9006
     [echo] a new port 9008
     [echo] b 9007
     [echo] c 9006
     [echo] b 9007
     [echo] c 9006
     [echo] a 9008
     [echo] d new port 9009
     [echo] c 9006
     [echo] b 9007
     [echo] a 9008
     [echo] d 9009
     [echo] b 9007
     [echo] a 9008
     [echo] d 9009
     [echo] c 9006
     [echo] a 9008
     [echo] b 9007
     [echo] d 9009
     [echo] a 9008
     [echo] b 9007
     [echo] d 9009
     [echo] a 9008
     [echo] b 9007
     [echo] d 9009
     [echo] a 9008
     [echo] d 9009
     [echo] a 9008
     [echo] d 9009
     [echo] d 9009

From the answer of @manouti I picked the synchronized command from Stefan Fanke and I am using each letter to create a specific variable to store the port number.

As you can see, each letter has its own unique port number assigned.

jiahao
  • 3,373
  • 2
  • 35
  • 36