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 29http and websocket tests in a same time.""" 30 31import glob 32import logging 33import os 34import sys 35import tempfile 36import time 37 38from webkitpy.common.system.executive import Executive 39from webkitpy.common.system.file_lock import FileLock 40from webkitpy.common.system.filesystem import FileSystem 41 42 43_log = logging.getLogger("webkitpy.layout_tests.port.http_lock") 44 45 46class 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) 134