Hazard Version
1 lock free算法的内存回收问题
hazard pointer算是比较通用也比较实用的内存回收机制,但是也有缺点,这里先介绍hazard pointer的做法,然后介绍一种改进方案。
2 hazard pointer
按hazardpointer的做法,一个节点要区分以下两种状态
一个节点在retire之后并不能立即reclaim,因为正在进行中的操作可能还会访问这个节点。内存回收的本质就是判断retired的节点什么时候可以reclaim。
hazard pointer这种方案的做法就是通过维护一个hazardpointer的集合来判断一个节点是否还可能被访问:
考虑3中提到的问题:只要在判断一个节点的指针是否出现在hazard pointer的集合中之前把这个节点的指针加入到hazard pointer集合就足够了。也就是说一个节点的事件按下面的顺序发生肯定是合法的:
1和2的时序是需要保证,方法就是:在把一个节点加入到hazard pointer集合后如果发现这个节点已经retire了,就认为这个节点的指针加入到hazard pointer集合失败。
3 hazard version
hazard pointer的要求有两个:
一个变通的方法是为每个retire的节点赋予一个retire version,每次操作之前要取一个全局version加入到hazard version集合。只要一个节点的retire version大于任意一个hazard version,那么这个节点就有可能在未来被访问。
3.1 具体方案
设置以下全局变量:
线程A: 执行正常操作
// 操作之前: 获取全局version,并加入到hazard version set
hazardVersion = atomic_read(&globalVersion)
hazardVersionSet.add(hazardVersion)
mem_barrier();
// ...... 做各种操作, 假设操作过程中retired node被放到retiredNodeList.
// 操作完成: 增加globalVersion, 并把globalVersion赋给每个retired node
retireVersion = atomic_add_and_fetch(&globalVersion)
for p in retiredNodeList:
p.retiredVersion= retireVersion
// 操作完成,把hazardVersion移除
hazardVersionSet.remove(hazardVersion)
// 最后把retired node加入到waitToReclaimNodeList
waitToReclaimNodeList.append(retiredNodeList)
线程B: 执行reclaim
// 假定待回收的节点放在waitToReclaimNodeList
startReclaimVersion = atomic_read(&globalVersion)
minHazardVersion = hazardVersionSet.min()
reclaimableVersion = min(startReclaimVersion,minHazardVersion)
for p in waitToReclaimNodeList:
ifp.retiredVersion < reclaimableVersion
reclaim(p)
3.3 注意的问题