在macOS上使用线程清理程序运行时,以下代码会产生警告。我看不到比赛在哪里。shared_ptr和weak_ptr的控制块是线程安全的,并且std::queue通过保持锁定来完成从中推入和弹出操作。
#include <future>
#include <memory>
#include <queue>
class Foo {
public:
Foo() {
fut = std::async(std::launch::async, [this] {
while (!shouldStop) {
std::scoped_lock lock(mut);
while (!requests.empty()) {
std::weak_ptr<float> requestData = requests.front();
requests.pop();
(void)requestData;
}
}
});
}
~Foo() {
shouldStop.store(true);
fut.get();
}
void add(const std::weak_ptr<float> subscriber) {
std::scoped_lock lock(mut);
requests.push(subscriber);
}
private:
std::atomic<bool> shouldStop = false;
std::future<void> fut;
std::queue<std::weak_ptr<float>> requests;
std::mutex mut;
};
int main() {
Foo foo;
int numIterations = 100000;
while (--numIterations) {
auto subscriber = std::make_shared<float>(); …Run Code Online (Sandbox Code Playgroud) 在Xcode 10.2中,我无法再在启用了线程清理程序的模拟器上运行我的应用程序。我得到错误
目前无法安装此应用。
WatchKit v3应用禁止使用Info.plist密钥:NSBuiltWithThreadSanitizer
我的应用程序包含一个监视应用程序和许多框架(一些用于监视,一些用于iOS),但是在尝试在模拟器上运行主应用程序时出现此错误。我检查了plist文件,并确认这不是我在任何地方指定的密钥。
当我检查DerivedData中的plist文件时,我确实看到我的watch OS框架的所有复制人都具有此密钥。但是我发现删除标志的唯一方法是完全禁用线程清理程序。如何仅针对手表而不是我的主应用禁用它?
如何检测使用 gcc 5 的构建是否已打开线程清理程序?两者之间的任何一个__has_feature(thread_sanitizer)都不__SANITIZE_THREAD__工作
#include <iostream>
using std::cout;
using std::endl;
int main() {
cout << __has_feature(thread_sanitizer) << endl;
cout << __SANITIZE_THREAD__ << endl;
}
Run Code Online (Sandbox Code Playgroud)
https://wandbox.org/permlink/t5qYme4Whyj54aYV。这在具有线程清理程序的 clang 版本上进行编译;但不适用于某些 gcc 版本(特别是 5)
功能检查和__SANITIZE_THREAD__宏都可用于检测线程清理程序何时打开,因此测试可以抑制误报(例如,当线程清理程序捕获实际上不是数据争用的错误时)请参阅此了解更多信息
我正在使用无锁堆栈(通过标记指针)来管理小内存块池。当块被插入到池中和从池中移除时,列表节点就地创建和销毁。
这是一个非常简化的测试程序,它只从堆栈中弹出。因此,没有 ABA 问题,也没有标记指针。足以证明我遇到的比赛:
#include <atomic>
#include <list>
#include <thread>
#include <type_traits>
struct Node {
Node() = default;
Node(Node *n) { next.store(n); }
std::atomic<Node *> next;
};
using Memory = std::aligned_storage_t<sizeof(Node)>;
struct Stack {
bool pop_and_use() {
for (Node *current_head = head.load(); current_head;) {
Node *next = current_head->next.load(); // READ RACE
if (head.compare_exchange_weak(current_head, next, std::memory_order_seq_cst)) {
current_head->~Node();
Memory *mem = reinterpret_cast<Memory *>(current_head);
new (mem) int{0}; // use memory with non-atomic write (WRITE RACE)
return true;
}
}
return false;
} …Run Code Online (Sandbox Code Playgroud) 请看下面的代码:
#include <pthread.h>
#include <boost/atomic.hpp>
class ReferenceCounted {
public:
ReferenceCounted() : ref_count_(1) {}
void reserve() {
ref_count_.fetch_add(1, boost::memory_order_relaxed);
}
void release() {
if (ref_count_.fetch_sub(1, boost::memory_order_release) == 1) {
boost::atomic_thread_fence(boost::memory_order_acquire);
delete this;
}
}
private:
boost::atomic<int> ref_count_;
};
void* Thread1(void* x) {
static_cast<ReferenceCounted*>(x)->release();
return NULL;
}
void* Thread2(void* x) {
static_cast<ReferenceCounted*>(x)->release();
return NULL;
}
int main() {
ReferenceCounted* obj = new ReferenceCounted();
obj->reserve(); // for Thread1
obj->reserve(); // for Thread2
obj->release(); // for the main()
pthread_t t[2];
pthread_create(&t[0], NULL, Thread1, obj); …Run Code Online (Sandbox Code Playgroud) c++ multithreading boost reference-counting thread-sanitizer
我正在一个大型项目中清理 ThreadSanitizer 警告。特别是在这种情况下,有一个生成的线程从文件、生产者中读取。然后有一个或多个解压线程作为线程池的一部分。最后,有一个线程实际上通过检索解压缩的块来进行处理。这当然允许同时解压缩多个块。
该项目在许多位置通过原子 bool 和 进行同步usleep(),特别包括这一处。当然,这并不理想,也是分配给我的事情之一。
但只要表示了互斥体和锁,我就看不出 ThreadSanitizer 会抱怨什么问题(除了与使用条件变量相比可能降低效率之外)。
ThreadSanitizer 抱怨数据争用“好像通过睡眠同步”,并提供了调用位置usleep()。我的问题是,当然通过睡眠同步并不理想,但只要尊重互斥体,我就不会看到数据竞争。据我了解互斥体的工作方式,我确实相信它们受到尊重。
因此,我尝试创建一组最小的重现步骤,以便准确识别 ThreadSanitizer 所抱怨的内容。
这是我想出的代码:
// g++ --std=c++11 -lpthread as-if-synchronized-via-sleep.cpp -g -fsanitize=thread -pie -fPIC
#include <iostream>
#include <iomanip>
#include <thread>
#include <vector>
#include <atomic>
#include <mutex>
#include <chrono>
#include <cstdlib>
#include <unistd.h>
class Data {
public:
char uncompressed[1024];
char compressed[1024];
std::atomic<bool> done = {false};
};
int main(int argc, char **argv) {
std::atomic<bool> done = {false};
std::atomic<int> count = {0};
std::mutex m;
std::vector<Data*> v;
std::thread provider{[&](){ …Run Code Online (Sandbox Code Playgroud) 我一直认为usingstd::cout << something是线程安全的。
对于这个小例子
#include <iostream>
#include <thread>
void f()
{
std::cout << "Hello from f\n";
}
void g()
{
std::cout << "Hello from g\n";
}
int main()
{
std::thread t1(f);
std::thread t2(g);
t1.join();
t2.join();
}
Run Code Online (Sandbox Code Playgroud)
我的期望是两个输出的顺序是未定义的(实际上这就是我在实践中观察到的),但调用是operator<<线程安全的。
然而,ThreadSanitizer、DRD 和 Helgrind 似乎都给出了有关访问 std::__1::ios_base::width(long) 和 std::__1::basic_ios<char, std::__1::char_traits >:: 的各种错误充满()
在编译器资源管理器上我没有看到任何错误。
在 FreeBSD 13 上,ThreadSanitizer 给了我 3 个警告,上面列出的两个警告加上底层 I/O 缓冲区的 malloc/memcpy。
同样在 FreeBSD 13 中,DRD 给出 4 个错误,width()两个fill()线程乘以 2 个错误。
最后,FreeBSD 13 Helgrind …
我有一个无锁队列的实现,我相信它是正确的(或者至少是无数据竞争的):
#include <atomic>
#include <iostream>
#include <optional>
#include <thread>
struct Job {
int id;
int data;
};
class JobQueue {
using stdmo = std::memory_order;
struct Node {
std::atomic<Node *> next = QUEUE_END;
Job job;
};
static inline Node *const QUEUE_END = nullptr;
static inline Node *const STACK_END = QUEUE_END + 1;
struct GenNodePtr {
Node *node;
std::uintptr_t gen;
};
alignas(64) std::atomic<Node *> jobs_back;
alignas(64) std::atomic<GenNodePtr> jobs_front;
alignas(64) std::atomic<GenNodePtr> stack_top;
public:
JobQueue()
: jobs_back{new Node{}},
jobs_front{GenNodePtr{jobs_back.load(stdmo::relaxed), 1}},
stack_top{GenNodePtr{STACK_END, 1}} {}
~JobQueue() …Run Code Online (Sandbox Code Playgroud) 上下文:我正在为我的程序使用线程清理程序,它显示我的程序存在数据争用。我 100% 确定为什么(也许有太多内存访问),线程清理程序不会给出无效访问的确切堆栈跟踪。只有
Previous read of size 4 at 0x7b1800004140 by thread T36:
[failed to restore the stack]
Run Code Online (Sandbox Code Playgroud)
我尝试history_size=7根据文档设置运行线程清理程序时,但仍然无法获取堆栈跟踪。
我知道C/C++出于安全考虑支持地址随机化,所以我用它-fno-stack-protector来编译我的程序,这使得多次运行时无效地址访问相同。
我想知道如何获取该地址上驻留的变量,以便我可以直接获取我非法访问它的位置?
我使用 bazel 作为我的构建系统,它间接依赖于 clang v13.0.0。