1pub mod error;
24
25#[cfg(target_os = "macos")]
26extern crate libc;
27#[cfg(target_os = "linux")]
28extern crate nix;
29extern crate thiserror;
30#[cfg(target_os = "windows")]
31extern crate widestring;
32#[cfg(target_os = "windows")]
33extern crate winapi;
34
35pub use self::inner::*;
36
37#[cfg(target_os = "windows")]
38mod inner {
39 use error::{Result, SingleInstanceError};
40 use std::ptr;
41 use widestring::WideCString;
42 use winapi::shared::winerror::{ERROR_ALREADY_EXISTS, ERROR_INVALID_HANDLE};
43 use winapi::um::errhandlingapi::GetLastError;
44 use winapi::um::handleapi::CloseHandle;
45 use winapi::um::synchapi::CreateMutexW;
46 use winapi::um::winnt::HANDLE;
47
48 pub struct SingleInstance {
50 handle: Option<HANDLE>,
51 }
52
53 unsafe impl Send for SingleInstance {}
54 unsafe impl Sync for SingleInstance {}
55
56 impl SingleInstance {
57 pub fn new(name: &str) -> Result<Self> {
59 let name = WideCString::from_str(name)?;
60 unsafe {
61 let handle = CreateMutexW(ptr::null_mut(), 0, name.as_ptr());
62 let last_error = GetLastError();
63
64 if handle.is_null() || handle == ERROR_INVALID_HANDLE as _ {
66 Err(SingleInstanceError::MutexError(last_error))
67 } else if last_error == ERROR_ALREADY_EXISTS {
68 CloseHandle(handle);
69 Ok(SingleInstance { handle: None })
70 } else {
71 Ok(SingleInstance {
72 handle: Some(handle),
73 })
74 }
75 }
76 }
77
78 pub fn is_single(&self) -> bool {
80 self.handle.is_some()
81 }
82 }
83
84 impl Drop for SingleInstance {
85 fn drop(&mut self) {
86 if let Some(handle) = self.handle.take() {
87 unsafe {
88 CloseHandle(handle);
89 }
90 }
91 }
92 }
93}
94
95#[cfg(target_os = "linux")]
96mod inner {
97 use error::Result;
98 use nix::sys::socket::{self, UnixAddr};
99 use nix::unistd;
100 use std::os::unix::prelude::RawFd;
101
102 pub struct SingleInstance {
104 maybe_sock: Option<RawFd>,
105 }
106
107 impl SingleInstance {
108 pub fn new(name: &str) -> Result<Self> {
110 let addr = UnixAddr::new_abstract(name.as_bytes())?;
111 let sock = socket::socket(
112 socket::AddressFamily::Unix,
113 socket::SockType::Stream,
114 socket::SockFlag::empty(),
115 None,
116 )?;
117
118 let maybe_sock = match socket::bind(sock, &socket::SockAddr::Unix(addr)) {
119 Ok(()) => Some(sock),
120 Err(nix::errno::Errno::EADDRINUSE) => None,
121 Err(e) => return Err(e.into()),
122 };
123
124 Ok(Self { maybe_sock })
125 }
126
127 pub fn is_single(&self) -> bool {
129 self.maybe_sock.is_some()
130 }
131 }
132
133 impl Drop for SingleInstance {
134 fn drop(&mut self) {
135 if let Some(sock) = self.maybe_sock {
136 let _ = unistd::close(sock);
138 }
139 }
140 }
141}
142
143#[cfg(target_os = "macos")]
144mod inner {
145 use error::Result;
146 use libc::{__error, flock, EWOULDBLOCK, LOCK_EX, LOCK_NB};
147 use std::fs::File;
148 use std::os::unix::io::AsRawFd;
149 use std::path::Path;
150
151 pub struct SingleInstance {
153 _file: File,
154 is_single: bool,
155 }
156
157 impl SingleInstance {
158 pub fn new(name: &str) -> Result<Self> {
160 let path = Path::new(name);
161 let file = if path.exists() {
162 File::open(path)?
163 } else {
164 File::create(path)?
165 };
166 unsafe {
167 let rc = flock(file.as_raw_fd(), LOCK_EX | LOCK_NB);
168 let is_single = rc == 0 || EWOULDBLOCK != *__error();
169 Ok(Self {
170 _file: file,
171 is_single,
172 })
173 }
174 }
175
176 pub fn is_single(&self) -> bool {
178 self.is_single
179 }
180 }
181}
182
183#[test]
184fn test_single_instance() {
185 {
186 let instance_a = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
187 assert!(instance_a.is_single());
188 let instance_b = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
189 assert!(!instance_b.is_single());
190 }
191 let instance_c = SingleInstance::new("aa2d0258-ffe9-11e7-ba89-0ed5f89f718b").unwrap();
192 assert!(instance_c.is_single());
193}