A forum for Spin users
You are not logged in.
Pages: 1
The following C code (based on a OS book) implements a concurrent queue. http://pages.cs.wisc.edu/~remzi/OSTEP/threads-locks-usage.pdf (Figure 29.8) has the basic code used here.
In the test driver, I just have one reader and one writer.
I'm not sure why the assert(tmp != NULL) in the Queue_Init false. It happens only if I include the sample_reader as part of the extraction in the prx file.
I'm using the Queue_Init only from Queue_Enqueue, which sets the ready flag to true, making it possible for the Queue_Dequeue to run.
Thanks a lot for your help in advance.
#include <pthread.h>
#include <assert.h>
struct __node_t {
int value;
struct __node_t *next;
};
typedef struct __node_t node_t;
struct __queue_t {
node_t *head;
node_t *tail;
pthread_mutex_t headLock;
pthread_mutex_t tailLock;
};
typedef struct __queue_t queue_t;
void Queue_Init(queue_t *q) {
node_t *tmp;
tmp = (node_t*)cq_malloc(sizeof(node_t));
assert(tmp != NULL);
tmp->next = NULL;
q->head = q->tail = tmp;
pthread_mutex_init(&q->headLock, NULL);
pthread_mutex_init(&q->tailLock, NULL);
}
void Queue_Enqueue(queue_t *q, int value) {
node_t *tmp;
tmp = (node_t*)cq_malloc(sizeof(node_t));
assert(tmp != NULL);
tmp->value = value;
tmp->next = NULL;
pthread_mutex_lock(&q->tailLock);
q->tail->next = tmp;
q->tail = tmp;
pthread_mutex_unlock(&q->tailLock);
}
int Queue_Dequeue(queue_t *q, int *value) {
node_t *tmp;
node_t *newHead;
pthread_mutex_lock(&q->headLock);
tmp = q->head;
newHead = tmp->next;
if (newHead == NULL) {
pthread_mutex_unlock(&q->headLock);
return -1; // queue was empty
}
*value = newHead->value;
q->head = newHead;
pthread_mutex_unlock(&q->headLock);
cq_free(tmp);
return 0;
}
/* Test driver */
queue_t queue;
int Ready = 0;
void
sample_writer(void)
{ int i;
Queue_Init(&queue);
Ready = 1;
for(i = 0; i < 5; i++)
{
Queue_Enqueue(&queue, i);
}
}
void
sample_reader(void)
{
int j = 0;
int i = 0;
int status = -1;
while(!Ready)
{
;
}
for(i = 0 ; i < 5; i++) {
status = Queue_Dequeue(&queue, &j);
if(status == -1)
{
i = i - 1;
}
else {
assert(i == j);
}
}
}
----
%X -a sample_writer
%X -a sample_reader
%X -e Queue_Init
%X -e Queue_Enqueue
%X -e Queue_Dequeue
%G
shortest: 1
%%
%C
#define MAX_HEAP 200
int cnt = 0;
char heap[MAX_HEAP];
char *
cq_malloc(unsigned int n)
{ char *p;
p = (void *) 0;
if (cnt + n <= sizeof(heap))
{ p = &heap[cnt];
cnt += n;
}
return p;
}
void
cq_free(void *p)
{
p = NULL;
}
------------------
Never mind. I managed to fix it.
Hi,
I'm trying the following C program but Modex is complaining.
Extract Model:
==============
modex prod_cons_two_signals.c
MODEX Version 2.7 - 1 January 2015
created: model and _modex_.run
Compile and Run:
================
sh _modex_.run
spin: model:66, Error: undeclared variable: get saw ''(' = 40'
---
The following C program uses get() and put(int). They both are externally defined in another file. I put %O put_get.o in my prx file. That's the only thing in my prx file.
/* 1 producer; 2 consumers; two signals (fill, empty) */
#include <pthread.h>
#include <assert.h>
pthread_mutex_t mutex;
pthread_cond_t empty;
pthread_cond_t fill;
extern int count;
extern int get();
extern void put(int);
void *producer(void *arg) {
int i;
while(1) {
pthread_mutex_lock(&mutex);
while (count == 1)
pthread_cond_wait(&empty, &mutex);
put(i);
pthread_cond_signal(&fill);
pthread_mutex_unlock(&mutex);
}
}
void *consumer(void *arg) {
int i;
while(1) {
pthread_mutex_lock(&mutex);
while(count == 0)
pthread_cond_wait(&fill, &mutex);
int tmp = get();
pthread_cond_signal(&empty);
pthread_mutex_unlock(&mutex);
}
}
void main()
{
pthread_t t1, t2, t3;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&fill, NULL);
pthread_cond_init(&empty, NULL);
pthread_create(&t1, NULL, producer, NULL);
pthread_create(&t2, NULL, consumer, NULL);
pthread_create(&t3, NULL, consumer, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_join(t3, NULL);
}
Hi,
Thanks. No, the shared variable can also be -1.
The problem I think is possibly in the interpretation of shared-- (or shared++) as one atomic entity. I may be wrong, of course.
I disassembled it for our discussion on a 64 bit VM.
shared++ is from instruction starting at 8 until 16.
0000000000000000 <thread1>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 89 7d f8 mov %rdi,-0x8(%rbp)
8: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # e <thread1+0xe>
e: 83 c0 01 add $0x1,%eax
11: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 17 <thread1+0x17>
17: b8 00 00 00 00 mov $0x0,%eax
1c: 5d pop %rbp
1d: c3 retq
shared-- is from instruction starting at 26 until 34.
000000000000001e <thread2>:
1e: 55 push %rbp
1f: 48 89 e5 mov %rsp,%rbp
22: 48 89 7d f8 mov %rdi,-0x8(%rbp)
26: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 2c <thread2+0xe>
2c: 83 e8 01 sub $0x1,%eax
2f: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 35 <thread2+0x17>
35: b8 00 00 00 00 mov $0x0,%eax
3a: 5d pop %rbp
3b: c3 retq
What if the second thread 2 ran first and read the shared variable into its register, say eax. Now the eax register has just 0.
Assume context switch happens to thread 1, which also read shared variable's value 0 into its copy of register eax. Assume thread 1 finished the job, now shared is 1.
Assume now thread 2 runs, it will decrement its own copy of the eax register, from 0 to -1. Now -1 will be copied back to shared.
Just out of curiosity, I ran the divine model checker and it agrees that there is a possibility of shared to be -1. I did not make any effort in understanding the long trace they generated.
You may also try: http://divine.fi.muni.cz/try.cgi
Hi,
I'm playing with Modex to better understand it.
For the following C program, I have two threads: one for increment and another for decrement of a global variable.
I think the global variable "shared" can take the value -1 as well. That is, the assert below is incorrect but Modex is not reporting it.
I wonder whether Modex treats each C statement as an atomic statement. The problem is that, as you know, shared=shared+1 or shared++ is made of multiple instructions at the assembly level. Thus, race condition is possible here.
Could you please share your thoughts?
#include <pthread.h>
#include <assert.h>
// verify race.c
int shared = 0;
pthread_mutex_t mutex;
void *
thread1(void *arg)
{
shared++;
return 0;
}
void *
thread2(void *arg)
{
shared--;
return 0;
}
int
main(void)
{
pthread_t t[2];
pthread_mutex_init(&mutex, 0);
pthread_create(&t[0], 0, thread1, 0);
pthread_create(&t[1], 0, thread2, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
assert(shared == 1 || shared == 0); /* Modex agrees to this - but shared can be -1, too */
return 0;
}
Hi,
I tried the following C program without any .prx file. The tool showed the following output:
unreached in proctype p_thread1
model:49, state 23, "-end-"
(1 of 23 states)
unreached in proctype p_thread2
model:71, state 23, "-end-"
(1 of 23 states)
unreached in proctype p_main
(0 of 23 states)
--
Could you please explain the reason for unreached in these processes?
There are two threads: One for increment and another for decrement of a global variable.
#include <pthread.h>
#include <assert.h>
// verify 3_threads_new.c
int shared = 0;
pthread_mutex_t mutex;
void *
thread1(void *arg)
{ int tmp;
pthread_mutex_lock( &mutex );
tmp = shared;
tmp++;
shared = tmp;
pthread_mutex_unlock( &mutex );
return 0;
}
void *
thread2(void *arg)
{ int tmp;
pthread_mutex_lock(&mutex);
tmp = shared;
tmp--;
shared = tmp;
pthread_mutex_unlock(&mutex);
return 0;
}
int
main(void)
{
pthread_t t[2];
pthread_mutex_init(&mutex, 0);
pthread_create(&t[0], 0, thread1, 0);
pthread_create(&t[1], 0, thread2, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
assert(shared == 1 || shared == 0);
return 0;
}
Hi,
I'm using the latest version of modex - downloaded it two weeks ago.
When I ran verify 4_mutex.c, which is part of the modex/example, it says no error was found.
However, when I ran ./pan it does show the violation of an assertion.
dharma@ubuntu:~/Downloads/modex/Examples$ verify 4_mutex.c
Extract Model:
==============
modex 4_mutex.c
MODEX Version 2.7 - 1 January 2015
created: model and _modex_.run
Compile and Run:
================
sh _modex_.run
hint: this search is more efficient if pan.c is compiled -DSAFETY
pan: out of memory
662568 bytes used
4.29497e+09 bytes more needed
1.57286e+10 bytes limit
hint: to reduce memory, recompile with
-DBITSTATE # supertrace, approximation
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Partial Order Reduction
Hash-Compact 4 search for:
never claim - (none specified)
assertion violations +
acceptance cycles - (not selected)
invalid end states +
State-vector 179 byte, depth reached 0, errors: 0
0 states, stored
0 states, matched
0 transitions (= stored+matched)
0 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
0.000 equivalent memory usage for states (stored*(State-vector + overhead))
0.000 actual memory usage for states (less than 1k)
4096.000 memory used for hash table (-w29)
0.534 memory used for DFS stack (-m10000)
0.632 total actual memory usage
pan: elapsed time 1.73e+07 seconds
pan: rate 0 states/second
No Errors Found
----
dharma@ubuntu:~/Downloads/modex/Examples$ ./pan
hint: this search is more efficient if pan.c is compiled -DSAFETY
pan:1: c_code line 103 precondition false: (now.cnt==1) (at depth 59)
pan: wrote model.trail
(Spin Version 6.4.3 -- 16 December 2014)
Warning: Search not completed
+ Partial Order Reduction
Hash-Compact 4 search for:
never claim - (none specified)
assertion violations +
acceptance cycles - (not selected)
invalid end states +
State-vector 260 byte, depth reached 100, errors: 1
3643 states, stored
2442 states, matched
6085 transitions (= stored+matched)
1012 atomic steps
hash conflicts: 0 (resolved)
Stats on memory usage (in Megabytes):
1.001 equivalent memory usage for states (stored*(State-vector + overhead))
0.373 actual memory usage for states (compression: 37.27%)
state-vector as stored = 79 byte + 28 byte overhead
128.000 memory used for hash table (-w24)
0.534 memory used for DFS stack (-m10000)
128.827 total actual memory usage
pan: elapsed time 0.07 seconds
pan: rate 52042.857 states/second
Hi,
I modeled the Petri net given in [url]http://en.wikipedia.org/wiki/File:Two-boundedness-cb.png[/url] using Promela as follows.
#define Place byte /* assume < 256 tokens per place */
Place p1, p2
Place cp1, cp2
#define inp1(x) (x>0) -> x=x-1
#define inp2(x,y) (x>0&&y>0) -> x = x-1; y=y-1
#define out1(x) x=x+1
#define out2(x,y) x=x+1; y=y+1
init
{ cp1 = 2; cp2 = 2; /* initial marking */
do
/*t1*/ :: atomic { inp1(cp1) -> out1(p1) }
/*t2*/ :: atomic { inp2(p1, cp2) -> out2(p2, cp1) }
/*t3*/ :: atomic { inp1(p2) -> out1(cp2) }
od
}
I was curious to check the 2-bounded property of this Petri net using spin. Got this output form spin:
unreached in init
cp_net3.pml:15, state 20, "-end-"
(1 of 20 states)
Does it mean that this model will not terminate? Of course, the petri net is always running without any deadlock as far as I can see manually.
Of course, this model is based on SPIN exercises/examples.
Great. Just modified the modex.c to use spin instead of spin4, and also added -O option directly in the modex.c file.
It works! Cool stuff. Thanks.
Tried the threads.c example given in the manual of Modex. Got this syntax error:
pan.m: In function 'do_transit':
pan.m:29: error: 'P1' has no member typed 'tmp'
pan.m:41: error: 'P1' has no member typed 'tmp'
pan.m:53: error: 'P1' has no member typed 'tmp'
pan.m:102: error: 'P0' has no member typed 'tmp'
pan.m:114: error: 'P0' has no member typed 'tmp'
pan.m:126: error: 'P0' has no member typed 'tmp'
No Errors Found
Any comment?
Ha ha, it was a typo in the ACM Inroads: " A Primer on Model Checking" by Mordechai (Moti) Ben-Ari, see page 44
Yes, counter == 10 not counter = 10.
Thanks!
Thanks. Tried it. It is mind-boggling to see the assertion violation. It is still unclear to me how come n is 0 when finish is 2. Any comment?
The output I got
ubuntu@ubuntu10:~/Downloads/Spin/iSpin/ltl_examples$ spin -p -g -l -v -t primer.pml
spin: warning, primer.pml, proctype P, 'byte counter' variable is never used
i=3 pno 1
1: proc 1 (P) primer.pml:5 (state 1) [counter = 10]
P(1):counter = 10
i=3 pno 0
2: proc 0 (P) primer.pml:5 (state 1) [counter = 10]
P(0):counter = 10
i=3 pno 1
3: proc 1 (P) primer.pml:12 (state 11) [finish = (finish+1)]
finish = 1
i=3 pno 0
4: proc 0 (P) primer.pml:12 (state 11) [finish = (finish+1)]
finish = 2
i=3 pno 2
5: proc 2 (WaitForFinish) primer.pml:16 (state 1) [((finish==2))]
i=3 pno 2
spin: primer.pml:17, Error: assertion violated
spin: text of failed assertion: assert((n>0))
6: proc 2 (WaitForFinish) primer.pml:17 (state 2) [assert((n>0))]
spin: trail ends after 6 steps
#processes: 3
n = 0
finish = 2
6: proc 2 (WaitForFinish) primer.pml:18 (state 3) <valid end state>
6: proc 1 (P) primer.pml:13 (state 12) <valid end state>
6: proc 0 (P) primer.pml:13 (state 12) <valid end state>
3 processes created
Hi,
Tried spin on the model given below. It reported a violation of the assertion. Is there a way to see the actual interleaving that led to this violation?
byte n = 0, finish = 0;
active [2] proctype P() {
byte register, counter = 0;
do :: counter = 10 -> break
:: else ->
register = n;
register++;
n = register;
counter++
od;
finish++
}
active proctype WaitForFinish() {
finish == 2;
assert (n > 0);
}
Tried spin -s -t primer.pml and got this output. It appears n = 0. But not clear to me what interleaving has led to this failure. Any comments? Thank a lot.
-----
spin: primer.pml:17, Error: assertion violated
spin: text of failed assertion: assert((n>0))
spin: trail ends after 6 steps
#processes: 3
n = 0
finish = 2
6: proc 2 (WaitForFinish) primer.pml:18 (state 3) <valid end state>
6: proc 1 (P) primer.pml:13 (state 12) <valid end state>
6: proc 0 (P) primer.pml:13 (state 12) <valid end state>
3 processes created
---
Pages: 1