mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-29 18:51:15 -03:00
Refactor history flushing
Use function names which explicitly state their flushing behavior. When writing history items to a new file, flushing is only done at the end. When appending items to an existing file, flushing is done for each item, in order to keep the `first_unwritten_new_item_index` counter consistent with the file system.
This commit is contained in:
committed by
Fabian Boehm
parent
5c8d822ee1
commit
5c0fddae70
@@ -152,19 +152,18 @@ pub enum SearchDirection {
|
|||||||
|
|
||||||
pub const VACUUM_FREQUENCY: usize = 25;
|
pub const VACUUM_FREQUENCY: usize = 25;
|
||||||
|
|
||||||
/// If the size of `buffer` is at least `min_size`, output the contents `buffer` to `fd`,
|
/// Output the contents `buffer` to `file` and clear the `buffer`.
|
||||||
/// and clear the string.
|
fn drain_buffer_into_file_no_flush(buffer: &mut Vec<u8>, file: &mut File) -> std::io::Result<()> {
|
||||||
fn flush_to_file(buffer: &mut Vec<u8>, file: &mut File, min_size: usize) -> std::io::Result<()> {
|
|
||||||
if buffer.is_empty() || buffer.len() < min_size {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write_all(buffer)?;
|
file.write_all(buffer)?;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
// Ensure that the file content is actually written to persistent storage.
|
Ok(())
|
||||||
// This might be unnecessary.
|
}
|
||||||
// It is used to help eliminate possible causes for
|
|
||||||
// https://github.com/fish-shell/fish-shell/issues/10300
|
/// Output the contents `buffer` to `file` and clear the `buffer`.
|
||||||
|
/// Flush the file and sync it to ensure that the updates actually reach the file system.
|
||||||
|
fn drain_buffer_into_file_and_flush(buffer: &mut Vec<u8>, file: &mut File) -> std::io::Result<()> {
|
||||||
|
drain_buffer_into_file_no_flush(buffer, file)?;
|
||||||
|
file.flush()?;
|
||||||
file.sync_all()
|
file.sync_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,10 +577,10 @@ fn remove_ephemeral_items(&mut self) {
|
|||||||
usize::min(self.first_unwritten_new_item_index, self.new_items.len());
|
usize::min(self.first_unwritten_new_item_index, self.new_items.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the fd of an existing history file, write a new history file to `dst_fd`.
|
/// Given an existing history file, write a new history file to `dst`.
|
||||||
/// Returns false on error, true on success
|
/// Returns false on error, true on success
|
||||||
fn rewrite_to_temporary_file(&self, existing_file: Option<&mut File>, dst: &mut File) -> bool {
|
fn rewrite_to_temporary_file(&self, existing_file: Option<&mut File>, dst: &mut File) -> bool {
|
||||||
// We are reading FROM existing_fd and writing TO dst_fd
|
// We are reading FROM existing_file and writing TO dst
|
||||||
|
|
||||||
// Make an LRU cache to save only the last N elements.
|
// Make an LRU cache to save only the last N elements.
|
||||||
let mut lru = LruCache::new(HISTORY_SAVE_MAX);
|
let mut lru = LruCache::new(HISTORY_SAVE_MAX);
|
||||||
@@ -640,9 +639,16 @@ fn rewrite_to_temporary_file(&self, existing_file: Option<&mut File>, dst: &mut
|
|||||||
let mut buffer = Vec::with_capacity(HISTORY_OUTPUT_BUFFER_SIZE + 128);
|
let mut buffer = Vec::with_capacity(HISTORY_OUTPUT_BUFFER_SIZE + 128);
|
||||||
for item in items {
|
for item in items {
|
||||||
append_history_item_to_buffer(&item, &mut buffer);
|
append_history_item_to_buffer(&item, &mut buffer);
|
||||||
if let Err(e) = flush_to_file(&mut buffer, dst, HISTORY_OUTPUT_BUFFER_SIZE) {
|
if buffer.len() >= HISTORY_OUTPUT_BUFFER_SIZE {
|
||||||
|
if let Err(e) = drain_buffer_into_file_no_flush(&mut buffer, dst) {
|
||||||
|
err = Some(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err.is_none() {
|
||||||
|
if let Err(e) = drain_buffer_into_file_and_flush(&mut buffer, dst) {
|
||||||
err = Some(e);
|
err = Some(e);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
@@ -654,7 +660,7 @@ fn rewrite_to_temporary_file(&self, existing_file: Option<&mut File>, dst: &mut
|
|||||||
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
flush_to_file(&mut buffer, dst, 0).is_ok()
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -818,6 +824,7 @@ fn save_internal_via_rewrite(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Saves history by appending to the file.
|
/// Saves history by appending to the file.
|
||||||
|
// TODO: return error information
|
||||||
fn save_internal_via_appending(&mut self) -> bool {
|
fn save_internal_via_appending(&mut self) -> bool {
|
||||||
FLOGF!(
|
FLOGF!(
|
||||||
history,
|
history,
|
||||||
@@ -834,6 +841,7 @@ fn save_internal_via_appending(&mut self) -> bool {
|
|||||||
|
|
||||||
// Get the path to the real history file.
|
// Get the path to the real history file.
|
||||||
let Some(history_path) = history_filename(&self.name, L!("")) else {
|
let Some(history_path) = history_filename(&self.name, L!("")) else {
|
||||||
|
// TODO: Why have a successful return here?
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -901,7 +909,12 @@ fn save_internal_via_appending(&mut self) -> bool {
|
|||||||
let item = &self.new_items[self.first_unwritten_new_item_index];
|
let item = &self.new_items[self.first_unwritten_new_item_index];
|
||||||
if item.should_write_to_disk() {
|
if item.should_write_to_disk() {
|
||||||
append_history_item_to_buffer(item, &mut buffer);
|
append_history_item_to_buffer(item, &mut buffer);
|
||||||
res = flush_to_file(&mut buffer, &mut history_file, HISTORY_OUTPUT_BUFFER_SIZE);
|
// Ensure that each item is written individually and makes it to storage.
|
||||||
|
// This is done to keep `self.first_unwritten_new_item_index` consistent with
|
||||||
|
// the file system.
|
||||||
|
// Flushing and syncing each iteration adds overhead,
|
||||||
|
// but hopefully there are not that many items to write when appending.
|
||||||
|
res = drain_buffer_into_file_and_flush(&mut buffer, &mut history_file);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -910,10 +923,6 @@ fn save_internal_via_appending(&mut self) -> bool {
|
|||||||
self.first_unwritten_new_item_index += 1;
|
self.first_unwritten_new_item_index += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.is_ok() {
|
|
||||||
res = flush_to_file(&mut buffer, &mut history_file, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since we just modified the file, update our history_file_id to match its current state
|
// Since we just modified the file, update our history_file_id to match its current state
|
||||||
// Otherwise we'll think the file has been changed by someone else the next time we go to
|
// Otherwise we'll think the file has been changed by someone else the next time we go to
|
||||||
// write.
|
// write.
|
||||||
|
|||||||
Reference in New Issue
Block a user