141 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Promela
		
	
	
	
	
	
		
		
			
		
	
	
			141 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Promela
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * This model describes a bug in aio_notify.  If ctx->notifier is
							 | 
						||
| 
								 | 
							
								 * cleared too late, a wakeup could be lost.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Author: Paolo Bonzini <pbonzini@redhat.com>
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This file is in the public domain.  If you really want a license,
							 | 
						||
| 
								 | 
							
								 * the WTFPL will do.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * To verify the buggy version:
							 | 
						||
| 
								 | 
							
								 *     spin -a -DBUG docs/aio_notify_bug.promela
							 | 
						||
| 
								 | 
							
								 *     gcc -O2 pan.c
							 | 
						||
| 
								 | 
							
								 *     ./a.out -a -f
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * To verify the fixed version:
							 | 
						||
| 
								 | 
							
								 *     spin -a docs/aio_notify_bug.promela
							 | 
						||
| 
								 | 
							
								 *     gcc -O2 pan.c
							 | 
						||
| 
								 | 
							
								 *     ./a.out -a -f
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Add -DCHECK_REQ to test an alternative invariant and the
							 | 
						||
| 
								 | 
							
								 * "notify_me" optimization.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int notify_me;
							 | 
						||
| 
								 | 
							
								bool event;
							 | 
						||
| 
								 | 
							
								bool req;
							 | 
						||
| 
								 | 
							
								bool notifier_done;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef CHECK_REQ
							 | 
						||
| 
								 | 
							
								#define USE_NOTIFY_ME 1
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define USE_NOTIFY_ME 0
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								active proctype notifier()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								        :: true -> {
							 | 
						||
| 
								 | 
							
								            req = 1;
							 | 
						||
| 
								 | 
							
								            if
							 | 
						||
| 
								 | 
							
								               :: !USE_NOTIFY_ME || notify_me -> event = 1;
							 | 
						||
| 
								 | 
							
								               :: else -> skip;
							 | 
						||
| 
								 | 
							
								            fi
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        :: true -> break;
							 | 
						||
| 
								 | 
							
								    od;
							 | 
						||
| 
								 | 
							
								    notifier_done = 1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef BUG
							 | 
						||
| 
								 | 
							
								#define AIO_POLL                                                    \
							 | 
						||
| 
								 | 
							
								    notify_me++;                                                    \
							 | 
						||
| 
								 | 
							
								    if                                                              \
							 | 
						||
| 
								 | 
							
								        :: !req -> {                                                \
							 | 
						||
| 
								 | 
							
								            if                                                      \
							 | 
						||
| 
								 | 
							
								                :: event -> skip;                                   \
							 | 
						||
| 
								 | 
							
								            fi;                                                     \
							 | 
						||
| 
								 | 
							
								        }                                                           \
							 | 
						||
| 
								 | 
							
								        :: else -> skip;                                            \
							 | 
						||
| 
								 | 
							
								    fi;                                                             \
							 | 
						||
| 
								 | 
							
								    notify_me--;                                                    \
							 | 
						||
| 
								 | 
							
								                                                                    \
							 | 
						||
| 
								 | 
							
								    req = 0;                                                        \
							 | 
						||
| 
								 | 
							
								    event = 0;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								#define AIO_POLL                                                    \
							 | 
						||
| 
								 | 
							
								    notify_me++;                                                    \
							 | 
						||
| 
								 | 
							
								    if                                                              \
							 | 
						||
| 
								 | 
							
								        :: !req -> {                                                \
							 | 
						||
| 
								 | 
							
								            if                                                      \
							 | 
						||
| 
								 | 
							
								                :: event -> skip;                                   \
							 | 
						||
| 
								 | 
							
								            fi;                                                     \
							 | 
						||
| 
								 | 
							
								        }                                                           \
							 | 
						||
| 
								 | 
							
								        :: else -> skip;                                            \
							 | 
						||
| 
								 | 
							
								    fi;                                                             \
							 | 
						||
| 
								 | 
							
								    notify_me--;                                                    \
							 | 
						||
| 
								 | 
							
								                                                                    \
							 | 
						||
| 
								 | 
							
								    event = 0;                                                      \
							 | 
						||
| 
								 | 
							
								    req = 0;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								active proctype waiter()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								       :: true -> AIO_POLL;
							 | 
						||
| 
								 | 
							
								    od;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Same as waiter(), but disappears after a while.  */
							 | 
						||
| 
								 | 
							
								active proctype temporary_waiter()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								       :: true -> AIO_POLL;
							 | 
						||
| 
								 | 
							
								       :: true -> break;
							 | 
						||
| 
								 | 
							
								    od;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef CHECK_REQ
							 | 
						||
| 
								 | 
							
								never {
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								        :: req -> goto accept_if_req_not_eventually_false;
							 | 
						||
| 
								 | 
							
								        :: true -> skip;
							 | 
						||
| 
								 | 
							
								    od;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								accept_if_req_not_eventually_false:
							 | 
						||
| 
								 | 
							
								    if
							 | 
						||
| 
								 | 
							
								        :: req -> goto accept_if_req_not_eventually_false;
							 | 
						||
| 
								 | 
							
								    fi;
							 | 
						||
| 
								 | 
							
								    assert(0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								/* There must be infinitely many transitions of event as long
							 | 
						||
| 
								 | 
							
								 * as the notifier does not exit.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * If event stayed always true, the waiters would be busy looping.
							 | 
						||
| 
								 | 
							
								 * If event stayed always false, the waiters would be sleeping
							 | 
						||
| 
								 | 
							
								 * forever.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								never {
							 | 
						||
| 
								 | 
							
								    do
							 | 
						||
| 
								 | 
							
								        :: !event    -> goto accept_if_event_not_eventually_true;
							 | 
						||
| 
								 | 
							
								        :: event     -> goto accept_if_event_not_eventually_false;
							 | 
						||
| 
								 | 
							
								        :: true      -> skip;
							 | 
						||
| 
								 | 
							
								    od;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								accept_if_event_not_eventually_true:
							 | 
						||
| 
								 | 
							
								    if
							 | 
						||
| 
								 | 
							
								        :: !event && notifier_done  -> do :: true -> skip; od;
							 | 
						||
| 
								 | 
							
								        :: !event && !notifier_done -> goto accept_if_event_not_eventually_true;
							 | 
						||
| 
								 | 
							
								    fi;
							 | 
						||
| 
								 | 
							
								    assert(0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								accept_if_event_not_eventually_false:
							 | 
						||
| 
								 | 
							
								    if
							 | 
						||
| 
								 | 
							
								        :: event     -> goto accept_if_event_not_eventually_false;
							 | 
						||
| 
								 | 
							
								    fi;
							 | 
						||
| 
								 | 
							
								    assert(0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 |