#! /usr/bin/python

# Program with less locking of the key hash

__author__="J Dean Brock"

import threading
import sys

def doUpdate(sharedInfo, key, myNum):
    siLock = sharedInfo['lock']
    siVals = sharedInfo['value']
    siLock.acquire()          # start exclusive access to siVals
    if key in siVals:
        rendezvousPt = siVals[key]
        del siVals[key]       # both threads still have a reference
        siLock.release()      # end exclusive access to siVals
        keyLock = rendezvousPt['cond']
        keyLock.acquire()     # get ready to wake the 1st thread
        retVal  = rendezvousPt['value'] + myNum
        rendezvousPt['value'] = retVal
        keyLock.notify()      # queue up the 1st thread
        keyLock.release()     # start up the 1st thread
    else:
        keyCond = threading.Condition()
        rendezvousPt = {'value': myNum, 'cond': keyCond }
        siVals[key] = rendezvousPt
        keyCond.acquire()     # make sure the notify can't be lost
        siLock.release()      # end exclusive access to siVals
        keyCond.wait()        # ZZZZZZZZ waiting 2nd thread
        keyCond.release()     
        retVal = rendezvousPt['value']
    return retVal

class counter(threading.Thread):
    def __init__(self, countUp, sharedInfo, myNum):
        threading.Thread.__init__(self)
        self.countUp = countUp
        self.sharedInfo = sharedInfo
        self.myNum = myNum
    def run(self):
        myName = 't' + str(self.myNum).zfill(2)
        myCountDown = self.countUp-1
        while (myCountDown >= 0):
            key = 'k' + str(myCountDown%100).zfill(2)
            retV=doUpdate(self.sharedInfo, key, myName)
            response = ('Return to ' + myName + ' for ' + str(key)
                        + ' is ' + str(retV) + '\n')
            print response,
            myCountDown = myCountDown - 1

def RunEm(numThreads=10):
    if numThreads>10:
        numThreads=10
    numIncs = 100/numThreads
    print "Run with ", numThreads, " threads doing ", numIncs, " paired adds"
    shLock = threading.Lock()
    shVals = {}
    shInfo = {'value': shVals, 'lock': shLock}
    tList = []
    for x in range(numThreads):
        newThread = counter(numIncs, shInfo, x)
        newThread.start()
        tList.append(newThread)
    for t in tList:
        t.join()

if __name__ == "__main__":
    arg = 100
    if len(sys.argv) > 1:
        arg=int(sys.argv[1])
    RunEm(arg)
