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::prelude::RawFd;
48
49 use nix::{
50 sys::socket::{self, UnixAddr},
51 unistd,
52 };
53
54 use super::Error;
55
56 pub struct SingleInstance {
58 maybe_sock: Option<RawFd>,
59 }
60
61 impl SingleInstance {
62 pub fn new(name: &str) -> Result<Self, Error> {
64 let addr = UnixAddr::new_abstract(name.as_bytes())?;
65 let sock = socket::socket(
66 socket::AddressFamily::Unix,
67 socket::SockType::Stream,
68 socket::SockFlag::SOCK_CLOEXEC,
71 None,
72 )?;
73
74 let maybe_sock = match socket::bind(sock, &socket::SockAddr::Unix(addr)) {
75 Ok(()) => Some(sock),
76 Err(nix::errno::Errno::EADDRINUSE) => None,
77 Err(e) => return Err(e.into()),
78 };
79
80 Ok(Self {
81 maybe_sock,
82 })
83 }
84
85 pub fn is_single(&self) -> bool {
87 self.maybe_sock.is_some()
88 }
89 }
90
91 impl Drop for SingleInstance {
92 fn drop(&mut self) {
93 if let Some(sock) = self.maybe_sock {
94 let _ = unistd::close(sock);
96 }
97 }
98 }
99}
100
101#[cfg(target_os = "macos")]
102mod inner {
103 use std::{fs::File, os::unix::io::AsRawFd, path::Path};
104
105 use libc::{__error, EWOULDBLOCK, LOCK_EX, LOCK_NB, flock};
106
107 use super::Error;
108
109 pub struct SingleInstance {
111 _file: File,
112 is_single: bool,
113 }
114
115 impl SingleInstance {
116 pub fn new(name: &str) -> Result<Self, Error> {
118 let path = Path::new(name);
119 let file = if path.exists() {
120 File::open(path)?
121 } else {
122 File::create(path)?
123 };
124 unsafe {
125 let rc = flock(file.as_raw_fd(), LOCK_EX | LOCK_NB);
126 let is_single = rc == 0 || EWOULDBLOCK != *__error();
127 Ok(Self {
128 _file: file,
129 is_single,
130 })
131 }
132 }
133
134 pub fn is_single(&self) -> bool {
136 self.is_single
137 }
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 static UNIQ_ID: &'static str = "aa2d0258-ffe9-11e7-ba89-0ed5f89f718b";
145 #[test]
146 fn test_single_instance() {
147 {
148 let instance_a = SingleInstance::new(UNIQ_ID).unwrap();
149 assert!(instance_a.is_single());
150 let instance_b = SingleInstance::new(UNIQ_ID).unwrap();
151 assert!(!instance_b.is_single());
152 }
153 let instance_c = SingleInstance::new(UNIQ_ID).unwrap();
154 assert!(instance_c.is_single());
155 }
156}