golang-evdev
golang-evdev copied to clipboard
Concurrency support: detect closed file descriptors
In order to cancel an ongoing read, I assumed is was sufficient to close the underlying file-descriptor by calling dev.File.Close()
- but the ReadOne
function (and I suppose the normal Read
also) hangs until a new message arrives on the input device (tested with evemu).
I don't really see any other way to abort a read in progress... I've looked briefly into the code but couldn't immediately see what would be wrong, it just seems to use normal Go i/o internally?
Tested with go version go1.11.2 linux/amd64
with evemu.
@bartmeuris Did you resolve this problem? I've been stuck on this problem for days. @gvalkov @kovetskiy Please give me a hand
Hey there, as far as I understand, go's os.File Close() interrupts Read().
it just seems to use normal Go i/o internally
Yes, there is just plain os.Open() called on the specified device
@Sky-Li could you elaborate on what the problem you are facing exactly? (maybe some test case?)
Hi @kovetskiy , I tried many functions to interrupts Read()
, such as SetReadDeadline()
, Close()
, but they have no effect.
Cause the code relies on the input device, so I don't know how to provide the test case on go playground.
Here is the example, work()
will keep hanging after calling the function Stop()
, until I feed the barcode scanner with some codes.
func (br *BarcodeReader) work() {
defer func() {
if r := recover(); r != nil {
_ = br.log.Errorf("recover in BarcodeReader work: %v", r)
}
}()
for {
select {
case <-br.finishChan:
break
default:
br.log.Debugf("Before br read.")
events := br.read()
br.log.Debugf("After br read.")
continue
}
break
}
close(br.finishChan)
br.log.Debugf("the code scanner stop working.")
}
func (br *BarcodeReader) Stop() {
br.finishChan <- true
if br.scanner != nil {
br.log.Debugf("br.scanner: ", *br.scanner)
if err := br.scanner.Release(); err != nil {
br.err = err
_ = br.log.Warnf("BarcodeReader cannot be release: %v", err)
return
}
br.log.Debugf("After br.scanner.Release()")
if err := br.scanner.File.SetReadDeadline(time.Now()); err != nil {
br.err = err
_ = br.log.Errorf("BarcodeReader cannot set read deadline: %v", err)
return
}
br.log.Debugf("After br.scanner.File.SetReadDeadline()")
if err := br.scanner.File.Close(); err != nil {
br.err = err
_ = br.log.Errorf("BarcodeReader cannot close file: %v", err)
return
}
br.log.Debugf("After br.scanner.File.Close()")
br.scanner = nil
}
}
I see, it doesn't work for me either. I also tried syscall.Close(int(device.File.Fd()))
which did not help too.
Also, from the man 2 close:
Multithreaded processes and close()
It is probably unwise to close file descriptors while they may be in
use by system calls in other threads in the same process. Since a
file descriptor may be reused, there are some obscure race conditions
that may cause unintended side effects.
Furthermore, consider the following scenario where two threads are
performing operations on the same file descriptor:
1. One thread is blocked in an I/O system call on the file
descriptor. For example, it is trying to write(2) to a pipe that
is already full, or trying to read(2) from a stream socket which
currently has no available data.
2. Another thread closes the file descriptor.
The behavior in this situation varies across systems. On some
systems, when the file descriptor is closed, the blocking system call
returns immediately with an error.
On Linux (and possibly some other systems), the behavior is
different. the blocking I/O system call holds a reference to the
underlying open file description, and this reference keeps the
description open until the I/O system call completes. (See open(2)
for a discussion of open file descriptions.) Thus, the blocking
system call in the first thread may successfully complete after the
close() in the second thread.
But if you really want to get rid of hanging read, I could only recommend you trying to patch the package a bit and use epoll (syscall.EpollCtl) instead of device.File.Read as the package does.
I haven't tried epoll before, I will consider and try to play with it. Anyway, thank you for the explanation and the suggestion @kovetskiy.