New file autoload.h that will ultimately handle autoloading completions and functions

This commit is contained in:
ridiculousfish 2012-01-25 00:36:55 -08:00
parent 4dfe36feb1
commit e94e1cc72f
4 changed files with 252 additions and 9 deletions

View file

@ -110,12 +110,16 @@
D0A0856513B3ACEE0099B651 /* xdgmimeint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xdgmimeint.cpp; sourceTree = "<group>"; };
D0A0856613B3ACEE0099B651 /* xdgmimemagic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xdgmimemagic.cpp; sourceTree = "<group>"; };
D0A0856713B3ACEE0099B651 /* xdgmimeparent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = xdgmimeparent.cpp; sourceTree = "<group>"; };
D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = autoload.cpp; sourceTree = "<group>"; };
D0C6FCCB14CFA4B7004CE8AD /* autoload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = autoload.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXGroup section */
D0A084F013B3AC130099B651 = {
isa = PBXGroup;
children = (
D0C6FCCB14CFA4B7004CE8AD /* autoload.h */,
D0C6FCC914CFA4B0004CE8AD /* autoload.cpp */,
D0A0850313B3ACEE0099B651 /* builtin.h */,
D0A0853013B3ACEE0099B651 /* builtin_commandline.cpp */,
D0A0853113B3ACEE0099B651 /* builtin_complete.cpp */,

View file

@ -95,7 +95,7 @@ FISH_OBJS := function.o builtin.o complete.o env.o exec.o expand.o \
highlight.o history.o kill.o parser.o proc.o reader.o sanity.o \
tokenizer.o wildcard.o wgetopt.o wutil.o input.o output.o intern.o \
env_universal.o env_universal_common.o input_common.o event.o \
signal.o io.o parse_util.o common.o screen.o path.o \
signal.o io.o parse_util.o common.o screen.o path.o autoload.o \
parser_keywords.o iothread.o builtin_scripts.o
FISH_INDENT_OBJS := fish_indent.o print_help.o common.o \
@ -905,8 +905,9 @@ clean:
# DO NOT DELETE THIS LINE -- make depend depends on it.
autoload.o: autoload.h common.h util.h
builtin.o: config.h fallback.h util.h wutil.h builtin.h io.h function.h
builtin.o: complete.h proc.h parser.h event.h common.h reader.h env.h
builtin.o: common.h complete.h proc.h parser.h event.h reader.h env.h
builtin.o: wgetopt.h sanity.h tokenizer.h wildcard.h input_common.h input.h
builtin.o: intern.h signal.h exec.h highlight.h halloc.h halloc_util.h
builtin.o: parse_util.h parser_keywords.h expand.h path.h builtin_set.cpp
@ -921,7 +922,6 @@ builtin_complete.o: io.h common.h complete.h wgetopt.h parser.h proc.h
builtin_complete.o: event.h reader.h
builtin_jobs.o: config.h fallback.h util.h wutil.h builtin.h io.h proc.h
builtin_jobs.o: parser.h event.h common.h wgetopt.h
builtin_scripts.o: builtin_scripts.h
builtin_set.o: config.h signal.h fallback.h util.h wutil.h builtin.h io.h
builtin_set.o: env.h common.h expand.h wgetopt.h proc.h parser.h event.h
builtin_ulimit.o: config.h fallback.h util.h builtin.h io.h common.h
@ -942,8 +942,8 @@ env_universal.o: config.h signal.h fallback.h util.h common.h wutil.h
env_universal.o: env_universal_common.h env_universal.h
env_universal_common.o: config.h signal.h fallback.h util.h common.h wutil.h
env_universal_common.o: env_universal_common.h
event.o: config.h signal.h fallback.h util.h wutil.h function.h proc.h io.h
event.o: parser.h event.h common.h halloc_util.h
event.o: config.h signal.h fallback.h util.h wutil.h function.h common.h
event.o: proc.h io.h parser.h event.h halloc_util.h
exec.o: config.h signal.h fallback.h util.h common.h wutil.h proc.h io.h
exec.o: exec.h parser.h event.h builtin.h function.h env.h wildcard.h
exec.o: sanity.h expand.h halloc.h halloc_util.h parse_util.h
@ -966,8 +966,8 @@ fish_tests.o: parser.h event.h tokenizer.h output.h exec.h path.h halloc.h
fish_tests.o: halloc_util.h
fishd.o: config.h signal.h fallback.h util.h common.h wutil.h
fishd.o: env_universal_common.h halloc.h halloc_util.h path.h print_help.h
function.o: config.h signal.h wutil.h fallback.h util.h function.h proc.h
function.o: io.h parser.h event.h common.h intern.h reader.h parse_util.h
function.o: config.h signal.h wutil.h fallback.h util.h function.h common.h
function.o: proc.h io.h parser.h event.h intern.h reader.h parse_util.h
function.o: parser_keywords.h env.h expand.h halloc.h halloc_util.h
halloc.o: config.h fallback.h util.h common.h halloc.h
halloc_util.o: config.h fallback.h util.h common.h halloc.h
@ -984,7 +984,7 @@ input_common.o: config.h fallback.h util.h common.h wutil.h input_common.h
input_common.o: env_universal.h env_universal_common.h iothread.h
intern.o: config.h fallback.h util.h wutil.h common.h intern.h
io.o: config.h fallback.h util.h wutil.h exec.h proc.h io.h common.h halloc.h
iothread.o: iothread.h
iothread.o: iothread.h signal.h
key_reader.o: config.h fallback.h input_common.h
kill.o: config.h signal.h fallback.h util.h wutil.h kill.h proc.h io.h
kill.o: sanity.h common.h env.h exec.h halloc.h path.h
@ -993,7 +993,7 @@ output.o: config.h signal.h fallback.h util.h wutil.h expand.h common.h
output.o: output.h halloc_util.h highlight.h env.h
parse_util.o: config.h fallback.h util.h wutil.h common.h tokenizer.h
parse_util.o: parse_util.h expand.h intern.h exec.h proc.h io.h env.h
parse_util.o: signal.h wildcard.h halloc_util.h builtin_scripts.h
parse_util.o: signal.h wildcard.h halloc_util.h
parser.o: config.h signal.h fallback.h util.h common.h wutil.h proc.h io.h
parser.o: parser.h event.h parser_keywords.h tokenizer.h exec.h wildcard.h
parser.o: function.h builtin.h env.h expand.h reader.h sanity.h

164
autoload.cpp Normal file
View file

@ -0,0 +1,164 @@
/** \file autoload.cpp
The classes responsible for autoloading functions and completions.
*/
#include "config.h"
#include "autoload.h"
#include "wutil.h"
#include <assert.h>
const size_t kLRULimit = 256;
/** A node in our LRU map */
class file_access_node_t {
public:
file_access_node_t *prev, *next;
const wcstring path;
file_access_attempt_t attempt;
file_access_node_t(const wcstring &pathVar) : path(pathVar) { }
bool operator<(const file_access_node_t &other) const { return path < other.path; }
};
/* By default, things are stale after 60 seconds */
const time_t kFishDefaultStalenessInterval = 60;
access_tracker_t::access_tracker_t(time_t stale, int the_mode) :
node_count(0),
mouth(NULL),
stale_interval(stale),
mode(the_mode)
{
VOMIT_ON_FAILURE(pthread_mutex_init(&lock, NULL));
}
access_tracker_t::~access_tracker_t() {
VOMIT_ON_FAILURE(pthread_mutex_destroy(&lock));
}
void access_tracker_t::vacuum_one_node(void) {
/* Removes the least recently used access */
assert(mouth && mouth->prev != mouth);
/* Remove us from the linked list */
file_access_node_t *condemned_node = mouth->prev;
condemned_node->prev->next = condemned_node->next;
condemned_node->next->prev = condemned_node->prev;
/* Remove us from the set */
access_set.erase(condemned_node);
/* Deleted */
node_count--;
delete condemned_node;
}
void access_tracker_t::promote_node(file_access_node_t *node) {
/* Promotes a node to the mouth, unless we're already there... */
if (node == mouth) return;
/* First unhook us */
node->prev->next = node->next;
node->next->prev = node->prev;
/* Now become the mouth */
node->next = mouth;
node->prev = mouth->prev;
mouth = node;
}
/* Return the node referenced by the given string, or NULL */
file_access_node_t *access_tracker_t::while_locked_find_node(const wcstring &path) const {
file_access_node_t key(path);
access_set_t::iterator iter = access_set.find(&key);
return iter != access_set.end() ? *iter : NULL;
}
file_access_attempt_t access_tracker_t::attempt_access(const wcstring& path) const {
file_access_attempt_t result = {0};
struct stat statbuf;
if (wstat(path.c_str(), &statbuf)) {
result.error = errno;
} else {
result.mod_time = statbuf.st_mtime;
if (waccess(path.c_str(), this->mode)) {
result.error = errno;
} else {
result.accessible = true;
}
}
// Note that we record the last checked time after the call, on the assumption that in a slow filesystem, the lag comes before the kernel check, not after.
result.stale = false;
result.last_checked = time(NULL);
return result;
}
bool access_tracker_t::access_file_only_cached(const wcstring &path, file_access_attempt_t &attempt) {
bool result = false;
/* Lock our lock */
scoped_lock locker(lock);
/* Search for the node */
file_access_node_t *node = while_locked_find_node(path);
if (node) {
promote_node(node);
attempt = node->attempt;
attempt.stale = (time(NULL) - node->attempt.last_checked > this->stale_interval);
result = true;
}
return result;
}
file_access_attempt_t access_tracker_t::access_file(const wcstring &path) {
file_access_attempt_t result;
/* Try just using our cache */
if (access_file_only_cached(path, result) && ! result.stale) {
return result;
}
/* Really access the file. Note we are not yet locked, and don't want to be, since this may be slow. */
result = attempt_access(path);
/* Take the lock so we can insert. */
scoped_lock locker(lock);
/* Maybe we had it cached and it was stale, or maybe someone else may have put it in the cache while we were unlocked */
file_access_node_t *node = while_locked_find_node(path);
if (node != NULL) {
/* Someone else put it in. Promote and overwrite it. */
node->attempt = result;
promote_node(node);
} else {
/* We did not find this node. Add it. */
file_access_node_t *node = new file_access_node_t(path);
node->attempt = result;
/* Insert into the set */
access_set.insert(node);
/* Insert it into the linked list */
if (mouth == NULL) {
/* One element circularly linked list! */
mouth = node->next = node->prev = node;
} else {
/* Normal circularly linked list operation */
node->next = mouth;
node->prev = mouth->prev;
mouth = node;
}
/* We have one more node now */
++node_count;
/* Clean up if we're over our limit */
while (node_count > kLRULimit)
vacuum_one_node();
}
return result;
}

75
autoload.h Normal file
View file

@ -0,0 +1,75 @@
/** \file autoload.h
The classes responsible for autoloading functions and completions.
*/
#ifndef FISH_PARSE_UTIL_H
#define FISH_PARSE_UTIL_H
#include <wchar.h>
#include <map>
#include <set>
#include <list>
#include "common.h"
extern const time_t kFishDefaultStalenessInterval;
/** A class responsible for recording an attempt to access a file. */
class file_access_attempt_t {
public:
time_t mod_time; /** The modification time of the file */
time_t last_checked; /** When we last checked the file */
bool accessible; /** Whether we believe we could access this file */
bool stale; /** Whether the access attempt is stale */
int error; /** If we could not access the file, the error code */
};
class file_access_node_t;
/** A predicate to compare dereferenced pointers */
struct dereference_less_t {
template <typename ptr_t>
bool operator()(ptr_t p1, ptr_t p2) const { return *p1 < *p2; }
};
/** A class responsible for tracking accesses to files, including auto-expiration. */
class access_tracker_t {
private:
file_access_node_t * while_locked_find_node(const wcstring &str) const;
void vacuum_one_node(void);
void promote_node(file_access_node_t *);
file_access_attempt_t attempt_access(const wcstring& path) const;
unsigned int node_count;
typedef std::set<file_access_node_t *, dereference_less_t> access_set_t;
access_set_t access_set;
file_access_node_t *mouth;
/* Lock for thread safety */
pthread_mutex_t lock;
/** How long until a file access attempt is considered stale. */
const time_t stale_interval;
/** Mode for waccess calls */
const int mode;
public:
/** Constructor, that takes a staleness interval */
access_tracker_t(time_t stale, int the_mode);
/** Destructor */
~access_tracker_t();
/** Attempt to access the given file, if the last cached access is stale. Caches and returns the access attempt. */
file_access_attempt_t access_file(const wcstring &path);
/** Returns whether there is a cached access (even if stale), without touching the disk; if the result is true, return by reference that access attempt. */
bool access_file_only_cached(const wcstring &path, file_access_attempt_t &attempt);
};
#endif