Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/serialport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ Napi::Value Close(const Napi::CallbackInfo& info) {
CloseBaton* baton = new CloseBaton(callback);
baton->fd = info[0].ToNumber().Int64Value();;

#ifndef WIN32
// Mark as closing before queueing worker so in-flight drain workers can exit.
markClosingFd(baton->fd);
#endif

baton->Queue();
return env.Undefined();
}
Expand Down
5 changes: 5 additions & 0 deletions src/serialport.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,9 @@ struct FlushBaton : VoidBaton {

int setup(int fd, OpenBaton *data);
int setBaudRate(ConnectionOptions *data);

#ifndef WIN32
void markClosingFd(int fd);
void unmarkClosingFd(int fd);
#endif
#endif // PACKAGES_SERIALPORT_SRC_SERIALPORT_H_
91 changes: 72 additions & 19 deletions src/serialport_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
#include "serialport.h"

#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <mutex>
#include <unordered_set>

#ifdef __APPLE__
#include <AvailabilityMacros.h>
Expand All @@ -28,7 +30,25 @@
#include "serialport_linux.h"
#endif

int ToStopBitsConstant(SerialPortStopBits stopBits);
int ToStopBitsConstant(SerialPortStopBits stopBits);
static const int DRAIN_POLL_INTERVAL_MS = 10;
static std::mutex gClosingFdsMutex;
static std::unordered_set<int> gClosingFds;

void markClosingFd(int fd) {
std::lock_guard<std::mutex> lock(gClosingFdsMutex);
gClosingFds.insert(fd);
}

void unmarkClosingFd(int fd) {
std::lock_guard<std::mutex> lock(gClosingFdsMutex);
gClosingFds.erase(fd);
}

bool isClosingFd(int fd) {
std::lock_guard<std::mutex> lock(gClosingFdsMutex);
return gClosingFds.find(fd) != gClosingFds.end();
}

int ToBaudConstant(int baudRate) {
switch (baudRate) {
Expand Down Expand Up @@ -358,13 +378,22 @@ int setBaudRate(ConnectionOptions *data) {
return 1;
}

void CloseBaton::Execute() {

if (-1 == close(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, unable to close fd %d", strerror(errno), fd);
this->SetError(errorString);
}
}
void CloseBaton::Execute() {
markClosingFd(fd);

// Avoid blocking close() on tty drivers that wait for pending TX.
int flags = fcntl(fd, F_GETFL);
if (flags != -1) {
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
tcflush(fd, TCOFLUSH);

if (-1 == close(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, unable to close fd %d", strerror(errno), fd);
this->SetError(errorString);
}
unmarkClosingFd(fd);
}

void SetBaton::Execute() {

Expand Down Expand Up @@ -472,11 +501,35 @@ void FlushBaton::Execute() {
}
}

void DrainBaton::Execute() {

if (-1 == tcdrain(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot drain", strerror(errno));
this->SetError(errorString);
return;
}
void DrainBaton::Execute() {
#if defined(__linux__) && defined(TIOCOUTQ)
while (true) {
if (isClosingFd(fd)) {
snprintf(errorString, sizeof(errorString), "Error: drain canceled by close");
this->SetError(errorString);
return;
}

int pendingBytes = 0;
if (-1 == ioctl(fd, TIOCOUTQ, &pendingBytes)) {
if (errno == ENOTTY || errno == EINVAL) {
break;
}
snprintf(errorString, sizeof(errorString), "Error: %s, cannot drain", strerror(errno));
this->SetError(errorString);
return;
}
if (pendingBytes <= 0) {
return;
}
usleep(DRAIN_POLL_INTERVAL_MS * 1000);
}
#endif
// TODO: On Unix platforms without TIOCOUTQ, drain still falls back to tcdrain().
// Add a cancellable drain strategy for those targets.
if (-1 == tcdrain(fd)) {
snprintf(errorString, sizeof(errorString), "Error: %s, cannot drain", strerror(errno));
this->SetError(errorString);
return;
}
}