mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Implemented LRU cache for autoloading.
This commit is contained in:
parent
8e56763c98
commit
2f1cac604d
7 changed files with 104 additions and 85 deletions
|
@ -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) {
|
||||
|
|
14
autoload.h
14
autoload.h
|
@ -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) { }
|
||||
};
|
||||
|
||||
|
||||
|
|
39
complete.cpp
39
complete.cpp
|
@ -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 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
60
function.cpp
60
function.cpp
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 )
|
||||
|
|
27
parse_util.h
27
parse_util.h
|
@ -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 );
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue