Bug 827749 - Aggressively duplicate file handles. r=cjones.
authorBen Turner <bent.mozilla@gmail.com>
Thu, 10 Jan 2013 19:15:57 +0100
changeset 118458 4d0c643ddbae5bc791f256d5fdd6c4897098ffa2
parent 118457 9038e0975bdc29b2080de0145d17a83bbd36e870
child 118459 e70316a75be9e479cfdc0a06aba0dbc1eedf08da
push id24166
push userMs2ger@gmail.com
push dateFri, 11 Jan 2013 13:57:41 +0000
treeherdermozilla-central@63c4b0f66a0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs827749
milestone21.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 827749 - Aggressively duplicate file handles. r=cjones.
ipc/glue/FileDescriptor.cpp
ipc/glue/FileDescriptor.h
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -3,63 +3,116 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileDescriptor.h"
 
 #include "mozilla/Assertions.h"
 #include "nsDebug.h"
 
 #ifdef XP_WIN
+
 #include <windows.h>
 #define INVALID_HANDLE INVALID_HANDLE_VALUE
-#else
+
+#else // XP_WIN
+
 #include <unistd.h>
+
+#ifndef OS_POSIX
+#define OS_POSIX
+#endif
+
+#include "base/eintr_wrapper.h"
 #define INVALID_HANDLE -1
-#endif
+
+#endif // XP_WIN
 
 using mozilla::ipc::FileDescriptor;
 
 FileDescriptor::FileDescriptor()
-: mHandle(INVALID_HANDLE)
+: mHandle(INVALID_HANDLE), mHandleCreatedByOtherProcess(false),
+  mHandleCreatedByOtherProcessWasUsed(false)
 { }
 
+FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
+: mHandle(INVALID_HANDLE), mHandleCreatedByOtherProcess(false),
+  mHandleCreatedByOtherProcessWasUsed(false)
+{
+  DuplicateInCurrentProcess(aHandle);
+}
+
+void
+FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
+{
+  MOZ_ASSERT(!mHandleCreatedByOtherProcess);
+
+  if (IsValid(aHandle)) {
+    PlatformHandleType newHandle;
+#ifdef XP_WIN
+    if (DuplicateHandle(GetCurrentProcess(), aHandle, GetCurrentProcess(),
+                        &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+#else // XP_WIN
+    if ((newHandle = dup(aHandle)) != INVALID_HANDLE) {
+#endif
+      mHandle = newHandle;
+      return;
+    }
+    NS_WARNING("Failed to duplicate file descriptor!");
+  }
+
+  mHandle = INVALID_HANDLE;
+}
+
+void
+FileDescriptor::CloseCurrentProcessHandle()
+{
+  MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
+                mHandleCreatedByOtherProcessWasUsed);
+
+  // Don't actually close handles created by another process.
+  if (mHandleCreatedByOtherProcess) {
+    return;
+  }
+
+  if (IsValid()) {
+#ifdef XP_WIN
+    if (!CloseHandle(mHandle)) {
+      NS_WARNING("Failed to close file handle!");
+    }
+#else // XP_WIN
+    HANDLE_EINTR(close(mHandle));
+#endif
+    mHandle = INVALID_HANDLE;
+  }
+}
+
 FileDescriptor::PickleType
 FileDescriptor::ShareTo(const FileDescriptor::IPDLPrivate&,
                         FileDescriptor::ProcessHandle aOtherProcess) const
 {
+  PlatformHandleType newHandle;
 #ifdef XP_WIN
-  if (mHandle == INVALID_HANDLE) {
-    return INVALID_HANDLE;
+  if (IsValid()) {
+    if (DuplicateHandle(GetCurrentProcess(), mHandle, aOtherProcess,
+                        &newHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+      return newHandle;
+    }
+    NS_WARNING("Failed to duplicate file handle!");
   }
-
-  PlatformHandleType newHandle;
-  if (!DuplicateHandle(GetCurrentProcess(), mHandle, aOtherProcess, &newHandle,
-                       0, FALSE, DUPLICATE_SAME_ACCESS)) {
-    NS_WARNING("Failed to duplicate file handle!");
-    return INVALID_HANDLE;
-  }
-
-  return newHandle;
+  return INVALID_HANDLE;
 #else // XP_WIN
-  if (mHandle == INVALID_HANDLE) {
-    return base::FileDescriptor();
+  if (IsValid()) {
+    newHandle = dup(mHandle);
+    return base::FileDescriptor(newHandle, /* auto_close */ true);
   }
-
-  PlatformHandleType newHandle = dup(mHandle);
-  if (newHandle < 0) {
-    NS_WARNING("Failed to duplicate file descriptor!");
-    return base::FileDescriptor();
-  }
-
-  // This file descriptor must be closed once the caller is done using it, so
-  // pass true here for the 'auto_close' argument.
-  return base::FileDescriptor(newHandle, true);
+  return base::FileDescriptor();
 #endif
 
   MOZ_NOT_REACHED("Must not get here!");
   return PickleType();
 }
 
+// static
 bool
-FileDescriptor::IsValid() const
+FileDescriptor::IsValid(PlatformHandleType aHandle)
 {
-  return mHandle != INVALID_HANDLE;
+  return aHandle != INVALID_HANDLE;
 }
--- a/ipc/glue/FileDescriptor.h
+++ b/ipc/glue/FileDescriptor.h
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_FileDescriptor_h
 #define mozilla_ipc_FileDescriptor_h
 
 #include "base/basictypes.h"
 #include "base/process.h"
+#include "mozilla/DebugOnly.h"
 #include "nscore.h"
 
 #ifdef XP_WIN
 // Need the HANDLE typedef.
 #include <winnt.h>
 #else
 #include "base/file_descriptor_posix.h"
 #endif
@@ -45,51 +46,105 @@ public:
 #endif
 
   // This should only ever be created by IPDL.
   struct IPDLPrivate
   {};
 
   FileDescriptor();
 
-  FileDescriptor(PlatformHandleType aHandle)
-  : mHandle(aHandle)
-  { }
+  FileDescriptor(const FileDescriptor& aOther)
+  {
+    *this = aOther;
+  }
+
+  FileDescriptor(PlatformHandleType aHandle);
 
   FileDescriptor(const IPDLPrivate&, const PickleType& aPickle)
 #ifdef XP_WIN
   : mHandle(aPickle)
 #else
   : mHandle(aPickle.fd)
 #endif
+  , mHandleCreatedByOtherProcess(true)
+  , mHandleCreatedByOtherProcessWasUsed(false)
   { }
 
+  ~FileDescriptor()
+  {
+    CloseCurrentProcessHandle();
+  }
+
+  FileDescriptor&
+  operator=(const FileDescriptor& aOther)
+  {
+    CloseCurrentProcessHandle();
+
+    if (aOther.mHandleCreatedByOtherProcess) {
+      mHandleCreatedByOtherProcess = true;
+      mHandleCreatedByOtherProcessWasUsed =
+        aOther.mHandleCreatedByOtherProcessWasUsed;
+      mHandle = aOther.PlatformHandle();
+    } else {
+      DuplicateInCurrentProcess(aOther.PlatformHandle());
+      mHandleCreatedByOtherProcess = false;
+      mHandleCreatedByOtherProcessWasUsed = false;
+    }
+
+    return *this;
+  }
+
   // Performs platform-specific actions to duplicate mHandle in the other
   // process (e.g. dup() on POSIX, DuplicateHandle() on Windows). Returns a
   // pickled value that can be passed to the other process via IPC.
   PickleType
   ShareTo(const IPDLPrivate&, ProcessHandle aOtherProcess) const;
 
   // Tests mHandle against a well-known invalid platform-specific file handle
   // (e.g. -1 on POSIX, INVALID_HANDLE_VALUE on Windows).
   bool
-  IsValid() const;
+  IsValid() const
+  {
+    return IsValid(mHandle);
+  }
 
   PlatformHandleType
   PlatformHandle() const
   {
+    if (mHandleCreatedByOtherProcess) {
+      mHandleCreatedByOtherProcessWasUsed = true;
+    }
     return mHandle;
   }
 
   bool
   operator==(const FileDescriptor& aOther) const
   {
     return mHandle == aOther.mHandle;
   }
 
 private:
+  static bool
+  IsValid(PlatformHandleType aHandle);
+
+  void
+  DuplicateInCurrentProcess(PlatformHandleType aHandle);
+
+  void
+  CloseCurrentProcessHandle();
+
   PlatformHandleType mHandle;
+
+  // If this is true then this instance is created by IPDL to ferry a handle to
+  // its eventual consumer and we never close the handle. If this is false then
+  // we are a RAII wrapper around the handle and we close the handle on
+  // destruction.
+  bool mHandleCreatedByOtherProcess;
+
+  // This is to ensure that we don't leak the handle (which is only possible
+  // when we're in the receiving process).
+  mutable DebugOnly<bool> mHandleCreatedByOtherProcessWasUsed;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_FileDescriptor_h