浏览代码

tile_loader: Download tiles in separate threads

Johannes Hofmann 8 年前
父节点
当前提交
7f3c28ca49
共有 1 个文件被更改,包括 138 次插入59 次删除
  1. 138
    59
      src/tile_loader.rs

+ 138
- 59
src/tile_loader.rs 查看文件

8
 use std::fs::File;
8
 use std::fs::File;
9
 use std::io::Write;
9
 use std::io::Write;
10
 use std::path::{Path, PathBuf};
10
 use std::path::{Path, PathBuf};
11
+use std::sync::{Arc, mpsc, Mutex};
11
 use std::sync::mpsc::TryRecvError;
12
 use std::sync::mpsc::TryRecvError;
12
-use std::sync::mpsc;
13
 use std::thread;
13
 use std::thread;
14
 use tile::Tile;
14
 use tile::Tile;
15
 use tile_source::TileSource;
15
 use tile_source::TileSource;
49
     )
49
     )
50
         where F: Fn(Tile) + Sync + Send + 'static,
50
         where F: Fn(Tile) + Sync + Send + 'static,
51
     {
51
     {
52
-        let mut client_opt = None;
53
-        let mut queue: Vec<(Tile, String, PathBuf, bool)> = vec![];
52
+        let mut queue: Vec<TileRequest> = vec![];
53
+        let remote_queue: Arc<Mutex<Vec<TileRequest>>> = Arc::new(Mutex::new(vec![]));
54
+        let mut view_opt: Option<View> = None;
55
+
56
+        let arc_notice_func = Arc::new(notice_func);
57
+
58
+        let (remote_request_tx, remote_request_rx) = mpsc::channel();
59
+        {
60
+            let arc_request_rx = Arc::new(Mutex::new(remote_request_rx));
61
+            for id in 0..2 {
62
+                let remote_queue = Arc::clone(&remote_queue);
63
+                let arc_request_rx = Arc::clone(&arc_request_rx);
64
+                let result_tx = result_tx.clone();
65
+                let arc_notice_func = Arc::clone(&arc_notice_func);
66
+                thread::spawn(move || Self::work_remote(id, remote_queue, arc_request_rx, result_tx, arc_notice_func));
67
+            }
68
+        }
54
 
69
 
55
         'outer: while let Ok(message) = request_rx.recv() {
70
         'outer: while let Ok(message) = request_rx.recv() {
56
-            let mut view_opt: Option<View> = None;
71
+            let mut need_to_sort = true;
57
 
72
 
58
             match message {
73
             match message {
59
-                LoaderMessage::SetViewLocation{view} => {
74
+                LoaderMessage::SetView(view) => {
60
                     view_opt = Some(view);
75
                     view_opt = Some(view);
61
                 },
76
                 },
62
-                LoaderMessage::GetTile{tile, url, path, write_to_file} => {
63
-                    queue.push((tile, url, path, write_to_file));
77
+                LoaderMessage::GetTile(request) => {
78
+                    queue.push(request);
64
                 }
79
                 }
65
             }
80
             }
66
 
81
 
67
             loop {
82
             loop {
68
                 loop {
83
                 loop {
69
-                    match request_rx.try_recv() {
70
-                        Ok(LoaderMessage::SetViewLocation{view}) => {
84
+                    let message = request_rx.try_recv();
85
+
86
+                    match message {
87
+                        Ok(LoaderMessage::SetView(view)) => {
71
                             view_opt = Some(view);
88
                             view_opt = Some(view);
89
+                            need_to_sort = true;
72
                         },
90
                         },
73
-                        Ok(LoaderMessage::GetTile{tile, url, path, write_to_file}) => {
74
-                            queue.push((tile, url, path, write_to_file));
91
+                        Ok(LoaderMessage::GetTile(request)) => {
92
+                            queue.push(request);
93
+                            need_to_sort = true;
75
                         },
94
                         },
76
                         Err(TryRecvError::Empty) => break,
95
                         Err(TryRecvError::Empty) => break,
77
                         Err(TryRecvError::Disconnected) => break 'outer,
96
                         Err(TryRecvError::Disconnected) => break 'outer,
78
                     }
97
                     }
79
                 }
98
                 }
80
 
99
 
81
-                if let Some(view) = view_opt {
82
-                    //TODO sort queue
83
-                    queue.as_mut_slice().sort_by(|&(tile_a, _, _, _), &(tile_b, _, _, _)| {
84
-                        compare_tiles(tile_a, tile_b, view)
85
-                    });
100
+                if need_to_sort {
101
+                    if let Some(view) = view_opt {
102
+                        need_to_sort = false;
103
+
104
+                        queue.as_mut_slice().sort_by(|a, b| {
105
+                            compare_tiles(a.tile, b.tile, view)
106
+                        });
107
+
108
+                        if let Ok(mut remote_queue) = remote_queue.lock() {
109
+                            remote_queue.as_mut_slice().sort_by(|a, b| {
110
+                                compare_tiles(a.tile, b.tile, view)
111
+                            });
112
+                        }
113
+                    }
86
                 }
114
                 }
87
 
115
 
88
                 match queue.pop() {
116
                 match queue.pop() {
89
                     None => break,
117
                     None => break,
90
-                    Some((tile, url, path, write_to_file)) => {
91
-                        println!("queue {:?} {:?}", tile, path);
92
-                        match image::open(&path) {
118
+                    Some(request) => {
119
+                        match image::open(&request.path) {
93
                             Ok(img) => {
120
                             Ok(img) => {
94
-                                result_tx.send((tile, Some(img))).unwrap();
95
-                                notice_func(tile);
121
+                                if let Err(_) = result_tx.send((request.tile, Some(img))) {
122
+                                    break 'outer;
123
+                                }
124
+                                arc_notice_func(request.tile);
96
                                 continue;
125
                                 continue;
97
                             },
126
                             },
98
                             Err(_) => {
127
                             Err(_) => {
99
-                                //TODO do not try to create a client every time when it failed before
100
-                                if client_opt.is_none() {
101
-                                    client_opt = Client::builder().build().ok();
102
-                                }
103
-
104
-                                if let Some(ref client) = client_opt {
105
-                                    println!("use client {:?}", tile);
106
-                                    if let Ok(mut response) = client.get(&url).send() {
107
-                                        let mut buf: Vec<u8> = vec![];
108
-                                        response.copy_to(&mut buf).unwrap();
109
-                                        if let Ok(img) = image::load_from_memory(&buf) {
110
-                                            result_tx.send((tile, Some(img))).unwrap();
111
-                                            notice_func(tile);
112
-
113
-                                            if write_to_file {
114
-                                                //TODO do something on write errors
115
-                                                let _ = Self::write_to_file(&path, &buf);
116
-                                            }
117
-
118
-                                            continue;
119
-                                        }
128
+                                if let Ok(mut remote_queue) = remote_queue.lock() {
129
+                                    //TODO restrict size of remote_queue
130
+                                    remote_queue.push(request);
131
+                                    if let Some(view) = view_opt {
132
+                                        remote_queue.as_mut_slice().sort_by(|a, b| {
133
+                                            compare_tiles(a.tile, b.tile, view)
134
+                                        });
135
+                                    }
136
+                                    if remote_request_tx.send(RemoteLoaderMessage::PopQueue).is_err() {
137
+                                        //TODO remote worker terminated
120
                                     }
138
                                     }
121
                                 }
139
                                 }
122
                             },
140
                             },
123
                         }
141
                         }
124
-                        result_tx.send((tile, None)).unwrap();
125
                     },
142
                     },
126
                 }
143
                 }
127
             }
144
             }
128
         }
145
         }
