| /* |
| * Copyright 2017, Data61 |
| * Commonwealth Scientific and Industrial Research Organisation (CSIRO) |
| * ABN 41 687 119 230. |
| * |
| * This software may be distributed and modified according to the terms of |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. |
| * See "LICENSE_BSD2.txt" for details. |
| * |
| * @TAG(DATA61_BSD) |
| */ |
| |
| #define MAX_RES_VAL 2 |
| #define NUM_THREADS 4 |
| #define WAIT_BOUND 2 |
| |
| /* A notification in seL4 is a single data word. |
| * Here we simplify things to a single bit */ |
| typedef notification_t { |
| bit data = 1; |
| }; |
| |
| /* Condition variable */ |
| typedef condition_var_t { |
| notification_t wait_nb; |
| int waiters = 0; |
| bit broadcasting = 0; |
| }; |
| |
| /* Monitor */ |
| typedef monitor_t { |
| notification_t lock; |
| condition_var_t consumer_cv; |
| condition_var_t producer_cv; |
| }; |
| |
| /* The shared resource. |
| * Producer increments when resource < MAX_RES_VAL |
| * Consumer decrements when resource > 0. |
| */ |
| byte resource = 0; |
| |
| /* Global monitor */ |
| monitor_t the_monitor; |
| |
| /* Some thread state used to check correctness */ |
| byte thread_state[4]; |
| /* 0 - Default |
| 1 - Has the monitor lock (i.e. is in the critical region) |
| 2 - Waiting on consumer_cv |
| 3 - Waiting on producer_cv |
| 4 - Sees a broadcast |
| 5 - thread terminated |
| */ |
| |
| /* We record the number of times a thread has waited in order to terminate |
| This is necessary to avoid infinite loops for which we can prove nothing */ |
| byte thread_waits[4]; |
| |
| /* seL4_Wait blocks on the notification. |
| * When the data word is 1 it resets to 0 and returns. */ |
| inline seL4_Wait(notif) |
| { |
| atomic { |
| (notif.data == 1) -> notif.data = 0; |
| } |
| } |
| |
| /* seL4_Signal updates the notification, it is non-blocking. |
| * It is a bitwise OR of the data word */ |
| inline seL4_Signal(notif) |
| { |
| notif.data = 1; |
| } |
| |
| /* Wait on a condition variable. The calling thread must own the monitor lock */ |
| inline cv_wait(monitor, cv, cv_id, procnum) |
| { |
| /* Increment the waiters count */ |
| cv.waiters++; |
| |
| /* Release the monitor lock */ |
| printf("%d signals monitor.lock and waits\n", procnum); |
| atomic { |
| seL4_Signal(monitor.lock); |
| printf("thread_state[%d] = %d\n", procnum-1, cv_id); |
| thread_state[procnum-1] = cv_id; |
| } |
| |
| /* Sleep until notified */ |
| printf("%d waits on cv.nb\n", procnum); |
| atomic { |
| seL4_Wait(cv.wait_nb); |
| thread_state[procnum-1] = 0; |
| printf("thread_state[%d] = %d\n", procnum-1, 0); |
| } |
| |
| int oldval; |
| atomic { |
| oldval = cv.waiters; |
| cv.waiters--; |
| } |
| |
| /* Signal other waiting threads if we're broadcasting */ |
| if |
| :: (oldval > 1 && cv.broadcasting == 1) -> seL4_Signal(cv.wait_nb); |
| :: (oldval <= 1 && cv.broadcasting == 1) -> cv.broadcasting = 0; |
| :: else -> skip; |
| fi |
| |
| /* Reaquire the lock */ |
| printf("%d waits on monitor.lock\n", procnum); |
| atomic { |
| monitor_acquire(monitor); |
| thread_state[procnum-1] = 1; |
| printf("%d acquires monitor lock\n", procnum); |
| } |
| |
| printf("%d wakes up\n", procnum); |
| } |
| |
| inline cv_signal(monitor, cv, procnum) |
| { |
| /* Decrement the waiters count */ |
| printf("%d signals cv\n", procnum); |
| if |
| :: (cv.waiters > 0) -> |
| /* Wake up a thread */ |
| printf("Signal wait_nb\n"); |
| seL4_Signal(cv.wait_nb); |
| :: else -> skip; |
| fi |
| |
| } |
| |
| inline cv_broadcast(monitor, cv, cv_id, procnum) |
| { |
| printf("%d broadcasts cv\n", procnum); |
| if |
| :: (cv.waiters > 0) -> |
| if |
| :: (thread_state[0] == cv_id && thread_waits[0] != WAIT_BOUND) -> thread_state[0] = 4; |
| :: skip; |
| fi |
| |
| if |
| :: (thread_state[1] == cv_id && thread_waits[1] != WAIT_BOUND) -> thread_state[1] = 4; |
| :: skip; |
| fi |
| |
| if |
| :: (thread_state[2] == cv_id && thread_waits[2] != WAIT_BOUND) -> thread_state[2] = 4; |
| :: skip; |
| fi |
| |
| if |
| :: (thread_state[3] == cv_id && thread_waits[3] != WAIT_BOUND) -> thread_state[3] = 4; |
| :: skip; |
| fi |
| |
| /* Wake up a thread */ |
| printf("Signal wait_nb (broadcast, waiters = %d)\n", cv.waiters); |
| cv.broadcasting = 1; |
| seL4_Signal(cv.wait_nb); |
| :: else -> skip; |
| fi |
| } |
| |
| inline monitor_acquire(monitor) |
| { |
| seL4_Wait(monitor.lock); |
| } |
| |
| inline monitor_release(monitor) |
| { |
| seL4_Signal(monitor.lock); |
| } |
| |
| proctype producer(byte procnum) |
| { |
| end: do |
| :: true; |
| progress_prod:; |
| atomic { |
| monitor_acquire(the_monitor); |
| thread_state[procnum-1] = 1; |
| } |
| printf("producer acquires m\n"); |
| |
| /* Producer waits for resource != MAX_RES_VAL */ |
| do |
| :: (resource == MAX_RES_VAL) -> |
| cv_wait(the_monitor, the_monitor.producer_cv, 3, procnum); |
| thread_waits[procnum-1]++; |
| if |
| :: (thread_waits[procnum-1] >= WAIT_BOUND) -> break; |
| :: else -> skip; |
| fi |
| :: else -> thread_waits[procnum-1]++; break; |
| od |
| |
| if |
| :: (thread_waits[procnum-1] >= WAIT_BOUND) -> monitor_release(the_monitor); break; |
| :: else -> skip; |
| fi |
| |
| /* Critical section */ |
| assert(resource != MAX_RES_VAL); |
| resource++; |
| printf("resource = %d\n", resource); |
| |
| /* cv_signal(the_monitor, the_monitor.producer_cv, procnum); |
| cv_signal(the_monitor, the_monitor.consumer_cv, procnum); |
| printf("producer signals cv\n"); */ |
| |
| if |
| :: (resource == MAX_RES_VAL) -> |
| printf("producer broadcasts\n"); |
| cv_broadcast(the_monitor, the_monitor.consumer_cv, 2, procnum); |
| :: else -> |
| skip; |
| /* printf("consumer signals\n"); |
| cv_signal(the_monitor, the_monitor.consumer_cv, procnum); */ |
| fi |
| |
| atomic { |
| monitor_release(the_monitor); |
| thread_state[procnum-1] = 0; |
| } |
| printf("producer releases m\n"); |
| od |
| printf("%d dies with thread_state = %d\n", procnum, thread_state[procnum-1]); |
| thread_state[procnum-1] = 5; |
| cv_signal(the_monitor, the_monitor.consumer_cv, procnum); |
| } |
| |
| proctype consumer(byte procnum) |
| { |
| end: do |
| :: true; |
| progress_cons:; |
| atomic { |
| monitor_acquire(the_monitor); |
| thread_state[procnum-1] = 1; |
| } |
| printf("consumer acquires m\n"); |
| |
| /* Consumer waits for resource != 0 */ |
| do |
| :: (resource == 0) -> |
| cv_wait(the_monitor, the_monitor.consumer_cv, 2, procnum); |
| thread_waits[procnum-1]++; |
| if |
| :: (thread_waits[procnum-1] >= WAIT_BOUND) -> break; |
| :: else -> skip; |
| fi |
| :: else -> thread_waits[procnum-1]++; break; |
| od |
| |
| if |
| :: (thread_waits[procnum-1] >= WAIT_BOUND) -> monitor_release(the_monitor); break; |
| :: else -> skip; |
| fi |
| |
| /* Critical section */ |
| assert(resource != 0); |
| resource--; |
| printf("resource = %d\n", resource); |
| |
| if |
| :: (resource == 0) -> |
| printf("consumer broadcasts\n"); |
| printf("the_monitor.lock.data = %d\n", the_monitor.lock.data); |
| cv_broadcast(the_monitor, the_monitor.producer_cv, 3, procnum); |
| :: else -> |
| skip; |
| /* printf("consumer signals\n"); |
| cv_signal(the_monitor, the_monitor.consumer_cv, procnum); */ |
| fi |
| |
| atomic { |
| monitor_release(the_monitor); |
| thread_state[procnum-1] = 0; |
| printf("consumer releases m\n"); |
| } |
| od |
| printf("%d dies with thread_state = %d\n", procnum, thread_state[procnum-1]); |
| thread_state[procnum-1] = 5; |
| cv_signal(the_monitor, the_monitor.producer_cv, procnum); |
| } |
| |
| init { |
| thread_state[0] = 0; |
| thread_state[1] = 0; |
| thread_state[2] = 0; |
| thread_state[3] = 0; |
| |
| thread_waits[0] = 0; |
| thread_waits[1] = 0; |
| thread_waits[2] = 0; |
| thread_waits[3] = 0; |
| |
| atomic { |
| run producer(1); |
| run producer(2); |
| run consumer(3); |
| run consumer(4); |
| } |
| } |
| |
| /* No more than one state may be in the critical region */ |
| /* ltl mutual_exclusion { []( |
| ((thread_state[0] == 1) -> !(thread_state[1] == 1 || thread_state[2] == 1 || thread_state[3] == 1)) |
| && ((thread_state[1] == 1) -> !(thread_state[0] == 1 || thread_state[2] == 1 || thread_state[3] == 1)) |
| && ((thread_state[2] == 1) -> !(thread_state[0] == 1 || thread_state[1] == 1 || thread_state[3] == 1)) |
| && ((thread_state[3] == 1) -> !(thread_state[0] == 1 || thread_state[1] == 1 || thread_state[2] == 1)))} */ |
| |
| /* If a thread is waiting during a broadcast, eventually it wakes up */ |
| ltl broadcast0 { []( (thread_state[0] == 4) -> (<> (thread_state[0] == 1)) ) } |
| ltl broadcast1 { []( (thread_state[1] == 4) -> (<> (thread_state[1] == 1)) ) } |
| ltl broadcast2 { []( (thread_state[2] == 4) -> (<> (thread_state[2] == 1)) ) } |
| ltl broadcast3 { []( (thread_state[3] == 4) -> (<> (thread_state[3] == 1)) ) } |