Rozwiązanie tego zadanie w środowiku wieloprocesorowym wymaga wykorzystania zarówno blokowania przerwań jak i blokady typu spinlock.
void semaphore_lock(semaphore_t *sem) { disable_interrupts(); spinlock_lock(&sem->spinlock); if (sem->value==1) { sem->value=0; spinlock_unlock(&sem->spinlock); } else { add_last(sem->queue, current); spinlock_lock(&scheduler_lock); remove(run_queue, current); spinlock_unlock(&scheduler_lock); spinlock_unlock(&sem->spinlock); scheduler(); } restore_interrupts(); } void semaphore_unlock(semaphore_t *sem) { disable_interrupts(); spinlock_lock(&sem->spinlock); if (empty(sem->queue)) { sem->value = 1; } else { struct task *next = remove_first(sem->queue); spinlock_lock(&scheduler_lock); add_last(run_queue, next); spinlock_unlock(&scheduler_lock); } spinlock_unlock(&sem->spinlock); restore_interrupts(); }
To rozwiązanie jest poprawne.
Zauważmy, że dostęp do kolejki run_queue, także musi być chroniony, gdyż inny proces może z niej korzystać na innym procesorze (w funkcji scheduler).
Można się także zastanowić nad rozwiązaniem, w którym zamieniona jest kolejność zdobywania blokad (najpierw sem->spinlock, a potem interrupts). Rozwiązanie takie jest poprawne, ale wydajność jego jest słaba. W takim rozwiązaniu jest moment, w którym po uzyskaniu spinlocka proces może stracić procesor, gdyż nie zablokował jeszcze przerwań. Jeśli rzeczywiście tak się zdarzy (na skutek przerwania od zegara), to proces będzie trzymał spinlocka długi czas (czekając aż znowu dostanie procesor) - blokując w ten sposób procesy starające się zdobyć tego spinlocka.