devserver: generalize lock dictionary; substitute multiprocessing for threading
* Migrate the lock dictionary functionality into a dedicated class, so
it can be easily used with other sets of resources.
* Replace multiprocessing primitives (processes, locks) with threading
equivalents. There's no apparent reason for using multiprocessing, and
threading primitives are expected to be faster/lighter.
BUG=None
TEST=Passes unit tests
Change-Id: I7e3671a55d17f6bc60217b278ccc7b69c72c6c48
Reviewed-on: https://gerrit.chromium.org/gerrit/33641
Reviewed-by: Gilad Arnold <garnold@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/devserver.py b/devserver.py
index 9590d6e..2ad13b8 100755
--- a/devserver.py
+++ b/devserver.py
@@ -8,13 +8,13 @@
import cherrypy
import logging
-import multiprocessing
import optparse
import os
import re
import sys
import subprocess
import tempfile
+import threading
import autoupdate
import devserver_util
@@ -33,6 +33,33 @@
pass
+class LockDict(object):
+ """A dictionary of locks.
+
+ This class provides a thread-safe store of threading.Lock objects, which can
+ be used to regulate access to any set of hashable resources. Usage:
+
+ foo_lock_dict = LockDict()
+ ...
+ with foo_lock_dict.lock('bar'):
+ # Critical section for 'bar'
+ """
+ def __init__(self):
+ self._lock = self._new_lock()
+ self._dict = {}
+
+ def _new_lock(self):
+ return threading.Lock()
+
+ def lock(self, key):
+ with self._lock:
+ lock = self._dict.get(key)
+ if not lock:
+ lock = self._new_lock()
+ self._dict[key] = lock
+ return lock
+
+
def _LeadingWhiteSpaceCount(string):
"""Count the amount of leading whitespace in a string.
@@ -196,8 +223,7 @@
def __init__(self):
self._builder = None
- self._lock_dict_lock = multiprocessing.Lock()
- self._lock_dict = {}
+ self._download_lock_dict = LockDict()
self._downloader_dict = {}
@cherrypy.expose
@@ -208,27 +234,6 @@
self._builder = builder.Builder()
return self._builder.Build(board, pkg, kwargs)
- def _get_lock_for_archive_url(self, archive_url):
- """Return a multiprocessing lock to use per archive_url.
-
- Use this lock to protect critical zones per archive_url.
-
- Usage:
- with DevserverInstance._get_lock_for_archive_url(archive_url):
- # CRITICAL AREA FOR ARCHIVE_URL.
-
- Returns:
- A multiprocessing lock that is archive_url specific.
- """
- with self._lock_dict_lock:
- lock = self._lock_dict.get(archive_url)
- if lock:
- return lock
- else:
- lock = multiprocessing.Lock()
- self._lock_dict[archive_url] = lock
- return lock
-
@staticmethod
def _canonicalize_archive_url(archive_url):
"""Canonicalizes archive_url strings.
@@ -261,7 +266,7 @@
# Guarantees that no two downloads for the same url can run this code
# at the same time.
- with self._get_lock_for_archive_url(archive_url):
+ with self._download_lock_dict.lock(archive_url):
try:
# If we are currently downloading, return. Note, due to the above lock
# we know that the foreground artifacts must have finished downloading