The mistake you keep making is assuming that it must be the (a) OS kernel that is doing this.
Well, it's not a mistake. If you remember, originally I've commented on your statement
because it is perfectly possible for a file handle to get closed without the program doing it explicitly
So we have the program and kernel. If you want to separate this into script, perl and kernel, then I'm ready to adjust my statement to be "perl won't close handle unless the script allows it". If you let the handle to go out of scope, then it means that you want it to be closed. If you didn't want it to be closed, then it is your mistake to let it go out of scope.
Well, it all becomes philosophical. I don't really care was it a bug in IO::Select or not. My main point was, the developer has full control over the life span of a socket. And if the developer looses some socket, then it is HIS error. Neither perl, nor kernel steal sockets from developer. If such thing happens, then it is a bug and it gets fixed as soon as it is discovered. But IO::Select has nothing to do with those bugs. So it was quite working and usable even when it was expecting that the socket is closed after it is removed from IO::Select. They just needed to put it into documentation. Nothing else.
And yes. If you get sockets messed up in threads, then it is either your error, or the error of threads support in perl. If it is in perl, then one should file bug report and hope for fix :)