Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions base/filesystem/file_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@

#include <base/arch.h>

// <windows.h> 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.
Expand Down
4 changes: 3 additions & 1 deletion base/filesystem/platform_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
// minwin.h, not <windows.h>: this header reaches every user of base::File, and
// <windows.h>'s macros (near, far, MemoryBarrier, ...) must not follow it there.
#include "base/win/minwin.h"
#include "base/win/scoped_handle.h"
#endif

Expand Down
3 changes: 3 additions & 0 deletions base/filesystem/win/file_util_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
#include "base/strings/format.h"

#include <windows.h>
// This file defines base::CreateDirectory; <windows.h> just mapped the name to
// CreateDirectoryW. The Win32 call below is spelled CreateDirectoryW directly.
#undef CreateDirectory
#include <base/threading/scoped_blocking_call.h>

namespace base {
Expand Down
2 changes: 2 additions & 0 deletions base/filesystem/win/memory_mapped_file_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <base/logging.h>
#include <base/filesystem/memory_mapped_file.h>

#include <windows.h>

namespace base {

bool MemoryMappedFile::Map() {
Expand Down
24 changes: 10 additions & 14 deletions base/threading/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ using SharedMutex = std::shared_mutex;

#if defined(__linux__) || defined(OS_LINUX) || defined(OS_POSIX)
#include <pthread.h>
#elif defined(_WIN32) || defined(OS_WIN)
#include <base/win/minwin.h>
#endif

namespace base {
Expand Down Expand Up @@ -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<PSRWLOCK>(&lock_)); }
void unlock() { ::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_)); }
bool try_lock() {
return !!::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

void lock_shared() { ::AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&lock_)); }
void unlock_shared() { ::ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(&lock_)); }
bool try_lock_shared() {
return !!::TryAcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&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}.
Expand Down
80 changes: 80 additions & 0 deletions base/threading/mutex_test.cc
Original file line number Diff line number Diff line change
@@ -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 <windows.h>: they
// reach nearly every file and must not bring its macros along, or names like
// MemoryBarrier or near get rewritten under them.
#include <base/threading/mutex.h>
#include <base/threading/spinning_mutex.h>

#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 <windows.h>, so only the other compilers can hold it to this.
#include <base/filesystem/file.h>

#if !defined(__MINGW32__) && \
(defined(MemoryBarrier) || defined(near) || defined(far) || defined(min) || defined(max))
#error "base/filesystem/file.h leaked a Windows macro"
#endif

#include <gtest/gtest.h>

#include <base/atomic.h>
#include <base/threading/lock_guard.h>
#include <base/threading/thread.h>

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<base::SpinningMutex> 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
42 changes: 42 additions & 0 deletions base/threading/mutex_win.cc
Original file line number Diff line number Diff line change
@@ -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 <windows.h>, stays out of every file that
// merely holds a mutex: its macros (MemoryBarrier, near, far, ...) would
// otherwise rewrite unrelated names there.

#include <base/threading/mutex.h>
#include <base/win/minwin.h>

namespace base {

#if !defined(BASE_USE_STD_MUTEX) || !BASE_USE_STD_MUTEX

void SharedMutex::lock() {
::AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

void SharedMutex::unlock() {
::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

bool SharedMutex::try_lock() {
return !!::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

void SharedMutex::lock_shared() {
::AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&lock_));
}

void SharedMutex::unlock_shared() {
::ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(&lock_));
}

bool SharedMutex::try_lock_shared() {
return !!::TryAcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(&lock_));
}

#endif

} // namespace base
17 changes: 17 additions & 0 deletions base/threading/spinning_mutex.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#if defined(_WIN32) || defined(OS_WIN)
#include <intrin.h>
#include <base/win/minwin.h>
#define PA_YIELD_PROCESSOR _mm_pause()
#elif defined(__x86_64__) || defined(__i386__)
#define PA_YIELD_PROCESSOR __asm__ __volatile__("pause")
Expand All @@ -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 <windows.h>. lock_ has SRWLOCK's layout, a single pointer.
void SpinningMutex::LockSlow() {
::AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

bool SpinningMutex::Try() {
return !!::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

void SpinningMutex::Release() {
::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}
#endif

void SpinningMutex::Reinit() {
Release();
}
Expand Down
30 changes: 7 additions & 23 deletions base/threading/spinning_mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
#include <errno.h>
#include <pthread.h>
#include <base/atomic.h>
#elif defined(_WIN32) || defined(OS_WIN)
#include <base/win/minwin.h>
#endif

namespace base {
Expand All @@ -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();

Expand Down Expand Up @@ -50,10 +51,8 @@ class BASE_EXPORT SpinningMutex {

base::Atomic<int32_t> 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
};

Expand Down Expand Up @@ -83,19 +82,4 @@ STRONG_INLINE void SpinningMutex::Release() {
}
#endif

#if defined(_WIN32) || defined(OS_WIN)

STRONG_INLINE void SpinningMutex::LockSlow() {
::AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

STRONG_INLINE bool SpinningMutex::Try() {
return !!::TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}

STRONG_INLINE void SpinningMutex::Release() {
::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&lock_));
}
#endif

} // namespace base