from myghty.util import SyncDict import random, time, weakref, sys # this script tests SyncDict for its thread safety, # ability to always return a value even for a dictionary # that loses data randomly, and # insures that when used as a registry, only one instance # of a particular key/value exists at any one time. try: import thread except: raise "this test requires a thread-enabled python" class item: def __init__(self, id): self.id = id def __str__(self): return "item id %d" % self.id # keep running indicator running = False # one item is referenced at a time (to insure singleton pattern) theitem = weakref.ref(item(0)) # creation func entrance detector to detect non-synchronized access # to the create function baton = None def create(id): global baton if baton is not None: raise "baton is not none !" baton = True try: global theitem if theitem() is not None: raise "create %d old item is still referenced" % id i = item(id) theitem = weakref.ref(i) global totalcreates totalcreates += 1 return i finally: baton = None def test(s, id, statusdict): print "create thread %d starting" % id statusdict[id] = True try: global running global totalgets try: while running: s.get('test', lambda: create(id)) totalgets += 1 time.sleep(random.random() * .00001) except: e = sys.exc_info()[0] running = False print e finally: print "create thread %d exiting" % id statusdict[id] = False def runtest(s): statusdict = {} global totalcreates totalcreates = 0 global totalremoves totalremoves = 0 global totalgets totalgets = 0 global running running = True for t in range(1, 10): thread.start_new_thread(test, (s, t, statusdict)) time.sleep(1) for x in range (0,10): if not running: break print "Removing item" totalremoves += 1 try: del s['test'] except KeyError: pass time.sleep(random.random() * .89) failed = not running running = False pause = True while pause: time.sleep(1) pause = False for v in statusdict.values(): if v: pause = True break if failed: raise "test failed" print "total object creates %d" % totalcreates print "total object gets %d" % totalgets print "total object removes %d" % totalremoves # normal dictionary test, where we will remove the value # periodically. the number of creates should be equal to # the number of removes plus one. print "\ntesting with normal dict" runtest(SyncDict(thread.allocate_lock(), {})) assert(totalremoves + 1 == totalcreates) # the goofydict is designed to act like a weakvaluedictionary, # where its values are dereferenced and disposed of # in between a has_key() and a # __getitem__() operation 50% of the time. # the number of creates should be about half of what the # number of gets is. class goofydict(dict): def has_key(self, key): if dict.has_key(self, key): if random.random() > 0.5: del self[key] return True else: return False print "\ntesting with goofy dict" runtest(SyncDict(thread.allocate_lock(), goofydict())) assert(float(totalcreates) / float(totalgets) < .52 and float(totalcreates) / float(totalgets) > .48) # the weakvaluedictionary test involves newly created items # that are instantly disposed since no strong reference exists to them. # the number of creates should be equal to the number of gets. print "\ntesting with weak dict" runtest(SyncDict(thread.allocate_lock(), weakref.WeakValueDictionary())) assert(totalcreates == totalgets)