Skip to content

Commit 3ca2e9a

Browse files
committed
feat(CSerialPortUnixBase): #27 unix support exclusive mode by lock file
Signed-off-by: itas109 <itas109@qq.com>
1 parent b5d621f commit 3ca2e9a

8 files changed

Lines changed: 272 additions & 64 deletions

File tree

examples/CommNoGui/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ SRC = ${DEMO_DIR}/CSerialPortDemoNoGui.cpp \
1212
${SRC_DIR}/SerialPortInfoUnixBase.cpp \
1313
${SRC_DIR}/SerialPort.cpp \
1414
${SRC_DIR}/SerialPortBase.cpp \
15+
${SRC_DIR}/SerialPortAsyncBase.cpp \
1516
${SRC_DIR}/SerialPortUnixBase.cpp
1617

1718
OBJ = ${SRC}

examples/CommNoGui/compile.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
#! /bin/bash
22

3-
g++ -std=c++11 CSerialPortDemoNoGui.cpp ../../src/SerialPortInfo.cpp ../../src/SerialPortInfoBase.cpp ../../src/SerialPortInfoUnixBase.cpp ../../src/SerialPort.cpp ../../src/SerialPortBase.cpp ../../src/SerialPortUnixBase.cpp -lpthread -I../../include -o CSerialPortDemoNoGui
3+
g++ -std=c++11 CSerialPortDemoNoGui.cpp ../../src/SerialPortInfo.cpp ../../src/SerialPortInfoBase.cpp ../../src/SerialPortInfoUnixBase.cpp ../../src/SerialPort.cpp ../../src/SerialPortBase.cpp ../../src/SerialPortUnixAsyncBase.cpp ../../src/SerialPortUnixBase.cpp -lpthread -I../../include -o CSerialPortDemoNoGui

examples/CommQT/mainwindow.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ void MainWindow::on_pushButtonOpen_clicked()
105105
ui->comboBoxBaudrate->currentText().toInt(),
106106
itas109::Parity(ui->comboBoxParity->currentIndex()),
107107
itas109::DataBits(ui->comboBoxDataBit->currentText().toInt()),
108-
itas109::StopBits(ui->comboBoxStopBit->currentIndex()));
108+
itas109::StopBits(ui->comboBoxStopBit->currentText().toInt()));
109109

110110
m_SerialPort.setReadIntervalTimeout(ui->lineEditReadIntervalTimeoutMS->text().toInt());
111111

examples/CommQT/mainwindow.ui

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,6 @@
392392
<string>1</string>
393393
</property>
394394
</item>
395-
<item>
396-
<property name="text">
397-
<string>1.5</string>
398-
</property>
399-
</item>
400395
<item>
401396
<property name="text">
402397
<string>2</string>

include/CSerialPort/SerialPortUnixBase.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323

2424
#include "SerialPortAsyncBase.h"
2525

26+
namespace itas109
27+
{
28+
class ILockFile;
29+
}
30+
2631
// Serial Programming Guide for POSIX Operating Systems
2732
// https://www.msweet.org/serial/serial.html
2833

@@ -195,6 +200,7 @@ class CSerialPortUnixBase : public CSerialPortAsyncBase
195200
int waitCommEventNative() override final;
196201

197202
private:
198-
int pipefd[2]; ///< pipe file descriptor for wake up read thread 用于唤醒读取线程的管道文件描述符
203+
int m_pipefd[2]; ///< pipe file descriptor for wake up read thread 用于唤醒读取线程的管道文件描述符
204+
std::unique_ptr<itas109::ILockFile> m_serialPortFileLock; ///< lock file for serial port 串口锁文件
199205
};
200206
#endif //__CSERIALPORT_UNIX_BASE_H__