129
     }
146
     }
130
 
147
 
148
+    fn work_remote<F>(
149
+        thread_id: u32,
150
+        queue: Arc<Mutex<Vec<TileRequest>>>,
151
+        request_rx: Arc<Mutex<mpsc::Receiver<RemoteLoaderMessage>>>,
152
+        result_tx: mpsc::Sender<(Tile, Option<DynamicImage>)>,
153
+        notice_func: Arc<F>,
154
+    )
155
+        where F: Fn(Tile) + Sync + Send + 'static,
156
+    {
157
+        let mut client_opt = None;
158
+
159
+        loop {
160
+            let message = request_rx.lock().ok().and_then(|r| r.recv().ok());
161
+            match message {
162
+                None => break,
163
+                Some(RemoteLoaderMessage::Terminate) => break,
164
+                Some(RemoteLoaderMessage::PopQueue) => {
165
+                    let ele: Option<TileRequest> = queue.lock().ok().and_then(|mut q| q.pop());
166
+
167
+                    if let Some(request) = ele {
168
+                        println!("thread {}: queue {:?} {:?}", thread_id, request.tile, request.path);
169
+                        if client_opt.is_none() {
170
+                            client_opt = Client::builder().build().ok();
171
+                        }
172
+
173
+                        if let Some(Ok(mut response)) = client_opt.as_ref().map(|c| c.get(&request.url).send()) {
174
+                            let mut buf: Vec<u8> = vec![];
175
+                            response.copy_to(&mut buf).unwrap();
176
+                            if let Ok(img) = image::load_from_memory(&buf) {
177
+                                if result_tx.send((request.tile, Some(img))).is_err() {
178
+                                    break;
179
+                                }
180
+
181
+                                notice_func(request.tile);
182
+
183
+                                if request.write_to_file {
184
+                                    //TODO do something on write errors
185
+                                    let _ = Self::write_to_file(&request.path, &buf);
186
+                                }
187
+
188
+                                continue;
189
+                            }
190
+                        }
191
+                        if result_tx.send((request.tile, None)).is_err() {
192
+                            break;
193
+                        }
194
+                    }
195
+                },
196
+            }
197
+        }
198
+    }
199
+
131
     pub fn async_request(&mut self, tile_coord: TileCoord, source: &TileSource, write_to_file: bool) {
200
     pub fn async_request(&mut self, tile_coord: TileCoord, source: &TileSource, write_to_file: bool) {
132
         if tile_coord.zoom > source.max_tile_zoom() {
201
         if tile_coord.zoom > source.max_tile_zoom() {
133
             return;
202
             return;
137
 
206
 
138
         if !self.pending.contains(&tile) {
207
         if !self.pending.contains(&tile) {
139
             self.pending.insert(tile);
208
             self.pending.insert(tile);
140
-            self.request_tx.send(LoaderMessage::GetTile{
141
-                tile: tile,
142
-                url: source.remote_tile_url(tile_coord),
143
-                path: source.local_tile_path(tile_coord),
144
-                write_to_file: write_to_file,
145
-            }).unwrap();
209
+            self.request_tx.send(LoaderMessage::GetTile(
210
+                TileRequest {
211
+                    tile: tile,
212
+                    url: source.remote_tile_url(tile_coord),
213
+                    path: source.local_tile_path(tile_coord),
214
+                    write_to_file: write_to_file,
215
+                }
216
+            )).unwrap();
146
         }
217
         }
147
     }
218
     }
148
 
219
 
172
                 }
243
                 }
