Я пытаюсь написать потокобезопасную блокировку чтения-записи на C с использованием семафоров POSIX. Вы можете увидеть текущее состояние исходного кода здесь. Я выполнил это, чтобы создать блокировку, предпочтительную для читателей.
Проблема в том, что я хотел бы обработать разрушение блокировки для любого возможного состояния, которое может быть при вызове rwl_destroy().
Если вызывается destroy и никакой другой поток не находится в блокировке, он блокирует wrt (используемый писателями), чтобы предотвратить доступ любого другого потока к данным, защищенным блокировкой. Затем функция уничтожения должна уничтожить семафоры и освободить память, выделенную для структуры 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 в настоящее время не используются, они были сделаны в моей попытке решить проблему.
ReadWriteLock*
является правильным типом дляrwl_init
-- указательmalloc
ed вrwl_init
эффективно теряется после возврата функции. Вы можете предпочестьReadWriteLock**
(и*lock = malloc(...)
и т. д.). Аналогично,lock = NULL
вrwl_destroy
не имеет длительного эффекта. - person Dirk   schedule 20.01.2015