Re: close on USB serial port hangs (read pending) on Snow Leopard
site_archiver@lists.apple.com Delivered-To: darwin-dev@lists.apple.com == The original problem: == The portable user-space synchronization solution: -- -- Terry _______________________________________________ Do not post admin requests to the list. They will be ignored. Darwin-dev mailing list (Darwin-dev@lists.apple.com) Help/Unsubscribe/Update your Subscription: http://lists.apple.com/mailman/options/darwin-dev/site_archiver%40lists.appl... 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 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. (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. This email sent to site_archiver@lists.apple.com
participants (1)
-
Terry Lambert