synch.h
/* 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. */
};
semaphore 구조체는 value와 semaphore에 의해 제한된 쓰레드들이 들어가는 waiters를 가지고 있다.
lock 구조체는 lock을 가지고 있는 쓰레드와 semaphore를 인자로 가지고 있다.
lock은 value가 1인 samaphore라는 것을 명심하자.
condition 구조체는 waiters 리스트만 가지고 있다.
synch.c
sema_init()
void
sema_init (struct semaphore *sema, unsigned value) {
ASSERT (sema != NULL);
sema->value = value;
list_init (&sema->waiters);
}
semaphore를 초기화 해주는 함수이다.
semaphore에 value를 할당해주고 sema->waiters를 초기화 한다.
sema_down()
void
sema_down (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
ASSERT (!intr_context ());
old_level = intr_disable ();
while (sema->value == 0) {
list_push_back (&sema->waiters, &thread_current ()->elem);
thread_block ();
}
sema->value--;
intr_set_level (old_level);
}
semaphore value를 1 줄이는 함수이다. PintOS에서는 sema->value가 0으로 떨어지지 않는다.
sema_down은 인터럽드 컨텍스트면 안되고 실행 중에 인터럽트가 실행되면 안된다.
value가 0이라면 sema->waiters 쓰레드들을 넣어 대기 시킨다.
아니라면 value를 1 줄이고 인터럽트 레벨을 이전 값으로 돌린다.
sema_up()
void
sema_up (struct semaphore *sema) {
enum intr_level old_level;
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
thread_unblock (list_entry (list_pop_front (&sema->waiters),
struct thread, elem));
sema->value++;
intr_set_level (old_level);
}
semaphore value를 1 늘이는 함수이다.
sema_up() 중에 인터럽트가 실행되면 안된다.
만약 waiters에 대기 중인 쓰레드가 있다면 하나만 pop하고 unblock 시켜준다.
이후 value값을 1 증가시키고 인터럽트를 이전 레벨로 되돌린다.
lock_init()
void
lock_init (struct lock *lock) {
ASSERT (lock != NULL);
lock->holder = NULL;
sema_init (&lock->semaphore, 1);
}
lock을 초기화한다.
lock->holder에 NULL을 넣고 value가 1인 semaphore를 생성한다.
lock_acquire()
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_acquire()은 인터럽트 컨텍스트에서 실행되면 안된다.
lock_held_by_current_thread() 함수를 통해 lock을 가지고 있는 쓰레드가 다시 lock을 요청하는 것을 방지한다.
sema_down()을 실행시키고 lock->holder를 실행되고 있는 쓰레드로 갱신한다.
lock_release()
void
lock_release (struct lock *lock) {
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
lock->holder = NULL;
sema_up (&lock->semaphore);
}
쓰레드가 가지고 있는 lock을 놓아준다.
lock_held_by_current_thread()를 통해 lock->holder와 현재 쓰레드가 같은지 확인한다.
이후 sema_up을 통해 lock을 푼다.
cond_init()
void
cond_init (struct condition *cond) {
ASSERT (cond != NULL);
list_init (&cond->waiters);
}
cond 구조체를 초기화 한다.
cond_wait()
void
cond_wait (struct condition *cond, struct lock *lock) {
struct semaphore_elem waiter;
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
sema_init (&waiter.semaphore, 0);
list_push_back (&cond->waiters, &waiter.elem);
lock_release (lock);
sema_down (&waiter.semaphore);
lock_acquire (lock);
}
cond_wait()는 인터럽트 컨텍스트에서 실행되면 안된다.
(작성 예정)
cond_signal()
void
cond_signal (struct condition *cond, struct lock *lock UNUSED) {
ASSERT (cond != NULL);
ASSERT (lock != NULL);
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
if (!list_empty (&cond->waiters))
sema_up (&list_entry (list_pop_front (&cond->waiters),
struct semaphore_elem, elem)->semaphore);
}
(작성 예정)
cond_broadcast()
void
cond_broadcast (struct condition *cond, struct lock *lock) {
ASSERT (cond != NULL);
ASSERT (lock != NULL);
while (!list_empty (&cond->waiters))
cond_signal (cond, lock);
}
(작성 예정)