do not block when fetching new tags

This commit is contained in:
Thomas Eppers 2023-02-20 13:56:33 +01:00
parent 431b062420
commit 70b163cf81
4 changed files with 110 additions and 63 deletions

41
Cargo.lock generated
View File

@ -140,27 +140,25 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
dependencies = [ dependencies = [
"futures-core", "futures-core",
] ]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54913bae956fb8df7f4dc6fc90362aa72e69148e3f39041fbe8742d21e0ac57" checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [ dependencies = [
"autocfg",
"proc-macro-hack",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
@ -168,30 +166,27 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.16" version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
dependencies = [ dependencies = [
"autocfg",
"futures-core", "futures-core",
"futures-macro", "futures-macro",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"slab", "slab",
] ]
@ -496,18 +491,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro-nested"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.28" version = "1.0.28"

View File

@ -73,6 +73,8 @@ impl Ui {
events: mpsc::Receiver<DeferredEvent>, events: mpsc::Receiver<DeferredEvent>,
sender: mpsc::Sender<UiEvent>, sender: mpsc::Sender<UiEvent>,
) -> Result<(), Error> { ) -> Result<(), Error> {
use std::sync::atomic::Ordering;
let fetching_tags = Arc::new(std::sync::atomic::AtomicBool::new(false));
loop { loop {
match events.recv() { match events.recv() {
Ok(DeferredEvent::Quit) => break, Ok(DeferredEvent::Quit) => break,
@ -92,23 +94,50 @@ impl Ui {
ui.details = ui.tags.create_detail_widget(); ui.details = ui.tags.create_detail_widget();
} }
Ok(DeferredEvent::TagNext) => { Ok(DeferredEvent::TagNext) => {
let (fetched_new_tags, mut tags) = { let mut tags_copy = {
let mut ui = ui.lock().unwrap(); let mut ui = ui.lock().unwrap();
if ui.tags.at_end_of_list() { match ui.tags.next() {
ui.info.set_text("Fetching more tags..."); None => {
sender.send(UiEvent::RefreshOnNewData)?; // return early, also releases lock
(true, ui.tags.clone()) ui.details = ui.tags.create_detail_widget();
} else { sender.send(UiEvent::RefreshOnNewData)?;
(false, ui.tags.clone()) continue;
}
Some(_) if !fetching_tags.load(Ordering::Relaxed) => {
fetching_tags.store(true, Ordering::Relaxed);
ui.info.set_text("Fetching more tags...");
sender.send(UiEvent::RefreshOnNewData)?;
ui.tags.clone()
}
Some(_) => {
// do nothing, as we are already fetching for new tags
continue;
}
} }
}; };
tags.next().await;
let mut ui = ui.lock().unwrap(); // fetching new tags
ui.tags = tags; let sender_copy = sender.clone();
ui.details = ui.tags.create_detail_widget(); let ui_copy = ui.clone();
if fetched_new_tags { let fetching_tags_copy = fetching_tags.clone();
ui.info.set_text("Fetching tags done"); std::thread::spawn(move || {
} tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
tags_copy.load_next_page().await;
let mut ui = ui_copy.lock().unwrap();
//set position to the position of old TagList
//it may have changed since tag fetching has been invoked
tags_copy.set_cursor(ui.tags.get_cursor().clone());
ui.tags = tags_copy;
ui.details = ui.tags.create_detail_widget();
ui.info.set_text("Fetching tags done");
sender_copy.send(UiEvent::RefreshOnNewData).unwrap();
fetching_tags_copy.store(false, Ordering::Relaxed);
})
});
} }
Err(e) => { Err(e) => {
let mut ui = ui.lock().unwrap(); let mut ui = ui.lock().unwrap();

View File

@ -78,6 +78,8 @@ impl Ui {
events: mpsc::Receiver<DeferredEvent>, events: mpsc::Receiver<DeferredEvent>,
sender: mpsc::Sender<UiEvent>, sender: mpsc::Sender<UiEvent>,
) -> Result<(), Error> { ) -> Result<(), Error> {
use std::sync::atomic::Ordering;
let fetching_tags = Arc::new(std::sync::atomic::AtomicBool::new(false));
loop { loop {
match events.recv() { match events.recv() {
Ok(DeferredEvent::Quit) => break, Ok(DeferredEvent::Quit) => break,
@ -97,21 +99,50 @@ impl Ui {
ui.details = ui.tags.create_detail_widget(); ui.details = ui.tags.create_detail_widget();
} }
Ok(DeferredEvent::TagNext) => { Ok(DeferredEvent::TagNext) => {
let (fetched_new_tags, mut tags) = { let mut tags_copy = {
let mut ui = ui.lock().unwrap(); let mut ui = ui.lock().unwrap();
if ui.tags.at_end_of_list() { match ui.tags.next() {
ui.info.set_text("Fetching more tags..."); None => {
sender.send(UiEvent::RefreshOnNewData)?; // return early, also releases lock
ui.details = ui.tags.create_detail_widget();
sender.send(UiEvent::RefreshOnNewData)?;
continue;
}
Some(_) if !fetching_tags.load(Ordering::Relaxed) => {
fetching_tags.store(true, Ordering::Relaxed);
ui.info.set_text("Fetching more tags...");
sender.send(UiEvent::RefreshOnNewData)?;
ui.tags.clone()
}
Some(_) => {
// do nothing, as we are already fetching for new tags
continue;
}
} }
(ui.tags.at_end_of_list(), ui.tags.clone())
}; };
tags.next().await;
let mut ui = ui.lock().unwrap(); // fetching new tags
ui.tags = tags; let sender_copy = sender.clone();
ui.details = ui.tags.create_detail_widget(); let ui_copy = ui.clone();
if fetched_new_tags { let fetching_tags_copy = fetching_tags.clone();
ui.info.set_text("Fetching tags done"); std::thread::spawn(move || {
} tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
tags_copy.load_next_page().await;
let mut ui = ui_copy.lock().unwrap();
//set position to the position of old TagList
//it may have changed since tag fetching has been invoked
tags_copy.set_cursor(ui.tags.get_cursor().clone());
ui.tags = tags_copy;
ui.details = ui.tags.create_detail_widget();
ui.info.set_text("Fetching tags done");
sender_copy.send(UiEvent::RefreshOnNewData).unwrap();
fetching_tags_copy.store(false, Ordering::Relaxed);
})
});
} }
Err(e) => { Err(e) => {
let mut ui = ui.lock().unwrap(); let mut ui = ui.lock().unwrap();

View File

@ -86,11 +86,12 @@ impl TagList {
} }
} }
pub fn at_end_of_list(&self) -> bool { pub fn set_cursor(&mut self, state: ListState) {
if let Some(i) = self.state.selected() { self.state = state;
return i == self.lines.len() - 2; }
}
false pub fn get_cursor(&self) -> &ListState {
&self.state
} }
pub fn render(&mut self, colored: bool) -> (List, &mut ListState) { pub fn render(&mut self, colored: bool) -> (List, &mut ListState) {
@ -177,16 +178,19 @@ impl TagList {
} }
/// select next tag /// select next tag
pub async fn next(&mut self) { /// returns Some when more tags need to be fetched otherwise None
pub fn next(&mut self) -> Option<()> {
if let Some(Line::Status(_)) = self.lines.get(0) { if let Some(Line::Status(_)) = self.lines.get(0) {
return; return None;
} }
match self.state.selected() { match self.state.selected() {
None if !self.lines.is_empty() => self.state.select(Some(0)), None if !self.lines.is_empty() => self.state.select(Some(0)),
None => (), None => (),
Some(i) if i == self.lines.len() - 2 => self.load_next_page().await, Some(i) if i == self.lines.len() - 2 => return Some(()),
// Some(i) if i == self.lines.len() - 2 => return self.load_next_page().await,
Some(i) => self.state.select(Some(i + 1)), Some(i) => self.state.select(Some(i + 1)),
} }
None
} }
/// select previous tag /// select previous tag