Acquire the lock with stale lock detection and retry.
(self)
| 153 | self._acquired = False |
| 154 | |
| 155 | def __enter__(self) -> "FileLock": |
| 156 | """Acquire the lock with stale lock detection and retry.""" |
| 157 | from ci.util.global_interrupt_handler import is_interrupted |
| 158 | |
| 159 | my_pid = os.getpid() |
| 160 | hostname = platform.node() |
| 161 | start_time = time.time() |
| 162 | stale_check_done = False |
| 163 | |
| 164 | while True: |
| 165 | if is_interrupted(): |
| 166 | raise KeyboardInterrupt() |
| 167 | |
| 168 | # Try to acquire |
| 169 | acquired = self._db.try_acquire( |
| 170 | self._lock_name, my_pid, hostname, self.operation, mode=self._mode |
| 171 | ) |
| 172 | |
| 173 | if acquired: |
| 174 | self._acquired = True |
| 175 | return self |
| 176 | |
| 177 | # Check for stale locks (once per acquisition attempt cycle) |
| 178 | if not stale_check_done: |
| 179 | if self._db.is_lock_stale(self._lock_name): |
| 180 | logger.info("Detected stale lock, breaking and retrying") |
| 181 | self._db.break_stale_lock(self._lock_name) |
| 182 | stale_check_done = True |
| 183 | continue # Retry immediately after breaking stale lock |
| 184 | |
| 185 | # Check timeout |
| 186 | if self.timeout is not None: |
| 187 | elapsed = time.time() - start_time |
| 188 | if elapsed >= self.timeout: |
| 189 | # Build informative error message |
| 190 | holders = self._db.get_lock_info(self._lock_name) |
| 191 | if holders: |
| 192 | holder = holders[0] |
| 193 | raise TimeoutError( |
| 194 | f"Failed to acquire lock {self._lock_name} within {self.timeout}s. " |
| 195 | f"Lock held by active process (PID {holder['owner_pid']}, " |
| 196 | f"operation: {holder['operation']})" |
| 197 | ) |
| 198 | else: |
| 199 | raise TimeoutError( |
| 200 | f"Failed to acquire lock {self._lock_name} within {self.timeout}s" |
| 201 | ) |
| 202 | |
| 203 | # Periodically check for stale locks |
| 204 | elapsed = time.time() - start_time |
| 205 | if elapsed > 1.0: |
| 206 | stale_check_done = False # Re-enable stale checks |
| 207 | |
| 208 | # Small sleep to prevent busy-waiting |
| 209 | time.sleep(0.1) |
| 210 | |
| 211 | def __exit__( |
| 212 | self, |
nothing calls this directly
no test coverage detected