@@ -7,6 +7,7 @@ use crate::dist::component::transaction::*;
7
7
8
8
use crate :: dist:: temp;
9
9
use crate :: errors:: * ;
10
+ use crate :: utils:: notifications:: Notification ;
10
11
use crate :: utils:: utils;
11
12
12
13
use std:: collections:: HashSet ;
@@ -194,13 +195,17 @@ fn set_file_perms(_dest_path: &Path, _src_path: &Path) -> Result<()> {
194
195
pub struct TarPackage < ' a > ( DirectoryPackage , temp:: Dir < ' a > ) ;
195
196
196
197
impl < ' a > TarPackage < ' a > {
197
- pub fn new < R : Read > ( stream : R , temp_cfg : & ' a temp:: Cfg ) -> Result < Self > {
198
+ pub fn new < R : Read > (
199
+ stream : R ,
200
+ temp_cfg : & ' a temp:: Cfg ,
201
+ notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
202
+ ) -> Result < Self > {
198
203
let temp_dir = temp_cfg. new_directory ( ) ?;
199
204
let mut archive = tar:: Archive :: new ( stream) ;
200
205
// The rust-installer packages unpack to a directory called
201
206
// $pkgname-$version-$target. Skip that directory when
202
207
// unpacking.
203
- unpack_without_first_dir ( & mut archive, & * temp_dir) ?;
208
+ unpack_without_first_dir ( & mut archive, & * temp_dir, notify_handler ) ?;
204
209
205
210
Ok ( TarPackage (
206
211
DirectoryPackage :: new ( temp_dir. to_owned ( ) , false ) ?,
@@ -209,11 +214,122 @@ impl<'a> TarPackage<'a> {
209
214
}
210
215
}
211
216
212
- fn unpack_without_first_dir < R : Read > ( archive : & mut tar:: Archive < R > , path : & Path ) -> Result < ( ) > {
217
+ #[ cfg( windows) ]
218
+ mod unpacker {
219
+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
220
+ use std:: sync:: Arc ;
221
+ use threadpool;
222
+
223
+ use crate :: utils:: notifications:: Notification ;
224
+
225
+ pub struct Unpacker < ' a > {
226
+ n_files : Arc < AtomicUsize > ,
227
+ pool : threadpool:: ThreadPool ,
228
+ notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
229
+ }
230
+
231
+ impl < ' a > Unpacker < ' a > {
232
+ pub fn new ( notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ) -> Unpacker {
233
+ // Defaults to hardware thread count threads; this is suitable for
234
+ // our needs as IO bound operations tend to show up as write latencies
235
+ // rather than close latencies, so we don't need to look at
236
+ // more threads to get more IO dispatched at this stage in the process.
237
+ let pool = threadpool:: Builder :: new ( )
238
+ . thread_name ( "CloseHandle" . into ( ) )
239
+ . build ( ) ;
240
+ Unpacker {
241
+ n_files : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
242
+ pool : pool,
243
+ notify_handler : notify_handler,
244
+ }
245
+ }
246
+
247
+ pub fn handle ( & mut self , unpacked : tar:: Unpacked ) {
248
+ if let tar:: Unpacked :: File ( f) = unpacked {
249
+ self . n_files . fetch_add ( 1 , Ordering :: Relaxed ) ;
250
+ let n_files = self . n_files . clone ( ) ;
251
+ self . pool . execute ( move || {
252
+ drop ( f) ;
253
+ n_files. fetch_sub ( 1 , Ordering :: Relaxed ) ;
254
+ } ) ;
255
+ }
256
+ }
257
+ }
258
+
259
+ impl < ' a > Drop for Unpacker < ' a > {
260
+ fn drop ( & mut self ) {
261
+ // Some explanation is in order. Even though the tar we are reading from (if
262
+ // any) will have had its FileWithProgress download tracking
263
+ // completed before we hit drop, that is not true if we are unwinding due to a
264
+ // failure, where the logical ownership of the progress bar is
265
+ // ambiguous, and as the tracker itself is abstracted out behind
266
+ // notifications etc we cannot just query for that. So: we assume no
267
+ // more reads of the underlying tar will take place: either the
268
+ // error unwinding will stop reads, or we completed; either way, we
269
+ // notify finished to the tracker to force a reset to zero; we set
270
+ // the units to files, show our progress, and set our units back
271
+ // afterwards. The largest archives today - rust docs - have ~20k
272
+ // items, and the download tracker's progress is confounded with
273
+ // actual handling of data today, we synthesis a data buffer and
274
+ // pretend to have bytes to deliver.
275
+ self . notify_handler
276
+ . map ( |handler| handler ( Notification :: DownloadFinished ) ) ;
277
+ self . notify_handler
278
+ . map ( |handler| handler ( Notification :: DownloadPushUnits ( "handles" ) ) ) ;
279
+ let mut prev_files = self . n_files . load ( Ordering :: Relaxed ) ;
280
+ self . notify_handler . map ( |handler| {
281
+ handler ( Notification :: DownloadContentLengthReceived (
282
+ prev_files as u64 ,
283
+ ) )
284
+ } ) ;
285
+ if prev_files > 50 {
286
+ println ! ( "Closing {} deferred file handles" , prev_files) ;
287
+ }
288
+ let buf: Vec < u8 > = vec ! [ 0 ; prev_files] ;
289
+ assert ! ( 32767 > prev_files) ;
290
+ let mut current_files = prev_files;
291
+ while current_files != 0 {
292
+ use std:: thread:: sleep;
293
+ sleep ( std:: time:: Duration :: from_millis ( 100 ) ) ;
294
+ prev_files = current_files;
295
+ current_files = self . n_files . load ( Ordering :: Relaxed ) ;
296
+ let step_count = prev_files - current_files;
297
+ self . notify_handler . map ( |handler| {
298
+ handler ( Notification :: DownloadDataReceived ( & buf[ 0 ..step_count] ) )
299
+ } ) ;
300
+ }
301
+ self . pool . join ( ) ;
302
+ self . notify_handler
303
+ . map ( |handler| handler ( Notification :: DownloadFinished ) ) ;
304
+ self . notify_handler
305
+ . map ( |handler| handler ( Notification :: DownloadPopUnits ) ) ;
306
+ }
307
+ }
308
+ }
309
+
310
+ #[ cfg( not( windows) ) ]
311
+ mod unpacker {
312
+ use crate :: utils:: notifications:: Notification ;
313
+ pub struct Unpacker { }
314
+ impl Unpacker {
315
+ pub fn new < ' a > ( _notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ) -> Unpacker {
316
+ Unpacker { }
317
+ }
318
+ pub fn handle ( & mut self , _unpacked : tar:: Unpacked ) { }
319
+ }
320
+ }
321
+
322
+ fn unpack_without_first_dir < ' a , R : Read > (
323
+ archive : & mut tar:: Archive < R > ,
324
+ path : & Path ,
325
+ notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
326
+ ) -> Result < ( ) > {
327
+ let mut unpacker = unpacker:: Unpacker :: new ( notify_handler) ;
213
328
let entries = archive
214
329
. entries ( )
215
330
. chain_err ( || ErrorKind :: ExtractingPackage ) ?;
216
331
let mut checked_parents: HashSet < PathBuf > = HashSet :: new ( ) ;
332
+
217
333
for entry in entries {
218
334
let mut entry = entry. chain_err ( || ErrorKind :: ExtractingPackage ) ?;
219
335
let relpath = {
@@ -249,6 +365,7 @@ fn unpack_without_first_dir<R: Read>(archive: &mut tar::Archive<R>, path: &Path)
249
365
entry. set_preserve_mtime ( false ) ;
250
366
entry
251
367
. unpack ( & full_path)
368
+ . map ( |unpacked| unpacker. handle ( unpacked) )
252
369
. chain_err ( || ErrorKind :: ExtractingPackage ) ?;
253
370
}
254
371
@@ -277,9 +394,17 @@ impl<'a> Package for TarPackage<'a> {
277
394
pub struct TarGzPackage < ' a > ( TarPackage < ' a > ) ;
278
395
279
396
impl < ' a > TarGzPackage < ' a > {
280
- pub fn new < R : Read > ( stream : R , temp_cfg : & ' a temp:: Cfg ) -> Result < Self > {
397
+ pub fn new < R : Read > (
398
+ stream : R ,
399
+ temp_cfg : & ' a temp:: Cfg ,
400
+ notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
401
+ ) -> Result < Self > {
281
402
let stream = flate2:: read:: GzDecoder :: new ( stream) ;
282
- Ok ( TarGzPackage ( TarPackage :: new ( stream, temp_cfg) ?) )
403
+ Ok ( TarGzPackage ( TarPackage :: new (
404
+ stream,
405
+ temp_cfg,
406
+ notify_handler,
407
+ ) ?) )
283
408
}
284
409
}
285
410
@@ -305,9 +430,17 @@ impl<'a> Package for TarGzPackage<'a> {
305
430
pub struct TarXzPackage < ' a > ( TarPackage < ' a > ) ;
306
431
307
432
impl < ' a > TarXzPackage < ' a > {
308
- pub fn new < R : Read > ( stream : R , temp_cfg : & ' a temp:: Cfg ) -> Result < Self > {
433
+ pub fn new < R : Read > (
434
+ stream : R ,
435
+ temp_cfg : & ' a temp:: Cfg ,
436
+ notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
437
+ ) -> Result < Self > {
309
438
let stream = xz2:: read:: XzDecoder :: new ( stream) ;
310
- Ok ( TarXzPackage ( TarPackage :: new ( stream, temp_cfg) ?) )
439
+ Ok ( TarXzPackage ( TarPackage :: new (
440
+ stream,
441
+ temp_cfg,
442
+ notify_handler,
443
+ ) ?) )
311
444
}
312
445
}
313
446
0 commit comments