Как мне реализовать Wait/Die блокировки, которые позволяют избежать deadlock на ЯП С
Само задание я взял отсюда https://stepik.org/lesson/44341/step/14?unit=22147 Реализовать Wait/Die блокировки, которые позволяют избежать deadlock-ов.
Решать это задание можно только на языке C. Вам будут доступны блокировки (struct lock), переменные состояния (struct condition) и атомарные Read/Modify/Write регистры (atomic_int, atomic_uint, atomic_short, atomic_ushort, atomic_long, atomic_ulong, atomic_llong, atomic_ullong) с интерфейсом, который использовался в видео.
Проверяющая система будет оценивать вашу реализацию на ряде простых однопоточных тестов:
Если только один поток пытается захватить блокировку, то захват должен быть успешным
Попытка захвата одной блокировки несколько раз из одного потока не должна приводить к deadlock-у
Кроме простых однопоточных тестов, естественно, будет и многопоточный: несколько потоков будут пытаться захватить блокировки в таком порядке, который с большой вероятностью приведет к deadlock-у. Если ваше решение не укладывается в лимит времени, значит в вашем коде скорее всего случился deadlock.
Требуемый интерфейс реализации описан в шаблоне кода:
/* Напоминание, как выглядят интерфейсы блокировки и переменной
состояния.
ВАЖНО: обратите внимание на функции lock_init и condition_init,
я не уделял этому внимание в видео, но блокировки и
переменные состояния нужно инициализировать.
*/
struct lock;
void lock_init(struct lock *lock);
void lock(struct lock *lock);
void unlock(struct lock *lock);
struct condition;
void condition_init(struct condition *cv);
void wait(struct condition *cv, struct lock *lock);
void notify_one(struct condition *cv);
void notify_all(struct condition *cv);
/* Далее следует интерфейс, который вам нужно реализовать.
ВАЖНО: в шаблоне кода структуры содержат поля, некоторые
функции уже реализованы и присутствуют комментарии
- это не более чем подсказка. Вы можете игнорировать
комментарии, изменять поля структур и реализации
функций на ваше усмотрение, при условии, что вы
сохранили интерфейс.
Вам нельзя изменять имена структур (wdlock_ctx и
wdlock), а также имена функций (wdlock_ctx_init,
wdlock_init, wdlock_lock, wdlock_unlock).
*/
struct wdlock_ctx;
struct wdlock {
/* wdlock_ctx должен хранить информацию обо всех
захваченных wdlock-ах, а это поле позволит связать
wdlock-и в список. */
struct wdlock *next;
/* Текущий владелец блокировки - из него мы извлекаем
timestamp связанный с блокировкой, если блокировка
свободна, то хранит NULL. */
const struct wdlock_ctx *owner;
/* lock и cv могут быть использованы чтобы дождаться
пока блокировка не освободится либо у нее не сменится
владелец. */
struct lock lock;
struct condition cv;
};
/* Каждый контекст имеет свой уникальный timestamp и хранит
список захваченных блокировок. */
struct wdlock_ctx {
unsigned long long timestamp;
struct wdlock *locks;
};
/* Всегда вызывается перед тем, как использовать контекст.
ВАЖНО: функция является частью интерфейса - не меняйте
ее имя и аргументы.
*/
void wdlock_ctx_init(struct wdlock_ctx *ctx)
{
static atomic_ullong next;
ctx->timestamp = atomic_fetch_add(&next, 1) + 1;
ctx->locks = NULL;
}
/* Всегда вызывается перед тем, как использовать блокировку.
ВАЖНО: функция является частью интерфейса - не меняйте
ее имя и аргументы.
*/
void wdlock_init(struct wdlock *lock)
{
lock_init(&lock->lock);
condition_init(&lock->cv);
lock->owner = NULL;
}
/* Функция для захвата блокировки l контекстом ctx. Если
захват блокировки прошел успешно функция должна вернуть
ненулевое значение. Если же захват блокировки провалился
из-за проверки timestamp-а, то функция должна вернуть 0.
Помните, что контекст должен хранить информацию о
захваченных блокировках, чтобы иметь возможность освободить
их в функции wdlock_unlock.
ВАЖНО: функция является частью интерфейса - не меняйте
ее имя и аргументы.
*/
int wdlock_lock(struct wdlock *l, struct wdlock_ctx *ctx)
{
// Ваш код здесь
}
/* Функция для освобождения всех блокировок, захваченных
контекстом ctx. При этом может случиться так, что этот
контекст не владеет ни одной блокировкой, в этом случае
ничего делать не нужно.
ВАЖНО: функция является частью интерфейса - не меняйте
ее имя и аргументы.
*/
void wdlock_unlock(struct wdlock_ctx *ctx)
{
// Здесь я не знаю что написать
}