@@ -386,13 +386,32 @@ def get_counter_field_names(argcount):
386386
387387# Classes to handle cached methods
388388class _CacheStats :
389- __slots__ = ("hits" , "misses" , "skips" )
389+ __slots__ = (
390+ "hits" , "misses" , "skips" ,
391+ "_hitlock" , "_misslock" , "_skiplock" ,
392+ )
390393
391394 def __init__ (self ):
392395 self .hits = 0
393396 self .misses = 0
394397 self .skips = 0
395398
399+ self ._hitlock = _thread .allocate_lock ()
400+ self ._misslock = _thread .allocate_lock ()
401+ self ._skiplock = _thread .allocate_lock ()
402+
403+ def add_hit (self ):
404+ with self ._hitlock :
405+ self .hits += 1
406+
407+ def add_miss (self ):
408+ with self ._misslock :
409+ self .misses += 1
410+
411+ def add_skip (self ):
412+ with self ._skiplock :
413+ self .skips += 1
414+
396415 @property
397416 def hit_percent (self ):
398417 # If there are no cache hits, return 100%
@@ -437,17 +456,17 @@ def clear(self, new_cache=None):
437456 def __call__ (self , * args , ** kwargs ):
438457 try :
439458 result = self ._internal_cache [args ]
440- self ._stats .hits += 1
459+ self ._stats .add_hit ()
441460 except KeyError :
442461 lock = self ._lock_cache .setdefault (args , _thread .allocate_lock ())
443462 with lock :
444463 try :
445464 result = self ._internal_cache [args ]
446- self ._stats .hits += 1
465+ self ._stats .add_hit ()
447466 except KeyError :
448467 result = self ._func (* args , ** kwargs )
449468 self ._internal_cache [args ] = result
450- self ._stats .misses += 1
469+ self ._stats .add_miss ()
451470
452471 return result
453472
@@ -482,7 +501,7 @@ def method_generator(cls, funcname):
482501 if args is None :
483502 # If the argument getter returns None
484503 # the method is not cacheable
485- source_exec .stats .skips += 1 # Add one to skip count
504+ source_exec .stats .add_skip () # Add one to skip count
486505 return None
487506
488507 # The first argument should always be a tuple of fields
0 commit comments