DirTree.cc 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #include "DirTree.hh"
  2. #include <inttypes.h>
  3. // "Meyer's singleton", construction is ordered by use, likewise (reverse) for destruction.
  4. // https://stackoverflow.com/a/17713799
  5. // https://laristra.github.io/flecsi/src/developer-guide/patterns/meyers_singleton.html
  6. static std::mutex& mDirCacheMutex() {
  7. static std::mutex mutex;
  8. return mutex;
  9. }
  10. static std::unordered_map<std::string, std::weak_ptr<DirTree>>& dirTreeCache() {
  11. static std::unordered_map<std::string, std::weak_ptr<DirTree>> cache;
  12. return cache;
  13. }
  14. struct DirTreeDeleter {
  15. void operator()(DirTree *tree) {
  16. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  17. std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
  18. cache.erase(tree->root);
  19. delete tree;
  20. // Free up memory.
  21. if (cache.size() == 0) {
  22. cache.rehash(0);
  23. }
  24. }
  25. };
  26. std::shared_ptr<DirTree> DirTree::getCached(std::string root) {
  27. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  28. std::unordered_map<std::string, std::weak_ptr<DirTree>> &cache = dirTreeCache();
  29. auto found = cache.find(root);
  30. std::shared_ptr<DirTree> tree;
  31. // Use cached tree, or create an empty one.
  32. if (found != cache.end()) {
  33. tree = found->second.lock();
  34. } else {
  35. tree = std::shared_ptr<DirTree>(new DirTree(root), DirTreeDeleter());
  36. cache.emplace(root, tree);
  37. }
  38. return tree;
  39. }
  40. DirTree::DirTree(std::string root, FILE *f) : root(root), isComplete(true) {
  41. size_t size;
  42. if (fscanf(f, "%zu", &size)) {
  43. for (size_t i = 0; i < size; i++) {
  44. DirEntry entry(f);
  45. entries.emplace(entry.path, entry);
  46. }
  47. }
  48. }
  49. // Internal find method that has no lock
  50. DirEntry *DirTree::_find(std::string path) {
  51. auto found = entries.find(path);
  52. if (found == entries.end()) {
  53. return NULL;
  54. }
  55. return &found->second;
  56. }
  57. DirEntry *DirTree::add(std::string path, uint64_t mtime, bool isDir) {
  58. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  59. DirEntry entry(path, mtime, isDir);
  60. auto it = entries.emplace(entry.path, entry);
  61. return &it.first->second;
  62. }
  63. DirEntry *DirTree::find(std::string path) {
  64. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  65. return _find(path);
  66. }
  67. DirEntry *DirTree::update(std::string path, uint64_t mtime) {
  68. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  69. DirEntry *found = _find(path);
  70. if (found) {
  71. found->mtime = mtime;
  72. }
  73. return found;
  74. }
  75. void DirTree::remove(std::string path) {
  76. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  77. DirEntry *found = _find(path);
  78. // Remove all sub-entries if this is a directory
  79. if (found && found->isDir) {
  80. std::string pathStart = path + DIR_SEP;
  81. for (auto it = entries.begin(); it != entries.end();) {
  82. if (it->first.rfind(pathStart, 0) == 0) {
  83. it = entries.erase(it);
  84. } else {
  85. it++;
  86. }
  87. }
  88. }
  89. entries.erase(path);
  90. }
  91. void DirTree::write(FILE *f) {
  92. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  93. fprintf(f, "%zu\n", entries.size());
  94. for (auto it = entries.begin(); it != entries.end(); it++) {
  95. it->second.write(f);
  96. }
  97. }
  98. void DirTree::getChanges(DirTree *snapshot, EventList &events) {
  99. std::lock_guard<std::mutex> lock(mDirCacheMutex());
  100. std::lock_guard<std::mutex> snapshotLock(snapshot->mMutex);
  101. for (auto it = entries.begin(); it != entries.end(); it++) {
  102. auto found = snapshot->entries.find(it->first);
  103. if (found == snapshot->entries.end()) {
  104. events.create(it->second.path);
  105. } else if (found->second.mtime != it->second.mtime && !found->second.isDir && !it->second.isDir) {
  106. events.update(it->second.path);
  107. }
  108. }
  109. for (auto it = snapshot->entries.begin(); it != snapshot->entries.end(); it++) {
  110. size_t count = entries.count(it->first);
  111. if (count == 0) {
  112. events.remove(it->second.path);
  113. }
  114. }
  115. }
  116. DirEntry::DirEntry(std::string p, uint64_t t, bool d) {
  117. path = p;
  118. mtime = t;
  119. isDir = d;
  120. state = NULL;
  121. }
  122. DirEntry::DirEntry(FILE *f) {
  123. size_t size;
  124. if (fscanf(f, "%zu", &size)) {
  125. path.resize(size);
  126. if (fread(&path[0], sizeof(char), size, f)) {
  127. int d = 0;
  128. fscanf(f, "%" PRIu64 " %d\n", &mtime, &d);
  129. isDir = d == 1;
  130. }
  131. }
  132. }
  133. void DirEntry::write(FILE *f) const {
  134. fprintf(f, "%zu%s%" PRIu64 " %d\n", path.size(), path.c_str(), mtime, isDir);
  135. }