173
 
244
 
174
                 if let Some(ref client) = self.client {
245
                 if let Some(ref client) = self.client {
175
-                    println!("use client {:?}", tile);
176
                     if let Ok(mut response) = client.get(&source.remote_tile_url(tile)).send() {
246
                     if let Ok(mut response) = client.get(&source.remote_tile_url(tile)).send() {
177
                         let mut buf: Vec<u8> = vec![];
247
                         let mut buf: Vec<u8> = vec![];
178
                         response.copy_to(&mut buf).unwrap();
248
                         response.copy_to(&mut buf).unwrap();
196
     }
266
     }
197
 
267
 
198
     pub fn set_view_location(&mut self, view: View) {
268
     pub fn set_view_location(&mut self, view: View) {
199
-        self.request_tx.send(LoaderMessage::SetViewLocation{
200
-            view: view,
201
-        }).unwrap();
269
+        self.request_tx.send(LoaderMessage::SetView(view)).unwrap();
202
     }
270
     }
203
 
271
 
204
     fn write_to_file<P: AsRef<Path>>(path: P, img_data: &[u8]) -> ::std::io::Result<()> {
272
     fn write_to_file<P: AsRef<Path>>(path: P, img_data: &[u8]) -> ::std::io::Result<()> {
205
-
206
         if let Some(dir) = path.as_ref().parent() {
273
         if let Some(dir) = path.as_ref().parent() {
207
             ::std::fs::create_dir_all(dir)?;
274
             ::std::fs::create_dir_all(dir)?;
208
         }
275
         }
209
 
276
 
210
-        //TODO remove
211
-        println!("write file {:?}", path.as_ref());
212
-
213
         let mut file = File::create(path)?;
277
         let mut file = File::create(path)?;
214
         file.write_all(img_data)
278
         file.write_all(img_data)
215
     }
279
     }
216
 }
280
 }
217
 
281
 
282
+#[derive(Debug)]
283
+struct TileRequest {
284
+    pub tile: Tile,
285
+    pub url: String,
286
+    pub path: PathBuf,
287
+    pub write_to_file: bool,
288
+}
289
+
290
+#[derive(Debug)]
218
 enum LoaderMessage {
291
 enum LoaderMessage {
219
-    GetTile{tile: Tile, url: String, path: PathBuf, write_to_file: bool},
220
-    SetViewLocation{view: View},
292
+    GetTile(TileRequest),
293
+    SetView(View),
294
+}
295
+
296
+#[derive(Debug)]
297
+enum RemoteLoaderMessage {
298
+    PopQueue,
299
+    Terminate,
221
 }
300
 }
222
 
301
 
223
 fn compare_tiles(a: Tile, b: Tile, view: View) -> Ordering {
302
 fn compare_tiles(a: Tile, b: Tile, view: View) -> Ordering {