public void close ( ) throws IOException { if ( tc . isEntryEnabled ( ) ) Tr . entry ( tc , "close" , new Object [ ] { this , _file } ) ; // By locking on the class rather than the object, and removing // the inner lock on the class, this seems to resolve the problems // reported in d347231 that file handles were not being released properly // synchronized(this) synchronized ( RLSAccessFile . class ) { _useCount -- ; if ( tc . isDebugEnabled ( ) ) Tr . debug ( tc , "remaining file use count" , new Integer ( _useCount ) ) ; // Check for 0 usage and close the actual file. // One needs to be aware that close() can be called both directly by the user on // file.close(), and also indirectly by the JVM. The JVM will call close() on a // filechannel.close() and also recursively call filechanel.close() and hence close() // on a file.close(). For this reason, filechannel.close() has been removed from // LogFileHandle calls - but it is still in CoordinationLock just in case the behaviour // of the JVM changes wrt lock releases. This behaviour can make interesting trace // reading because one can get the use count dropping to -1 either because of two // serial calls (filechannel.close() + file.close()) or two recursive calls (ie // file.close() calling filechannel.close() calling file.close()). // Trace an innocious exception to help debug any recursion problems. if ( tc . isDebugEnabled ( ) && ( _useCount <= 0 ) ) { Tr . debug ( tc , "call stack" , new Exception ( "Dummy traceback" ) ) ; } if ( _useCount == 0 ) { super . close ( ) ; // Outer lock is now on class, so no need to lock here (d347231) // synchronized(RLSAccessFile.class) // { _accessFiles . remove ( _file ) ; // } } } if ( tc . isEntryEnabled ( ) ) Tr . exit ( tc , "close" ) ; }
The logic in close needs to account for recursive calls . See notes below .