@@ -57,6 +57,7 @@ mod test;
57
57
pub mod threaded;
58
58
59
59
use std:: io:: { self , Write } ;
60
+ use std:: ops:: { Deref , DerefMut } ;
60
61
use std:: path:: { Path , PathBuf } ;
61
62
use std:: sync:: mpsc:: Receiver ;
62
63
use std:: time:: { Duration , Instant } ;
@@ -66,12 +67,73 @@ use anyhow::{Context, Result};
66
67
67
68
use crate :: process;
68
69
use crate :: utils:: notifications:: Notification ;
70
+ use threaded:: PoolReference ;
71
+
72
+ /// Carries the implementation specific data for complete file transfers into the executor.
73
+ #[ derive( Debug ) ]
74
+ pub enum FileBuffer {
75
+ Immediate ( Vec < u8 > ) ,
76
+ // A reference to the object in the pool, and a handle to write to it
77
+ Threaded ( PoolReference ) ,
78
+ }
79
+
80
+ impl FileBuffer {
81
+ /// All the buffers space to be re-used when the last reference to it is dropped.
82
+ pub ( crate ) fn clear ( & mut self ) {
83
+ if let FileBuffer :: Threaded ( ref mut contents) = self {
84
+ contents. clear ( )
85
+ }
86
+ }
87
+
88
+ pub ( crate ) fn len ( & self ) -> usize {
89
+ match self {
90
+ FileBuffer :: Immediate ( ref vec) => vec. len ( ) ,
91
+ FileBuffer :: Threaded ( PoolReference :: Owned ( owned, _) ) => owned. len ( ) ,
92
+ FileBuffer :: Threaded ( PoolReference :: Mut ( mutable, _) ) => mutable. len ( ) ,
93
+ }
94
+ }
95
+
96
+ pub ( crate ) fn finished ( self ) -> Self {
97
+ match self {
98
+ FileBuffer :: Threaded ( PoolReference :: Mut ( mutable, pool) ) => {
99
+ FileBuffer :: Threaded ( PoolReference :: Owned ( mutable. downgrade ( ) , pool) )
100
+ }
101
+ _ => self ,
102
+ }
103
+ }
104
+ }
105
+
106
+ impl Deref for FileBuffer {
107
+ type Target = Vec < u8 > ;
108
+
109
+ fn deref ( & self ) -> & Self :: Target {
110
+ match self {
111
+ FileBuffer :: Immediate ( ref vec) => & vec,
112
+ FileBuffer :: Threaded ( PoolReference :: Owned ( owned, _) ) => owned,
113
+ FileBuffer :: Threaded ( PoolReference :: Mut ( mutable, _) ) => mutable,
114
+ }
115
+ }
116
+ }
117
+
118
+ impl DerefMut for FileBuffer {
119
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
120
+ match self {
121
+ FileBuffer :: Immediate ( ref mut vec) => vec,
122
+ FileBuffer :: Threaded ( PoolReference :: Owned ( _, _) ) => {
123
+ unimplemented ! ( )
124
+ }
125
+ FileBuffer :: Threaded ( PoolReference :: Mut ( mutable, _) ) => mutable,
126
+ }
127
+ }
128
+ }
129
+
130
+ pub ( crate ) const IO_CHUNK_SIZE : usize = 16_777_216 ;
69
131
70
132
/// Carries the implementation specific channel data into the executor.
71
133
#[ derive( Debug ) ]
72
134
pub enum IncrementalFile {
73
135
ImmediateReceiver ,
74
- ThreadedReceiver ( Receiver < Vec < u8 > > ) ,
136
+ ThreadedReceiver ( Receiver < FileBuffer > ) ,
75
137
}
76
138
77
139
// The basic idea is that in single threaded mode we get this pattern:
@@ -116,7 +178,7 @@ pub enum IncrementalFile {
116
178
#[ derive( Debug ) ]
117
179
pub enum Kind {
118
180
Directory ,
119
- File ( Vec < u8 > ) ,
181
+ File ( FileBuffer ) ,
120
182
IncrementalFile ( IncrementalFile ) ,
121
183
}
122
184
@@ -160,7 +222,7 @@ impl Item {
160
222
}
161
223
}
162
224
163
- pub fn write_file ( full_path : PathBuf , content : Vec < u8 > , mode : u32 ) -> Self {
225
+ pub fn write_file ( full_path : PathBuf , mode : u32 , content : FileBuffer ) -> Self {
164
226
let len = content. len ( ) ;
165
227
Self {
166
228
full_path,
@@ -177,7 +239,7 @@ impl Item {
177
239
full_path : PathBuf ,
178
240
mode : u32 ,
179
241
state : IncrementalFileState ,
180
- ) -> Result < ( Self , Box < dyn FnMut ( Vec < u8 > ) -> bool + ' a > ) > {
242
+ ) -> Result < ( Self , Box < dyn FnMut ( FileBuffer ) -> bool + ' a > ) > {
181
243
let ( chunk_submit, content_callback) = state. incremental_file_channel ( & full_path, mode) ?;
182
244
let result = Self {
183
245
full_path,
@@ -210,19 +272,19 @@ impl IncrementalFileState {
210
272
& self ,
211
273
path : & Path ,
212
274
mode : u32 ,
213
- ) -> Result < ( Box < dyn FnMut ( Vec < u8 > ) -> bool > , IncrementalFile ) > {
275
+ ) -> Result < ( Box < dyn FnMut ( FileBuffer ) -> bool > , IncrementalFile ) > {
214
276
use std:: sync:: mpsc:: channel;
215
277
match * self {
216
278
IncrementalFileState :: Threaded => {
217
- let ( tx, rx) = channel :: < Vec < u8 > > ( ) ;
279
+ let ( tx, rx) = channel :: < FileBuffer > ( ) ;
218
280
let content_callback = IncrementalFile :: ThreadedReceiver ( rx) ;
219
- let chunk_submit = move |chunk : Vec < u8 > | tx. send ( chunk) . is_ok ( ) ;
281
+ let chunk_submit = move |chunk : FileBuffer | tx. send ( chunk) . is_ok ( ) ;
220
282
Ok ( ( Box :: new ( chunk_submit) , content_callback) )
221
283
}
222
284
IncrementalFileState :: Immediate ( ref state) => {
223
285
let content_callback = IncrementalFile :: ImmediateReceiver ;
224
286
let mut writer = immediate:: IncrementalFileWriter :: new ( path, mode, state. clone ( ) ) ?;
225
- let chunk_submit = move |chunk : Vec < u8 > | writer. chunk_submit ( chunk) ;
287
+ let chunk_submit = move |chunk : FileBuffer | writer. chunk_submit ( chunk) ;
226
288
Ok ( ( Box :: new ( chunk_submit) , content_callback) )
227
289
}
228
290
}
@@ -258,6 +320,14 @@ pub trait Executor {
258
320
259
321
/// Get any state needed for incremental file processing
260
322
fn incremental_file_state ( & self ) -> IncrementalFileState ;
323
+
324
+ /// Get a disk buffer E.g. this gets the right sized pool object for
325
+ /// optimized situations, or just a malloc when optimisations are off etc
326
+ /// etc.
327
+ fn get_buffer ( & mut self , len : usize ) -> FileBuffer ;
328
+
329
+ /// Query the memory budget to see if a particular size buffer is available
330
+ fn buffer_available ( & self , len : usize ) -> bool ;
261
331
}
262
332
263
333
/// Trivial single threaded IO to be used from executors.
@@ -267,7 +337,17 @@ pub fn perform<F: Fn(usize)>(item: &mut Item, chunk_complete_callback: F) {
267
337
// Files, write them.
268
338
item. result = match & mut item. kind {
269
339
Kind :: Directory => create_dir ( & item. full_path ) ,
270
- Kind :: File ( ref contents) => write_file ( & item. full_path , & contents, item. mode ) ,
340
+ Kind :: File ( ref mut contents) => {
341
+ contents. clear ( ) ;
342
+ match contents {
343
+ FileBuffer :: Immediate ( ref contents) => {
344
+ write_file ( & item. full_path , & contents, item. mode )
345
+ }
346
+ FileBuffer :: Threaded ( ref mut contents) => {
347
+ write_file ( & item. full_path , & contents, item. mode )
348
+ }
349
+ }
350
+ }
271
351
Kind :: IncrementalFile ( incremental_file) => write_file_incremental (
272
352
& item. full_path ,
273
353
incremental_file,
@@ -367,6 +447,7 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
367
447
/// Get the executor for disk IO.
368
448
pub fn get_executor < ' a > (
369
449
notify_handler : Option < & ' a dyn Fn ( Notification < ' _ > ) > ,
450
+ ram_budget : usize ,
370
451
) -> Result < Box < dyn Executor + ' a > > {
371
452
// If this gets lots of use, consider exposing via the config file.
372
453
let thread_count = match process ( ) . var ( "RUSTUP_IO_THREADS" ) {
@@ -377,6 +458,6 @@ pub fn get_executor<'a>(
377
458
} ;
378
459
Ok ( match thread_count {
379
460
0 | 1 => Box :: new ( immediate:: ImmediateUnpacker :: new ( ) ) ,
380
- n => Box :: new ( threaded:: Threaded :: new ( notify_handler, n) ) ,
461
+ n => Box :: new ( threaded:: Threaded :: new ( notify_handler, n, ram_budget ) ) ,
381
462
} )
382
463
}
0 commit comments