1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
/* You may find the license in the LICENSE file */
const EXPORTED_SYMBOLS = [
'prealloc'
];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const module = Cu.import;
const Exception = Components.Exception;
const FileOutputStream = Components.Constructor('@mozilla.org/network/file-output-stream;1', 'nsIFileOutputStream', 'init');
module('resource://dta/utils.jsm');
module('resource://dta/version.jsm');
module('resource://dta/cothread.jsm');
Debug.logString("Using CoThread implementation");
// Should we use the optimized Windows implementation?
const WINDOWSIMPL = Version.OS == 'winnt';
// Size cap: Use Windows implementation (on Windows) even if run on main thread
const WINDOWSIMPL_SIZEMAX = (1 << 25); // 32MB
//Minimum size of a preallocation.
//If requested size is less then no actual pre-allocation will be performed.
const SIZE_MIN = (WINDOWSIMPL ? 30 : 2048) * 1024;
//Step size of the allocation
//Do this step wise to avoid certain "sparse files" cases
const SIZE_STEP = (1 << 23); // 8MB
/**
* Pre-allocates a given file on disk
* and calls given callback when done
*
* @param file (nsIFile) file to allocate
* @param size (int) Size to allocate
* @param perms (int) *nix file permissions
* @param callback (function) Callback called once done
* @param tp (function) Scope (this) to call the callback function in
* @return (nsICancelable) Pre-allocation object.
*/
function prealloc(file, size, perms, callback, tp) {
tp = tp || null;
callback = (callback || function(){}).bind(tp);
if (size <= SIZE_MIN || !isFinite(size)) {
Debug.logString("pa: not preallocating");
callback(false);
return null;
}
return new WorkerJob(file, size, perms, callback);
}
function WorkerJob(file, size, perms, callback) {
this.file = file;
this.size = size;
this.perms = perms;
this.callback = callback;
try {
this._stream = new FileOutputStream(this.file, 0x02 | 0x08, this.perms, 0);
}
catch (ex) {
this.callback(false);
return;
}
let g = this.run.bind(this);
this.coThread = new CoThreadInterleaved((i for (i in g())), 1);
this.coThread.run(this.finish.bind(this));
}
WorkerJob.prototype = {
result: false,
run: function worker_run() {
if (WINDOWSIMPL && this.size < WINDOWSIMPL_SIZEMAX) {
for (let i in this._run_windows()) yield i;
}
else {
for (let i in this._run_other()) yield i;
}
},
finish: function() {
this._close();
delete this.coThread;
this.callback(this.result);
},
_run_windows: function worker_run_windows() {
let size = this.size;
try {
let seekable = this._stream.QueryInterface(Ci.nsISeekableStream);
seekable.seek(0x02, 0);
size -= seekable.tell();
while (!this.terminated && size > 0) {
let count = Math.min(size, 1 << 26 /* 64MB */);
size -= count;
seekable.seek(0x01, count);
seekable.setEOF();
yield true;
}
this.result = true;
}
catch (ex) {
Debug.log("pa: Windows implementation failed!", ex);
for (let i in this._run_other()) yield i;
}
},
_run_other: function worker_run_other() {
try {
let seekable = this._stream.QueryInterface(Ci.nsISeekableStream);
let i = seekable.tell();
if (i < this.size - 1) {
i += SIZE_STEP;
for (; !this.terminated && i < this.size + SIZE_STEP; i += SIZE_STEP) {
seekable.seek(0x00, Math.min(i, this.size - 1));
seekable.write("a", 1);
yield true;
}
this.result = true;
}
}
catch (ex) {
Debug.log("pa: Failed to run prealloc loop", ex);
}
},
_close: function() {
try { this._stream.close(); } catch (ex) { }
delete this._stream;
},
cancel: function() {
this.terminated = true;
this._close();
}
};
|