1

POSIX セマフォを使用して C でスレッドセーフな読み書きロックを作成しようとしています。ソース コードの現在の状態は、こちらで確認できます。これに従って、読者優先のロックを作成しました。

問題は、rwl_destroy() が呼び出されたときに発生する可能性のある状態のロックの破棄を処理したいということです。

destroy が呼び出され、他のスレッドがロックされていない場合、wrt (ライターによって使用される) をロックして、他のスレッドがロックによって保護されたデータにアクセスできないようにします。次に、destroy 関数はセマフォを破棄し、ReadWriteLock 構造体に割り当てられたメモリを解放する必要があります。しかし、別のスレッドがロックを待機している場合はどうなるでしょうか。ドキュメントによると、このスレッドは未定義の状態のままになります。

ロックを使いやすくするために、それを避けようとしています。

編集:

現在のコードは次のとおりです。

typedef struct ReadWriteLock
{
sem_t wrt;
sem_t mtx;
sem_t delFlag;
int readcount;
int active;
}ReadWriteLock;

//forward declaration
/* This function is used to take the state of the lock.
 * Return values:
 *      [*] 1 is returned when the lock is alive.
 *      [*] 0 is returned when the lock is marked for delete.
 *      [*] -1 is returned if an error was encountered.
 */
int isActive(ReadWriteLock*);

int rwl_init(ReadWriteLock* lock)
{
lock = malloc(sizeof(ReadWriteLock));
if (lock == NULL)
{
    perror("rwl_init - could not allocate memory for lock\n");
    return -1;
}
if (sem_init(&(lock->wrt), 0, 1) == -1)
{
    perror("rwl_init - could not allocate wrt semaphore\n");
    free(lock);
    lock = NULL;
    return -1;
}
if (sem_init(&(lock->mtx), 0, 1) == -1)
{
    perror("rwl_init - could not allocate mtx semaphore\n");
    sem_destroy(&(lock->wrt));
    free(lock);
    lock = NULL;
    return -1;
}
if (sem_init(&(lock->delFlag), 0 , 1) == -1)
{
    perror("rwl_init - could not allocate delFlag semaphore\n");
    sem_destroy(&(lock->wrt));
    sem_destroy(&(lock->mtx));
    free(lock);
    lock = NULL;
    return -1;
}

lock->readcount = 0;
lock->active = 1;
return 0;
}

int rwl_destroy(ReadWriteLock* lock)
{
errno = 0;
if (sem_trywait(&(lock->wrt)) == -1)
    perror("rwl_destroy - trywait on wrt failed.");
if ( errno == EAGAIN)
    perror("rwl_destroy - wrt is locked, undefined behaviour.");

errno = 0;
if (sem_trywait(&(lock->mtx)) == -1)
    perror("rwl_destroy - trywait on mtx failed.");
if ( errno == EAGAIN)
    perror("rwl_destroy - mtx is locked, undefined behaviour.");

if (sem_destroy(&(lock->wrt)) == -1)
    perror("rwl_destroy - destroy wrt failed");
if (sem_destroy(&(lock->mtx)) == -1)
    perror("rwl_destroy - destroy mtx failed");
if (sem_destroy(&(lock->delFlag)) == -1)
    perror("rwl_destroy - destroy delFlag failed");

free(lock);
lock = NULL;
return 0;
}

int isActive(ReadWriteLock* lock)
{
errno = 0;
if (sem_trywait(&(lock->delFlag)) == -1)
{
    perror("isActive - trywait on delFlag failed.");
    return -1;
}
if ( errno == EAGAIN)
{//delFlag is down, lock is marked for delete
    perror("isActive - tried to lock but ReadWriteLock was marked for delete");
    return 0;
}
return 1;
}

私はこれらの機能も持っています:

int rwl_writeLock(ReadWriteLock*);

int rwl_writeUnlock(ReadWriteLock*);

int rwl_readLock(ReadWriteLock*);

int rwl_readUnlock(ReadWriteLock*);

したがって、私の質問は、上記で説明した未定義の状態を回避するために、これらの関数をどのように変更するかです。ReadWriteLock を破棄しようとする前に、このコードのユーザーがすべてのロックを解放することは可能ですか?

isActive() 関数と delFlag セマフォは現在使用されていません。問題を解決するために作成されました。

4

1 に答える 1

0

ReadWriteLock インスタンスの「廃棄」状態を実装する必要があります (「アクティブ」フィールドは適切に見えますが、使用しないのはなぜですか?)。

sem_wait() 呼び出しの前後に、rwl_writeLock / rwl_readLock で 2 回チェックします。このトリックは、「ダブルチェック ロック パターン」としてよく知られています。sem_wait に入る前に Lock が削除されていることがわかった場合は、関数を終了してください。sem_wait に入った後にロックが削除されていることがわかった場合は、すぐに sem_post を実行して終了します。

destroy() ルーチンで、active=0 を設定してから、sem_post を両方のセマフォに設定します (sem_post が失敗しても気にしないでください)。その後も sem_destroy が必要な場合は、少しスリープしてから (すべてのリーダーとライターがシグナルを受信する時間を確保します)、sem_destroy を実行します。

PS セマフォがもう使用されていないことが確実な場合は、実際に sem_destroy を呼び出す必要はありません。

于 2015-01-20T16:44:13.493 に答える