aboutsummaryrefslogtreecommitdiffhomepage
path: root/autoload.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-01-25 00:36:55 -0800
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2012-01-25 00:36:55 -0800
commite94e1cc72fa6eb95549493788e72219922785733 (patch)
tree1cac0e597ba57b933f08dd4298125268f14d85b5 /autoload.cpp
parent4dfe36feb16923b454ea2e44cc512adaff879127 (diff)
New file autoload.h that will ultimately handle autoloading completions and functions
Diffstat (limited to 'autoload.cpp')
-rw-r--r--autoload.cpp164
1 files changed, 164 insertions, 0 deletions
diff --git a/autoload.cpp b/autoload.cpp
new file mode 100644
index 00000000..1f47edbd
--- /dev/null
+++ b/autoload.cpp
@@ -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;
+}