source: icGREP/icgrep-devel/icgrep/toolchain/object_cache_daemon.cpp @ 6237

Last change on this file since 6237 was 6209, checked in by nmedfort, 8 months ago

Initial cache janitor daemon test

File size: 4.9 KB
Line 
1#include "object_cache_util.hpp"
2#include <thread>
3#include <unistd.h>
4#include <syslog.h>
5#include <sched.h>
6#include <time.h>
7
8#define LOWEST_PRIORITY (19)
9
10inline bool startDaemon() {
11    // are we in the parent process?
12    if (fork()) return false;
13    // make this process the session leader
14    setsid();
15    UNUSED auto dirchanged = chdir("/");
16    assert (dirchanged == 0);
17    umask(0);
18    if (fork()) exit(0);
19    close(STDIN_FILENO); //redirect to /dev/null?
20    close(STDOUT_FILENO);
21    close(STDERR_FILENO);
22    return true;
23}
24
25// white-list the potentially cached files
26inline bool isCachedFile(const fs::path & path) {
27    system::error_code ec;
28    if (BOOST_UNLIKELY(!fs::is_regular_file(path, ec))) return false;
29    const auto ext = path.extension();
30    return (ext.compare(OBJECT_FILE_EXTENSION) == 0 || ext.compare(KERNEL_FILE_EXTENSION)  == 0);
31}
32
33inline void setLowestPriority() {
34    UNUSED auto nicesetting = nice(LOWEST_PRIORITY);
35    assert (nicesetting != -1);
36}
37
38inline void writeTime() {
39}
40
41inline int runCacheCleanUp(const fs::path cachePath) noexcept {
42
43    // Iteratively delete any files in "cachePath" that haven't been touched in
44    // CACHE_ENTRY_EXPIRY_PERIOD until the process is killed or no more deletable
45    // files exist in the directory.
46
47    setLowestPriority();
48
49    if (startDaemon()) {
50
51        openlog(CACHE_JANITOR_FILE_NAME, LOG_PID | LOG_CONS, LOG_DAEMON);
52        syslog(LOG_NOTICE | LOG_USER, "starting in %s ...", cachePath.c_str());
53        // call nice again even though the priority should have been inherited
54        // from the parent process.
55        setLowestPriority();
56
57        // TODO: what if the CacheDaysLimit setting changes as this is running?
58        try {
59            bool createPidFile = true;
60            for (;;) {
61
62                // File locks are advisory locks. So if the pid file is deleted
63                // and another made while this process is asleep, it would wrongly
64                // assume that it still owns the lock. To handle this, when this
65                // daemon wakes up it releases and attempts to relock the file.
66                // If it fails to do so, it aborts. This does open a window for
67                // the ObjectCache to spawn a daemon that is immediately killed
68                // but this shouldn't be expected behaviour.
69
70                const FileLock lock(cachePath, createPidFile);
71                if (BOOST_UNLIKELY(!lock.locked())) {
72                    syslog(LOG_NOTICE | LOG_USER, "cannot lock pid file; stopping...");
73                    return -1;
74                }
75                lock.write_pid();
76
77                sched_yield();
78                auto itr = fs::directory_iterator(cachePath);
79                const auto end = fs::directory_iterator();
80                auto nextCleanUpTime = currentTime() + CACHE_ENTRY_EXPIRY_PERIOD;
81                bool isEmpty = true;
82                while (BOOST_LIKELY(itr != end)) {
83                    const auto e = itr->path();
84                    system::error_code ec;
85                    itr.increment(ec);
86                    if (BOOST_UNLIKELY(!!ec)) {
87                        syslog(LOG_EMERG | LOG_USER, "halted due to filesystem corruption.");
88                        return -3;
89                    }
90                    if (BOOST_LIKELY(isCachedFile(e))) {
91                        const auto time = fs::last_write_time(e);
92                        const auto expiryTime = time + CACHE_ENTRY_EXPIRY_PERIOD;
93                        if (BOOST_UNLIKELY(currentTime() < expiryTime)) {
94                            nextCleanUpTime = std::min(nextCleanUpTime, expiryTime);
95                            isEmpty = false;
96                        } else {
97                            fs::remove(e);
98                            syslog(LOG_INFO | LOG_USER, "removing %s", e.c_str());
99                        }
100                    }
101                    sched_yield();
102                }
103
104                // Cache directory is empty; shut the daemon down.
105                if (BOOST_UNLIKELY(isEmpty)) {
106                    syslog(LOG_NOTICE | LOG_USER, "cache directory is empty; stopping...");
107                    return 0;
108                }
109
110                // Sleep until the next cleanup round
111                char writtenTime[26];
112                ctime_r(&nextCleanUpTime, writtenTime);
113                writtenTime[24] = '\0';  // Removes the newline that is added
114                syslog(LOG_NOTICE | LOG_USER, "sleeping until %s...", writtenTime);
115                std::this_thread::sleep_until(system_clock::from_time_t(nextCleanUpTime));
116                syslog(LOG_INFO | LOG_USER, "waking...");
117                createPidFile = false;
118            }
119        } catch (std::exception & e) {
120            syslog(LOG_ERR | LOG_USER, "halted due to %s", e.what());
121            return -2;
122        }
123    }
124    return 0;
125}
126
127int main(int argc, const char * const argv[]) {
128    if (BOOST_LIKELY(argc == 2)) {
129        return runCacheCleanUp(argv[1]);
130    } else {
131        return -1;
132    }
133}
Note: See TracBrowser for help on using the repository browser.