0

I'm trying to use an agent callback concurrently. Unfortunately, no matter what I do it always seems to run sequentially instead of parallel. (without the agent it doesn't)

main class(APPLICATION):

class
    APPLICATION

inherit
    ARGUMENTS

create
    make

feature {NONE} -- Initialization

    make
            -- Run application.
        local
            a1 : separate PROCEDURE
            a2 : separate PROCEDURE
        do
            create my_counter.make (1, 100_000_000)
            create my_counter2.make (2, 100_000_000)

            launch_counter(my_counter)
            launch_counter(my_counter2)

        end

feature -- Launch

    launch_counter(c: separate COUNTER)
        do
            c.run (5, agent print_hello)
        end

    print_hello
        do
            print("Hello!%N")
        end

feature -- Access

    my_counter : separate COUNTER
    my_counter2 : separate COUNTER

end

counter class:

class
    COUNTER

inherit
    EXECUTION_ENVIRONMENT


create
    make

feature -- Initilzation
    make (i: INTEGER; delay: INTEGER)
        do
            id := i
            delay_time := delay
        end

feature -- Stuff

    run (times: INTEGER; p: separate PROCEDURE)
    local
        c : INTEGER
    do
        from
            c := times
        until
            c = 0
        loop
            print("COUNTER: " + id.out)
            p.call
            sleep(delay_time)
            c := c - 1

        end

    end

feature {NONE} -- Access

    delay_time : INTEGER
    id: INTEGER

end

expected output:

COUNTER: 1Hello!
COUNTER: 2Hello!
COUNTER: 1Hello!
etc.

actual output:

COUNTER: 1Hello!
COUNTER: 1Hello!
COUNTER: 1Hello!
COUNTER: 1Hello!
COUNTER: 1Hello!
COUNTER: 2Hello!
COUNTER: 2Hello!
COUNTER: 2Hello!
COUNTER: 2Hello!
COUNTER: 2Hello!

What would I have to change to make this run as expected?

algorithms
  • 1,085
  • 1
  • 12
  • 21

1 Answers1

1

The agent object keeps a reference to the target, in your example to the root object of type APPLICAITON, therefore all calls to print_hello get synchronized. To avoid that, the objects on which the agent is called should be recorded in a COUNTER object and used from there.

This can be achieved by adding an attribute action to the class COUNTER and updating its creation procedure

make (i: INTEGER; delay: INTEGER; a: separate PROCEDURE)
    do
        id := i
        delay_time := delay
        action := a
    end
...
action: separate PROCEDURE

Then instead of p.call in COUNTER.run the following code will be used (the feature run has no argument p anymore):

        separate action as p do
            p.call
        end

Now p is not locked on entry to the feature run and therefore callbacks can be executed alternately by different processors.

Alexander Kogtenkov
  • 5,770
  • 1
  • 27
  • 35
  • Thanks a lot. How would I go about telling the counter to stop/pause if the inner loop is not just a counter but an infinite loop? (or i just want to stop it before reaching its maximum count) When I just use a setter and a condition in "UNTIL", it seems that the setter never gets called because the object stays in his run method forever. – algorithms Jul 08 '17 at 11:26
  • nevermind, I found out how to do it. I had to wrap the stop condition in a separate processor. – algorithms Jul 08 '17 at 13:49
  • 1
    Hint: if the separate processor keeping the stop condition has no behavior by itself, i.e. is only used to set and to get the condition, the ccorresponding separate object `stop` can be created in a passive separate region with the syntax `create stop.make`. Then no additional thread for it will be created. – Alexander Kogtenkov Jul 08 '17 at 15:05