Commit 651ccc0c authored by Frank Barchard's avatar Frank Barchard Committed by Commit Bot

Fix data races in libyuv::TestCpuFlag().

Detect the compiler's support of C11 atomics, and use C11 atomics when
available.

Note that libyuv::MaskCpuFlags() is still not thread-safe.

BUG=libyuv:641
TEST= cpu_thread_test.cc adds a pthread based test
R=wangcheng@google.com

Change-Id: If05b1e16da833105a0159ed67ef20f4e61bc7abd
Reviewed-on: https://chromium-review.googlesource.com/510079
Commit-Queue: Frank Barchard <fbarchard@google.com>
Reviewed-by: 's avatarCheng Wang <wangcheng@google.com>
parent 77f6916d
......@@ -89,6 +89,7 @@ LOCAL_SRC_FILES := \
unit_test/compare_test.cc \
unit_test/convert_test.cc \
unit_test/cpu_test.cc \
unit_test/cpu_thread_test.cc \
unit_test/math_test.cc \
unit_test/planar_test.cc \
unit_test/rotate_argb_test.cc \
......
......@@ -238,6 +238,7 @@ if (libyuv_include_tests) {
"unit_test/compare_test.cc",
"unit_test/convert_test.cc",
"unit_test/cpu_test.cc",
"unit_test/cpu_thread_test.cc",
"unit_test/math_test.cc",
"unit_test/planar_test.cc",
"unit_test/rotate_argb_test.cc",
......
Name: libyuv
URL: http://code.google.com/p/libyuv/
Version: 1656
Version: 1657
License: BSD
License File: LICENSE
......
......@@ -59,7 +59,12 @@ int ArmCpuCaps(const char* cpuinfo_name);
// returns non-zero if instruction set is detected
static __inline int TestCpuFlag(int test_flag) {
LIBYUV_API extern int cpu_info_;
return (!cpu_info_ ? InitCpuFlags() : cpu_info_) & test_flag;
#ifdef __ATOMIC_RELAXED
int cpu_info = __atomic_load_n(&cpu_info_, __ATOMIC_RELAXED);
#else
int cpu_info = cpu_info_;
#endif
return (!cpu_info ? InitCpuFlags() : cpu_info) & test_flag;
}
// For testing, allow CPU flags to be disabled.
......
......@@ -11,6 +11,6 @@
#ifndef INCLUDE_LIBYUV_VERSION_H_
#define INCLUDE_LIBYUV_VERSION_H_
#define LIBYUV_VERSION 1656
#define LIBYUV_VERSION 1657
#endif // INCLUDE_LIBYUV_VERSION_H_
......@@ -39,6 +39,7 @@
'unit_test/color_test.cc',
'unit_test/convert_test.cc',
'unit_test/cpu_test.cc',
'unit_test/cpu_thread_test.cc',
'unit_test/math_test.cc',
'unit_test/planar_test.cc',
'unit_test/rotate_argb_test.cc',
......
......@@ -14,7 +14,7 @@
#include "libyuv/row.h"
#if defined(_MSC_VER)
#include <intrin.h> // For __popcnt
#include <intrin.h> // For __popcnt
#endif
#ifdef __cplusplus
......
......@@ -43,6 +43,9 @@ extern "C" {
#define SAFEBUFFERS
#endif
// cpu_info_ variable for SIMD instruction sets detected.
LIBYUV_API int cpu_info_ = 0;
// Low level cpuid for X86.
#if (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
defined(__x86_64__)) && \
......@@ -192,10 +195,6 @@ LIBYUV_API SAFEBUFFERS int MipsCpuCaps(const char* cpuinfo_name,
return 0;
}
// CPU detect function for SIMD instruction sets.
LIBYUV_API
int cpu_info_ = 0; // cpu_info is not initialized yet.
// Test environment variable for disabling CPU features. Any non-zero value
// to disable. Zero ignored to make it easy to set the variable on/off.
#if !defined(__native_client__) && !defined(_M_ARM)
......@@ -215,7 +214,7 @@ static LIBYUV_BOOL TestEnv(const char*) {
}
#endif
LIBYUV_API SAFEBUFFERS int InitCpuFlags(void) {
static SAFEBUFFERS int GetCpuFlags(void) {
int cpu_info = 0;
#if !defined(__pnacl__) && !defined(__CLR_VER) && defined(CPU_X86)
uint32 cpu_info0[4] = {0, 0, 0, 0};
......@@ -321,14 +320,27 @@ LIBYUV_API SAFEBUFFERS int InitCpuFlags(void) {
cpu_info = 0;
}
cpu_info |= kCpuInitialized;
cpu_info_ = cpu_info;
return cpu_info;
}
// Note that use of this function is not thread safe.
LIBYUV_API
void MaskCpuFlags(int enable_flags) {
cpu_info_ = InitCpuFlags() & enable_flags;
#ifdef __ATOMIC_RELAXED
__atomic_store_n(&cpu_info_, GetCpuFlags() & enable_flags, __ATOMIC_RELAXED);
#else
cpu_info_ = GetCpuFlags() & enable_flags;
#endif
}
LIBYUV_API
int InitCpuFlags(void) {
MaskCpuFlags(-1);
#ifdef __ATOMIC_RELAXED
return __atomic_load_n(&cpu_info_, __ATOMIC_RELAXED);
#else
return cpu_info_;
#endif
}
#ifdef __cplusplus
......
/*
* Copyright 2017 The LibYuv Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <gtest/gtest.h>
#include "libyuv/cpu_id.h"
// TODO(fbarchard): Port to other platforms
#if defined(__linux__)
#define LIBYUV_HAVE_PTHREAD 1
#endif
#ifdef LIBYUV_HAVE_PTHREAD
#include <pthread.h>
#endif
namespace libyuv {
#ifdef LIBYUV_HAVE_PTHREAD
void* ThreadMain(void* arg) {
int* flags = static_cast<int*>(arg);
*flags = TestCpuFlag(kCpuHasSSSE3);
return nullptr;
}
#endif // LIBYUV_HAVE_PTHREAD
// Call TestCpuFlag() from two threads. ThreadSanitizer should not report any
// data race.
TEST(LibYUVCpuThreadTest, TestCpuFlagMultipleThreads) {
#ifdef LIBYUV_HAVE_PTHREAD
int cpu_flags1;
int cpu_flags2;
int ret;
pthread_t thread1;
pthread_t thread2;
MaskCpuFlags(0); // Reset to 0 to allow auto detect.
ret = pthread_create(&thread1, nullptr, ThreadMain, &cpu_flags1);
ASSERT_EQ(ret, 0);
ret = pthread_create(&thread2, nullptr, ThreadMain, &cpu_flags2);
ASSERT_EQ(ret, 0);
ret = pthread_join(thread1, nullptr);
EXPECT_EQ(ret, 0);
ret = pthread_join(thread2, nullptr);
EXPECT_EQ(ret, 0);
EXPECT_EQ(cpu_flags1, cpu_flags2);
#else
printf("pthread unavailable; Test skipped.");
#endif // LIBYUV_HAVE_PTHREAD
}
} // namespace libyuv
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment