1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
/* Copyright 2012-present Facebook, Inc.
* Licensed under the Apache License, Version 2.0 */
#include "watchman.h"
#include "watchman_synchronized.h"
using watchman::FileDescriptor;
/** The state saving thread is responsible for writing out the
* persistent information about the users watches.
* It runs in its own thread so that we avoid the possibility
* of self deadlock if various threads were to immediately
* save the state when things are changing.
*
* This uses a simple condition variable to wait for and be
* notified of state changes.
*/
namespace {
struct state {
bool needsSave{false};
};
watchman::Synchronized<state, std::mutex> saveState;
std::condition_variable stateCond;
std::thread state_saver_thread;
}
static bool do_state_save(void);
static void state_saver() {
bool do_save;
w_set_thread_name("statesaver");
while (!w_is_stopping()) {
{
auto state = saveState.wlock();
if (!state->needsSave) {
stateCond.wait(state.getUniqueLock());
}
do_save = state->needsSave;
state->needsSave = false;
}
if (do_save) {
do_state_save();
}
}
}
void w_state_shutdown(void) {
if (dont_save_state) {
return;
}
stateCond.notify_one();
state_saver_thread.join();
}
bool w_state_load(void)
{
json_error_t err;
if (dont_save_state) {
return true;
}
state_saver_thread = std::thread(state_saver);
auto state = json_load_file(watchman_state_file, 0, &err);
if (!state) {
w_log(W_LOG_ERR, "failed to parse json from %s: %s\n",
watchman_state_file,
err.text);
return false;
}
if (!w_root_load_state(state)) {
return false;
}
return true;
}
#if defined(HAVE_MKOSTEMP) && defined(sun)
// Not guaranteed to be defined in stdlib.h
extern int mkostemp(char *, int);
#endif
std::unique_ptr<watchman_stream> w_mkstemp(char* templ) {
#if defined(_WIN32)
char *name = _mktemp(templ);
if (!name) {
return nullptr;
}
// Most annoying aspect of windows is the latency around
// file handle exclusivity. We could avoid this dumb loop
// by implementing our own mkostemp, but this is the most
// expedient option for the moment.
for (size_t attempts = 0; attempts < 10; ++attempts) {
auto stm = w_stm_open(name, O_RDWR | O_CLOEXEC | O_CREAT | O_TRUNC, 0600);
if (stm) {
return stm;
}
if (errno == EACCES) {
/* sleep override */ usleep(2000);
continue;
}
return nullptr;
}
return nullptr;
#else
FileDescriptor fd;
# ifdef HAVE_MKOSTEMP
fd = FileDescriptor(mkostemp(templ, O_CLOEXEC));
# else
fd = FileDescriptor(mkstemp(templ));
# endif
if (!fd) {
return nullptr;
}
fd.setCloExec();
return w_stm_fdopen(std::move(fd));
#endif
}
static bool do_state_save(void) {
w_jbuffer_t buffer;
auto state = json_object();
auto file =
w_stm_open(watchman_state_file, O_WRONLY | O_TRUNC | O_CREAT, 0600);
if (!file) {
w_log(
W_LOG_ERR,
"save_state: unable to open %s for write: %s\n",
watchman_state_file,
strerror(errno));
return false;
}
state.set("version", typed_string_to_json(PACKAGE_VERSION, W_STRING_UNICODE));
/* now ask the different subsystems to fill out the state */
if (!w_root_save_state(state)) {
return false;
}
/* we've prepared what we're going to save, so write it out */
buffer.jsonEncodeToStream(state, file.get(), JSON_INDENT(4));
return true;
}
/** Arranges for the state to be saved.
* Does not immediately save the state. */
void w_state_save(void) {
if (dont_save_state) {
return;
}
saveState.wlock()->needsSave = true;
stateCond.notify_one();
}
/* vim:ts=2:sw=2:et:
*/
|