Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cbe8813

Browse files
committed
Add locks to make the caches well behaved in multi-threaded code.
Store builtins in cell variables to speed-up the common path, reducing the chance of a lock needing to block at all.
1 parent d9e8cc6 commit cbe8813

1 file changed

Lines changed: 39 additions & 24 deletions

File tree

Lib/functools.py

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
from collections import OrderedDict, Counter
1616
from heapq import nsmallest
1717
from operator import itemgetter
18+
try:
19+
from _thread import allocate_lock as Lock
20+
except:
21+
from _dummy_thread import allocate_lock as Lock
1822

1923
# update_wrapper() and wraps() are tools to help write
2024
# wrapper functions that can handle naive introspection
@@ -115,37 +119,42 @@ def lfu_cache(maxsize=100):
115119
http://en.wikipedia.org/wiki/Cache_algorithms#Least-Frequently_Used
116120
117121
"""
118-
def decorating_function(user_function):
122+
def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len):
119123
cache = {} # mapping of args to results
120124
use_count = Counter() # times each key has been accessed
121125
kwd_mark = object() # separate positional and keyword args
126+
lock = Lock()
122127

123128
@wraps(user_function)
124129
def wrapper(*args, **kwds):
125130
key = args
126131
if kwds:
127132
key += (kwd_mark,) + tuple(sorted(kwds.items()))
128-
use_count[key] += 1 # count a use of this key
129133
try:
130-
result = cache[key]
131-
wrapper.hits += 1
134+
with lock:
135+
use_count[key] += 1 # count a use of this key
136+
result = cache[key]
137+
wrapper.hits += 1
132138
except KeyError:
133139
result = user_function(*args, **kwds)
134-
cache[key] = result
135-
wrapper.misses += 1
136-
if len(cache) > maxsize:
137-
# purge the 10% least frequently used entries
138-
for key, _ in nsmallest(maxsize // 10,
139-
use_count.items(),
140-
key=itemgetter(1)):
141-
del cache[key], use_count[key]
140+
with lock:
141+
use_count[key] += 1 # count a use of this key
142+
cache[key] = result
143+
wrapper.misses += 1
144+
if len(cache) > maxsize:
145+
# purge the 10% least frequently used entries
146+
for key, _ in nsmallest(maxsize // 10,
147+
use_count.items(),
148+
key=itemgetter(1)):
149+
del cache[key], use_count[key]
142150
return result
143151

144152
def clear():
145153
"""Clear the cache and cache statistics"""
146-
cache.clear()
147-
use_count.clear()
148-
wrapper.hits = wrapper.misses = 0
154+
with lock:
155+
cache.clear()
156+
use_count.clear()
157+
wrapper.hits = wrapper.misses = 0
149158

150159
wrapper.hits = wrapper.misses = 0
151160
wrapper.clear = clear
@@ -161,30 +170,36 @@ def lru_cache(maxsize=100):
161170
http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
162171
163172
"""
164-
def decorating_function(user_function):
173+
def decorating_function(user_function, tuple=tuple, sorted=sorted, len=len):
165174
cache = OrderedDict() # ordered least recent to most recent
166175
kwd_mark = object() # separate positional and keyword args
176+
lock = Lock()
167177

168178
@wraps(user_function)
169179
def wrapper(*args, **kwds):
170180
key = args
171181
if kwds:
172182
key += (kwd_mark,) + tuple(sorted(kwds.items()))
173183
try:
174-
result = cache.pop(key)
175-
wrapper.hits += 1
184+
with lock:
185+
result = cache[key]
186+
del cache[key]
187+
cache[key] = result # record recent use of this key
188+
wrapper.hits += 1
176189
except KeyError:
177190
result = user_function(*args, **kwds)
178-
wrapper.misses += 1
179-
if len(cache) >= maxsize:
180-
cache.popitem(0) # purge least recently used cache entry
181-
cache[key] = result # record recent use of this key
191+
with lock:
192+
cache[key] = result # record recent use of this key
193+
wrapper.misses += 1
194+
if len(cache) > maxsize:
195+
cache.popitem(0) # purge least recently used cache entry
182196
return result
183197

184198
def clear():
185199
"""Clear the cache and cache statistics"""
186-
cache.clear()
187-
wrapper.hits = wrapper.misses = 0
200+
with lock:
201+
cache.clear()
202+
wrapper.hits = wrapper.misses = 0
188203

189204
wrapper.hits = wrapper.misses = 0
190205
wrapper.clear = clear

0 commit comments

Comments
 (0)