include/CSerialPort/ilockfile.hpp

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/**
2+
* @file ilockfile.hpp
3+
* @author itas109 (itas109@qq.com) \n\n
4+
* Blog : https://blog.csdn.net/itas109 \n
5+
* Github : https://github.com/itas109 \n
6+
* Gitee : https://gitee.com/itas109 \n
7+
* QQ Group : 129518033
8+
* @brief lightweight cross-platform lock file library 轻量级跨平台锁文件类
9+
*/
10+
#ifndef __I_LOCKFILE_HPP__
11+
#define __I_LOCKFILE_HPP__
12+
13+
#pragma once
14+
15+
#include <stdio.h>
16+
#include <string.h>
17+
#include <errno.h>
18+
#include <string> // std::atoi
19+
20+
#if defined(_WIN32)
21+
#define LOCKFILE_UNSUPPORTED
22+
#else
23+
#include <unistd.h>
24+
#include <fcntl.h>
25+
#include <sys/stat.h>
26+
#include <signal.h>
27+
#endif
28+
29+
namespace itas109
30+
{
31+
class ILockFile
32+
{
33+
public:
34+
ILockFile()
35+
: m_fd(-1)
36+
{
37+
m_path[0] = '\0';
38+
}
39+
40+
~ILockFile()
41+
{
42+
unlock();
43+
}
44+
45+
bool lock(const char *deviceName)
46+
{
47+
#ifdef LOCKFILE_UNSUPPORTED
48+
(void)deviceName;
49+
return false;
50+
#else
51+
if (!buildLockPath(deviceName))
52+
{
53+
return false;
54+
}
55+
56+
if (tryCreate())
57+
{
58+
return true;
59+
}
60+
61+
if (errno == EEXIST)
62+
{
63+
if (checkStaleAndRecover())
64+
{
65+
return true;
66+
}
67+
return false;
68+
}
69+
70+
if (errno == EACCES || errno == EPERM)
71+
{
72+
return false;
73+
}
74+
75+
return false;
76+
#endif
77+
}
78+
79+
void unlock()
80+
{
81+
#ifndef LOCKFILE_UNSUPPORTED
82+
if (m_fd >= 0)
83+
{
84+
close(m_fd);
85+
unlink(m_path);
86+
m_fd = -1;
87+
}
88+
#endif
89+
}
90+
91+
const char *path() const
92+
{
93+
return m_path;
94+
}
95+
96+
private:
97+
#ifndef LOCKFILE_UNSUPPORTED
98+
bool buildLockPath(const char *deviceName)
99+
{
100+
const char *base = strrchr(deviceName, '/');
101+
base = base ? base + 1 : deviceName;
102+
103+
const char *dirs[] = {
104+
#if defined(__APPLE__)
105+
"/var/spool/uucp",
106+
#elif defined(__linux__)
107+
"/run/lock",
108+
"/var/lock",
109+
"/var/spool/uucp",
110+
#else
111+
"/var/spool/lock",
112+
"/var/spool/uucp",
113+
#endif
114+
".", // current directory
115+
};
116+
117+
for (size_t i = 0; i < sizeof(dirs) / sizeof(dirs[0]); ++i)
118+
{
119+
if (access(dirs[i], W_OK) == 0)
120+
{
121+
snprintf(m_path, sizeof(m_path), "%s/LCK..%s", dirs[i], base);
122+
return true;
123+
}
124+
}
125+
return false;
126+
}
127+
128+
bool tryCreate()
129+
{
130+
m_fd = open(m_path, O_CREAT | O_EXCL | O_WRONLY, 0644);
131+
if (m_fd < 0)
132+
{
133+
return false;
134+
}
135+
136+
char buf[32];
137+
snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
138+
write(m_fd, buf, strlen(buf));
139+
return true;
140+
}
141+
142+
bool checkStaleAndRecover()
143+
{
144+
// get pid from lock file
145+
int fd = open(m_path, O_RDONLY);
146+
if (fd < 0)
147+
{
148+
return false;
149+
}
150+
151+
char buf[32] = {0};
152+
read(fd, buf, sizeof(buf) - 1);
153+
close(fd);
154+
155+
pid_t pid = (pid_t)std::atoi(buf);
156+
if (pid <= 0)
157+
{
158+
return false;
159+
}
160+
161+
// check if process is running
162+
if (kill(pid, 0) == -1 && errno == ESRCH)
163+
{
164+
// stale lock
165+
unlink(m_path);
166+
return tryCreate();
167+
}
168+
return false;
169+
}
170+
#endif
171+
172+
private:
173+
char m_path[256];
174+
int m_fd;
175+
};
176+
177+
} // namespace itas109
178+
#endif // __I_LOCKFILE_HPP__

0 commit comments

Comments
 (0)