00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #ifndef _PASSENGER_CHANGE_FILE_CHECKER_H_
00026 #define _PASSENGER_CHANGE_FILE_CHECKER_H_
00027
00028 #include <string>
00029
00030 #include <boost/thread.hpp>
00031 #include <errno.h>
00032
00033 #include "CachedFileStat.hpp"
00034 #include "SystemTime.h"
00035
00036 namespace Passenger {
00037
00038 using namespace std;
00039 using namespace oxt;
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 class FileChangeChecker {
00057 private:
00058 struct Entry {
00059 string filename;
00060 time_t lastMtime;
00061 time_t lastCtime;
00062
00063 Entry(const string &filename) {
00064 this->filename = filename;
00065 this->lastMtime = 0;
00066 this->lastCtime = 0;
00067 }
00068 };
00069
00070 typedef shared_ptr<Entry> EntryPtr;
00071 typedef list<EntryPtr> EntryList;
00072 typedef map<string, EntryList::iterator> EntryMap;
00073
00074 CachedFileStat cstat;
00075 mutable boost::mutex lock;
00076 unsigned int maxSize;
00077 EntryList entries;
00078 EntryMap fileToEntry;
00079
00080 public:
00081
00082
00083
00084
00085
00086 FileChangeChecker(unsigned int maxSize = 0)
00087 : cstat(maxSize)
00088 {
00089 this->maxSize = maxSize;
00090 }
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 bool changed(const string &filename, unsigned int throttleRate = 0) {
00116 boost::unique_lock<boost::mutex> l(lock);
00117 EntryMap::iterator it(fileToEntry.find(filename));
00118 EntryPtr entry;
00119 struct stat buf;
00120 bool result, newEntry = false;
00121 int ret;
00122
00123 if (it == fileToEntry.end()) {
00124
00125
00126
00127 if (maxSize != 0 && fileToEntry.size() == maxSize) {
00128 EntryList::iterator listEnd(entries.end());
00129 listEnd--;
00130 string filename((*listEnd)->filename);
00131 entries.pop_back();
00132 fileToEntry.erase(filename);
00133 }
00134
00135
00136 entry = EntryPtr(new Entry(filename));
00137 entries.push_front(entry);
00138 fileToEntry[filename] = entries.begin();
00139 newEntry = true;
00140 } else {
00141
00142 entry = *it->second;
00143
00144
00145 entries.erase(it->second);
00146 entries.push_front(entry);
00147 fileToEntry[filename] = entries.begin();
00148 }
00149
00150 ret = cstat.stat(filename, &buf, throttleRate);
00151 if (newEntry) {
00152
00153 if (ret == -1) {
00154 entry->lastMtime = 0;
00155 entry->lastCtime = 0;
00156 return false;
00157 } else {
00158 entry->lastMtime = buf.st_mtime;
00159 entry->lastCtime = buf.st_ctime;
00160 return true;
00161 }
00162 } else {
00163
00164 if (ret == -1 && errno == ENOENT) {
00165 result = entry->lastMtime != 0 || entry->lastCtime != 0;
00166 entry->lastMtime = 0;
00167 entry->lastCtime = 0;
00168 } else if (ret == -1) {
00169 result = false;
00170 } else {
00171 result = entry->lastMtime != buf.st_mtime || entry->lastCtime != buf.st_ctime;
00172 entry->lastMtime = buf.st_mtime;
00173 entry->lastCtime = buf.st_ctime;
00174 }
00175 return result;
00176 }
00177 }
00178
00179
00180
00181
00182
00183
00184 void setMaxSize(unsigned int maxSize) {
00185 boost::unique_lock<boost::mutex> l(lock);
00186 if (maxSize != 0) {
00187 int toRemove = fileToEntry.size() - maxSize;
00188 for (int i = 0; i < toRemove; i++) {
00189 string filename(entries.back()->filename);
00190 entries.pop_back();
00191 fileToEntry.erase(filename);
00192 }
00193 }
00194 this->maxSize = maxSize;
00195 cstat.setMaxSize(maxSize);
00196 }
00197
00198
00199
00200
00201 bool knows(const string &filename) const {
00202 boost::unique_lock<boost::mutex> l(lock);
00203 return fileToEntry.find(filename) != fileToEntry.end();
00204 }
00205 };
00206
00207 }
00208
00209 #endif