single_instance/
single_instance.rs1#[derive(thiserror::Error, Debug)]
23pub enum Error {
24 #[cfg(any(target_os = "linux", target_os = "android"))]
25 #[error("new abstract addr error")]
26 Nix(#[from] nix::Error),
27
28 #[cfg(target_os = "macos")]
29 #[error("file open or create error")]
30 Io(#[from] std::io::Error),
31}
32
33pub use self::inner::*;
44
45#[cfg(any(target_os = "linux", target_os = "android"))]
46mod inner {
47 use std::os::unix::io::{AsRawFd, OwnedFd};
48
49 use nix::sys::socket::{self, UnixAddr};
50
51 use super::Error;
52
53 pub struct SingleInstance {
55 maybe_sock: Option<OwnedFd>,
56 }
57
58 impl SingleInstance {
59 pub fn new(name: &str) -> Result<Self, Error> {
61 let addr = UnixAddr::new_abstract(name.as_bytes())?;
62 let sock = socket::socket(
63 socket::AddressFamily::Unix,
64 socket::SockType::Stream,
65 socket::SockFlag::SOCK_CLOEXEC,
68 None,
69 )?;
70
71 let maybe_sock = match socket::bind(sock.as_raw_fd(), &addr) {
72 Ok(()) => Some(sock),
73 Err(nix::errno::Errno::EADDRINUSE) => None,
74 Err(e) => return Err(e.into()),
75 };
76
77 Ok(Self {
78 maybe_sock,
79 })
80 }
81
82 pub fn is_single(&self) -> bool {
84 self.maybe_sock.is_some()
85 }
86 }
87
88 impl Drop for SingleInstance {
89 fn drop(&mut self) {
90 if let Some(sock) = self.maybe_sock.take() {
92 drop(sock);
93 }
94 }
95 }
96}
97
98#[cfg(target_os = "macos")]
99mod inner {
100 use std::{fs::File, os::unix::io::AsRawFd, path::Path};
101
102 use libc::{__error, EWOULDBLOCK, LOCK_EX, LOCK_NB, flock};
103
104 use super::Error;
105
106 pub struct SingleInstance {
108 _file: File,
109 is_single: bool,
110 }
111
112 impl SingleInstance {
113 pub fn new(name: &str) -> Result<Self, Error> {
115 let path = Path::new(name);
116 let file = if path.exists() {
117 File::open(path)?
118 } else {
119 File::create(path)?
120 };
121 unsafe {
122 let rc = flock(file.as_raw_fd(), LOCK_EX | LOCK_NB);
123 let is_single = rc == 0 || EWOULDBLOCK != *__error();
124 Ok(Self {
125 _file: file,
126 is_single,
127 })
128 }
129 }
130
131 pub fn is_single(&self) -> bool {
133 self.is_single
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 static UNIQ_ID: &'static str = "aa2d0258-ffe9-11e7-ba89-0ed5f89f718b";
142 #[test]
143 fn test_single_instance() {
144 {
145 let instance_a = SingleInstance::new(UNIQ_ID).unwrap();
146 assert!(instance_a.is_single());
147 let instance_b = SingleInstance::new(UNIQ_ID).unwrap();
148 assert!(!instance_b.is_single());
149 }
150 let instance_c = SingleInstance::new(UNIQ_ID).unwrap();
151 assert!(instance_c.is_single());
152 }
153}