telescope-frecency.nvim/lua/frecency/file_lock.lua
JINNOUCHI Yasushi c140e6ff9c
feat: make query() faster and more lazier (#241)
* refactor: simplify logic to load web_devicons

* refactor: make register() asynchronous

* fix: load lazily modules outside this plugin

* refactor: simplify logic to wait initialization

* refactor: use uv.hrtime() instead of os.clock()

* fix: avoid errors in calling plenary.log in async

* test: store elapsed time to check in tests

* test: fix module names

This becomes a problem only in Ubuntu because macOS and Windows does not
care cases in filenames.

* test: fix types and unused modules

* style: fix by stylua

* refactor: make recency / entry_maker loaded lazily
2024-08-25 19:28:52 +09:00

109 lines
3.1 KiB
Lua

local log = require "frecency.log"
local lazy_require = require "frecency.lazy_require"
local Path = lazy_require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = lazy_require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class FrecencyFileLock
---@field base string
---@field config FrecencyFileLockRawConfig
---@field lock string
---@field target string
local FileLock = {}
---@class FrecencyFileLockConfig
---@field retry? integer default: 5
---@field unlink_retry? integer default: 5
---@field interval? integer default: 500
---@class FrecencyFileLockRawConfig
---@field retry integer default: 5
---@field unlink_retry integer default: 5
---@field interval integer default: 500
---@param target string
---@param file_lock_config? FrecencyFileLockConfig
---@return FrecencyFileLock
FileLock.new = function(target, file_lock_config)
log.debug(("file_lock new(): %s"):format(target))
local config = vim.tbl_extend("force", { retry = 5, unlink_retry = 5, interval = 500 }, file_lock_config or {})
return setmetatable({ config = config, lock = target .. ".lock", target = target }, { __index = FileLock })
end
---@async
---@return string? err
function FileLock:get()
local count = 0
local unlink_count = 0
local err, fd
while true do
count = count + 1
local dir = Path.new(self.lock):parent()
if not dir:exists() then
-- TODO: make this call be async
log.debug(("file_lock get(): mkdir parent: %s"):format(dir.filename))
---@diagnostic disable-next-line: undefined-field
dir:mkdir { parents = true }
end
err, fd = async.uv.fs_open(self.lock, "wx", tonumber("600", 8))
if not err then
break
end
async.util.sleep(self.config.interval)
if count >= self.config.retry then
log.debug(("file_lock get(): retry count reached. try to delete the lock file: %d"):format(count))
err = async.uv.fs_unlink(self.lock)
if err then
log.debug("file_lock get() failed: " .. err)
unlink_count = unlink_count + 1
if unlink_count >= self.config.unlink_retry then
log.error("file_lock get(): failed to unlink the lock file: " .. err)
return "failed to get lock"
end
end
end
log.debug(("file_lock get() retry: %d"):format(count))
end
err = async.uv.fs_close(fd)
if err then
log.debug("file_lock get() failed: " .. err)
return err
end
end
---@async
---@return string? err
function FileLock:release()
local err = async.uv.fs_stat(self.lock)
if err then
log.debug("file_lock release() not found: " .. err)
return "lock not found"
end
err = async.uv.fs_unlink(self.lock)
if err then
log.debug("file_lock release() unlink failed: " .. err)
return err
end
end
---@async
---@generic T
---@param f fun(target: string): T
---@return string? err
---@return T
function FileLock:with(f)
local err = self:get()
if err then
return err, nil
end
local ok, result_or_err = pcall(f, self.target)
err = self:release()
if err then
return err, nil
elseif ok then
return nil, result_or_err
end
return result_or_err, nil
end
return FileLock