最近竹翁发现的一个bug最终查明是并发处理造成的,bug的详细分析见这里
mergeserver的scan模块有这样的特征:
open()打开一个scan句柄,然后通过get_next_row()这个流式接口异步的驱动网络框架向chunkserver发送读数据请求,网络框架收到结果数据后会将结果挂入finish队列,如果队列上有线程等待,则以事件通知的方式唤醒该等待线程处理结果。线程处理数据的过程是:首先从finish队列中把请求结果取出来,然后找到它在wait队列中的位置,把它从wait队列中移出来,再把它放到result队列中并调用process_result结果处理函数。当用户读取到足够的数据后会调用close()接口关闭scan句柄。在scan句柄关闭的时候会 (1)将已经发出去但还没有返回结果的请求标记成invalid(wait队列上的元素全部标记成invalid), (2)将finish队列上还没有处理的请求全部释放。
如果一切都如愿倒不会出什么状况,比如close()发生在所有结果都返回后的时刻,或者发生在所有数据包要么在finish队列,要么在wait队列,要么在result队列,但是,一定一定,不要飘在空中。飘在空中,问题可能就来了。
上面的问题简化一下就是一个生产者-消费者模型。网络框架是生产者,读数线程(get_next_row调用者)是消费者。二者通过带有事件通知机制的finish队列联系。
稍微复杂一点:网络框架是生产者,读数线程(get_next_row调用者)是消费者。当生产者生产出来的商品过期后,生产者要负责销毁商品。
再复杂一点:商品过期的标准是消费者提出的,它会告知生产者,从哪天起生产出来的商品是过期的。
问题来了:消费者刚提出过期标准,同一时刻就来了一件商品,这件商品成了无主商品:消费者不要了,生产者也认为已经送出。
解决办法:商品本身带有引用计数,当发现自己不被任何人拥有的时候进行自销毁或由他人代理销毁。