diff --git a/base/filesystem/file_util.h b/base/filesystem/file_util.h
index ca17903d..83f605e2 100644
--- a/base/filesystem/file_util.h
+++ b/base/filesystem/file_util.h
@@ -26,6 +26,13 @@
#include
+// maps CreateDirectory to CreateDirectoryW. Declare and call the
+// base function under its own name whether or not a Windows header came first,
+// as environment_variables.h does for its names.
+#if defined(CreateDirectory)
+#undef CreateDirectory
+#endif
+
namespace base {
// Create a directory within another directory.
diff --git a/base/filesystem/platform_file.h b/base/filesystem/platform_file.h
index 8bb25552..b663b7ca 100644
--- a/base/filesystem/platform_file.h
+++ b/base/filesystem/platform_file.h
@@ -7,7 +7,9 @@
#include "build/build_config.h"
#if defined(OS_WIN)
-#include
+// minwin.h, not : this header reaches every user of base::File, and
+// 's macros (near, far, MemoryBarrier, ...) must not follow it there.
+#include "base/win/minwin.h"
#include "base/win/scoped_handle.h"
#endif
diff --git a/base/filesystem/win/file_util_win.cc b/base/filesystem/win/file_util_win.cc
index 67a4f3df..86c0bf53 100644
--- a/base/filesystem/win/file_util_win.cc
+++ b/base/filesystem/win/file_util_win.cc
@@ -9,6 +9,9 @@
#include "base/strings/format.h"
#include
+// This file defines base::CreateDirectory; just mapped the name to
+// CreateDirectoryW. The Win32 call below is spelled CreateDirectoryW directly.
+#undef CreateDirectory
#include
namespace base {
diff --git a/base/filesystem/win/memory_mapped_file_win.cc b/base/filesystem/win/memory_mapped_file_win.cc
index 2152ebeb..e1178104 100644
--- a/base/filesystem/win/memory_mapped_file_win.cc
+++ b/base/filesystem/win/memory_mapped_file_win.cc
@@ -4,6 +4,8 @@
#include
#include
+#include
+
namespace base {
bool MemoryMappedFile::Map() {
diff --git a/base/threading/mutex.h b/base/threading/mutex.h
index 2b2b1c09..0505cd06 100644
--- a/base/threading/mutex.h
+++ b/base/threading/mutex.h
@@ -28,8 +28,6 @@ using SharedMutex = std::shared_mutex;
#if defined(__linux__) || defined(OS_LINUX) || defined(OS_POSIX)
#include
-#elif defined(_WIN32) || defined(OS_WIN)
-#include
#endif
namespace base {
@@ -60,24 +58,22 @@ class SharedMutex {
#elif defined(_WIN32) || defined(OS_WIN)
-class SharedMutex {
+// Out of line (mutex_win.cc) so this header never pulls a Windows header and
+// its macros into the including file.
+class BASE_EXPORT SharedMutex {
public:
constexpr SharedMutex() noexcept = default;
SharedMutex(const SharedMutex&) = delete;
SharedMutex& operator=(const SharedMutex&) = delete;
- void lock() { ::AcquireSRWLockExclusive(reinterpret_cast(&lock_)); }
- void unlock() { ::ReleaseSRWLockExclusive(reinterpret_cast(&lock_)); }
- bool try_lock() {
- return !!::TryAcquireSRWLockExclusive(reinterpret_cast(&lock_));
- }
-
- void lock_shared() { ::AcquireSRWLockShared(reinterpret_cast(&lock_)); }
- void unlock_shared() { ::ReleaseSRWLockShared(reinterpret_cast(&lock_)); }
- bool try_lock_shared() {
- return !!::TryAcquireSRWLockShared(reinterpret_cast(&lock_));
- }
+ void lock();
+ void unlock();
+ bool try_lock();
+
+ void lock_shared();
+ void unlock_shared();
+ bool try_lock_shared();
private:
// Matches the SRWLOCK ABI (single void*). SRWLOCK_INIT is {nullptr}.
diff --git a/base/threading/mutex_test.cc b/base/threading/mutex_test.cc
new file mode 100644
index 00000000..c2c033af
--- /dev/null
+++ b/base/threading/mutex_test.cc
@@ -0,0 +1,80 @@
+// Copyright (C) 2026 Vincent Hengel.
+// For licensing information see LICENSE at the root of this distribution.
+
+// These headers come first, before anything else can pull in : they
+// reach nearly every file and must not bring its macros along, or names like
+// MemoryBarrier or near get rewritten under them.
+#include
+#include
+
+#if defined(MemoryBarrier) || defined(near) || defined(far) || defined(min) || defined(max)
+#error "a base lock header leaked a Windows macro"
+#endif
+
+// base::File needs HANDLE, which it takes from minwin.h. On mingw minwin.h is
+// all of , so only the other compilers can hold it to this.
+#include
+
+#if !defined(__MINGW32__) && \
+ (defined(MemoryBarrier) || defined(near) || defined(far) || defined(min) || defined(max))
+#error "base/filesystem/file.h leaked a Windows macro"
+#endif
+
+#include
+
+#include
+#include
+#include
+
+namespace {
+
+TEST(SpinningMutex, TryFailsWhileHeld) {
+ base::SpinningMutex mutex;
+ mutex.lock();
+ EXPECT_FALSE(mutex.try_lock());
+ mutex.unlock();
+ EXPECT_TRUE(mutex.try_lock());
+ mutex.unlock();
+}
+
+TEST(SpinningMutex, SerializesIncrements) {
+ constexpr int kThreads = 4;
+ constexpr int kIterations = 20000;
+ base::SpinningMutex mutex;
+ int counter = 0;
+ base::Thread* threads[kThreads];
+ for (auto& t : threads) {
+ t = new base::Thread(
+ "mutex-inc",
+ [&] {
+ for (int i = 0; i < kIterations; i++) {
+ base::LockGuard guard(mutex);
+ ++counter;
+ }
+ },
+ /*start_now=*/true);
+ }
+ for (auto* t : threads) {
+ EXPECT_TRUE(t->Join());
+ delete t;
+ }
+ EXPECT_EQ(counter, kThreads * kIterations);
+}
+
+TEST(SharedMutex, ReadersShareWritersExclude) {
+ base::SharedMutex mutex;
+ mutex.lock_shared();
+ EXPECT_TRUE(mutex.try_lock_shared());
+ EXPECT_FALSE(mutex.try_lock());
+ mutex.unlock_shared();
+ mutex.unlock_shared();
+
+ mutex.lock();
+ EXPECT_FALSE(mutex.try_lock_shared());
+ EXPECT_FALSE(mutex.try_lock());
+ mutex.unlock();
+ EXPECT_TRUE(mutex.try_lock());
+ mutex.unlock();
+}
+
+} // namespace
diff --git a/base/threading/mutex_win.cc b/base/threading/mutex_win.cc
new file mode 100644
index 00000000..af584cea
--- /dev/null
+++ b/base/threading/mutex_win.cc
@@ -0,0 +1,42 @@
+// Copyright (C) 2026 Vincent Hengel.
+// For licensing information see LICENSE at the root of this distribution.
+//
+// SharedMutex on SRWLOCK. Out of line rather than inline in mutex.h so that
+// minwin.h, which on mingw is all of , stays out of every file that
+// merely holds a mutex: its macros (MemoryBarrier, near, far, ...) would
+// otherwise rewrite unrelated names there.
+
+#include
+#include
+
+namespace base {
+
+#if !defined(BASE_USE_STD_MUTEX) || !BASE_USE_STD_MUTEX
+
+void SharedMutex::lock() {
+ ::AcquireSRWLockExclusive(reinterpret_cast(&lock_));
+}
+
+void SharedMutex::unlock() {
+ ::ReleaseSRWLockExclusive(reinterpret_cast(&lock_));
+}
+
+bool SharedMutex::try_lock() {
+ return !!::TryAcquireSRWLockExclusive(reinterpret_cast(&lock_));
+}
+
+void SharedMutex::lock_shared() {
+ ::AcquireSRWLockShared(reinterpret_cast(&lock_));
+}
+
+void SharedMutex::unlock_shared() {
+ ::ReleaseSRWLockShared(reinterpret_cast(&lock_));
+}
+
+bool SharedMutex::try_lock_shared() {
+ return !!::TryAcquireSRWLockShared(reinterpret_cast(&lock_));
+}
+
+#endif
+
+} // namespace base
diff --git a/base/threading/spinning_mutex.cc b/base/threading/spinning_mutex.cc
index 980269ee..6aed3cc2 100644
--- a/base/threading/spinning_mutex.cc
+++ b/base/threading/spinning_mutex.cc
@@ -7,6 +7,7 @@
#if defined(_WIN32) || defined(OS_WIN)
#include
+#include
#define PA_YIELD_PROCESSOR _mm_pause()
#elif defined(__x86_64__) || defined(__i386__)
#define PA_YIELD_PROCESSOR __asm__ __volatile__("pause")
@@ -25,6 +26,22 @@
namespace base {
+#if defined(_WIN32) || defined(OS_WIN)
+// Out of line on Windows so spinning_mutex.h never needs minwin.h, which on
+// mingw is all of . lock_ has SRWLOCK's layout, a single pointer.
+void SpinningMutex::LockSlow() {
+ ::AcquireSRWLockExclusive(reinterpret_cast(&lock_));
+}
+
+bool SpinningMutex::Try() {
+ return !!::TryAcquireSRWLockExclusive(reinterpret_cast(&lock_));
+}
+
+void SpinningMutex::Release() {
+ ::ReleaseSRWLockExclusive(reinterpret_cast(&lock_));
+}
+#endif
+
void SpinningMutex::Reinit() {
Release();
}
diff --git a/base/threading/spinning_mutex.h b/base/threading/spinning_mutex.h
index 23409b59..12436b41 100644
--- a/base/threading/spinning_mutex.h
+++ b/base/threading/spinning_mutex.h
@@ -11,8 +11,6 @@
#include
#include
#include
-#elif defined(_WIN32) || defined(OS_WIN)
-#include
#endif
namespace base {
@@ -21,8 +19,11 @@ class BASE_EXPORT SpinningMutex {
public:
inline constexpr SpinningMutex();
STRONG_INLINE void Acquire();
- STRONG_INLINE void Release();
- STRONG_INLINE bool Try();
+ // Inline on POSIX; out of line on Windows (spinning_mutex.cc), so that
+ // this header, which every base::Mutex user includes, never pulls a Windows
+ // header and its macros into the including file.
+ void Release();
+ bool Try();
void AssertAcquired() const {}
void Reinit();
@@ -50,10 +51,8 @@ class BASE_EXPORT SpinningMutex {
base::Atomic state_{kUnlocked};
#elif defined(_WIN32) || defined(OS_WIN)
- struct PA_CHROME_SRWLOCK {
- void* Ptr;
- };
- PA_CHROME_SRWLOCK lock_{SRWLOCK_INIT};
+ // SRWLOCK's layout, a single pointer; SRWLOCK_INIT is all zero.
+ void* lock_ = nullptr;
#endif
};
@@ -83,19 +82,4 @@ STRONG_INLINE void SpinningMutex::Release() {
}
#endif
-#if defined(_WIN32) || defined(OS_WIN)
-
-STRONG_INLINE void SpinningMutex::LockSlow() {
- ::AcquireSRWLockExclusive(reinterpret_cast(&lock_));
-}
-
-STRONG_INLINE bool SpinningMutex::Try() {
- return !!::TryAcquireSRWLockExclusive(reinterpret_cast(&lock_));
-}
-
-STRONG_INLINE void SpinningMutex::Release() {
- ::ReleaseSRWLockExclusive(reinterpret_cast(&lock_));
-}
-#endif
-
} // namespace base