lock, semaphore 모두 세마포어로 구현하는 과정에서, sema_down에서의 list_push_back() 함수를 이용해 waiting list에 스레드들을 맨 뒤로 보낸다.
// sema_down() 함수.
while (sema->value == 0) {
list_push_back (&sema->waiters, &thread_current ()->elem);
thread_block ();
}
conditional variable 역시 마찬가지.
/* A counting semaphore. */
struct semaphore {
unsigned value; /* Current value. */
struct list waiters; /* List of waiting threads. */
};
/* Lock. */
struct lock {
struct thread *holder; /* Thread holding lock (for debugging). */
struct semaphore semaphore; /* Binary semaphore controlling access. */
};
/* Condition variable. */
struct condition {
struct list waiters; /* List of waiting threads. */
};
holder
멤버에 저장한다.acquire
가 가능하다.release
가 가능하다./* Lock. */
struct lock {
struct thread *holder; /* Thread holding lock (for debugging). */
struct semaphore semaphore; /* Binary semaphore controlling access. */
};
synch.c/lock_init()
/* Initializes LOCK. A lock can be held by at most a single
thread at any given time. Our locks are not "recursive", that
is, it is an error for the thread currently holding a lock to
try to acquire that lock.
A lock is a specialization of a semaphore with an initial
value of 1. The difference between a lock and such a
semaphore is twofold. First, a semaphore can have a value
greater than 1, but a lock can only be owned by a single
thread at a time. Second, a semaphore does not have an owner,
meaning that one thread can "down" the semaphore and then
another one "up" it, but with a lock the same thread must both
acquire and release it. When these restrictions prove
onerous, it's a good sign that a semaphore should be used,
instead of a lock. */
void
lock_init (struct lock *lock) {
ASSERT (lock != NULL);
lock->holder = NULL;
sema_init (&lock->semaphore, 1);
}
synch.c/lock_acquire()
무조건 현재 락을 소유하고 있지 않은 스레드만 가능하다.
해당 공유자원을 활용할 때 쓴다. 현재 공유자원을 사용하기 위해 lock의 value(sema)를 0으로 낮춰주고 그 다음 코드로 진행한다.
만약 현재 value(sema)가 0이라면 이미 누군가가 공유 자원을 사용 중이라는 의미이므로 while문을 돌면서 waiting list에서 대기하게 된다.
/* Acquires LOCK, sleeping until it becomes available if
necessary. The lock must not already be held by the current
thread.
This function may sleep, so it must not be called within an
interrupt handler. This function may be called with
interrupts disabled, but interrupts will be turned back on if
we need to sleep. */
void
lock_acquire (struct lock *lock) {
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock)); // 무조건 락을 소유하고 있지 않은 스레드만 가능
sema_down (&lock->semaphore); // 만약
lock->holder = thread_current (); // 현재 lock의 권한을 갖고 있는 스레드를 명시해준다.
}
synch.c/lock_release()