先日、以下のようなコードでフリーズする現象が発生したので原因を調査した。
【リスト1】
::SetCommEvent( hDevice, EV_TXEMPTY );
::WriteFile( ... );
::WaitCommEvent( hDevice, &dwComEvent, &ov );
::WaitForSingleObject( ov.hEvent, INFINITE );
注意:EV_TXEMPTYは実際のシリアルデバイスの送信バッファが空になったタイミングとは異なります。
このコードは受信側の都合上、少しでもウェイトを入れるために実装したものです。
試行錯誤で分かったことは、イベント発生とWaitCommEvent実行のタイミングにより
イベントが消えてしまうようです。
3台のPC(XP SP2,XP SP3,Vista SP2)で確認したが全てで再現した。
ただしCPU性能が高いPCは頻度が低く、CPU性能が低いPCは頻度が高い。
以下のようにWriteFileとWaitCommEventを入れ替えると不具合が発生しない。
(今のところ問題なし)
【リスト2】
::SetCommEvent( hDevice, EV_TXEMPTY );
::WaitCommEvent( hDevice, &dwComEvent, &ov );
::WriteFile( ... );
::WaitForSingleObject( ov.hEvent, INFINITE );
まあ、本来はこのように実装する(イベントを発生させる動作を実行する前に待ち状態にする)のが普通だが、非同期I/Oを2つ実行することと、SetCommEvent実行後に発生したイベントをWaitCommEventで検出できたのでリスト1のように実装した。