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

You should implement a "disposed" state of your ReadWriteLock instance (the "active" field looks appropriate, but you don't use it, why?).

Check it twice in rwl_writeLock / rwl_readLock, before and after the sem_wait() call. This trick is well-known as a "double-checking lock pattern". If you find your Lock to be deleted before entering sem_wait, just leave the function. If you find your Lock to be deleted after entering sem_wait, do sem_post immediately and leave.

In your destroy() routine, set active=0, then sem_post to both semaphores (don't bother if sem_post fails). If you still need the sem_destroy afterwards, usleep a bit (so all readers and writers have their time to receive the signal) and do sem_destroy.

P.S. You actually have no need to call sem_destroy if you are sure that the semaphore is not used anymore.

于 2015-01-20T16:44:13.493 回答