doomemacs/modules/email/mu4e/autoload/mu-lock.el

97 lines
4.3 KiB
EmacsLisp
Raw Normal View History

;;; email/mu4e/autoload/mu-lock.el -*- lexical-binding: t; -*-
(autoload 'file-notify-rm-watch "filenotify")
(autoload 'file-notify-add-watch "filenotify")
(defvar +mu4e-lock-file (expand-file-name "mu4e_lock" temporary-file-directory)
"Location of the lock file which stores the PID of the process currenty running mu4e")
(defvar +mu4e-lock-request-file (expand-file-name "mu4e_lock_request" temporary-file-directory)
"Location of the lock file for which creating indicated that another process wants the lock to be released")
(defvar +mu4e-lock-greedy nil
"Whether to 'grab' the `+mu4e-lock-file' if nobody else has it, i.e. start Mu4e")
(defvar +mu4e-lock-relaxed t
"Whether if someone else wants the lock (signaled via `+mu4e-lock-request-file'), we should stop Mu4e and let go of it")
(defun +mu4e-lock-pid-info ()
"Get info on the PID refered to in `+mu4e-lock-file' in the form (pid . process-attributes)
If the file or process do not exist, the lock file is deleted an nil returned."
(when (file-exists-p +mu4e-lock-file)
(let* ((coding-system-for-read 'utf-8)
(pid (string-to-number
(with-temp-buffer
(insert-file-contents +mu4e-lock-file)
(buffer-string))))
(process (process-attributes pid)))
(if (and process (string-match-p "[Ee]macs" (alist-get 'comm process)))
(cons pid process)
(delete-file +mu4e-lock-file) nil))))
;;;###autoload
(defun +mu4e-lock-available (&optional strict)
"If the `+mu4e-lock-file' is available (unset or owned by this emacs) return t.
If STRICT only accept an unset lock file."
(not (when-let* ((lock-info (+mu4e-lock-pid-info))
(pid (car lock-info)))
(when (or strict (/= (emacs-pid) pid)) t))))
;;;###autoload
(defun +mu4e-lock-file-delete-maybe ()
"Check `+mu4e-lock-file', and delete it if this process is responsible for it."
(when (+mu4e-lock-available)
(delete-file +mu4e-lock-file)
(file-notify-rm-watch +mu4e-lock--request-watcher)))
;;;###autoload
(defun +mu4e-lock-start (orig-fun &optional callback)
"Check `+mu4e-lock-file', and if another process is responsible for it, abort starting.
Else, write to this process' PID to the lock file"
(unless (+mu4e-lock-available)
(call-process "touch" nil nil nil +mu4e-lock-request-file)
(message "Lock file exists, requesting that it be given up")
(sleep-for 0.1)
(delete-file +mu4e-lock-request-file))
(if (not (+mu4e-lock-available))
(user-error "Unfortunately another Emacs is already doing stuff with Mu4e, and you can only have one at a time")
(write-region (number-to-string (emacs-pid)) nil +mu4e-lock-file)
(delete-file +mu4e-lock-request-file)
(call-process "touch" nil nil nil +mu4e-lock-request-file)
(funcall orig-fun callback)
(when +mu4e-lock--request-watcher
(file-notify-rm-watch +mu4e-lock--request-watcher))
(file-notify-add-watch +mu4e-lock-request-file
'(change)
#'+mu4e-lock--request)))
(defvar +mu4e-lock--file-watcher nil)
(defvar +mu4e-lock--file-just-deleted nil)
(defvar +mu4e-lock--request-watcher nil)
(defun +mu4e-lock-add-watcher ()
(setq +mu4e-lock--file-just-deleted nil)
(file-notify-rm-watch +mu4e-lock--file-watcher)
(setq +mu4e-lock--file-watcher
(file-notify-add-watch +mu4e-lock-file
'(change)
#'+mu4e-lock--file-updated)))
(defun +mu4e-lock--request (event)
"Handle another process requesting the Mu4e lock."
(when (eq (nth 1 event) 'created)
(when +mu4e-lock-relaxed
(mu4e--stop)
(file-notify-rm-watch +mu4e-lock--file-watcher)
(file-notify-rm-watch +mu4e-lock--request-watcher)
(message "Someone else wants to use Mu4e, releasing lock")
(delete-file +mu4e-lock-file)
(run-at-time 0.2 nil #'+mu4e-lock-add-watcher))
(delete-file +mu4e-lock-request-file)))
(defun +mu4e-lock--file-updated (event)
(if +mu4e-lock--file-just-deleted
(+mu4e-lock-add-watcher)
(when (eq (nth 1 event) 'deleted)
(setq +mu4e-lock--file-just-deleted t)
(when (and +mu4e-lock-greedy (+mu4e-lock-available t))
(message "Noticed Mu4e lock was available, grabbed it")
(run-at-time 0.2 nil #'mu4e)))))