Any MoinMoin installation which is not based on plain CGI uses threads. We have several shared data structures that should be protected.
- acl cache in Page module
- plugin cache in wikiutil module - (proposal)
See plugin.patch
Use this decorator:
Example of the problem code:
1 import threading
2 _plugins_lock = threading.Lock()
3
4 #[...]
5 global _plugins
6 global _plugins_lock
7
8 # Try to import from the wiki. Wiki plugins are located under
9 # 'wikiconfigname.plugin' module. Try cache first (fast!) or
10 # import from disk (slow)
11 modulename = '%s.plugin.%s.%s' % (request.cfg.siteid, kind, name)
12 key = modulename + '/' + function
13
14 _plugins_lock.acquire()
15 try:
16 try:
17 plugin = _plugins[key]
18 except KeyError:
19 plugin = pysupport.importName(modulename, function)
20 _plugins[key] = plugin
21 finally:
22 _plugins_lock.release()
- Why is there locking required?
- plugin = _plugins[key] is thread save
- _plugins[key] = plugin is thrad save and a lost update in between doesn't hurt
Here is a draft for these - a dict like object that use locking for setitem and delitem methods.
1 import threading
2
3 class ThreadSafeMixin:
4 """ Thread safe mixin, can be used by multiple threads
5
6 The mixin provide thread safe __getitem__, __delitem__ and
7 __setitem__ methods. The real work should be done by the class
8 getItem, delItem and setItem methods, wich will operate on the
9 instance data.
10 """
11
12 def __init__(self):
13 self._lock = threading.Lock()
14
15 def __setitem__(self, key, value):
16 self._lock.acquire()
17 try:
18 self.setItem(key, value)
19 finally:
20 self._lock.release()
21
22 def __delitem__(self, key):
23 self._lock.acquire()
24 try:
25 self.delItem(key)
26 finally:
27 self._lock.release()
28
29 def __getitem__(self, key):
30 self._lock.acquire()
31 try:
32 self.getItem(key)
33 finally:
34 self._lock.release()
This kind of thread savety doesn't help anything. You get lost updates if you get data and write it back:
On the other hand: Dicts are already thread save! Because of the Global interpreter Lock (GIL) all dict operations are atomic.
To make a really save dict you need to aquire the lock of the dict before you get the value you want to modify and release it after you wrote the data back:
If your code generate invalid internal state you need an even more aggressive locking. You need protect reading by locks, too.