当前位置: 首页 > 知识库问答 >
问题:

MCS锁实现中的死锁

邢永安
2023-03-14
Hardware:
Darwin Kernel Version 13.2.0: Thu Apr 17 23:03:13 PDT 2014; root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64
  atomics.hpp

  1 #ifndef ATOMIC_UTILS_H
  2 #define ATOMIC_UTILS_H
  3
  4 #include
  5
  6 #define BARRIER() __asm__ volatile ( "": : :"memory" )
  7
  8 #define CPU_RELAX() __asm__ volatile( "pause\n\t": : :"memory" )
  9
 10 #define STORE_FENCE() __asm__ volatile("mfence" ::: "memory");
 11
 12 class AtomicUtils
 13 {
 14     public:
 15
 16     /**
 17      * check if the value at addr is equal to oldval, if so replace it with newva    l
 18      * and return the oldval
 19      */
 20     inline static size_t compareAndExchange( volatile size_t* addr, size_t oldval    , size_t newval )
 21     {
 22       size_t ret;
 23       __asm__ volatile( "lock cmpxchgq %2, %1\n\t"
 24                     :"=a"(ret), "+m"(*addr)
 25                     : "r"(newval), "0"(oldval)
 26                     : "memory" );
 27       return ret;
 28     }
 29
 30     /**
 31      * Atomically stores x into addr and returns the previous
 32      * stored in addr
 33      */
 34   inline static size_t loadAndStore( size_t x, volatile size_t* addr )
 36     {
 37       size_t ret;
 38       __asm__ volatile( "lock xchgq %1, %0\n\t"
 39                         : "+m"(*addr), "=r"(ret)
 40                         : "1"(x) );
 41       return ret;
 42     }
 43
 44 };
 45
 46 #endif
  mcs.hpp

  1 #ifndef MCS_LOCK_H
  2 #define MCS_LOCK_H
  3
  4 #include "atomics.hpp"
  5 #include 
  6
  7 class MCSLock
  8 {
  9     struct mcs_lock_t
 10     {
 11         mcs_lock_t():next(0), locked(false){}
 12         struct mcs_lock_t* next;
 13         bool locked;
 14     };
 15
 16     public:
 17     typedef struct mcs_lock_t mcs_lock;
 18
 19     private:
 20     mcs_lock** tail;
 21     static boost::thread_specific_ptr tls_node;
 22
 23     public:
 24     MCSLock( mcs_lock** lock_tail ):tail( lock_tail )
 25     {
 26       if( tls_node.get() == 0 )
 27           tls_node.reset( new mcs_lock() );
 28     }
 29
 30     void lock()
 31     {
 32       mcs_lock* thread_node = tls_node.get();
 33       thread_node->next = 0;
 34       thread_node->locked = true;
 35
 36       volatile mcs_lock* pred = reinterpret_cast(
 37                            AtomicUtils::loadAndStore(
 38                                reinterpret_cast( thread_node ),
 39                                reinterpret_cast( tail )
 40                            )
 41                        );
 42       if( pred != 0 )
 43       {
 44         pred->next = *tail;
 45
 46         STORE_FENCE();
 47         //BARRIER(); // Required to prevent re ordering between prev->next = tail     and thread_node->locked. ( WR harzard )
 48
 49         // Spin on a local variable. Someone unlock me plz !!
 50         while( thread_node->locked )
 51             CPU_RELAX();
 52
 53       }
 54     }
 55
 56     void unlock()
 57     {
 58         mcs_lock* thread_node = tls_node.get();
 59         if( thread_node->next == 0 )
 60         {
 61             // If false, then we a new thread has request for lock. Now release t    he lock for the new thread
 62             if(
 63                     AtomicUtils::compareAndExchange(
 64                         reinterpret_cast( tail ),
 65                         reinterpret_cast( thread_node ),
 66                         0
 67                     )  == reinterpret_cast( thread_node ) 68             )
 69             {
 70                 return;
 71             }
 72
 73             while( thread_node->next == 0 )
 74                 CPU_RELAX();
 75         }
 76
 77         thread_node->next->locked = false;
 78     }
 79 };
 80
 81 boost::thread_specific_ptr MCSLock::tls_node;
 82 #endif
