environment('production')) { fwrite(STDERR, "\033[33mWARNING: RSX_ALWAYS_WRITE_LOCK is enabled in production. " . 'ALL requests are serialized with exclusive write lock. ' . "This severely impacts performance and should only be used for critical operations.\033[0m\n"); } console_debug('CONCURRENCY', "Acquiring global application '" . ($lock_type == RsxLocks::WRITE_LOCK ? 'WRITE' : 'READ') . "'lock"); try { self::$application_lock_token = RsxLocks::get_lock( RsxLocks::SERVER_LOCK, RsxLocks::LOCK_APPLICATION, $lock_type, config('rsx.locking.timeout', 30) ); } catch (Exception $e) { // If we can't acquire the lock, the application cannot proceed shouldnt_happen( "Failed to acquire application {$lock_type} lock: " . $e->getMessage() . "\n" . 'This likely means another process has an exclusive lock.' ); } console_debug('CONCURRENCY', 'Global application lock acquired'); // Check if log rotation is needed (development mode only) // TODO: cant log rotate before getting build key // We logrotate somewhere else, dont we? // if (env('APP_ENV') !== 'production') { // $current_hash = Manifest::get_build_key(); // $version_file = storage_path('rsx-tmp/log_version'); // $should_rotate = false; // // Ensure directory exists // $dir = dirname($version_file); // if (!is_dir($dir)) { // @mkdir($dir, 0755, true); // } // // Check if version file exists and compare hash // if (file_exists($version_file)) { // $stored_hash = trim(file_get_contents($version_file)); // if ($stored_hash !== $current_hash) { // $should_rotate = true; // } // } else { // // File doesn't exist, first run // $should_rotate = true; // } // // Rotate logs if needed // if ($should_rotate) { // Debugger::logrotate(); // // Update version file with new hash // file_put_contents($version_file, $current_hash); // } // } } /** * Upgrade the application lock to exclusive write mode * * Used when performing operations that require exclusive access, * such as manifest rebuilding or bundle compilation. * * @return string New lock token after upgrade * @throws RuntimeException if upgrade fails */ public static function upgrade_to_write_lock(): string { if (!self::$application_lock_token) { shouldnt_happen('Cannot upgrade lock - no application lock held'); } try { $new_token = RsxLocks::upgrade_lock( self::$application_lock_token, config('rsx.locking.timeout', 30) ); self::$application_lock_token = $new_token; return $new_token; } catch (Exception $e) { throw new RuntimeException( 'Failed to upgrade application lock to write mode: ' . $e->getMessage() ); } } /** * Get the current application lock token * * @return string|null */ public static function get_application_lock_token(): ?string { return self::$application_lock_token; } /** * Check if we hold the application lock * * @return bool */ public static function has_application_lock(): bool { return self::$application_lock_token !== null; } /** * Shutdown handler - releases locks * * @return void */ public static function shutdown(): void { if (self::$application_lock_token) { try { RsxLocks::release_lock(self::$application_lock_token); } catch (Exception $e) { // Ignore errors during shutdown } finally { self::$application_lock_token = null; } } } /** * Force release of application lock (emergency use only) * * @return void */ public static function force_release(): void { if (self::$application_lock_token) { RsxLocks::release_lock(self::$application_lock_token); self::$application_lock_token = null; } } /** * Temporarily release application lock for testing * Used by rsx:debug to prevent lock contention with Playwright * * @return void */ public static function temporarily_release_lock(): void { if (self::$application_lock_token) { RsxLocks::release_lock(self::$application_lock_token); self::$application_lock_token = null; } } }