How should I do that the two receiving processes not to be twice in a row in Promela model?

295 Views Asked by At

I am a beginner in the spin. I am trying that the model runs the two receiving processes (function called consumer in the model) alternatively, ie. (consumer 1, consumer 2, consumer 1, consumer 2,...). But when I run this code, my output for 2 consumer processes are showing randomly. Can someone help me? This is my code I am struggling with.

mtype = {P, C};

mtype turn = P;

chan ch1 = [1] of {bit};
byte current_consumer = 1;
byte previous_consumer;

active [2] proctype Producer()
{`
    bit a = 0;
    do
    :: atomic {
        turn == P ->
            ch1 ! a;
            printf("The producer %d --> sent %d!\n", _pid, a);
            a = 1 - a;
            turn = C;
    }
    od
    
}

active [2] proctype Consumer()
{
    bit b;

    do
    :: atomic{
        turn == C ->
            current_consumer = _pid;
            ch1 ? b;
            printf("The consumer %d --> received %d!\n\n", _pid, b);
            assert(current_consumer == _pid);
            turn = P;
    }
    od
    
}

Sample out is as photo

1

There are 1 best solutions below

0
On

First of all, let me draw your attention to this excerpt of atomic's documentation:

If any statement within the atomic sequence blocks, atomicity is lost, and other processes are then allowed to start executing statements. When the blocked statement becomes executable again, the execution of the atomic sequence can be resumed at any time, but not necessarily immediately. Before the process can resume the atomic execution of the remainder of the sequence, the process must first compete with all other active processes in the system to regain control, that is, it must first be scheduled for execution.

In your model, this is currently not causing any problem because ch1 is a buffered channel (i.e. it has size >= 1). However, any small change in the model could break this invariant.


From the comments, I understand that your goal is to alternate consumers, but you don't really care which producer is sending the data.

To be honest, your model already contains two examples of how processes can alternate with one another:

  • The Producer/Consumers alternate one another via turn, by assigning a different value each time
  • The Producer/Consumers alternate one another also via ch1, since this has size 1

However, both approaches are alternating Producer/Consumers rather than Consumers themselves.


One approach I like is message filtering with eval (see docs): each Consumer knows its own id, waits for a token with its own id in a separate channel, and only when that is available it starts doing some work.

byte current_consumer;
chan prod2cons = [1] of { bit };
chan cons = [1] of { byte };

proctype Producer(byte id; byte total)
{
    bit a = 0;
    do
        :: true ->
            // atomic is only for printing purposes
            atomic {
                prod2cons ! a;
                printf("The producer %d --> sent %d\n", id, a);
            }
            a = 1 - a;
    od
}

proctype Consumer(byte id; byte total)
{
    bit b;
    do
        :: cons?eval(id) ->
            current_consumer = id;
            atomic {
                prod2cons ? b;
                printf("The consumer %d --> received %d\n\n", id, b);
            }
            assert(current_consumer == id);

            // yield turn to the next Consumer
            cons ! ((id + 1) % total)
    od
}

init {
    run Producer(0, 2);
    run Producer(1, 2);

    run Consumer(0, 2);
    run Consumer(1, 2);

    // First consumer is 0
    cons!0;
}

This model, briefly:

  • Producers/Consumers alternate via prod2cons, a channel of size 1. This enforces the following behavior: after some producers created a message some consumer must consume it.
  • Consumers alternate via cons, a channel of size 1 containing a token value indicating which consumer is currently allowed to perform some work. All consumers peek on the contents of cons, but only the one with a matching id is allowed to consume the token and move on. At the end of its turn, the consumer creates a new token with the next id in the chain. Consumers alternate in a round robin fashion.

The output is:

      The producer 0 --> sent 0
                  The consumer 1 --> received 0

          The producer 1 --> sent 1
              The consumer 0 --> received 1

          The producer 1 --> sent 0
                  The consumer 1 --> received 0

           ...

      The producer 0 --> sent 0
                  The consumer 1 --> received 0

      The producer 0 --> sent 1
              The consumer 0 --> received 1

      The producer 0 --> sent 0
                  The consumer 1 --> received 0

      The producer 0 --> sent 1
              The consumer 0 --> received 1

Notice that producers do not necessarily alternate with one another, whereas consumers do -- as requested.