mcs_test.cpp

  1 #include "mcs.hpp"
  2 #include <iostream>
  3 #include <pthread.h>
  4 #include <vector>
  5 #define NUM_THREADS 16
  6 #define NUM_ITERATIONS 100
  7
  8 std::vector<int> elements;
  9 MCSLock::mcs_lock *tail = 0;
 10
 11 void* thread_run( void* data )
 12 {
 13   MCSLock lock( &tail );
 14   for( int i = 0; i < NUM_ITERATIONS; ++i )
 15   {
 16       lock.lock();
 17       elements.push_back( i );
 18       lock.unlock();
 19   }
 20
 21   return 0;
 22 }
 23
 24 int main()
 25 {
 26     pthread_t threads[ NUM_THREADS ];
 27     elements.reserve( NUM_THREADS * NUM_ITERATIONS );
 28
 29     {
 30         for( int i = 0; i < NUM_THREADS; ++i )
 31             pthread_create( &threads[i], NULL, thread_run, NULL );
 32
 33         for( int i = 0; i < NUM_THREADS; ++i )
 34             pthread_join( threads[i], NULL );
 35
 36         std::cout <<"\nExiting main thread: " << std::endl;
 37     }
 38 }

上面的代码是使用clang编译的

问题:

我看到第50行中有1或2个线程卡在lock()中。除了主线程,卡在lock()中的线程没有其他线程活着。这意味着当其他线程调用解锁()时,它们不知何故不会为其他变量设置锁定=假并退出。

请问有什么关于调试的建议吗?

在这上面困了好几个小时也没有线索。

共有1个答案

许嘉福
2023-03-14

clang没有内置这些内联asm块(比如gcc的__sync_val_compare_and_swap)吗?为什么要重新发明车轮?

其次,我真的会考虑添加内存破坏者来加载AndStore。在执行 xchgq 之前,您需要确保编译器在寄存器中保存的任何写入都被刷新到内存中。类似地,它将阻止 gcc 优化内存读取到 xchgq 之前。两者都会很糟糕。

第三,我将检查while循环的asm输出(thread_node-

这些可能不能解决你的问题,但这就是我要开始的地方。

 类似资料:
  • 本文向大家介绍java实现死锁的示例代码,包括了java实现死锁的示例代码的使用技巧和注意事项,需要的朋友参考一下 什么是死锁 我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行。如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进;对于

  • 我试图将一长串文件添加到mysql中,并使用带有grails的spring ACL服务来附加权限。因此,在我的控制器中,我有: 我不用担心Files domain对象,它支持大量数据(特别是因为我已经禁用了mysql中的版本控制),问题出在使用aclUtilService的filesService上,

  • 死锁概念 死锁(Deadlock)就是一个进程拿着资源A请求资源B,另一个进程拿着资源B请求资源A,双方都不释放自己的资源,导致两个进程都进行不下去。 示例程序 我们可以写代码模拟进程死锁的例子。 package main func main() { ch := make(chan int) <-ch } 运行结果 root@fa13d0439d7a:/go/src# go run de

  • 问题内容: 我是休眠的新手,已经开始使用C3P0作为休眠的连接池管理器,因为没有它,在与MySQL服务器没有联系8个小时之后,我遇到了超时问题。 切换到C3P0后,我最近遇到了死锁,我不确定它是如何发生的。我虽然显示了所有堆栈跟踪和配置。 这是配置日志 这是我设置的属性 这是被锁定的线程之一的线程转储。 由于我对此并不陌生,因此无法从日志和转储中识别问题。我还注意到C3P0创建了很多线程。 问题答

  • 本文向大家介绍C#中lock死锁实例教程,包括了C#中lock死锁实例教程的使用技巧和注意事项,需要的朋友参考一下 在c#中有个关键字lock,它的作用是锁定某一代码块,让同一时间只有一个线程访问该代码块,本文就来谈谈lock关键字的原理和其中应注意的几个问题: lock的使用原型是: 首先要明白为什么上面这段话能够锁定代码,其中的奥妙就是X这个对象,事实上X是任意一种引用类型,它在这儿起的作用就

  • 本文向大家介绍互斥锁死锁,包括了互斥锁死锁的使用技巧和注意事项,需要的朋友参考一下 死锁可以在使用互斥锁的多线程Pthread程序中发生。让我们看看它如何发生。未锁定的互斥锁由pthread_mutex_init()函数初始化。 使用pthread_mutex_lock()和pthread_mutex_unlock()获取并释放互斥锁。如果线程尝试获取锁定的互斥锁,则对pthread_mutex_