Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added a history speed test

Profile driven caching of config directory
Style fixes
  • Loading branch information...
commit b9283d48b570e66650a6443bc7ebb8048f589546 1 parent 33fc5c9
ridiculousfish ridiculousfish authored
6 common.cpp
View
@@ -523,12 +523,12 @@ wcstring wsetlocale(int category, const wchar_t *locale)
*/
char *ctype = setlocale(LC_CTYPE, NULL);
bool unicode = (strstr(ctype, ".UTF") || strstr(ctype, ".utf"));
-
+
ellipsis_char = unicode ? L'\x2026' : L'$';
-
+
// U+23CE is the "return" character
omitted_newline_char = unicode ? L'\x23CE' : L'~';
-
+
if (!res)
return wcstring();
else
50 fish_tests.cpp
View
@@ -978,7 +978,8 @@ class history_tests_t
static void test_history(void);
static void test_history_merge(void);
static void test_history_formats(void);
-
+ static void test_history_speed(void);
+
static void test_history_races(void);
static void test_history_races_pound_on_history();
};
@@ -1110,16 +1111,16 @@ void history_tests_t::test_history_races_pound_on_history()
void history_tests_t::test_history_races(void)
{
say(L"Testing history race conditions");
-
+
// Ensure history is clear
history_t *hist = new history_t(L"race_test");
hist->clear();
delete hist;
-
+
// Test concurrent history writing
#define RACE_COUNT 10
pid_t children[RACE_COUNT];
-
+
for (size_t i=0; i < RACE_COUNT; i++)
{
pid_t pid = fork();
@@ -1136,21 +1137,21 @@ void history_tests_t::test_history_races(void)
children[i] = pid;
}
}
-
+
// Wait for all children
for (size_t i=0; i < RACE_COUNT; i++)
{
int stat;
waitpid(children[i], &stat, WUNTRACED);
}
-
+
// Compute the expected lines
wcstring_list_t lines[RACE_COUNT];
for (size_t i=0; i < RACE_COUNT; i++)
{
lines[i] = generate_history_lines(children[i]);
}
-
+
// Count total lines
size_t line_count = 0;
for (size_t i=0; i < RACE_COUNT; i++)
@@ -1160,7 +1161,7 @@ void history_tests_t::test_history_races(void)
// Ensure we consider the lines that have been outputted as part of our history
time_barrier();
-
+
/* Ensure that we got sane, sorted results */
hist = new history_t(L"race_test");
hist->chaos_mode = true;
@@ -1170,7 +1171,7 @@ void history_tests_t::test_history_races(void)
history_item_t item = hist->item_at_index(hist_idx);
if (item.empty())
break;
-
+
// The item must be present in one of our 'lines' arrays
// If it is present, then every item after it is assumed to be missed
size_t i;
@@ -1181,7 +1182,7 @@ void history_tests_t::test_history_races(void)
{
// Delete everything from the found location onwards
lines[i].resize(where - lines[i].begin());
-
+
// Break because we found it
break;
}
@@ -1193,7 +1194,7 @@ void history_tests_t::test_history_races(void)
}
// every write should add at least one item
assert(hist_idx >= RACE_COUNT);
-
+
//hist->clear();
delete hist;
}
@@ -1408,6 +1409,32 @@ void history_tests_t::test_history_formats(void)
}
}
+void history_tests_t::test_history_speed(void)
+{
+ say(L"Testing history speed");
+ history_t *hist = new history_t(L"speed_test");
+ wcstring item = L"History Speed Test - X";
+
+ /* Test for 10 seconds */
+ double start = timef();
+ double end = start + 4;
+ double stop = 0;
+ size_t count = 0;
+ for (;;)
+ {
+ item[item.size() - 1] = L'0' + (count % 10);
+ hist->add(item);
+ count++;
+
+ stop = timef();
+ if (stop >= end)
+ break;
+ }
+ printf("%lu items - %.2f msec per item\n", (unsigned long)count, (stop - start) * 1E6 / count);
+ hist->clear();
+ delete hist;
+}
+
/**
Main test
@@ -1448,6 +1475,7 @@ int main(int argc, char **argv)
history_tests_t::test_history_merge();
history_tests_t::test_history_races();
history_tests_t::test_history_formats();
+ //history_tests_t::test_history_speed();
say(L"Encountered %d errors in low-level tests", err_count);
70 history.cpp
View
@@ -501,7 +501,7 @@ void history_t::add(const history_item_t &item)
/* We have to add a new item */
new_items.push_back(item);
}
-
+
/* We may or may not vacuum. We try to vacuum every kVacuumFrequency items, but start the countdown at a random number so that even if the user never runs more than 25 commands, we'll eventually vacuum. If countdown_to_vacuum is -1, it means we haven't yet picked a value for the counter. */
const int kVacuumFrequency = 25;
if (countdown_to_vacuum < 0)
@@ -510,7 +510,7 @@ void history_t::add(const history_item_t &item)
/* Generate a number in the range [0, kVacuumFrequency) */
countdown_to_vacuum = rand_r(&seed) / (RAND_MAX / kVacuumFrequency + 1);
}
-
+
/* Determine if we're going to vacuum */
bool vacuum = false;
if (countdown_to_vacuum == 0)
@@ -552,7 +552,7 @@ void history_t::remove(const wcstring &str)
if (new_items[idx].str() == str)
{
new_items.erase(new_items.begin() + idx);
-
+
/* If this index is before our first_unwritten_new_item_index, then subtract one from that index so it stays pointing at the same item. If it is equal to or larger, then we have not yet writen this item, so we don't have to adjust the index. */
if (idx < first_unwritten_new_item_index)
{
@@ -922,11 +922,11 @@ static bool map_file(const wcstring &name, const char **out_map_start, size_t *o
int fd = wopen_cloexec(filename, O_RDONLY);
if (fd >= 0)
{
-
+
/* Get the file ID if requested */
if (file_id != NULL)
*file_id = history_file_identify(fd);
-
+
/* Take a read lock to guard against someone else appending. This is released when the file is closed (below). We will read the file after taking the lock, but that's not a problem, because we never modify already written data. In short, the purpose of this lock is to ensure we don't see the file size change mid-update. */
if (history_file_lock(fd, F_RDLCK))
{
@@ -1157,7 +1157,7 @@ void history_t::compact_new_items()
{
// This item was not inserted because it was already in the set, so delete the item at this index
new_items.erase(new_items.begin() + idx);
-
+
if (idx < first_unwritten_new_item_index)
{
/* Decrement first_unwritten_new_item_index if we are deleting a previously written item */
@@ -1171,7 +1171,7 @@ bool history_t::save_internal_via_rewrite()
{
/* This must be called while locked */
ASSERT_IS_LOCKED(lock);
-
+
bool ok = true;
wcstring tmp_name_template = history_filename(name, L".XXXXXX");
@@ -1247,7 +1247,7 @@ bool history_t::save_internal_via_rewrite()
}
free(narrow_str);
}
-
+
if (out_fd >= 0)
{
/* Success */
@@ -1256,7 +1256,7 @@ bool history_t::save_internal_via_rewrite()
{
/* Be block buffered. In chaos mode, choose a tiny buffer so as to magnify the effects of race conditions. Otherwise, use the default buffer */
setvbuf(out, NULL, _IOFBF, chaos_mode ? 1 : 0);
-
+
/* Write them out */
for (history_lru_cache_t::iterator iter = lru.begin(); iter != lru.end(); ++iter)
{
@@ -1267,7 +1267,7 @@ bool history_t::save_internal_via_rewrite()
break;
}
}
-
+
if (0 == fclose(out))
{
/* fclose closed out_fd, so mark it as -1 so we don't try to close it later */
@@ -1294,7 +1294,7 @@ bool history_t::save_internal_via_rewrite()
}
}
}
-
+
if (out_fd >= 0)
close(out_fd);
@@ -1303,18 +1303,20 @@ bool history_t::save_internal_via_rewrite()
/* Make sure we clear all nodes, since this doesn't happen automatically */
lru.evict_all_nodes();
}
-
+
if (ok)
{
/* We've saved everything, so we have no more unsaved items */
this->first_unwritten_new_item_index = new_items.size();
+
+ /* We deleted our deleted items */
this->deleted_items.clear();
-
+
/* Our history has been written to the file, so clear our state so we can re-reference the file. */
this->clear_file_state();
}
-
+
return ok;
}
@@ -1322,20 +1324,20 @@ bool history_t::save_internal_via_appending()
{
/* This must be called while locked */
ASSERT_IS_LOCKED(lock);
-
+
/* No deleting allowed */
assert(deleted_items.empty());
-
+
bool ok = false;
-
+
/* If the file is different (someone vacuumed it) then we need to update our mmap */
bool file_changed = false;
-
+
/* Get the path to the real history file */
wcstring history_path = history_filename(name, wcstring());
-
+
signal_block();
-
+
/* Open the file */
int out_fd = wopen_cloexec(history_path, O_WRONLY | O_APPEND);
if (out_fd >= 0)
@@ -1343,7 +1345,7 @@ bool history_t::save_internal_via_appending()
/* Check to see if the file changed */
if (history_file_identify(out_fd) != mmap_file_id)
file_changed = true;
-
+
/* Exclusive lock on the entire file. This is released when we close the file (below). */
if (history_file_lock(out_fd, F_WRLCK))
{
@@ -1351,22 +1353,22 @@ bool history_t::save_internal_via_appending()
Note that this is sketchy for a few reasons:
- Another shell may have appended its own items with a later timestamp, so our file may no longer be sorted by timestamp.
- Another shell may have appended the same items, so our file may now contain duplicates.
-
+
We cannot modify any previous parts of our file, because other instances may be reading those portions. We can only append.
-
+
Originally we always rewrote the file on saving, which avoided both of these problems. However, appending allows us to save history after every command, which is nice!
-
+
Periodically we "clean up" the file by rewriting it, so that most of the time it doesn't have duplicates, although we don't yet sort by timestamp (the timestamp isn't really used for much anyways).
*/
-
+
FILE *out = fdopen(out_fd, "a");
if (out)
{
/* Be block buffered. In chaos mode, choose a tiny buffer so as to magnify the effects of race conditions. Otherwise, use the default buffer */
setvbuf(out, NULL, _IOFBF, chaos_mode ? 1 : 0);
-
+
bool errored = false;
-
+
/* Write all items at or after first_unwritten_new_item_index */
while (first_unwritten_new_item_index < new_items.size())
{
@@ -1376,11 +1378,11 @@ bool history_t::save_internal_via_appending()
errored = true;
break;
}
-
+
/* We wrote this item, hooray */
first_unwritten_new_item_index++;
}
-
+
if (0 == fclose(out))
{
/* fclose just closed our out_fd; mark it as -1 so we don't re-close it */
@@ -1390,24 +1392,24 @@ bool history_t::save_internal_via_appending()
{
errored = true;
}
-
+
/* We're OK if we did not error */
ok = ! errored;
}
}
}
-
+
if (out_fd >= 0)
close(out_fd);
signal_unblock();
-
+
/* If someone has replaced the file, forget our file state */
if (file_changed)
{
this->clear_file_state();
}
-
+
return ok;
}
@@ -1418,7 +1420,7 @@ void history_t::save_internal(bool vacuum)
ASSERT_IS_LOCKED(lock);
/* Nothing to do if there's no new items */
- if (new_items.empty() && deleted_items.empty())
+ if (first_unwritten_new_item_index >= new_items.size() && deleted_items.empty())
return;
/* Compact our new items so we don't have duplicates */
8 history.h
View
@@ -123,7 +123,7 @@ class history_t
/** New items. Note that these are NOT discarded on save. We need to keep these around so we can distinguish between items in our history and items in the history of other shells that were started after we were started. */
std::vector<history_item_t> new_items;
-
+
/** The index of the first new item that we have not yet written. */
size_t first_unwritten_new_item_index;
@@ -138,7 +138,7 @@ class history_t
/** The type of file we mmap'd */
history_file_type_t mmap_type;
-
+
/** The file ID of the file we mmap'd */
file_id_t mmap_file_id;
@@ -165,7 +165,7 @@ class history_t
/** Saves history by rewriting the file */
bool save_internal_via_rewrite();
-
+
/** Saves history by appending to the file */
bool save_internal_via_appending();
@@ -198,7 +198,7 @@ class history_t
/** Saves history */
void save();
-
+
/** Performs a full (non-incremental) save */
void save_and_vacuum();
26 path.cpp
View
@@ -323,9 +323,9 @@ bool path_can_be_implicit_cd(const wcstring &path, wcstring *out_path, const wch
return result;
}
-bool path_get_config(wcstring &path)
+static wcstring path_create_config()
{
- int done = 0;
+ bool done = false;
wcstring res;
const env_var_t xdg_dir = env_get_string(L"XDG_CONFIG_HOME");
@@ -334,7 +334,7 @@ bool path_get_config(wcstring &path)
res = xdg_dir + L"/fish";
if (!create_directory(res))
{
- done = 1;
+ done = true;
}
}
else
@@ -345,22 +345,26 @@ bool path_get_config(wcstring &path)
res = home + L"/.config/fish";
if (!create_directory(res))
{
- done = 1;
+ done = true;
}
}
}
- if (done)
- {
- path = res;
- return true;
- }
- else
+ if (! done)
{
+ res.clear();
+
debug(0, _(L"Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access."));
- return false;
}
+ return res;
+}
+/* Cache the config path */
+bool path_get_config(wcstring &path)
+{
+ static const wcstring result = path_create_config();
+ path = result;
+ return ! result.empty();
}
static void replace_all(wcstring &str, const wchar_t *needle, const wchar_t *replacement)
8 reader.cpp
View
@@ -760,10 +760,10 @@ void reader_repaint_if_needed()
{
if (data == NULL)
return;
-
+
bool needs_reset = data->screen_reset_needed;
bool needs_repaint = needs_reset || data->repaint_needed;
-
+
if (needs_reset)
{
s_reset(&data->screen, screen_reset_current_line_and_prompt);
@@ -2702,7 +2702,7 @@ const wchar_t *reader_readline()
std::vector<completion_t> comp;
int finished=0;
struct termios old_modes;
-
+
/* Coalesce redundant repaints. When we get a repaint, we set this to true, and skip repaints until we get something else. */
bool coalescing_repaints = false;
@@ -2793,7 +2793,7 @@ const wchar_t *reader_readline()
if (last_char != R_YANK && last_char != R_YANK_POP)
yank_len=0;
-
+
const wchar_t *buff = data->command_line.c_str();
switch (c)
{
65 screen.cpp
View
@@ -170,10 +170,10 @@ struct prompt_layout_t
{
/* How many lines the prompt consumes */
size_t line_count;
-
+
/* Width of the longest line */
size_t max_line_width;
-
+
/* Width of the last line */
size_t last_line_width;
};
@@ -187,7 +187,7 @@ static prompt_layout_t calc_prompt_layout(const wchar_t *prompt)
{
size_t current_line_width = 0;
size_t j, k;
-
+
prompt_layout_t prompt_layout = {};
prompt_layout.line_count = 1;
@@ -741,52 +741,55 @@ static bool test_stuff(screen_t *scr)
{
data_buffer_t output;
scoped_buffer_t scoped_buffer(&output);
-
+
s_move(scr, &output, 0, 0);
int screen_width = common_get_width();
-
+
const wchar_t *left = L"left";
const wchar_t *right = L"right";
-
+
for (size_t idx = 0; left[idx]; idx++)
{
s_write_char(scr, &output, left[idx]);
}
-
+
s_move(scr, &output, screen_width - wcslen(right), 0);
-
+
for (size_t idx = 0; right[idx]; idx++)
{
s_write_char(scr, &output, right[idx]);
}
-
+
if (! output.empty())
{
write_loop(STDOUT_FILENO, &output.at(0), output.size());
output.clear();
}
-
+
sleep(5);
-
- for (size_t i=0; i < 1; i++) {
+
+ for (size_t i=0; i < 1; i++)
+ {
writembs(cursor_left);
}
-
+
if (! output.empty())
{
write_loop(1, &output.at(0), output.size());
output.clear();
}
-
- while (1) {
+
+ while (1)
+ {
int c = getchar();
if (c != EOF) break;
}
-
- while (1) {
+
+ while (1)
+ {
int c = getchar();
if (c != EOF) break;
}
@@ -826,13 +829,13 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
need_clear_screen = true;
s_move(scr, &output, 0, 0);
s_reset(scr, screen_reset_current_line_contents);
-
+
need_clear_lines = need_clear_lines || scr->need_clear_lines;
need_clear_screen = need_clear_screen || scr->need_clear_screen;
}
scr->actual_width = screen_width;
}
-
+
scr->need_clear_lines = false;
scr->need_clear_screen = false;
@@ -910,14 +913,14 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
s_write_mbs(&output, clr_eos);
has_cleared_screen = true;
}
-
+
perform_any_impending_soft_wrap(scr, current_width, (int)i);
s_move(scr, &output, current_width, (int)i);
s_set_color(scr, &output, o_line.color_at(j));
s_write_char(scr, &output, o_line.char_at(j));
current_width += fish_wcwidth_min_0(o_line.char_at(j));
}
-
+
/* Clear the screen if we have not done so yet. */
if (should_clear_screen_this_line && ! has_cleared_screen)
{
@@ -960,12 +963,12 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r
s_set_color(scr, &output, 0xffffffff);
s_write_str(&output, right_prompt);
scr->actual.cursor.x += right_prompt_width;
-
- /* We output in the last column. Some terms (Linux) push the cursor further right, past the window. Others make it "stick." Since we don't really know which is which, issue a cr so it goes back to the left.
-
+
+ /* We output in the last column. Some terms (Linux) push the cursor further right, past the window. Others make it "stick." Since we don't really know which is which, issue a cr so it goes back to the left.
+
However, if the user is resizing the window smaller, then it's possible the cursor wrapped. If so, then a cr will go to the beginning of the following line! So instead issue a bunch of "move left" commands to get back onto the line, and then jump to the front of it (!)
*/
-
+
s_move(scr, &output, scr->actual.cursor.x - (int)right_prompt_width, scr->actual.cursor.y);
s_write_str(&output, L"\r");
scr->actual.cursor.x = 0;
@@ -1051,7 +1054,7 @@ static screen_layout_t compute_layout(screen_t *s,
prompt_layout_t left_prompt_layout = calc_prompt_layout(left_prompt);
prompt_layout_t right_prompt_layout = calc_prompt_layout(right_prompt);
-
+
size_t left_prompt_width = left_prompt_layout.last_line_width;
size_t right_prompt_width = right_prompt_layout.last_line_width;
@@ -1122,9 +1125,9 @@ static screen_layout_t compute_layout(screen_t *s,
2. Left prompt visible, right prompt visible, command line visible, autosuggestion truncated (possibly to zero)
3. Left prompt visible, right prompt hidden, command line visible, autosuggestion hidden
4. Newline separator (left prompt visible, right prompt hidden, command line visible, autosuggestion visible)
-
+
A remark about layout #4: if we've pushed the command line to a new line, why can't we draw the right prompt? The issue is resizing: if you resize the window smaller, then the right prompt will wrap to the next line. This means that we can't go back to the line that we were on, and things turn to chaos very quickly.
-
+
*/
bool done = false;
@@ -1310,10 +1313,10 @@ void s_reset(screen_t *s, screen_reset_mode_t mode)
{
s->actual_lines_before_reset = maxi(s->actual_lines_before_reset, s->actual.line_count());
}
-
+
if (repaint_prompt && ! abandon_line)
{
-
+
/* If the prompt is multi-line, we need to move up to the prompt's initial line. We do this by lying to ourselves and claiming that we're really below what we consider "line 0" (which is the last line of the prompt). This will cause us to move up to try to get back to line 0, but really we're getting back to the initial line of the prompt. */
const size_t prompt_line_count = calc_prompt_lines(s->actual_left_prompt);
assert(prompt_line_count >= 1);
@@ -1336,7 +1339,7 @@ void s_reset(screen_t *s, screen_reset_mode_t mode)
int screen_width = common_get_width();
wcstring abandon_line_string;
abandon_line_string.reserve(screen_width);
-
+
int non_space_width = wcwidth(omitted_newline_char);
if (screen_width >= non_space_width)
{
Please sign in to comment.
Something went wrong with that request. Please try again.