Bug 1063635 Part 2 - Call native writeAtomic code instead of JS backend when applicable. r=Yoric
authorMilindL <i.milind.luthra@gmail.com>
Thu, 27 Jul 2017 19:41:00 +0530
changeset 386284 71f8ad3208d996e35ce44bc0718b859d77dde4fe
parent 386283 2a9fc9a1825be8787184d344c26f30d3a61513eb
child 386285 09157e44e113b4fdb403623a8b9c78c75f188f26
push id53324
push userdteller@mozilla.com
push dateSat, 14 Oct 2017 20:46:48 +0000
treeherderautoland@09157e44e113 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1063635
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1063635 Part 2 - Call native writeAtomic code instead of JS backend when applicable. r=Yoric MozReview-Commit-ID: fiIS2xPc2r
toolkit/components/osfile/modules/osfile_async_front.jsm
toolkit/components/osfile/modules/osfile_native.jsm
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -1164,32 +1164,41 @@ File.exists = function exists(path) {
  * If the process or the operating system freezes or crashes
  * during the short window between these operations,
  * the destination file will have been moved to its backup.
  *
  * @return {promise}
  * @resolves {number} The number of bytes actually written.
  */
 File.writeAtomic = function writeAtomic(path, buffer, options = {}) {
+  const useNativeImplementation = nativeWheneverAvailable &&
+                                  !options.compression &&
+                                  !(isTypedArray(buffer) && "byteOffset" in buffer && buffer.byteOffset > 0);
   // Copy |options| to avoid modifying the original object but preserve the
   // reference to |outExecutionDuration|, |outSerializationDuration| option if it is passed.
   options = clone(options, ["outExecutionDuration", "outSerializationDuration"]);
-  // As options.tmpPath is a path, we need to encode it as |Type.path| message
-  if ("tmpPath" in options) {
+  // As options.tmpPath is a path, we need to encode it as |Type.path| message, but only
+  // if we are not using the native implementation.
+  if ("tmpPath" in options && !useNativeImplementation) {
     options.tmpPath = Type.path.toMsg(options.tmpPath);
   }
   if (isTypedArray(buffer) && (!("bytes" in options))) {
     options.bytes = buffer.byteLength;
   }
   let refObj = {};
+  let promise;
   TelemetryStopwatch.start("OSFILE_WRITEATOMIC_JANK_MS", refObj);
-  let promise = Scheduler.post("writeAtomic",
+  if (useNativeImplementation) {
+    promise = Scheduler.push(() => Native.writeAtomic(path, buffer, options));
+  } else {
+  promise = Scheduler.post("writeAtomic",
     [Type.path.toMsg(path),
      Type.void_t.in_ptr.toMsg(buffer),
      options], [options, buffer, path]);
+  }
   TelemetryStopwatch.finish("OSFILE_WRITEATOMIC_JANK_MS", refObj);
   return promise;
 };
 
 File.removeDir = function(path, options = {}) {
   return Scheduler.post("removeDir",
     [Type.path.toMsg(path), options], path);
 };
--- a/toolkit/components/osfile/modules/osfile_native.jsm
+++ b/toolkit/components/osfile/modules/osfile_native.jsm
@@ -62,8 +62,55 @@ this.read = function(path, options = {})
         resolve(success.result);
       },
       function onError(operation, oserror) {
         reject(new SysAll.Error(operation, oserror, path));
       }
     );
   });
 };
+
+/**
+ * Native implementation of OS.File.writeAtomic.
+ * This should not be called when |buffer| is a view with some non-zero byte offset.
+ * Does not handle option |compression|.
+ */
+this.writeAtomic = function(path, buffer, options = {}) {
+  // Sanity check on types of options - we check only the encoding, since
+  // the others are checked inside Internals.writeAtomic.
+  if ("encoding" in options && typeof options.encoding !== "string") {
+    return Promise.reject(new TypeError("Invalid type for option encoding"));
+  }
+
+  if (typeof buffer == "string") {
+    // Normalize buffer to a C buffer by encoding it
+    let encoding = options.encoding || "utf-8";
+    buffer = new TextEncoder(encoding).encode(buffer);
+  }
+
+  if (ArrayBuffer.isView(buffer)) {
+    // We need to throw an error if it's a buffer with some byte offset.
+    if ("byteOffset" in buffer && buffer.byteOffset > 0) {
+      return Promise.reject(new Error("Invalid non-zero value of Typed Array byte offset"));
+    }
+    buffer = buffer.buffer;
+  }
+
+  return new Promise((resolve, reject) => {
+    Internals.writeAtomic(
+      path,
+      buffer,
+      options,
+      function onSuccess(success) {
+        success.QueryInterface(Ci.nsINativeOSFileResult);
+        if ("outExecutionDuration" in options) {
+          options.outExecutionDuration =
+            success.executionDurationMS +
+            (options.outExecutionDuration || 0);
+        }
+        resolve(success.result);
+      },
+      function onError(operation, oserror) {
+        reject(new SysAll.Error(operation, oserror, path));
+      }
+    );
+  });
+};