Bug 1063635 Part 2 - Call native writeAtomic code instead of JS backend when applicable. r=Yoric
☠☠ backed out by 8b985e3ae457 ☠ ☠
authorMilindL <i.milind.luthra@gmail.com>
Thu, 27 Jul 2017 19:41:00 +0530
changeset 378197 0d9eb13ac53e266dfd2f7b83fb8afe91961ea837
parent 378196 44ff3bc7bee5b58bc757751a029af048bd2ca683
child 378198 61a23e8fbf542270c5d796d321f28d368341672a
push id32426
push userkwierso@gmail.com
push dateFri, 01 Sep 2017 21:59:36 +0000
treeherdermozilla-central@73e8f351b28f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersYoric
bugs1063635
milestone57.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));
+      }
+    );
+  });
+};