Add a semaphore to limit the number of threads downloading avatars

This commit is contained in:
Daniel García Moreno 2017-12-11 09:43:04 +01:00
parent b3cbdd4d99
commit 34883301f2
5 changed files with 35 additions and 6 deletions

View file

@ -1,6 +1,6 @@
extern crate url;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use self::url::Url;
use std::sync::mpsc::{Sender, Receiver};
@ -45,6 +45,7 @@ impl Backend {
internal_tx: None,
data: Arc::new(Mutex::new(data)),
user_info_cache: CacheMap::new().timeout(60*60),
limit_threads: Arc::new((Mutex::new(0u8), Condvar::new())),
}
}

View file

@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, Condvar};
use std::sync::mpsc::Sender;
use error::Error;
@ -133,6 +133,8 @@ pub struct Backend {
// user info cache, uid -> (name, avatar)
pub user_info_cache: CacheMap<Arc<Mutex<(String, String)>>>,
// semaphore to limit the number of threads downloading images
pub limit_threads: Arc<(Mutex<u8>, Condvar)>,
}
impl Clone for Backend {
@ -142,6 +144,7 @@ impl Clone for Backend {
data: self.data.clone(),
internal_tx: self.internal_tx.clone(),
user_info_cache: self.user_info_cache.clone(),
limit_threads: self.limit_threads.clone(),
}
}
}

View file

@ -101,7 +101,21 @@ pub fn get_avatar_async(bk: &Backend, member: Option<Member>, tx: Sender<String>
let alias = m.get_alias().clone();
let avatar = m.avatar.clone();
let thread_count = bk.limit_threads.clone();
thread::spawn(move || {
// waiting, less than 20 threads at the same time
// this is a semaphore
// TODO: use std::sync::Semaphore when it's on stable version
// https://doc.rust-lang.org/1.1.0/std/sync/struct.Semaphore.html
let &(ref num, ref cvar) = &*thread_count;
{
let mut start = num.lock().unwrap();
while *start >= 20 {
start = cvar.wait(start).unwrap()
}
*start += 1;
}
match get_user_avatar_img(&baseu, uid, alias.unwrap_or_default(), avatar.unwrap_or_default()) {
Ok(fname) => {
tx.send(fname.clone()).unwrap();
@ -110,6 +124,13 @@ pub fn get_avatar_async(bk: &Backend, member: Option<Member>, tx: Sender<String>
tx.send(String::new()).unwrap();
}
}
// freeing the cvar for new threads
{
let mut counter = num.lock().unwrap();
*counter -= 1;
}
cvar.notify_one();
});
Ok(())

View file

@ -36,7 +36,7 @@ impl<'a> MemberBox<'a> {
username.set_text(&self.member.get_alias().unwrap_or_default());
let avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 3);
get_member_avatar(backend.clone(), avatar.clone(), Some(self.member.clone()), 30);
get_member_avatar(backend.clone(), avatar.clone(), Some(self.member.clone()), 30, 3);
avatar.set_alignment(0.5, 0.);
w.pack_start(&avatar, false, false, 5);
@ -48,7 +48,11 @@ impl<'a> MemberBox<'a> {
}
}
pub fn get_member_avatar(backend: Sender<BKCommand>, img: gtk::Image, m: Option<Member>, size: i32) {
pub fn get_member_avatar(backend: Sender<BKCommand>, img: gtk::Image, m: Option<Member>, size: i32, tries: i32) {
if tries <= 0 {
return;
}
let (tx, rx): (Sender<String>, Receiver<String>) = channel();
backend.send(BKCommand::GetAvatarAsync(m.clone(), tx)).unwrap();
gtk::timeout_add(50, move || match rx.try_recv() {
@ -59,7 +63,7 @@ pub fn get_member_avatar(backend: Sender<BKCommand>, img: gtk::Image, m: Option<
} else {
// trying again if fail
img.set_from_icon_name("avatar-default-symbolic", 5);
get_member_avatar(backend.clone(), img.clone(), m.clone(), size);
get_member_avatar(backend.clone(), img.clone(), m.clone(), size, tries - 1);
}
gtk::Continue(false)

View file

@ -127,7 +127,7 @@ impl<'a> MessageBox<'a> {
avatar = gtk::Image::new_from_icon_name("avatar-default-symbolic", 5);
}
get_member_avatar(backend.clone(), avatar.clone(), m.cloned(), 40);
get_member_avatar(backend.clone(), avatar.clone(), m.cloned(), 40, 3);
avatar.set_alignment(0.5, 0.);
avatar