Rename WebKitTools to Tools
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / http_lock.py
1 #!/usr/bin/env python
2 # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
3 # Copyright (C) 2010 Andras Becsi (abecsi@inf.u-szeged.hu), University of Szeged
4 #
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 # 1. Redistributions of source code must retain the above copyright
11 #    notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 #    notice, this list of conditions and the following disclaimer in the
14 #    documentation and/or other materials provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
17 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 """This class helps to block NRWT threads when more NRWTs run
29 http and websocket tests in a same time."""
30
31 import glob
32 import logging
33 import os
34 import sys
35 import tempfile
36 import time
37
38 from webkitpy.common.system.executive import Executive
39 from webkitpy.common.system.file_lock import FileLock
40 from webkitpy.common.system.filesystem import FileSystem
41
42
43 _log = logging.getLogger("webkitpy.layout_tests.port.http_lock")
44
45
46 class HttpLock(object):
47
48     def __init__(self, lock_path, lock_file_prefix="WebKitHttpd.lock.",
49                  guard_lock="WebKit.lock"):
50         self._lock_path = lock_path
51         if not self._lock_path:
52             self._lock_path = tempfile.gettempdir()
53         self._lock_file_prefix = lock_file_prefix
54         self._lock_file_path_prefix = os.path.join(self._lock_path,
55                                                    self._lock_file_prefix)
56         self._guard_lock_file = os.path.join(self._lock_path, guard_lock)
57         self._guard_lock = FileLock(self._guard_lock_file)
58         self._process_lock_file_name = ""
59         self._executive = Executive()
60
61     def cleanup_http_lock(self):
62         """Delete the lock file if exists."""
63         if os.path.exists(self._process_lock_file_name):
64             _log.debug("Removing lock file: %s" % self._process_lock_file_name)
65             FileSystem().remove(self._process_lock_file_name)
66
67     def _extract_lock_number(self, lock_file_name):
68         """Return the lock number from lock file."""
69         prefix_length = len(self._lock_file_path_prefix)
70         return int(lock_file_name[prefix_length:])
71
72     def _lock_file_list(self):
73         """Return the list of lock files sequentially."""
74         lock_list = glob.glob(self._lock_file_path_prefix + '*')
75         lock_list.sort(key=self._extract_lock_number)
76         return lock_list
77
78     def _next_lock_number(self):
79         """Return the next available lock number."""
80         lock_list = self._lock_file_list()
81         if not lock_list:
82             return 0
83         return self._extract_lock_number(lock_list[-1]) + 1
84
85     def _curent_lock_pid(self):
86         """Return with the current lock pid. If the lock is not valid
87         it deletes the lock file."""
88         lock_list = self._lock_file_list()
89         if not lock_list:
90             return
91         try:
92             current_lock_file = open(lock_list[0], 'r')
93             current_pid = current_lock_file.readline()
94             current_lock_file.close()
95             if not (current_pid and self._executive.check_running_pid(int(current_pid))):
96                 _log.debug("Removing stuck lock file: %s" % lock_list[0])
97                 FileSystem().remove(lock_list[0])
98                 return
99         except (IOError, OSError):
100             return
101         return int(current_pid)
102
103     def _create_lock_file(self):
104         """The lock files are used to schedule the running test sessions in first
105         come first served order. The guard lock ensures that the lock numbers are
106         sequential."""
107         if not os.path.exists(self._lock_path):
108             _log.debug("Lock directory does not exist: %s" % self._lock_path)
109             return False
110
111         if not self._guard_lock.acquire_lock():
112             _log.debug("Guard lock timed out!")
113             return False
114
115         self._process_lock_file_name = (self._lock_file_path_prefix +
116                                         str(self._next_lock_number()))
117         _log.debug("Creating lock file: %s" % self._process_lock_file_name)
118         lock_file = open(self._process_lock_file_name, 'w')
119         lock_file.write(str(os.getpid()))
120         lock_file.close()
121         self._guard_lock.release_lock()
122         return True
123
124
125     def wait_for_httpd_lock(self):
126         """Create a lock file and wait until it's turn comes. If something goes wrong
127         it wont do any locking."""
128         if not self._create_lock_file():
129             _log.debug("Warning, http locking failed!")
130             return
131
132         while self._curent_lock_pid() != os.getpid():
133             time.sleep(1)