首页
社区
课程
招聘
[原创]nodejs的隐藏bug
发表于: 2014-9-19 17:01 6707

[原创]nodejs的隐藏bug

2014-9-19 17:01
6707

偶然发现的严重问题,所以提醒下各位用nodejs写web或者其他程序的人们:程序可能死锁或者数据不对,导致程序挂掉,具体是这样子的:

nodejs的所有异步操作都依赖跨平台开源库libuv。
libuv里面为了实现线程间同步,使用了读写锁。
windowsNT6平台,系统自带SRWLock,所以此时libuv会使用系统提供的读写锁接口,这个没问题。
问题出在NT5平台:
NT5平台没有内建的读写锁实现,所以libuv自己实现了一套读写锁,大概逻辑是这样的:

//thread.c

//这里是加读锁,实际也就是将写锁阻塞,使得写操作的线程等待,同时将读计数+1
//具体来说:
//如果计数+1后的值是1,说明是第一个读线程,所以实际上之前没做阻塞写线程的事情,这里真正做一次阻塞写线程的事情,也就是
//  if (++rwlock->fallback_.num_readers_ == 1)
//    uv_mutex_lock(&rwlock->fallback_.write_mutex_);
// 这一句的任务

inline static void uv__rwlock_fallback_rdlock(uv_rwlock_t* rwlock) {
  uv_mutex_lock(&rwlock->fallback_.read_mutex_);

  if (++rwlock->fallback_.num_readers_ == 1)
    uv_mutex_lock(&rwlock->fallback_.write_mutex_);

  uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
}

//这里是释放读锁,实际也就是将写锁解除阻塞,使得阻塞的写操作线程被唤醒,同时将读计数--1
//如果计数-1后的值是0,说明没有线程在读了,可以让阻塞的写线程唤醒了

inline static void uv__rwlock_fallback_rdunlock(uv_rwlock_t* rwlock) {
  uv_mutex_lock(&rwlock->fallback_.read_mutex_);

  if (--rwlock->fallback_.num_readers_ == 0)
    uv_mutex_unlock(&rwlock->fallback_.write_mutex_);

  uv_mutex_unlock(&rwlock->fallback_.read_mutex_);
}

这个逻辑的bug所在:
1、fallback_.write_mutex_和fallback_.read_mutex_都是临界体,windows上临街体是线程相关的锁,获取和释放都必须在同一个线程。。。必须!!!
    而上面的逻辑是:当“读计数是0时就解锁之前阻塞的写线程”,“计数变0”时,未必当前线程就是当初获取这个锁的线程,所以不满足临界体的调用要求。
2、严格来说不算bug:每次在获取锁之前,它的代码都是先阻塞所有其他线程(类似uv_mutex_lock(&rwlock->fallback_.read_mutex_);这样的),然后操作相关锁,完了解锁。这个实际上完全可以用原子替代,windows上Interlocked_系列函数,linux上是atom_系列函数。同时,那个计数值也是可以用原子操作替代的,以提高效率。

同时说段扯淡的话:鄙人认为一切宣传所谓“跨平台”的程序,都是扯淡,各自都有各自特殊的地方,肯定都得有自己的专门处理,你非得把它们纠结成一个东西。而且对开发人员讲,能同时熟悉多个操作系统平台,几乎没可能,这里的bug就是很明显的例子,libuv库的作者显然对windows系统来说是个半吊子货。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2


windows 的临界区
获取和释放都必须在同一个线程



有这回事么?  如果这样,那Windows 多线程同步就不能用临界区了?     我印象中是可以的。
2014-9-19 18:50
0
雪    币: 65
活跃值: (112)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
临界体必须在同一个线程enter和leave就不能用来做线程间同步了么?这个结论怎么得出的?
2014-9-19 22:08
0
雪    币: 478
活跃值: (50)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
目测二楼理解错了,是这样:一个线程进了临界区,那他必须得在同一个线程中显式得让自己退出临界区。
2014-9-20 06:53
0
雪    币: 185
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
在群里讨论了一下。 知道楼主的意思了。

但其实楼主这种说法确实有问题的。。

如果你说只能在一个线程,那我就推出来  不能在第二个线程获取释放。

但其实本意应该是在A线程获取的需要在在相同的A线程释放。
2014-9-20 11:30
0
游客
登录 | 注册 方可回帖
返回
//