Further investigation showed that had happened more than once, sometimes with big blocks of memory left attached to the session.
I have now exterminated those cases.
I also did a massive cut-and-paste operation to insert code that would throw an exception every time that an attempt to assert a transient lock failed. (Normally, it should wait a short period of time, the other lock should clear, and the operation should complete.) This was frustrating, but in the defense of the folks who wrote the code that wasn't throwing the exception, I hadn't added the quick-and-easy method of throwing the exception with all of the error information in it when they first started using the locks, so...
(Now, it's just call the constructor for the exception and pass in the lock object so that it can load up from the failure info. One line, nice and easy does it. This is because I am lazy and don't want to write this code more than once. :) )
All of this fell into the category of "more fun than human programmers should be allowed to have".