# alg_cvtest.py
#
# A port of the OS161 cvtest function

# We want all the symbols from sim161
from sim161 import *

# We need to increase resources for the wait code
import deadlock

NCVLOOPS = 5
NTHREADS = 12

# global state
testval1 = 0

# The lock variables
testlock = None
testcv   = None
donesem  = None


# Test code
def cvtestthread( num ):

    global testval1

    for i in range( 0, NCVLOOPS ):
        lock_acquire( testlock )
        while( testval1 != num ):
            cv_wait( testcv, testlock )

        print "Thread %d" % num
        testval1 = (testval1 + NTHREADS - 1) % NTHREADS

        cv_broadcast( testcv, testlock )

        lock_release( testlock )

    V(donesem)

##
## Main code 
##
## cvtest function in the code

def main( args ):

    # Modern versions of python require us to use the `global'
    # keyword to be able to write to global variables.
    global testval1, testlock, testcv, donesem

    testlock = lock_create("testlock")
    testcv = cv_create("testcv")

    # The `donesem' is special in this code
    # The deadlock detection algorithm needs to know that this is
    # a multiple resource.
    # We create it with a count of zero, then tell the deadlock
    # detection algorithm directly that there are more
    # This way when we have the correct count in the sim161 library for
    # our required semantics, and the deadlock algorithm knows what
    # is really happening
    #
    # This is a problem because the OS161 interface is not really designed
    # for deadlock detection internally, where a semaphore's initial count
    # does not necessarily represent the number of releases on it.
    donesem = sem_create("donesem", NTHREADS-1)
    deadlock.increase_resource( "donesem", NTHREADS )

    print "Starting CV test..."
    print "Threads should print out in reverse order."

    testval1 = NTHREADS-1;

    for i in range( 0, NTHREADS ):
        # Create the thread
        name = "synchtest %s" % i
        thread_create( name, cvtestthread, i )

        # Acquire the `donesem' on behalf of the new thread,
        # as it will release it later
        deadlock.acquire_resource( name, "donesem" )

    for i in range( 0, NTHREADS ):
        P( donesem )

    print "CV test code exiting"
    exit(1)

