Re: close on USB serial port hangs (read pending) on Snow Leopard
Re: close on USB serial port hangs (read pending) on Snow Leopard
- Subject: Re: close on USB serial port hangs (read pending) on Snow Leopard
- From: Terry Lambert <email@hidden>
- Date: Thu, 18 Feb 2010 20:30:13 -0800
On Feb 18, 2010, at 7:27 PM, Fritz Anderson wrote:
On 18 Feb 2010, at 8:39 PM, Keun-young Park wrote:
Maybe closing from different thread is causing problem. You can use
select and use other mechanism like socket to send message from
main thread to the read thread to send close request. When close
request comes, the read thread can close the serial port and exit.
I'm willing to believe this. But a question: If the read thread is
blocked on the read(), how does it receive the close request? (Or is
this another possible use for select()?) It's late where I am, so
I'm a bit slow.
— F
== The original problem:
Closing a descriptor out from under a read or write does not cancel
the outstanding I/O. The close thread is blocking waiting for the I/O
operations in progress to drain out prior to destroying the resources
which are potentially in use by those I/Os. For example, if you are
closing an fd on a serial port on one thread, not caring that your
read posted by another thread is waking up at the same time as a
result of incoming data simultaneous with the close.
This occasionally resulted in a thread with an outstanding I/O having
the I/O complete and write back to a deallocated memory region. This
was bad.
Note that we are aware that some operating systems will attempt to
cancel the read, and succeed with the drivers they themselves supply.
However, not all possible synchronization primitives which might be
used in third party drivers record their wait channels in the device
driver, allowing the driver to perform cancellation of the outstanding
blocking operations. This is an architectural difference, and,
frankly, not a standards mandated behaviour in either direction. All
platforms suffer from the possibility of a third party driver using a
non-OS supplied synchronization primitive. Code that assumes
cancellation-by-close is not portable. Upper-level kernel code that
assumes this is inherently dangerous, if you load third party drivers.
== The portable user-space synchronization solution:
(A)
This is a problem which needs to be addressed in the user space code.
Ideally, from a kernel's perspective, all I/O operations to a given
descriptor would be marshalled to a single thread. This is sometimes
inconvenient if you are porting code rather than designing it initially.
Without that, you would instead record the state of the thread
blocking on the descriptor in a connection state object, and rather
than closing the descriptor, you could close the connection object.
The connection close method would first change the state of the
connection object to prevent new I/Os by other thread being started,
then use pthread_kill() on the list of threads to send explicit
signals to abort the operations in progress in the kernel, and once
they had drained out, perform the actual close on the underlying fd.
(B)
Keun-young Park's suggestion could be interpreted to say that you
should only issue reads when you know the read will immediately return
rather than blocking in the kernel. This is also a viable design
pattern, but somewhat more complicated to achieve. Here's how it can
done:
The canonical UNIX way to do this is to read with a large buffer, but
have previous used a tcsetattr() to ensure that the device is in raw
mode to avoid waiting for a newline, and that vmin is set to 1 byte,
which requires at least one byte, but can read up to your buffers size
worth of bytes. The issue is that select() only reports data
available, not how /much/ data is available, but you want to issue
large reads to reduce overhead, and still return with the data which
*is* available, if there is less than that.
By using a socket, pipe, or other IPC mechanism using a file
descriptor as the "control channel" for the read thread, you could
wake it up by writing data to the other end (or if you didn't, it
would wake up when there was data to be read from the device).
(C)
Another non-synchronizing approach would be to use a select with a
timeout, or set a vtime as well as a vmin on the descriptor. You
would still be polling, but you could do it at much longer intervals,
and escape the performance issues. Waking up once every 10 seconds
(or whatever is a tolerable wait for a close operation) is going
better than buzz-looping, no matter how you look at it.
--
My personal suggestion would be to provide a connection state object
(struct, or whatever, depending on what language you are programming
in). Note that this is the same approach used by stdio
implementations to deal with buffer serialization for the stdio
buffers: each FILE * object has a lock which it uses in the threads
version of the library routine in order to synchronize access to both
the buffer and to the underlying fd, when refilling or flushing the
buffer.
-- Terry _______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden