Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
factor cookie sync out into its own class
Summary: This will aid in subsequent refactoring. One important difference here is that we no longer need to acquire a write lock to the root; the cookie sync was abusing this to generate a serial number for a unique cookie file name. We now just use a local atomic integer for that purpose instead. As a result of that, we no longer need to record the root number in the generated cookie name, so we're back to the original cookie format from before adding root numbers, which is closer to those asserted in `tests/integration/othercookies.php`. Reviewed By: farnz Differential Revision: D4045069 fbshipit-source-id: 84bf0589cb2a4a58f571847649897c2f8ad4f070
- Loading branch information
Showing
13 changed files
with
208 additions
and
160 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
/* Copyright 2012-present Facebook, Inc. | ||
* Licensed under the Apache License, Version 2.0 */ | ||
|
||
#include "watchman.h" | ||
|
||
namespace watchman { | ||
|
||
CookieSync::CookieSync(const w_string& dir) { | ||
setCookieDir(dir); | ||
} | ||
|
||
void CookieSync::setCookieDir(const w_string& dir) { | ||
cookieDir_ = dir; | ||
|
||
char hostname[256]; | ||
gethostname(hostname, sizeof(hostname)); | ||
hostname[sizeof(hostname) - 1] = '\0'; | ||
|
||
cookiePrefix_ = w_string::printf( | ||
"%.*s%c" WATCHMAN_COOKIE_PREFIX "%s-%d-", | ||
int(cookieDir_.size()), | ||
cookieDir_.data(), | ||
WATCHMAN_DIR_SEP, | ||
hostname, | ||
int(getpid())); | ||
} | ||
|
||
bool CookieSync::syncToNow(std::chrono::milliseconds timeout) { | ||
Cookie cookie; | ||
w_stm_t file; | ||
int errcode = 0; | ||
|
||
auto cookie_lock = std::unique_lock<std::mutex>(cookie.mutex); | ||
|
||
/* generate a cookie name: cookie prefix + id */ | ||
auto path_str = w_string::printf( | ||
"%.*s%" PRIu32, cookiePrefix_.size(), cookiePrefix_.data(), serial_++); | ||
|
||
/* insert our cookie in the map */ | ||
{ | ||
auto wlock = cookies_.wlock(); | ||
auto& map = *wlock; | ||
map[path_str] = &cookie; | ||
} | ||
|
||
/* compute deadline */ | ||
auto deadline = std::chrono::system_clock::now() + timeout; | ||
|
||
/* touch the file */ | ||
file = w_stm_open( | ||
path_str.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0700); | ||
if (!file) { | ||
errcode = errno; | ||
w_log( | ||
W_LOG_ERR, | ||
"sync_to_now: creat(%s) failed: %s\n", | ||
path_str.c_str(), | ||
strerror(errcode)); | ||
goto out; | ||
} | ||
w_stm_close(file); | ||
|
||
w_log(W_LOG_DBG, "sync_to_now [%s] waiting\n", path_str.c_str()); | ||
|
||
/* timed cond wait (unlocks cookie lock, reacquires) */ | ||
if (!cookie.cond.wait_until( | ||
cookie_lock, deadline, [&] { return cookie.seen; })) { | ||
w_log( | ||
W_LOG_ERR, | ||
"sync_to_now: %s timedwait failed: %d: istimeout=%d %s\n", | ||
path_str.c_str(), | ||
errcode, | ||
errcode == ETIMEDOUT, | ||
strerror(errcode)); | ||
goto out; | ||
} | ||
w_log(W_LOG_DBG, "sync_to_now [%s] done\n", path_str.c_str()); | ||
|
||
out: | ||
cookie_lock.unlock(); | ||
|
||
// can't unlink the file until after the cookie has been observed because | ||
// we don't know which file got changed until we look in the cookie dir | ||
unlink(path_str.c_str()); | ||
|
||
{ | ||
auto map = cookies_.wlock(); | ||
map->erase(path_str); | ||
} | ||
|
||
if (!cookie.seen) { | ||
errno = errcode; | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
void CookieSync::notifyCookie(const w_string& path) const { | ||
auto map = cookies_.rlock(); | ||
auto cookie_iter = map->find(path); | ||
w_log( | ||
W_LOG_DBG, | ||
"cookie for %s? %s\n", | ||
path.c_str(), | ||
cookie_iter != map->end() ? "yes" : "no"); | ||
|
||
if (cookie_iter != map->end()) { | ||
auto cookie = cookie_iter->second; | ||
cookie->seen = true; | ||
cookie->cond.notify_one(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* Copyright 2012-present Facebook, Inc. | ||
* Licensed under the Apache License, Version 2.0 */ | ||
#pragma once | ||
#include "watchman_synchronized.h" | ||
#define WATCHMAN_COOKIE_PREFIX ".watchman-cookie-" | ||
|
||
namespace watchman { | ||
|
||
class CookieSync { | ||
public: | ||
explicit CookieSync(const w_string& dir); | ||
void setCookieDir(const w_string& dir); | ||
|
||
/* Ensure that we're synchronized with the state of the | ||
* filesystem at the current time. | ||
* We do this by touching a cookie file and waiting to | ||
* observe it via inotify. When we see it we know that | ||
* we've seen everything up to the point in time at which | ||
* we're asking questions. | ||
* Returns true if we observe the change within the requested | ||
* time, false otherwise. */ | ||
bool syncToNow(std::chrono::milliseconds timeout); | ||
|
||
/* If path is a valid cookie in the map, notify the waiter. | ||
* Returns true if the path matches the cookie prefix (not just | ||
* whether the cookie is currently valid). | ||
* Returns false if the path does not match our cookie prefix. | ||
*/ | ||
void notifyCookie(const w_string& path) const; | ||
|
||
// We need to guarantee that we never collapse a cookie notification | ||
// out of the pending list, because we absolutely must observe it coming | ||
// in via the kernel notification mechanism in order for synchronization | ||
// to be correct. | ||
// Since we don't have a w_root_t available, we can't tell what the | ||
// precise cookie prefix is for the current pending list here, so | ||
// we do a substring match. Not the most elegant thing in the world. | ||
static inline bool isPossiblyACookie(const w_string_t* path) { | ||
return w_string_contains_cstr_len( | ||
path, WATCHMAN_COOKIE_PREFIX, sizeof(WATCHMAN_COOKIE_PREFIX) - 1); | ||
} | ||
|
||
const w_string& cookiePrefix() const { | ||
return cookiePrefix_; | ||
} | ||
|
||
const w_string& cookieDir() const { | ||
return cookieDir_; | ||
} | ||
|
||
private: | ||
struct Cookie { | ||
std::condition_variable cond; | ||
std::mutex mutex; | ||
bool seen{false}; | ||
}; | ||
|
||
// path to the query cookie dir | ||
w_string cookieDir_; | ||
// valid filename prefix for cookies we create | ||
w_string cookiePrefix_; | ||
// Serial number for cookie filename | ||
std::atomic<uint32_t> serial_{0}; | ||
Synchronized<std::unordered_map<w_string, Cookie*>> cookies_; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.