1# 2# Copyright (C) 2018 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the 'License'); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an 'AS IS' BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# 16 17import time 18 19from host_controller import common 20from host_controller.utils.ipc import file_lock 21 22MIN_SLEEP_TIME_IN_SECS = 1 23MAX_SLEEP_TIME_IN_SECS = 60 24 25 26class FileLockSemaphore(file_lock.FileLock): 27 """Class for system-wide semaphores for inter process-group sync. 28 29 Inherited FileLock to make the decrement and increment operation atomic, 30 thus any read and write operations to the file must be leaded by Lock() 31 and be followed by Unlock() operations, defined in FileLock 32 33 Attributes: 34 _name: string, name of the file used as semaphore. 35 """ 36 37 def __init__(self, name): 38 super(FileLockSemaphore, self).__init__(name, "r+") 39 self._name = name 40 self._InitSemaphore() 41 42 def Acquire(self): 43 """P() operation for the FileLockSemaphore. 44 45 To minimize the starvation, the sleep time will decrease 46 for the processes that have waited longer than the others. 47 """ 48 sleep_time = MAX_SLEEP_TIME_IN_SECS 49 while self.LockDevice(self._name, True, True): 50 if self.sem_value > 0: 51 break 52 self.UnlockDevice(self._name) 53 time.sleep(sleep_time) 54 sleep_time = max(sleep_time / 2, MIN_SLEEP_TIME_IN_SECS) 55 56 self._Decrement() 57 self.UnlockDevice(self._name) 58 59 def Release(self): 60 """V() operation for the FileLockSemaphore.""" 61 self.LockDevice(self._name, True, True) 62 self._Increment() 63 self.UnlockDevice(self._name) 64 65 def _InitSemaphore(self): 66 """Initializes the file content for semaphore operations. 67 68 The value of the counter must remain in the range 69 [0, common.MAX_ADB_FASTBOOT_PROCESS] inclusive. 70 """ 71 self.LockDevice(self._name, True, True) 72 73 try: 74 diff = common.MAX_ADB_FASTBOOT_PROCESS - self.sem_max_value 75 value_to_init = min( 76 max(0, self.sem_value + diff), common.MAX_ADB_FASTBOOT_PROCESS) 77 except IndexError: 78 value_to_init = common.MAX_ADB_FASTBOOT_PROCESS 79 80 self._lock_fd[self._name].seek(0) 81 self._lock_fd[self._name].truncate(0) 82 self._lock_fd[self._name].write( 83 "%s\n%s" % (common.MAX_ADB_FASTBOOT_PROCESS, value_to_init)) 84 85 self.UnlockDevice(self._name) 86 87 @property 88 def sem_value(self): 89 """Reads the current counter value of the semaphore. 90 91 Returns: 92 int, the value of the current counter. 93 """ 94 self._lock_fd[self._name].seek(0) 95 return int(self._lock_fd[self._name].read().split()[1]) 96 97 @property 98 def sem_max_value(self): 99 """Reads the current maximum counter value of the semaphore. 100 101 Since the maximum counter value may vary at the deployment time, 102 the existing HC process group needs to look for the maximum value 103 every time it tries to access the semaphore 104 105 Returns: 106 int, the value of the maximum counter. 107 """ 108 self._lock_fd[self._name].seek(0) 109 return int(self._lock_fd[self._name].read().split()[0]) 110 111 def _Decrement(self): 112 """Decrements the internal counter of the semaphore.""" 113 current_value = self.sem_value 114 current_max = self.sem_max_value 115 self._lock_fd[self._name].seek(len(str(current_max)) + 1) 116 self._lock_fd[self._name].write("%s" % max(0, (current_value - 1))) 117 118 def _Increment(self): 119 """Increments the internal counter of the semaphore.""" 120 current_value = self.sem_value 121 current_max = self.sem_max_value 122 self._lock_fd[self._name].seek(len(str(current_max)) + 1) 123 self._lock_fd[self._name].write("%s" % min(current_max, 124 (current_value + 1))) 125