Implemented LRU cache for autoloading.

This commit is contained in:
ridiculousfish 2012-01-25 18:40:08 -08:00
parent 8e56763c98
commit 2f1cac604d
7 changed files with 104 additions and 85 deletions

View file

@ -172,6 +172,8 @@ lru_cache_impl_t::lru_cache_impl_t() : node_count(0), mouth(L"") {
mouth.prev = mouth.next = &mouth;
}
void lru_cache_impl_t::node_was_evicted(lru_node_t *node) { }
void lru_cache_impl_t::evict_node(lru_node_t *condemned_node) {
/* We should never evict the mouth */
assert(condemned_node != NULL && condemned_node != &mouth);
@ -184,8 +186,8 @@ void lru_cache_impl_t::evict_node(lru_node_t *condemned_node) {
node_set.erase(condemned_node);
node_count--;
/* Tell it */
condemned_node->evicted();
/* Tell ourselves */
this->node_was_evicted(condemned_node);
}
void lru_cache_impl_t::evict_last_node(void) {

View file

@ -84,13 +84,10 @@ public:
/** Constructor */
lru_node_t(const wcstring &keyVar) : prev(NULL), next(NULL), key(keyVar) { }
bool operator<(const lru_node_t &other) const { return key < other.key; }
/** Callback when the node is evicted from an LRU cache */
virtual void evicted(void) { }
};
class lru_cache_impl_t {
private:
private:
void promote_node(lru_node_t *);
void evict_node(lru_node_t *node);
void evict_last_node(void);
@ -105,7 +102,11 @@ class lru_cache_impl_t {
/** Head of the linked list */
lru_node_t mouth;
public:
protected:
/** Overridable callback for when a node is evicted */
virtual void node_was_evicted(lru_node_t *node);
public:
/** Constructor */
lru_cache_impl_t();
@ -128,6 +129,9 @@ class lru_cache_t : public lru_cache_impl_t {
public:
node_type_t *get_node(const wcstring &key) { return static_cast<node_type_t>(lru_cache_impl_t::get_node(key)); }
bool add_node(node_type_t *node) { return lru_cache_impl_t::add_node(node); }
protected:
virtual void node_was_evicted(lru_node_t *node) { this->node_was_evicted(static_cast<node_type_t *>(node)); }
virtual void node_was_evicted(node_type_t *node) { }
};

View file

@ -181,7 +181,27 @@ static complete_entry_t *first_entry=0;
*/
static hash_table_t *condition_cache=0;
static autoload_t completion_autoloader(L"fish_complete_path", internal_completion_scripts, sizeof internal_completion_scripts / sizeof *internal_completion_scripts );
/* Autoloader for completions */
class completion_autoload_t : public autoload_t {
public:
completion_autoload_t();
virtual void command_removed(const wcstring &cmd);
};
static completion_autoload_t completion_autoloader;
/** Constructor */
completion_autoload_t::completion_autoload_t() : autoload_t(L"fish_complete_path",
internal_completion_scripts,
sizeof internal_completion_scripts / sizeof *internal_completion_scripts)
{
}
/** Callback when an autoloaded completion is removed */
void completion_autoload_t::command_removed(const wcstring &cmd) {
complete_remove( cmd.c_str(), COMMAND, 0, 0 );
}
static void complete_free_entry( complete_entry_t *c );
@ -229,7 +249,7 @@ static void complete_destroy()
}
first_entry = 0;
completion_autoloader.reset( 0 );
completion_autoloader.reset();
}
@ -1346,23 +1366,10 @@ static int short_ok( const wchar_t *arg,
return 1;
}
/**
This is an event handler triggered when the definition of a
specified function is changed. It automatcally removes the
specified function.
This is to make sure that the function disappears if the file is
removed or if it contains a syntax error.
*/
static void complete_load_handler( const wchar_t *cmd )
{
complete_remove( cmd, COMMAND, 0, 0 );
}
void complete_load( const wchar_t *name, int reload )
{
CHECK( name, );
completion_autoloader.load( name, &complete_load_handler, reload );
completion_autoloader.load( name, reload );
}
/**

View file

@ -40,9 +40,6 @@
#include "halloc_util.h"
#include "builtin_scripts.h"
/* Autoloader for functions */
static autoload_t function_autoloader(L"fish_function_path", internal_function_scripts, sizeof internal_function_scripts / sizeof *internal_function_scripts);
/**
Table containing all functions
*/
@ -52,6 +49,30 @@ static function_map_t loaded_functions;
/* Lock for functions */
static pthread_mutex_t functions_lock;
/* Autoloader for functions */
class function_autoload_t : public autoload_t {
public:
function_autoload_t();
virtual void command_removed(const wcstring &cmd);
};
static function_autoload_t function_autoloader;
/** Constructor */
function_autoload_t::function_autoload_t() : autoload_t(L"fish_function_path",
internal_function_scripts,
sizeof internal_function_scripts / sizeof *internal_function_scripts)
{
}
/** Removes a function from our internal table, returning true if it was found and false if not */
static bool function_remove_ignore_autoload(const wcstring &name);
/** Callback when an autoloaded function is removed */
void function_autoload_t::command_removed(const wcstring &cmd) {
function_remove(cmd);
}
/* Helper macro for vomiting */
#define VOMIT_ON_FAILURE(a) do { if (0 != (a)) { int err = errno; fprintf(stderr, "%s failed on line %d in file %s: %d (%s)\n", #a, __LINE__, __FILE__, err, strerror(err)); abort(); }} while (0)
@ -78,7 +99,7 @@ static int load( const wchar_t *name )
}
is_autoload = 1;
res = function_autoloader.load( name, &function_remove, 1 );
res = function_autoloader.load( name, true );
is_autoload = was_autoload;
return res;
}
@ -199,32 +220,25 @@ int function_exists_no_autoload( const wchar_t *cmd )
return function_exists_internal( cmd, false );
}
void function_remove( const wchar_t *name )
static bool function_remove_ignore_autoload(const wcstring &name)
{
event_t ev;
CHECK( name, );
scoped_lock lock(functions_lock);
bool erased = (loaded_functions.erase(name) > 0);
if( !erased ) {
return;
if (erased) {
event_t ev;
ev.type=EVENT_ANY;
ev.function_name=name.c_str();
event_remove( &ev );
}
return erased;
ev.type=EVENT_ANY;
ev.function_name=name;
event_remove( &ev );
}
/*
Notify the autoloader that the specified function is erased, but
only if this call to fish_remove is not made by the autoloader
itself.
*/
if( !is_autoload )
{
function_autoloader.unload( name, 0 );
}
void function_remove( const wcstring &name )
{
if (function_remove_ignore_autoload(name))
function_autoloader.unload( name );
}
shared_ptr<function_info_t> function_get(const wcstring &name)

View file

@ -106,7 +106,7 @@ void function_add( function_data_t *data, const parser_t &parser );
/**
Remove the function with the specified name.
*/
void function_remove( const wchar_t *name );
void function_remove( const wcstring &name );
/**
Gets a function by name.

View file

@ -601,13 +601,6 @@ void parse_util_token_extent( const wchar_t *buff,
}
autoload_function_t::~autoload_function_t() { }
void autoload_function_t::evicted(void) {
delete this;
}
autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t * const scripts, size_t script_count) :
env_var_name(env_var_name_var),
builtin_scripts(scripts),
@ -615,21 +608,24 @@ autoload_t::autoload_t(const wcstring &env_var_name_var, const builtin_script_t
{
}
void autoload_t::reset( void (*on_load)(const wchar_t *cmd) )
{
function_cache.evict_all_nodes();
/* TODO: Must call on_load on all non-placeholders */
void autoload_t::node_was_evicted(autoload_function_t *node) {
// Tell ourselves that the command was removed, unless it was a placeholder
if (! node->is_placeholder)
this->command_removed(node->key);
delete node;
}
int autoload_t::unload( const wchar_t *cmd, void (*on_load)(const wchar_t *cmd) )
void autoload_t::reset( )
{
CHECK( cmd, 0 );
return function_cache.evict_node(cmd);
this->evict_all_nodes();
}
int autoload_t::load( const wcstring &cmd,
void (*on_load)(const wchar_t *cmd),
int reload )
int autoload_t::unload( const wcstring &cmd )
{
return this->evict_node(cmd);
}
int autoload_t::load( const wcstring &cmd, bool reload )
{
int res;
int c, c2;
@ -653,7 +649,7 @@ int autoload_t::load( const wcstring &cmd,
if( path_var != this->path )
{
this->path = path_var;
reset( on_load);
this->reset();
}
/**
@ -680,7 +676,7 @@ int autoload_t::load( const wcstring &cmd,
/*
Do the actual work in the internal helper function
*/
res = this->load_internal( cmd, on_load, reload, path_list );
res = this->load_internal( cmd, reload, path_list );
int erased = is_loading_set.erase(cmd);
assert(erased);
@ -708,7 +704,6 @@ static bool script_name_precedes_script_name(const builtin_script_t &script1, co
*/
int autoload_t::load_internal( const wcstring &cmd,
void (*on_load)(const wchar_t *cmd),
int reload,
const wcstring_list_t &path_list )
{
@ -772,9 +767,9 @@ int autoload_t::load_internal( const wcstring &cmd,
func = new autoload_function_t(cmd);
func->access = access;
if( on_load )
on_load(cmd.c_str());
// Remove this command because we are going to reload it
command_removed(cmd);
reloaded = 1;
}
else if( func )

View file

@ -15,9 +15,6 @@
struct autoload_function_t : public lru_node_t
{
autoload_function_t(const wcstring &key) : lru_node_t(key), is_placeholder(false) { bzero(&access, sizeof access); }
virtual ~autoload_function_t();
virtual void evicted(void);
file_access_attempt_t access; /** The last access attempt */
bool is_placeholder; /** Whether we are a placeholder that stands in for "no such function" */
};
@ -28,10 +25,8 @@ struct builtin_script_t;
/**
A class that represents a path from which we can autoload, and the autoloaded contents.
*/
class autoload_t {
class autoload_t : private lru_cache_t<autoload_function_t> {
private:
/** Access tracker */
lru_cache_t<autoload_function_t> function_cache;
/** The environment variable name */
const wcstring env_var_name;
@ -80,7 +75,14 @@ private:
return autoload_functions.size();
}
int load_internal( const wcstring &cmd, void (*on_load)(const wchar_t *cmd), int reload, const wcstring_list_t &path_list );
int load_internal( const wcstring &cmd, int reload, const wcstring_list_t &path_list );
virtual void node_was_evicted(autoload_function_t *node);
protected:
/** Overridable callback for when a command is removed */
virtual void command_removed(const wcstring &cmd) { }
public:
@ -98,17 +100,13 @@ private:
\param on_unload a callback function to run if a suitable file is found, which has not already been run. unload will also be called for old files which are unloaded.
\param reload wheter to recheck file timestamps on already loaded files
*/
int load( const wcstring &cmd,
void (*on_unload)(const wchar_t *cmd),
int reload );
int load( const wcstring &cmd, bool reload );
/**
Reset the loader for the specified path variable. This will cause
all information on loaded files in the specified directory to be
reset.
\param on_unload a callback function which will be called before (re)loading a file, may be used to unload the previous file.
*/
void reset( void (*on_unload)(const wchar_t *cmd) );
void reset();
/**
Tell the autoloader that the specified file, in the specified path,
@ -118,8 +116,7 @@ private:
\param on_unload a callback function which will be called before (re)loading a file, may be used to unload the previous file.
\return non-zero if the file was removed, zero if the file had not yet been loaded
*/
int unload( const wchar_t *cmd,
void (*on_unload)(const wchar_t *cmd) );
int unload( const wcstring &cmd );
};