-
-
[原创]nodejs的隐藏bug
-
发表于:
2014-9-19 17:01
6701
-
偶然发现的严重问题,所以提醒下各位用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系统来说是个半吊子货。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)