TL;DR
ゴルーチンが処理を進めるのを妨げるバグ。Go の並行処理のバグを調査した論文「Understanding Real-World Concurrency Bugs in Go」に出てくる言葉である。
仕組み
この論文では、Docker や Kubernetes など 6 つの Go のソフトウェアから集めた 171 件の並行処理のバグを、2 つの軸で分類している。1 つ目は振る舞いの軸で、1 つ以上のゴルーチンが意図せず止まり、先に進めなくなるものをブロッキングバグと呼ぶ。すべてのゴルーチンが処理を終えるが、その振る舞いが望ましくないものは非ブロッキングバグと呼ぶ。2 つ目は原因の軸で、共有メモリの保護の誤りによるものと、メッセージパッシングの誤りによるものに分ける。
ブロッキングバグは、ゴルーチンが待っているリソースがいつまでも手に入らないときに起きる。従来のデッドロックは複数のスレッドの循環待ちを指すが、ブロッキングバグはそれより広い。循環待ちがなくても、どのゴルーチンも供給しないリソースを待ち続ける場合を含む。
例
論文によると、Mutex の誤用(二重ロック、ロックを取る順序の衝突、アンロックのし忘れ)によるブロッキングバグが 28 件あった。また、ブロッキングバグの約 58% はメッセージパッシングが原因だった。誰も送信しないチャネルを待ち続けるなど、チャネルの使い方の誤りがこれに含まれる。
注意点
Go のランタイムには、すべてのゴルーチンが進めなくなったときに報告するデッドロック検出器が組み込まれている。しかし論文の実験では、再現した 21 件のブロッキングバグのうち、この検出器が見つけられたのは 2 件だけだった。