Author: stefan2 Date: Sun Dec 18 13:26:08 2011 New Revision: 1220392 URL: http://svn.apache.org/viewvc?rev=1220392&view=rev Log: On file_handle_cache branch: The flush operation will now only remove handles for the specified file name. Also, the file handle cache will be a FSFS session (FFD) - local object because the membuffer cache eliminates the need to share handles between requests. This also reduces the need for clever long-term memory management.
* subversion/include/private/svn_file_handle_cache.h (svn_file_handle_cache__flush): add file_name parameter (svn_file_handle_cache__get_global_cache): drop * subversion/libsvn_subr/svn_file_handle_cache.c (internal_close_file): add support for files still being used by the application (svn_file_handle_cache__flush_internal): re-implement (svn_file_handle_cache__flush): adapt pass-through function * subversion/libsvn_subr/svn_cache_config.c (svn_file_handle_cache__get_global_cache): drop * subversion/libsvn_fs_fs/fs_fs.c (sync_file_handle_cache_and_close): renamed from sync_file_handle_cache; now it is "close file and leave no cache entries for it" (svn_fs_fs__put_node_revision, write_next_ids, svn_fs_fs__set_entry, svn_fs_fs__add_change, rep_write_contents_close, svn_fs_fs__set_proplist, commit_body): adapt callers * subversion/libsvn_fs_fs/caching.c (close_unused_file_handles): drop, no longer used (init_callbacks): no specific clean-up for file handle cache necessary anymore (svn_fs_fs__initialize_caches): create a local file handle cache instance Modified: subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_cache_config.c subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c Modified: subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h?rev=1220392&r1=1220391&r2=1220392&view=diff ============================================================================== --- subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h (original) +++ subversion/branches/file-handle-cache/subversion/include/private/svn_file_handle_cache.h Sun Dec 18 13:26:08 2011 @@ -104,10 +104,13 @@ svn_error_t * svn_file_handle_cache__close(svn_file_handle_cache__handle_t *f); /** - * Close all file handles currently not held by the application. + * Close all file handles pertaining to the given FILE_NAME currently not + * held by the application and disconnect those held by the application + * from CACHE. */ svn_error_t * -svn_file_handle_cache__flush(svn_file_handle_cache_t *cache); +svn_file_handle_cache__flush(svn_file_handle_cache_t *cache, + const char *file_name); /** * Creates a new file handle cache in @a cache. Up to @a max_handles @@ -123,14 +126,6 @@ svn_file_handle_cache__create_cache(svn_ svn_boolean_t thread_safe, apr_pool_t *pool); -/** - * Access the process-global (singleton) open file handle cache. The first - * call will automatically allocate the cache using the current cache config. - * Even for file handle limit of 0, a cache object will be returned. - */ -svn_file_handle_cache_t * -svn_file_handle_cache__get_global_cache(void); - /** @} */ Modified: subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c?rev=1220392&r1=1220391&r2=1220392&view=diff ============================================================================== --- subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c (original) +++ subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/caching.c Sun Dec 18 13:26:08 2011 @@ -93,34 +93,6 @@ warn_on_cache_errors(svn_error_t *err, return SVN_NO_ERROR; } -static apr_status_t -close_unused_file_handles(void *cache_void) -{ - svn_file_handle_cache_t *cache = cache_void; - apr_status_t result = APR_SUCCESS; - - /* Close all file handles that are not in use anymore. So, as long as - * no requests to a given repository are being processed, we may change - * the file content and / or structure. However, content that has been - * cached *above* the APR layer (e.g. fulltext caches) will *not* be - * changed and may become inconsistent with the disk content. - * - * This will cause concurrent threads to perform somewhat slower because - * they might have put those handles to good use a few moments later. - * They now have to actually re-open the respective files. - */ - svn_error_t *err = svn_file_handle_cache__flush(cache); - - /* process error returns */ - if (err) - { - result = err->apr_err; - svn_error_clear(err); - } - - return result; -} - #ifdef SVN_DEBUG_CACHE_DUMP_STATS /* Baton to be used for the dump_cache_statistics() pool cleanup function, */ struct dump_cache_baton_t @@ -212,12 +184,6 @@ init_callbacks(svn_cache__t *cache, warn_on_cache_errors, fs, pool)); - - /* Schedule file handle cache cleanup after finishing the request. */ - apr_pool_cleanup_register(pool, - svn_file_handle_cache__get_global_cache(), - close_unused_file_handles, - apr_pool_cleanup_null); } return SVN_NO_ERROR; @@ -408,7 +374,11 @@ svn_fs_fs__initialize_caches(svn_fs_t *f SVN_ERR(init_callbacks(ffd->node_revision_cache, fs, no_handler, pool)); /* initialize file handle cache as configured */ - ffd->file_handle_cache = svn_file_handle_cache__get_global_cache(); + SVN_ERR(svn_file_handle_cache__create_cache + (&ffd->file_handle_cache, + svn_cache_config_get()->file_handle_count, + FALSE, + fs->pool)); return SVN_NO_ERROR; } Modified: subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c?rev=1220392&r1=1220391&r2=1220392&view=diff ============================================================================== --- subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c (original) +++ subversion/branches/file-handle-cache/subversion/libsvn_fs_fs/fs_fs.c Sun Dec 18 13:26:08 2011 @@ -611,15 +611,24 @@ with_txn_current_lock(svn_fs_t *fs, return SVN_NO_ERROR; } -/* A frequently used utility method: close all cached, idle file handles. - * Call this at the end of write transactions to ensure that successive - * reads will see the new file content. +/* A frequently used utility method: the given FILE using POOL. Also + * remove all entries for the respective file name from the file handle + * cache in FS. */ static svn_error_t * -sync_file_handle_cache(svn_fs_t *fs) +sync_file_handle_cache_and_close(svn_fs_t *fs, + apr_file_t *file, + apr_pool_t *pool) { + const char *file_name = ""; fs_fs_data_t *ffd = fs->fsap_data; - return svn_file_handle_cache__flush(ffd->file_handle_cache); + + apr_status_t apr_err = apr_file_name_get(&file_name, file); + if (apr_err) + return svn_error_wrap_apr(apr_err, _("Can't get file name")); + + SVN_ERR(svn_io_file_close(file, pool)); + return svn_file_handle_cache__flush(ffd->file_handle_cache, file_name); } /* A structure used by unlock_proto_rev() and unlock_proto_rev_body(), @@ -2541,10 +2550,8 @@ svn_fs_fs__put_node_revision(svn_fs_t *f svn_fs_fs__fs_supports_mergeinfo(fs), pool)); - SVN_ERR(svn_io_file_close(noderev_file, pool)); - /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(fs); + return sync_file_handle_cache_and_close(fs, noderev_file, pool); } @@ -5039,10 +5046,9 @@ write_next_ids(svn_fs_t *fs, SVN_ERR(svn_stream_printf(out_stream, pool, "%s %s\n", node_id, copy_id)); SVN_ERR(svn_stream_close(out_stream)); - SVN_ERR(svn_io_file_close(file, pool)); /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(fs); + return sync_file_handle_cache_and_close(fs, file, pool); } /* Find out what the next unique node-id and copy-id are for @@ -5280,10 +5286,8 @@ svn_fs_fs__set_entry(svn_fs_t *fs, strlen(name), name)); } - SVN_ERR(svn_io_file_close(file, pool)); - /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(fs); + return sync_file_handle_cache_and_close(fs, file, pool); } /* Write a single change entry, path PATH, change CHANGE, and copyfrom @@ -5384,10 +5388,8 @@ svn_fs_fs__add_change(svn_fs_t *fs, SVN_ERR(write_change_entry(file, path, change, TRUE, pool)); - SVN_ERR(svn_io_file_close(file, pool)); - /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(fs); + return sync_file_handle_cache_and_close(fs, file, pool); } /* This baton is used by the representation writing streams. It keeps @@ -5667,12 +5669,12 @@ rep_write_contents_close(void *baton) SVN_ERR(svn_fs_fs__put_node_revision(b->fs, b->noderev->id, b->noderev, FALSE, b->pool)); - SVN_ERR(svn_io_file_close(b->file, b->pool)); + SVN_ERR(sync_file_handle_cache_and_close(b->fs, b->file, b->pool)); SVN_ERR(unlock_proto_rev(b->fs, rep->txn_id, b->lockcookie, b->pool)); svn_pool_destroy(b->pool); /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(b->fs); + return SVN_NO_ERROR; } /* Store a writable stream in *CONTENTS_P that will receive all data @@ -5764,7 +5766,7 @@ svn_fs_fs__set_proplist(svn_fs_t *fs, | APR_BUFFERED, APR_OS_DEFAULT, pool)); out = svn_stream_from_aprfile2(file, TRUE, pool); SVN_ERR(svn_hash_write2(proplist, out, SVN_HASH_TERMINATOR, pool)); - SVN_ERR(svn_io_file_close(file, pool)); + SVN_ERR(sync_file_handle_cache_and_close(fs, file, pool)); /* Mark the node-rev's prop rep as mutable, if not already done. */ if (!noderev->prop_rep || !noderev->prop_rep->txn_id) @@ -5775,7 +5777,7 @@ svn_fs_fs__set_proplist(svn_fs_t *fs, } /* we wrote to the db -> sync file contents */ - return sync_file_handle_cache(fs); + return SVN_NO_ERROR; } /* Read the 'current' file for filesystem FS and store the next @@ -6394,10 +6396,9 @@ commit_body(void *baton, apr_pool_t *poo SVN_ERR(svn_io_file_write_full(proto_file, buf, strlen(buf), NULL, pool)); SVN_ERR(svn_io_file_flush_to_disk(proto_file, pool)); - SVN_ERR(svn_io_file_close(proto_file, pool)); /* we wrote to the db -> sync file contents */ - SVN_ERR(sync_file_handle_cache(cb->fs)); + SVN_ERR(sync_file_handle_cache_and_close(cb->fs, proto_file, pool)); /* We don't unlock the prototype revision file immediately to avoid a race with another caller writing to the prototype revision file Modified: subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_cache_config.c URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_cache_config.c?rev=1220392&r1=1220391&r2=1220392&view=diff ============================================================================== --- subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_cache_config.c (original) +++ subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_cache_config.c Sun Dec 18 13:26:08 2011 @@ -160,76 +160,6 @@ svn_cache__get_global_membuffer_cache(vo return cache; } -/* Access the process-global (singleton) open file handle cache. The first - * call will automatically allocate the cache using the current cache config. - * Even for file handle limit of 0, a cache object will be returned. - */ -svn_file_handle_cache_t * -svn_file_handle_cache__get_global_cache(void) -{ - static svn_file_handle_cache_t * volatile cache = NULL; - - if (!cache) - { - svn_error_t *err; - - svn_file_handle_cache_t *old_cache = NULL; - svn_file_handle_cache_t *new_cache = NULL; - - /* auto-allocate cache */ - apr_allocator_t *allocator = NULL; - apr_pool_t *pool = NULL; - - if (apr_allocator_create(&allocator)) - return NULL; - - /* APR files are relatively large objects. Make sure we return - * memory back to OS after a spike in allocation. - */ - apr_allocator_max_free_set(allocator, - SVN_ALLOCATOR_RECOMMENDED_MAX_FREE); - - /* ordinary root pool for using that allocator - */ - pool = svn_pool_create_ex(NULL, allocator); - apr_allocator_owner_set(allocator, pool); - - /* now, create the new cache object - */ - err = svn_file_handle_cache__create_cache( - &new_cache, - svn_cache_config_get()->file_handle_count, - !svn_cache_config_get()->single_threaded, - pool); - - /* Some error occured. An new and therefore empty cache is a - * a relatively small object, so memory usage is not an issue - * right now. Moreover, we rely on the cache being available - * (even if the capacity was zero). - */ - if (err) - { - /* Not much we can do here other than bail out ... - */ - SVN_ERR_MALFUNCTION_NO_RETURN(); - } - - /* Handle race condition: if we are the first to create a - * cache object, make it our global singleton. Otherwise, - * discard the new cache and keep the existing one. - * - * Cast is necessary because of APR bug: - * https://issues.apache.org/bugzilla/show_bug.cgi?id=50731 - */ - old_cache = apr_atomic_casptr((volatile void **)&cache, new_cache, NULL); - if (old_cache != NULL) - apr_pool_destroy(pool); - } - - return cache; -} - - void svn_cache_config_set(const svn_cache_config_t *settings) { Modified: subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c URL: http://svn.apache.org/viewvc/subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c?rev=1220392&r1=1220391&r2=1220392&view=diff ============================================================================== --- subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c (original) +++ subversion/branches/file-handle-cache/subversion/libsvn_subr/svn_file_handle_cache.c Sun Dec 18 13:26:08 2011 @@ -426,9 +426,16 @@ internal_file_open(cache_entry_t **resul static svn_error_t * internal_close_file(svn_file_handle_cache_t *cache, cache_entry_t *entry) { - /* any cached file handle held by the application must have either - * been returned or invalidated before, i.e. this entry must be "idle" */ - assert(! entry->open_handle); + /* If the application still used this file, disconnect it from the cache. + */ + if (entry->open_handle) + { + entry->open_handle->cache = NULL; + entry->open_handle->entry = NULL; + + entry->open_handle = NULL; + entry->file = NULL; + } /* remove entry from the index (if it is in there) and the * list of entries for the same file name @@ -459,7 +466,9 @@ internal_close_file(svn_file_handle_cach remove_from_list(&cache->used_entries, &entry->global_link); /* actually close the file handle. */ - SVN_ERR(svn_io_file_close(entry->file, entry->pool)); + if (entry->file) + SVN_ERR(svn_io_file_close(entry->file, entry->pool)); + entry->file = NULL; entry->name = NULL; svn_pool_clear(entry->pool); @@ -837,29 +846,29 @@ svn_file_handle_cache__close(svn_file_ha return SVN_NO_ERROR; } -/* Close all file handles currently not held by the application. +/* Close all cached file handles pertaining to FILE_NAME. */ static svn_error_t * -svn_file_handle_cache__flush_internal(svn_file_handle_cache_t *cache) +svn_file_handle_cache__flush_internal(svn_file_handle_cache_t *cache, + const char *file_name) { - /* close all idle file handles */ - while (cache->idle_entries.count) - SVN_ERR(close_oldest_idle(cache)); - - /* if the application does not hold any cached file handles, we can - * discard all cache structures and re-allocate them to reduce the - * memory footprint. - */ - if (!cache->used_entries.count) + cache_entry_t *next; + cache_entry_t *entry = find_first(cache, file_name); + + if (entry) { - /* release all cache data structures, in particular the ever-growing - * hash and the unused cache entries with all their sub-pools. - */ - svn_pool_clear(cache->pool); + while (get_previous_entry(&entry->sibling_link)) + entry = get_previous_entry(&entry->sibling_link); - /* re-init global structures */ - cache->first_by_name = apr_hash_make(cache->pool); - init_list(&cache->unused_entries); + for (next = get_next_entry (&entry->sibling_link); entry; entry = next) + { + next = get_next_entry (&entry->sibling_link); + + /* Handles still held by the application will simply be + * disconnected from the cache but the underlying file + * will not be closed.*/ + SVN_ERR(internal_close_file(cache, entry)); + } } return SVN_NO_ERROR; @@ -869,7 +878,8 @@ svn_file_handle_cache__flush_internal(sv * serialize accesss to the internal data. */ svn_error_t * -svn_file_handle_cache__flush(svn_file_handle_cache_t *cache) +svn_file_handle_cache__flush(svn_file_handle_cache_t *cache, + const char *file_name) { SVN_MUTEX__WITH_LOCK(cache->mutex, svn_file_handle_cache__flush_internal(cache));