メインコンテンツ

ロック保持中のブロック操作

ロック保持中のタスクによる長時間かかる操作の実行

説明

このチェッカーは、既定の Polyspace® as You Code 解析では非アクティブにされますPolyspace as You Code 解析で非アクティブにされるチェッカー (Polyspace Access)を参照してください

この欠陥は、タスク (スレッド) がロックを保持しながら時間がかかる可能性のある操作を実行した場合に発生します。

チェッカーは、以下の関数の呼び出しを長時間かかる可能性があると見なします。

  • recv などのネットワークにアクセスする関数

  • forkpipesystem などのシステム呼び出し関数

  • getcharscanf などの I/O 操作用の関数

  • fopenremovelstat などのファイル処理関数

  • mkdirrmdir などのディレクトリ操作関数

チェッカーは、ロックの保持および解放を行う特定のプリミティブ型 (たとえば、pthread_mutex_lockpthread_mutex_unlock) を自動的に検出します。自動的に検出されるすべてのプリミティブ型のリストは、Polyspace でのスレッド作成とクリティカル セクションの自動検出を参照してください。

リスク

スレッドでロックの保持中に長時間かかる操作を実行すると、そのロックを使用する他のスレッドはロックが使用可能になるまで待機しなければなりません。その結果、システム パフォーマンスが低下したり、デッドロックが発生する可能性があります。

修正方法

ロックが保持される前か、ロックが解放された後にブロック操作を実行します。

このチェッカーで検出される一部の関数は、時間がかからない可能性のある方法で呼び出すことができます。たとえば、関数 recv は、利用可能なメッセージがない場合に呼び出しが失敗するようになるパラメーター O_NONBLOCK を指定して呼び出せます。このパラメーターを指定して呼び出すと、recv はメッセージが利用可能になるまで待機しません。

すべて展開する

#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
 
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

この例では、pthread_create を使用して作成した各スレッドにおいて、関数 thread_foopthread_mutex_lock を使用してロックを取得した後に、recv でネットワーク I/O 操作を実行しています。同じロック変数 mutex を使用する他のスレッドは、操作が完了してロックが使用可能になるまで待機しなければなりません。

修正 — ロックの取得前にブロック操作を実行

1 つの修正方法として、ロックの取得前に recv を呼び出します。

#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
  
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
    /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

結果情報

グループ: 同時実行
言語: C | C++
既定値: オフ
コマンド ライン構文: BLOCKING_WHILE_LOCKED
影響度: Low

バージョン履歴

R2018b で導入