telescope-frecency.nvim/lua/frecency/file_lock.lua
JINNOUCHI Yasushi a03eb9b78a
fix!: show debug msg only when debug is true (#220)
* docs: fix the format for some docs

* fix!: show debug msg only when `debug` is `true`

Fix #212

* test: enable debug logging in tests
2024-07-06 19:13:39 +09:00

103 lines
2.8 KiB
Lua

local log = require "frecency.log"
local Path = require "plenary.path" --[[@as FrecencyPlenaryPath]]
local async = require "plenary.async" --[[@as FrecencyPlenaryAsync]]
---@class FrecencyFileLock
---@field base string
---@field config FrecencyFileLockConfig
---@field filename string
local FileLock = {}
---@class FrecencyFileLockConfig
---@field retry integer default: 5
---@field unlink_retry integer default: 5
---@field interval integer default: 500
---@param path string
---@param file_lock_config? FrecencyFileLockConfig
---@return FrecencyFileLock
FileLock.new = function(path, file_lock_config)
local config = vim.tbl_extend("force", { retry = 5, unlink_retry = 5, interval = 500 }, file_lock_config or {})
local self = setmetatable({ config = config }, { __index = FileLock })
self.filename = path .. ".lock"
return self
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.filename):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.filename, "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.filename)
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.filename)
if err then
log.debug("file_lock release() not found: " .. err)
return "lock not found"
end
err = async.uv.fs_unlink(self.filename)
if err then
log.debug("file_lock release() unlink failed: " .. err)
return err
end
end
---@async
---@generic T
---@param f fun(): 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)
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