The code you are describing has an inherent race condition - if another thread could be a in blocking read()
on a file descriptor when you close()
that file descriptor, the other thread could just as well be just about to call read()
instead.
You can't call close()
unless you know that all other threads are no longer in a position to be using that file descriptor at all.
The easiest way to handle cases like you describe is for one thread to be the 'owning' thread of each file descriptor, that is responsible for closing the file descriptor. Other threads don't directly close it - instead they mark the file descriptor as "to be closed" in some shared data structure and wake up the owning thread.
You can make it possible to wake the owning thread by having it not block in read()
but instead block in select()
or poll()
with another file descriptor - usually a pipe - in the set as well as the target file descriptor. The thread is woken by writing to the other end of that pipe.