Add a utility for disabling some CRT assertions in debug mode
authorChristopher Dykes <cdykes@fb.com>
Wed, 10 Aug 2016 00:00:14 +0000 (17:00 -0700)
committerFacebook Github Bot 3 <facebook-github-bot-3-bot@fb.com>
Wed, 10 Aug 2016 00:08:33 +0000 (17:08 -0700)
Summary:
See the comment on `msvcReturnInvalidParams` for more info on the issue.
This also adds a use of it to TestUtilTest.

Reviewed By: yfeldblum

Differential Revision: D3691526

fbshipit-source-id: f0f596e9b4c830e1d31de01926162636757329e8

folly/experimental/TestUtil.cpp
folly/experimental/TestUtil.h
folly/experimental/test/TestUtilTest.cpp

index 9657211400c36a4182c97e6f6eb3b70d386b2f88..138be1a674fd6b445c302b29bb3e9fa44c11acd0 100644 (file)
 #include <folly/portability/Fcntl.h>
 #include <folly/portability/Unistd.h>
 
+#ifdef _WIN32
+#include <crtdbg.h>
+#endif
+
 namespace folly {
 namespace test {
 
@@ -128,6 +132,32 @@ ChangeToTempDir::~ChangeToTempDir() {
 
 namespace detail {
 
+SavedState disableInvalidParameters() {
+#ifdef _WIN32
+  SavedState ret;
+  ret.previousThreadLocalHandler = _set_thread_local_invalid_parameter_handler(
+      [](const wchar_t*,
+         const wchar_t*,
+         const wchar_t*,
+         unsigned int,
+         uintptr_t) {});
+  ret.previousCrtReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
+  return ret;
+#else
+  return SavedState();
+#endif
+}
+
+#ifdef _WIN32
+void enableInvalidParameters(SavedState state) {
+  _set_thread_local_invalid_parameter_handler(
+      (_invalid_parameter_handler)state.previousThreadLocalHandler);
+  _CrtSetReportMode(_CRT_ASSERT, state.previousCrtReportMode);
+}
+#else
+void enableInvalidParameters(SavedState) {}
+#endif
+
 bool hasPCREPatternMatch(StringPiece pattern, StringPiece target) {
   return boost::regex_match(
     target.begin(),
index 5b4112febb6440d555a04e644f32e9c1e1336bcf..828ca1fd0ad808ab248b0a96388e652ee9b7b764 100644 (file)
@@ -19,6 +19,7 @@
 #include <map>
 #include <string>
 #include <folly/Range.h>
+#include <folly/ScopeGuard.h>
 #include <folly/experimental/io/FsUtil.h>
 
 namespace folly {
@@ -118,6 +119,33 @@ private:
   TemporaryDirectory dir_;
 };
 
+namespace detail {
+struct SavedState {
+  void* previousThreadLocalHandler;
+  int previousCrtReportMode;
+};
+SavedState disableInvalidParameters();
+void enableInvalidParameters(SavedState state);
+}
+
+// Ok, so fun fact: The CRT on windows will actually abort
+// on certain failed parameter validation checks in debug
+// mode rather than simply returning -1 as it does in release
+// mode. We can however, ensure consistent behavior by
+// registering our own thread-local invalid parameter handler
+// for the duration of the call, and just have that handler
+// immediately return. We also have to disable CRT asertion
+// alerts for the duration of the call, otherwise we get
+// the abort-retry-ignore window.
+template <typename Func>
+auto msvcSuppressAbortOnInvalidParams(Func func) -> decltype(func()) {
+  auto savedState = detail::disableInvalidParameters();
+  SCOPE_EXIT {
+    detail::enableInvalidParameters(savedState);
+  };
+  return func();
+}
+
 /**
  * Easy PCRE regex matching. Note that pattern must match the ENTIRE target,
  * so use .* at the start and end of the pattern, as appropriate.  See
index 8c5329a7b85f3546953b763a729cfd0b3808d84c..3110f6fa4bb7ceecd9eae3103129b9da3bbe5199 100644 (file)
@@ -45,13 +45,15 @@ TEST(TemporaryFile, Simple) {
     EXPECT_EQ(1, r);
   }
 
-  // The file must have been closed.  This assumes that no other thread
-  // has opened another file in the meanwhile, which is a sane assumption
-  // to make in this test.
-  ssize_t r = write(fd, &c, 1);
-  int savedErrno = errno;
-  EXPECT_EQ(-1, r);
-  EXPECT_EQ(EBADF, savedErrno);
+  msvcSuppressAbortOnInvalidParams([&] {
+    // The file must have been closed.  This assumes that no other thread
+    // has opened another file in the meanwhile, which is a sane assumption
+    // to make in this test.
+    ssize_t r = write(fd, &c, 1);
+    int savedErrno = errno;
+    EXPECT_EQ(-1, r);
+    EXPECT_EQ(EBADF, savedErrno);
+  });
 }
 
 TEST(TemporaryFile, Prefix) {