|
Document of c-modernization-kit (util) 1.0.0
|
trace_dispose のスレッドセーフ化として、trace_stop → trace_dispose の呼び出し順序を 内部で保証する設計を採用した(trace-provider.c の config_rwlock 参照)。
この設計では trace_dispose が排他ロックを取得するまで待機するため、 **write 系 API が永久にブロックしないこと**が前提条件となる。 本ドキュメントはその前提が成立するかを各コンポーネントについて調査した結果を記録する。
| 箇所 | 操作 | ブロッキング条件 | リスク | 対処 |
|---|---|---|---|---|
| trace-provider.c config_lock_shared | rwlock 共有ロック取得 | trace_stop/trace_dispose が排他ロック保持中 | 低〜中 | タイムアウト実装済み (100 ms) |
| file-provider.c mutex | ファイル書き込みロック取得 | 別スレッドがローテーション処理中 | 中 | タイムアウト実装済み (100 ms) |
| file-provider.c write() O_DSYNC | ファイルへの同期書き込み | NFS/SMB マウント障害 | 高 | **既知制限**(mutex タイムアウトで部分的に緩和) |
| syslog-provider.c syslog() | syslog ソケットへの書き込み | rsyslog 停止・/dev/log ソケット満杯 | 中 | **既知制限**(API レベルで回避不可) |
| ETW (etw-provider.c) | ETW カーネルバッファへの書き込み | 通常発生しない | 低 | 対処不要 |
対象コード: trace_write / trace_writef / trace_hex_write / trace_hex_writef
ブロッキング条件: trace_dispose または trace_stop が排他ロックを保持している間、新たな共有ロック取得はブロックする。
リスク評価: 低〜中
対処: pthread_rwlock_timedrdlock / TryAcquireSRWLockShared スピンループで 100 ms タイムアウトを実装。タイムアウト時はメッセージを破棄して -1 を返す。
対象コード: trace_file_provider_write
ブロッキング条件: 別スレッドが同じファイルに書き込み中またはローテーション処理中の場合、mutex 取得がブロックする。 ローテーション処理には rename()・unlink() が含まれ、NFS/SMB 上ではネットワーク往復のため 秒単位のブロックが発生しうる。
リスク評価: 中
対処: pthread_mutex_timedlock / TryEnterCriticalSection スピンループで 100 ms タイムアウトを実装。タイムアウト時はメッセージを破棄して -1 を返す。
対象コード: trace_file_provider_write の write(handle->fd, buf, len)
ブロッキング条件: O_DSYNC フラグによりカーネルはデータが物理メディアに書き込まれるまで待機する。 NFS/SMB マウント上のファイルへの書き込み時にネットワーク障害が発生した場合、 カーネルのマウントタイムアウト(デフォルト約 600 秒)まで write() がブロックする。
リスク評価: 高(NFS/SMB 使用時)
現状の緩和策: mutex タイムアウト (100 ms) により、**別スレッドが既に write() でブロックしている場合**は 新規スレッドが 100 ms でタイムアウトして離脱できる。 ただし mutex を取得したスレッド自身は write() がブロックするため解放されない。
制限: write() 呼び出しに対してシステムコールレベルのタイムアウトを設定する標準的な手段は存在しない。 (O_NONBLOCK はブロック型デバイス/NFS には効果がない。alarm()/SIGALRM は 非同期シグナルであり安全なキャンセルが困難。)
推奨事項: トレースファイルパスは、ローカルファイルシステム(/var/log、/tmp 等)に設定すること。 NFS/SMB マウント上へのトレースファイル書き込みは推奨しない。
対象コード: syslog_provider_write → syslog(level, "%s", message)
ブロッキング条件:
Linux の syslog() は /dev/log Unix ドメインソケットに接続して送信する。 ソケットタイプによって挙動が異なる:
openlog(ident, LOG_NDELAY | LOG_PID, facility) の LOG_NDELAY フラグは 初回接続を即座に確立するが、その後の書き込みブロックは防止しない。
リスク評価: 中(SOCK_STREAM パスのみ)
制限: glibc の syslog() API にはタイムアウト設定や非同期送信の手段が提供されていない。 /dev/log ソケットへの send() 呼び出しの前後でソケット fd を直接操作する方法は glibc 内部実装に依存するため採用しない。
推奨事項: 現代の systemd 環境 (rsyslog/journald with SOCK_DGRAM) では実用上問題はない。 SOCK_STREAM 接続が懸念される場合は OS トレース (os_level) を TRACE_LV_NONE に 設定してファイルトレースのみを使用することを検討すること。
対象コード: etw_provider_write → TraceLoggingWrite
ブロッキング条件: ETW はリングバッファへの書き込みであり、カーネルが非同期に ETL ファイルや Consumer へ転送する。バッファが満杯の場合はイベントがドロップされる (ブロックしない)。
リスク評価: 低
対処: 不要。
| 観点 | 説明 |
|---|---|
| 正常系の I/O コスト | syslog/ETW: ~1–10 μs。ローカルファイル書き込み: ~10–100 μs。100 ms は 1000 倍以上のマージン |
| アプリケーション応答性 | trace_dispose のブロック時間を合理的な範囲に抑える |
| メッセージロスのトレードオフ | 正常系ではタイムアウトが発生しないため、メッセージロスのリスクは実質ゼロ |