<?php
namespace NeoDuplicateFull\NeoGlobal; if (!defined("ABSPATH")) { die("neoWP direct access not allowed"); }


// Synclock for e.g. the neoWP plugin cache folder










function synclock_dir($dir, $callback, $timeout = 2, $scope = null) {
    if (!\NeoDuplicateFull\NeoGlobal\fs_file_exists($dir)) { \NeoDuplicateFull\NeoGlobal\throw_global_exception("Directory does not exist in synclock_dir. Path: " . $dir . ". Scope: $scope" . "."); }
    if ($timeout < 2) { \NeoDuplicateFull\NeoGlobal\throw_global_exception("Timeout too low in synclock_dir. Minimum is 2 seconds because filemtime in FAT32 is only seconds precise and rounding errors require 2 seconds to get 1-2 seconds in real timeout."); }

    static $synclocked_dirs = [];
    $synclock_dir_and_scope = untrailingslashit($dir) . "@" . ($scope ?? "");
    if (isset($synclocked_dirs[$synclock_dir_and_scope])) { return $callback(); } $synclocked_dirs[$synclock_dir_and_scope] = true;
    try {

        static $ignore_all_warnings_and_errors = null; $ignore_all_warnings_and_errors ??= function ($callback) { if (function_exists('\NeoDuplicateFull\NeoGlobal\_synclock_ignore_all_warnings_and_errors')) { \NeoDuplicateFull\NeoGlobal\_synclock_ignore_all_warnings_and_errors($callback); } else { $callback(); } };

        try {

            $dir = untrailingslashit($dir);
            if ($scope && strlen($scope) > 16) { $scope = substr($scope, 0, 8) . "..." . substr($scope, -8) . "-" . substr(md5($scope), 0, 8); }
            $scope_filename_part = $scope === null ? "" : "-$scope";
            $lock_file = $dir . "/.lock$scope_filename_part-" . \NeoDuplicateFull\NeoGlobal\unique_planck_date();
            $lock_files_for_scope = function () use ($dir, $scope, $scope_filename_part) { $lock_files = \NeoDuplicateFull\NeoGlobal\fs_glob($scope === null ? $dir . "/.lock-*" : $dir . "/.lock$scope_filename_part-*") ?: []; return $scope === null ? \NeoDuplicateFull\NeoGlobal\array_filter_better($lock_files, fn ($filename) => substr_count(basename($filename), "-") === 1) : $lock_files; };
            $touch_success = \NeoDuplicateFull\NeoGlobal\fs_touch($lock_file);
            if (!$touch_success) { \NeoDuplicateFull\NeoGlobal\throw_global_exception("Could not create lock file in synclock_dir. Path: " . $dir . ". Scope: $scope" . "."); }

            while (basename($lock_file) !== basename((function ($files) { sort($files); return $files[0] ?? ""; })($lock_files_for_scope()))) {
                $touch_success = \NeoDuplicateFull\NeoGlobal\fs_touch($lock_file);
                if (!$touch_success) { \NeoDuplicateFull\NeoGlobal\throw_global_exception("Could not touch lock file in synclock_dir. Path: " . $dir . ". Scope: $scope" . "."); }

                $ignore_all_warnings_and_errors(function() use ($timeout, $lock_files_for_scope) {
                    foreach ($lock_files_for_scope() as $filename) { if (\NeoDuplicateFull\NeoGlobal\is_cache_file_outdated($filename, $timeout)) { \NeoDuplicateFull\NeoGlobal\fs_unlink($filename); } }
                });
                usleep((1) * 1000);
            }


            return $callback();
        } finally {
            if (!\NeoDuplicateFull\NeoGlobal\fs_file_exists($lock_file)) { \NeoDuplicateFull\NeoGlobal\global_warn_with_module_name("_global--synclock", "This PHP process took too long in synclock_dir. It was cleaned up by another PHP process. Path: $dir Scope: $scope"); }
            else { $ignore_all_warnings_and_errors(fn () => \NeoDuplicateFull\NeoGlobal\fs_unlink($lock_file)); }
        }
    } finally {
        unset($synclocked_dirs[$synclock_dir_and_scope]);
    }
}
