I have the following program that models a FIFO with a process in PROMELA:
mtype = { PUSH, POP, IS_EMPTY, IS_FULL };
#define PRODUCER_UID 0
#define CONSUMER_UID 1
proctype fifo(chan inputs, outputs)
{
mtype command;
int data, tmp, src_uid;
bool data_valid = false;
do
:: true ->
inputs?command(tmp, src_uid);
if
:: command == PUSH ->
if
:: data_valid ->
outputs!IS_FULL(true, src_uid);
:: else ->
data = tmp
data_valid = true;
outputs!PUSH(data, src_uid);
fi
:: command == POP ->
if
:: !data_valid ->
outputs!IS_EMPTY(true, src_uid);
:: else ->
outputs!POP(data, src_uid);
data = -1;
data_valid = false;
fi
:: command == IS_EMPTY ->
outputs!IS_EMPTY(!data_valid, src_uid);
:: command == IS_FULL ->
outputs!IS_FULL(data_valid, src_uid);
fi;
od;
}
proctype producer(chan inputs, outputs)
{
mtype command;
int v;
do
:: true ->
atomic {
inputs!IS_FULL(false, PRODUCER_UID) ->
outputs?IS_FULL(v, PRODUCER_UID);
}
if
:: v == 1 ->
skip
:: else ->
select(v: 0..16);
printf("P[%d] - produced: %d\n", _pid, v);
access_fifo:
atomic {
inputs!PUSH(v, PRODUCER_UID);
outputs?command(v, PRODUCER_UID);
}
assert(command == PUSH);
fi;
od;
}
proctype consumer(chan inputs, outputs)
{
mtype command;
int v;
do
:: true ->
atomic {
inputs!IS_EMPTY(false, CONSUMER_UID) ->
outputs?IS_EMPTY(v, CONSUMER_UID);
}
if
:: v == 1 ->
skip
:: else ->
access_fifo:
atomic {
inputs!POP(v, CONSUMER_UID);
outputs?command(v, CONSUMER_UID);
}
assert(command == POP);
printf("P[%d] - consumed: %d\n", _pid, v);
fi;
od;
}
init {
chan inputs = [0] of { mtype, int, int };
chan outputs = [0] of { mtype, int, int };
run fifo(inputs, outputs); // pid: 1
run producer(inputs, outputs); // pid: 2
run consumer(inputs, outputs); // pid: 3
}
I want to add wr_ptr and rd_ptr in the program to indicate write and read pointers relative to the depth of FIFO when a PUSH update is performed:
wr_ptr = wr_ptr % depth;
empty=0;
if
:: (rd_ptr == wr_ptr) -> full=true;
fi
and similar chances on POP updates
Could you please help me to add this to this program?
or should i make it an ltl property and use that to check it?
from comments: and i want to verify this property, for example If the fifo is full, one should not have a write request, that is the right syntax?full means that fifo is full and wr_idx is the write pointer, I do not know how to access the full, empty, wr_idx, rd_idx, depth on the fifo process in the properties ltl fifo_no_write_when_full {[] (full -> ! wr_idx)}
Here is an example of the process-based FIFO with size 1 that I gave you here adapted for an arbitrary size, which can be configured with FIFO_SIZE. For verification purposes, I would keep this value as small as possible (e.g. 3), because otherwise you are just widening the state space without including any more significant behaviour.
mtype = { PUSH, POP, IS_EMPTY, IS_FULL };
#define PRODUCER_UID 0
#define CONSUMER_UID 1
#define FIFO_SIZE 10
proctype fifo(chan inputs, outputs)
{
mtype command;
int tmp, src_uid;
int data[FIFO_SIZE];
byte head = 0;
byte count = 0;
bool res;
do
:: true ->
inputs?command(tmp, src_uid);
if
:: command == PUSH ->
if
:: count >= FIFO_SIZE ->
outputs!IS_FULL(true, src_uid);
:: else ->
data[(head + count) % FIFO_SIZE] = tmp;
count = count + 1;
outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid);
fi
:: command == POP ->
if
:: count <= 0 ->
outputs!IS_EMPTY(true, src_uid);
:: else ->
outputs!POP(data[head], src_uid);
atomic {
head = (head + 1) % FIFO_SIZE;
count = count - 1;
}
fi
:: command == IS_EMPTY ->
res = count <= 0;
outputs!IS_EMPTY(res, src_uid);
:: command == IS_FULL ->
res = count >= FIFO_SIZE;
outputs!IS_FULL(res, src_uid);
fi;
od;
}
No change to producer, consumer or init was necessary:
proctype producer(chan inputs, outputs)
{
mtype command;
int v;
do
:: true ->
atomic {
inputs!IS_FULL(false, PRODUCER_UID) ->
outputs?IS_FULL(v, PRODUCER_UID);
}
if
:: v == 1 ->
skip
:: else ->
select(v: 0..16);
printf("P[%d] - produced: %d\n", _pid, v);
access_fifo:
atomic {
inputs!PUSH(v, PRODUCER_UID);
outputs?command(v, PRODUCER_UID);
}
assert(command == PUSH);
fi;
od;
}
proctype consumer(chan inputs, outputs)
{
mtype command;
int v;
do
:: true ->
atomic {
inputs!IS_EMPTY(false, CONSUMER_UID) ->
outputs?IS_EMPTY(v, CONSUMER_UID);
}
if
:: v == 1 ->
skip
:: else ->
access_fifo:
atomic {
inputs!POP(v, CONSUMER_UID);
outputs?command(v, CONSUMER_UID);
}
assert(command == POP);
printf("P[%d] - consumed: %d\n", _pid, v);
fi;
od;
}
init {
chan inputs = [0] of { mtype, int, int };
chan outputs = [0] of { mtype, int, int };
run fifo(inputs, outputs); // pid: 1
run producer(inputs, outputs); // pid: 2
run consumer(inputs, outputs); // pid: 3
}
Now you should have enough material to work on and be ready to write your own properties. On this regard, in your question you write:
I do not know how to access the full, empty, wr_idx, rd_idx, depth on the fifo process in the properties ltl fifo_no_write_when_full {[] (full -> ! wr_idx)}
First of all, please note that in my code rd_idx corresponds to head, depth (should) correspond to count and that I did not use an explicit wr_idx because the latter can be derived from the former two: it is given by (head + count) % FIFO_SIZE. This is not just a choice of code cleanliness, because having fewer variables in a Promela model actually helps with memory consumption and running time of the verification process.
Of course, if you really want to have wr_idx in your model you are free to add it yourself. (:
Second, if you look at the Promela manual for ltl properties, you find that:
The names or symbols must be defined to represent boolean expressions on global variables from the model.
So in other words, it's not possible to put local variables inside an ltl expression. If you want to use them, then you should take them out from the process's local space and put them in the global space.
So, to check fifo_no_write_when_full* you could:
move the declaration of count out in the global space
add a label fifo_write: here:
:: command == PUSH ->
if
:: count >= FIFO_SIZE ->
outputs!IS_FULL(true, src_uid);
:: else ->
fifo_write:
data[(head + count) % FIFO_SIZE] = tmp;
count = count + 1;
outputs!PUSH(data[(head + count - 1) % FIFO_SIZE], src_uid);
fi
check the property:
ltl fifo_no_write_when_full { [] ( (count >= FIFO_SIZE) -> ! fifo#fifo_write) }
Third, before any attempt to verify any of your properties with the usual commands, e.g.
~$ spin -a fifo.pml
~$ gcc -o fifo pan.c
~$ ./fifo -a -N fifo_no_write_when_full
you should modify producer and consumer so that neither of them executes for an indefinite amount of time and therefore keep the search space at a small depth. Otherwise you are likely to get an error of the sort
error: max search depth too small
and have the verification exhaust all of your hardware resources without reaching any sensible conclusion.
*: actually the name fifo_no_write_when_full is quite generic and might have multiple interpretations, e.g.
the fifo does not perform a push when it is full
the producer is not able to push if the fifo is full
In the example I provided I chose to adopt the first interpretation of the property.
Related
bool lock[N];
int turn=0;
int offset=0;
int M = N-1;
int pidToN(int pid); //returns a unique number between (0,N-1) for a given pid; mapping pids
void critical()
{
int pidn = pidToN(getpid());
lock[pidn] = true;
turn = M-pidn;
if(turn == pidn)
{
val=1;
turn+=val%N;
}
else
val=0;
while(lock(M-pidn+val) && turn == (M-pidn+val) && lock(M-pidn-val) && turn == (M-pidn-val));
//critical section
lock[pidn] = false;
}
Does this implemention work? Essentially thread[i] tries to pass to thread[N-1-i] and vice versa. If i = N/2 (the thread at the middle, if it exists, which passes to itself) then I increment it by certain val (1 in this case) which then waits.
Couldn't come up with any race conditions.
Any help would be appreciated.
Ok, so I'm trying to model a CLH-RW lock in Promela.
The way the lock works is simple, really:
The queue consists of a tail, to which both readers and writers enqueue a node containing a single bool succ_must_wait they do so by creating a new node and CAS-ing it with the tail.
The tail thereby becomes the node's predecessor, pred.
Then they spin-wait on pred.succ_must_wait until it is false.
Readers first increment a reader counter ncritR and then set their own flag to false, allowing multiple readers at in the critical section at the same time. Releasing a readlock simply means decrementing ncritR again.
Writers wait until ncritR reaches zero, then enter the critical section. They do not set their flag to false until the lock is released.
I'm kind of struggling to model this in promela, though.
My current attempt (see below) tries to make use of arrays, where each node basically consists of a number of array entries.
This fails because let's say A enqueues itself, then B enqueues itself. Then the queue will look like this:
S <- A <- B
Where S is a sentinel node.
The problem now is, that when A runs to completeness and re-enqueues, the queue will look like
S <- A <- B <- A'
In actual execution, this is absolutely fine because A and A' are distinct node objects. And since A.succ_must_wait will have been set to false when A first released the lock, B will eventually make progress, and therefore A' will eventually make progress.
What happens in the array-based promela model below, though, is that A and A' occupy the same array positions, causing B to miss the fact that A has released the lock, thereby creating a deadlock where B is (wrongly) waiting for A' instead of A and A' is waiting (correctly) for B.
A possible "solution" to this could be to have A wait until B acknowledges the release. But that would not be true to how the lock works.
Another "solution" would be to wait for a CHANGE in pred.succ_must_wait, where a release would increment succ_must_wait, rather than reset it to 0.
But I'm intending to model a version of the lock, where pred may change (i.e. where a node may be allowed to disregard some of its predecessors), and I'm not entirely convinced something like the increasing version wouldn't cause an issue with this change.
So what's the "smartest" way to model an implicit queue like this in promela?
/* CLH-RW Lock */
/*pid: 0 = init, 1-2 = reader, 3-4 = writer*/
ltl liveness{
([]<> reader[1]#progress_reader)
&& ([]<> reader[2]#progress_reader)
&& ([]<> writer[3]#progress_writer)
&& ([]<> writer[4]#progress_writer)
}
bool initialised = 0;
byte ncritR;
byte ncritW;
byte tail;
bool succ_must_wait[5]
byte pred[5]
init{
assert(_pid == 0);
ncritR = 0;
ncritW = 0;
/*sentinel node*/
tail =0;
pred[0] = 0;
succ_must_wait[0] = 0;
initialised = 1;
}
active [2] proctype reader()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
succ_must_wait[_pid] = 1;
atomic {
pred[_pid] = tail;
tail = _pid;
}
(succ_must_wait[pred[_pid]] == 0)
ncritR++;
succ_must_wait[_pid] = 0;
atomic {
/*freeing previous node for garbage collection*/
pred[_pid] = 0;
}
/*CRITICAL SECTION*/
progress_reader:
assert(ncritR >= 1);
assert(ncritW == 0);
ncritR--;
atomic {
/*necessary to model the fact that the next access creates a new queue node*/
if
:: tail == _pid -> tail = 0;
:: else ->
fi
}
od
}
active [2] proctype writer()
{
assert(_pid >= 1);
(initialised == 1)
do
:: else ->
succ_must_wait[_pid] = 1;
atomic {
pred[_pid] = tail;
tail = _pid;
}
(succ_must_wait[pred[_pid]] == 0)
(ncritR == 0)
atomic {
/*freeing previous node for garbage collection*/
pred[_pid] = 0;
}
ncritW++;
/* CRITICAL SECTION */
progress_writer:
assert(ncritR == 0);
assert(ncritW == 1);
ncritW--;
succ_must_wait[_pid] = 0;
atomic {
/*necessary to model the fact that the next access creates a new queue node*/
if
:: tail == _pid -> tail = 0;
:: else ->
fi
}
od
}
First of all, a few notes:
You don't need to initialize your variables to 0, since:
The default initial value of all variables is zero.
see the docs.
You don't need to enclose a single instruction inside an atomic {} statement, since any elementary statement is executed atomically. For better efficiency of the verification process, whenever possible, you should use d_step {} instead. Here you can find a related stackoverflow Q/A on the topic.
init {} is guaranteed to have _pid == 0 when one of the two following conditions holds:
no active proctype is declared
init {} is declared before any other active proctype appearing in the source code
Active Processes, includig init {}, are spawned in order of appearance inside the source code. All other processes are spawned in order of appearance of the corresponding run ... statement.
I identified the following issues on your model:
the instruction pred[_pid] = 0 is useless because that memory location is only read after the assignment pred[_pid] = tail
When you release the successor of a node, you set succ_must_wait[_pid] to 0 only and you don't invalidate the node instance onto which your successor is waiting for. This is the problem that you identified in your question, but was unable to solve. The solution I propose is to add the following code:
pid j;
for (j: 1..4) {
if
:: pred[j] == _pid -> pred[j] = 0;
:: else -> skip;
fi
}
This should be enclosed in an atomic {} block.
You correctly set tail back to 0 when you find that the node that has just left the critical section is also the last node in the queue. You also correctly enclose this operation in an atomic {} block. However, it may happen that --when you are about to enter this atomic {} block-- some other process --who was still waiting in some idle state-- decides to execute the initial atomic block and copies the current value of tail --which corresponds to the node that has just expired-- into his own pred[_pid] memory location. If now the node that has just exited the critical section attempts to join it once again, setting his own value of succ_must_wait[_pid] to 1, you will get another instance of circular wait among processes. The correct approach is to merge this part with the code releasing the successor.
The following inline function can be used to release the successor of a given node:
inline release_succ(i)
{
d_step {
pid j;
for (j: 1..4) {
if
:: pred[j] == i ->
pred[j] = 0;
:: else ->
skip;
fi
}
succ_must_wait[i] = 0;
if
:: tail == _pid -> tail = 0;
:: else -> skip;
fi
}
}
The complete model, follows:
byte ncritR;
byte ncritW;
byte tail;
bool succ_must_wait[5];
byte pred[5];
init
{
skip
}
inline release_succ(i)
{
d_step {
pid j;
for (j: 1..4) {
if
:: pred[j] == i ->
pred[j] = 0;
:: else ->
skip;
fi
}
succ_must_wait[i] = 0;
if
:: tail == _pid -> tail = 0;
:: else -> skip;
fi
}
}
active [2] proctype reader()
{
loop:
succ_must_wait[_pid] = 1;
d_step {
pred[_pid] = tail;
tail = _pid;
}
trying:
(succ_must_wait[pred[_pid]] == 0)
ncritR++;
release_succ(_pid);
// critical section
progress_reader:
assert(ncritR > 0);
assert(ncritW == 0);
ncritR--;
goto loop;
}
active [2] proctype writer()
{
loop:
succ_must_wait[_pid] = 1;
d_step {
pred[_pid] = tail;
tail = _pid;
}
trying:
(succ_must_wait[pred[_pid]] == 0) && (ncritR == 0)
ncritW++;
// critical section
progress_writer:
assert(ncritR == 0);
assert(ncritW == 1);
ncritW--;
release_succ(_pid);
goto loop;
}
I added the following properties to the model:
p0: the writer with _pid equal to 4 goes through its progress state infinitely often, provided that it is given the chance to execute some instruction infinitely often:
ltl p0 {
([]<> (_last == 4)) ->
([]<> writer[4]#progress_writer)
};
This property should be true.
p1: there is never more than one reader in the critical section:
ltl p1 {
([] (ncritR <= 1))
};
Obviously, we expect this property to be false in a model that matches your specification.
p2: there is never more than one writer in the critical section:
ltl p2 {
([] (ncritW <= 1))
};
This property should be true.
p3: there isn't any node that is the predecessor of two other nodes at the same time, unless such node is node 0:
ltl p3 {
[] (
(((pred[1] != 0) && (pred[2] != 0)) -> (pred[1] != pred[2])) &&
(((pred[1] != 0) && (pred[3] != 0)) -> (pred[1] != pred[3])) &&
(((pred[1] != 0) && (pred[4] != 0)) -> (pred[1] != pred[4])) &&
(((pred[2] != 0) && (pred[3] != 0)) -> (pred[2] != pred[3])) &&
(((pred[2] != 0) && (pred[4] != 0)) -> (pred[2] != pred[4])) &&
(((pred[3] != 0) && (pred[4] != 0)) -> (pred[3] != pred[4]))
)
};
This property should be true.
p4: it is always true that whenever writer with _pid equal to 4 tries to access the critical section then it will eventually get there:
ltl p4 {
[] (writer[4]#trying -> <> writer[4]#progress_writer)
};
This property should be true.
The outcome of the verification matches our expectations:
~$ spin -search -ltl p0 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p0)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3305, errors: 0
...
~$ spin -search -ltl p1 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p1)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 1692, errors: 1
...
~$ spin -search -ltl p2 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p2)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
~$ spin -search -ltl p3 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p3)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
~$ spin -search -ltl p4 -a clhrw_lock.pml
...
Full statespace search for:
never claim + (p4)
assertion violations + (if within scope of claim)
acceptance cycles + (fairness disabled)
invalid end states - (disabled by never claim)
State-vector 68 byte, depth reached 3115, errors: 0
...
I am working on a promela model that is fairly simple. Using two different modules, it acts as a crosswalk/Traffic light. The first module is the traffic light that outputs the current signal (green, red, yellow, pending). This module also receives as an input a signal called "pedestrian" which acts as an indicator that there are pedestrians wanting to cross. The second module acts as the crosswalk. It receives output signals from the traffic light module (green, yellow, green). It outputs the pedestrian signal to the traffic light module. This module simply defines whether the pedestrian(s) is crossing, waiting or not present. My issue is that once the count value goes to 60, a timeout occurs. I believe the statement "SigG_out ! 1" is causing the error but I do not know why. I have attached the image of the trace I receive from the command line. I am completely new to Spin and Promela and so I am not sure how to use the information form the trace to find my issue in the code. Any help is greatly appreciated.
Here is the code for the complete model:
mtype = {red, green, yellow, pending, none, crossing, waiting};
mtype traffic_mode;
mtype crosswalk_mode;
int count;
chan pedestrian_chan = [0] of {byte};
chan sigR_chan = [0] of {byte};
chan sigG_chan = [0] of {byte};
chan sigY_chan = [0] of {byte};
ltl l1 {!<> (pedestrian_chan[0] == 1) && (traffic_mode == green || traffic_mode == yellow || traffic_mode == pending)}
ltl l2 {[]<> (pedestrian_chan[0] == 1) -> crosswalk_mode == crossing }
proctype traffic_controller(chan pedestrian_in, sigR_out, sigG_out, sigY_out)
{
do
::if
::(traffic_mode == red) ->
count = count + 1;
if
::(count >= 60) ->
sigG_out ! 1;
count = 0;
traffic_mode = green;
:: else -> skip;
fi
::(traffic_mode == green) ->
if
::(count < 60) ->
count = count + 1;
::(pedestrian_in == 1 & count < 60) ->
count = count + 1;
traffic_mode = pending;
::(pedestrian_in == 1 & count >= 60)
count = 0;
traffic_mode = yellow;
fi
::(traffic_mode == pending) ->
count = count + 1;
if
::(count >= 60) ->
sigY_out ! 1;
count = 0;
traffic_mode = yellow;
::else -> skip;
fi
::(traffic_mode == yellow) ->
count = count + 1;
if
::(count >= 5) ->
sigR_out ! 1;
count = 0;
traffic_mode = red;
:: else -> skip;
fi
fi
od
}
proctype crosswalk(chan sigR_in, sigG_in, sigY_in, pedestrian_out)
{
do
::if
::(crosswalk_mode == crossing) ->
if
::(sigG_in == 1) -> crosswalk_mode = none;
fi
::(crosswalk_mode == none) ->
if
:: (1 == 1) -> crosswalk_mode = none
:: (1 == 1) ->
pedestrian_out ! 1
crosswalk_mode = waiting
fi
::(crosswalk_mode == waiting) ->
if
::(sigR_in == 1) -> crosswalk_mode = crossing;
fi
fi
od
}
init
{
count = 0;
traffic_mode = red;
crosswalk_mode = crossing;
atomic
{
run traffic_controller(pedestrian_chan, sigR_chan, sigG_chan, sigY_chan);
run crosswalk(sigR_chan, sigG_chan, sigY_chan, pedestrian_chan);
}
}
You are using channels incorrectly, this line in particular I wouldn't even know how to interpret it:
:: (sigG_in == 1) ->
Your channels are synchronous, which means that whenever a process sends something on one side, another process must listen on the other end of the channel in order to deliver the message. Otherwise, the process blocks until when the situation changes. Your channels are synchronous because you declared them of size 0.
To read from a channel, you need to use the proper syntax:
int some_var;
...
some_channel?some_var;
// here some_var contains value received through some_channel
It seems to be a bit pointless to use three different channels to send different signals. What about using three different values?
mtype = { RED, GREEN, YELLOW };
chan c = [0] of { mtype };
...
c!RED
...
// (some other process)
...
mtype var;
c?var;
// here var contains RED
...
When broadcasting BinaryString through a TChan, what gets copied the whole Binary or just the reference ?
if the whole binary gets copied how to send only the reference ?
Only a reference is written to the TChan, the payload is not copied. It would be far too inefficient to copy all the data all the time, and since the data is immutable (in general, you can cheat), it is safe to only transfer references.
To be a bit more precise than Daniel (and confirm Daniels suspicion in his comment): A pointer to the constructor of the BinaryString (do you mean ByteString?) is written to the TVar.
Let us confirm by checking the relevant code. TChan is built upon TVar, and uses writeTVar to write the value, whch is implemented in GHC.Conc.Sync (and re-exported by GHC.Cont and Control.Concurrent.STM.TVar):
-- |Write the supplied value into a TVar
writeTVar :: TVar a -> a -> STM ()
writeTVar (TVar tvar#) val = STM $ \s1# ->
case writeTVar# tvar# val s1# of
s2# -> (# s2#, () #)
The argument is just passed along to the function writeTVar#, which is a primitive operation which is implemented in rts/PrimOps.cmm:
stg_writeTVarzh
{
W_ trec;
W_ tvar;
W_ new_value;
/* Args: R1 = TVar closure */
/* R2 = New value */
MAYBE_GC (R1_PTR & R2_PTR, stg_writeTVarzh); // Call to stmWriteTVar may allocate
trec = StgTSO_trec(CurrentTSO);
tvar = R1;
new_value = R2;
foreign "C" stmWriteTVar(MyCapability() "ptr", trec "ptr", tvar "ptr", new_value "ptr") [];
jump %ENTRY_CODE(Sp(0));
}
This wrap the following code in rts/STM.c:
void stmWriteTVar(Capability *cap,
StgTRecHeader *trec,
StgTVar *tvar,
StgClosure *new_value) {
StgTRecHeader *entry_in = NULL;
TRecEntry *entry = NULL;
TRACE("%p : stmWriteTVar(%p, %p)", trec, tvar, new_value);
ASSERT (trec != NO_TREC);
ASSERT (trec -> state == TREC_ACTIVE ||
trec -> state == TREC_CONDEMNED);
entry = get_entry_for(trec, tvar, &entry_in);
if (entry != NULL) {
if (entry_in == trec) {
// Entry found in our trec
entry -> new_value = new_value;
} else {
// Entry found in another trec
TRecEntry *new_entry = get_new_entry(cap, trec);
new_entry -> tvar = tvar;
new_entry -> expected_value = entry -> expected_value;
new_entry -> new_value = new_value;
}
} else {
// No entry found
StgClosure *current_value = read_current_value(trec, tvar);
TRecEntry *new_entry = get_new_entry(cap, trec);
new_entry -> tvar = tvar;
new_entry -> expected_value = current_value;
new_entry -> new_value = new_value;
}
TRACE("%p : stmWriteTVar done", trec);
}
And here we see that new_value is a pointer that is never looked at, and stored as such.
I am trying to use Spin Model Checker to modelcheck a Game between two objects (A and B). The objects move on a board, and each location is defined by its (x,y) coordinates. The two objects are supposed to not collide. I have three processes: init, A Model, B Model. I am model checking an ltl property: (liveness property to check if the two objects ever occupy same location)
ltl prop1 { [] (!(x_a == x_b) && !(y_a == y_b)) }
The error trail that I get is:
init -> A Model -> B Model -> init
However, I should not get an error trail (counterexample) based on the data that is shown: x_a=2, x_b=1, y_a=1, y_b=1.
Also the first init does go through all the lines of init process, but the second one only shows to the last line of it.
Also my A Model and B Model only consist of guards and actions in a 'do' block as shown below. However they are more complex and have if blocks on the right of '->'
active proctype AModel(){
do
:: someValue == 1 -> go North
:: someValue == 2 -> go South
:: someValue == 3 -> go East
:: someValue == 4 -> go West
:: else -> skip;
od
}
Do I need to put anything in an atomic block? The reason I am asking is that the line that the error trail is showing does not even go into the 'do' block, and it is just the first line of the two models.
EDIT:
The LTL property was wrong. I changed that to:
ltl prop1 { [] (!((x_a == x_b) && (y_a == y_b))) }
However, I am still getting the exact same error trail.
Your LTL property is wrongly implemented. Essentially, the counter example that SPIN found is a true counter example for the LTL as stated.
[] ( !(x_a == x_b) && !(y_z == y_b) ) =>
[] ( !(2 == 1) && !(1 == 1) ) =>
[] ( !0 && !1) =>
[] ( 1 && 0) =>
[] 0 =>
false
The LTL should be:
always not (same location) =>
[] (! ((x_a == x_b) && (y_a == y_b))) =>
[] (! ((2 == 1) && (1 == 1))) =>
[] (! (0 && 1) =>
[] (! 0) =>
[] 1 =>
true
Regarding your init and tasks. When starting your tasks you want to be sure that initialization is complete before the tasks run. I'll use one of two approaches:
init { ... atomic { run taskA(); run taskB() } where tasks are spawned once all initialization is complete`
or
bool init_complete = false;
init { ...; init_complete = true }
proctype taskA () { /* init local stuff */ ...; init-complete -> /* begin real works */ ... }
Your LTL may be failing during the initialization.
And based on your problem, if you ever change x or y you'd better change both at once in an atomic{}.