static void notify_loading_app(status_t result, bool suspend) { Team* team = thread_get_current_thread()->team; TeamLocker teamLocker(team); if (team->loading_info) { // there's indeed someone waiting struct team_loading_info* loadingInfo = team->loading_info; team->loading_info = NULL; loadingInfo->result = result; loadingInfo->done = true; // we're done with the team stuff, get the scheduler lock instead teamLocker.Unlock(); InterruptsSpinLocker schedulerLocker(gSchedulerLock); // wake up the waiting thread if (loadingInfo->thread->state == B_THREAD_SUSPENDED) scheduler_enqueue_in_run_queue(loadingInfo->thread); // suspend ourselves, if desired if (suspend) { thread_get_current_thread()->next_state = B_THREAD_SUSPENDED; scheduler_reschedule(); } } }
/*! You must call this function with interrupts disabled, and the semaphore's spinlock held. Note that it will unlock the spinlock itself. Since it cannot free() the semaphore's name with interrupts turned off, it will return that one in \a name. */ static void uninit_sem_locked(struct sem_entry& sem, char** _name) { KTRACE("delete_sem(sem: %ld)", sem.u.used.id); notify_sem_select_events(&sem, B_EVENT_INVALID); sem.u.used.select_infos = NULL; // free any threads waiting for this semaphore SpinLocker schedulerLocker(gSchedulerLock); while (queued_thread* entry = sem.queue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, B_BAD_SEM_ID); } schedulerLocker.Unlock(); int32 id = sem.id; sem.id = -1; *_name = sem.u.used.name; sem.u.used.name = NULL; RELEASE_SEM_LOCK(sem); // append slot to the free list GRAB_SEM_LIST_LOCK(); free_sem_slot(id % sMaxSems, id + sMaxSems); atomic_add(&sUsedSems, -1); RELEASE_SEM_LIST_LOCK(); }
void Notify(status_t status = B_OK) { InterruptsSpinLocker _(fLock); TRACE("ReadRequest %p::Notify(), fNotified %d\n", this, fNotified); if (!fNotified) { SpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fThread, status); fNotified = true; } }
static status_t delete_sem_internal(sem_id id, bool checkPermission) { if (sSemsActive == false) return B_NO_MORE_SEMS; if (id < 0) return B_BAD_SEM_ID; int32 slot = id % sMaxSems; cpu_status state = disable_interrupts(); GRAB_SEM_LIST_LOCK(); GRAB_SEM_LOCK(sSems[slot]); if (sSems[slot].id != id) { RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); TRACE(("delete_sem: invalid sem_id %ld\n", id)); return B_BAD_SEM_ID; } if (checkPermission && sSems[slot].u.used.owner == team_get_kernel_team_id()) { RELEASE_SEM_LOCK(sSems[slot]); RELEASE_SEM_LIST_LOCK(); restore_interrupts(state); dprintf("thread %ld tried to delete kernel semaphore %ld.\n", thread_get_current_thread_id(), id); return B_NOT_ALLOWED; } if (sSems[slot].u.used.owner >= 0) { list_remove_link(&sSems[slot].u.used.team_link); sSems[slot].u.used.owner = -1; } else panic("sem %ld has no owner", id); RELEASE_SEM_LIST_LOCK(); char* name; uninit_sem_locked(sSems[slot], &name); SpinLocker schedulerLocker(gSchedulerLock); scheduler_reschedule_if_necessary_locked(); schedulerLocker.Unlock(); restore_interrupts(state); free(name); return B_OK; }
bigtime_t cpu_get_active_time(int32 cpu) { if (cpu < 0 || cpu > smp_get_num_cpus()) return 0; // We need to grab the scheduler lock here, because the thread activity // time is not maintained atomically (because there is no need to). InterruptsSpinLocker schedulerLocker(gSchedulerLock); return gCPU[cpu].active_time; }
status_t BlockAndUnlock(Thread *thread, MutexLocker *setLocker) { thread_prepare_to_block(thread, B_CAN_INTERRUPT, THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore"); // Unlock the set before blocking setLocker->Unlock(); InterruptsSpinLocker schedulerLocker(gSchedulerLock); // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to // interruption, we will still be queued. A WakeUpThread() at this point will // call thread_unblock() and might thus screw with our trying to re-lock the // mutex. return thread_block_locked(thread); }
void x86_hardware_interrupt(struct iframe* frame) { int32 vector = frame->vector - ARCH_INTERRUPT_BASE; bool levelTriggered = false; Thread* thread = thread_get_current_thread(); if (sCurrentPIC->is_spurious_interrupt(vector)) { TRACE(("got spurious interrupt at vector %ld\n", vector)); return; } levelTriggered = sCurrentPIC->is_level_triggered_interrupt(vector); if (!levelTriggered) { // if it's not handled by the current pic then it's an apic generated // interrupt like local interrupts, msi or ipi. if (!sCurrentPIC->end_of_interrupt(vector)) apic_end_of_interrupt(); } int_io_interrupt_handler(vector, levelTriggered); if (levelTriggered) { if (!sCurrentPIC->end_of_interrupt(vector)) apic_end_of_interrupt(); } cpu_status state = disable_interrupts(); if (thread->cpu->invoke_scheduler) { SpinLocker schedulerLocker(thread->scheduler_lock); scheduler_reschedule(B_THREAD_READY); schedulerLocker.Unlock(); restore_interrupts(state); } else if (thread->post_interrupt_callback != NULL) { void (*callback)(void*) = thread->post_interrupt_callback; void* data = thread->post_interrupt_data; thread->post_interrupt_callback = NULL; thread->post_interrupt_data = NULL; restore_interrupts(state); callback(data); } }
/*! Forcibly removes a thread from a semaphores wait queue. May have to wake up other threads in the process. Must be called with semaphore lock held. The thread lock must not be held. */ static void remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem) { if (!entry->queued) return; sem->queue.Remove(entry); entry->queued = false; sem->u.used.count += entry->count; // We're done with this entry. We only have to check, if other threads // need unblocking, too. // Now see if more threads need to be woken up. We get the scheduler lock // for that time, so the blocking state of threads won't change (due to // interruption or timeout). We need that lock anyway when unblocking a // thread. SpinLocker schedulerLocker(gSchedulerLock); while ((entry = sem->queue.Head()) != NULL) { if (thread_is_blocked(entry->thread)) { // The thread is still waiting. If its count is satisfied, unblock // it. Otherwise we can't unblock any other thread. if (entry->count > sem->u.used.net_count) break; thread_unblock_locked(entry->thread, B_OK); sem->u.used.net_count -= entry->count; } else { // The thread is no longer waiting, but still queued, which means // acquiration failed and we can just remove it. sem->u.used.count += entry->count; } sem->queue.Remove(entry); entry->queued = false; } schedulerLocker.Unlock(); // select notification, if the semaphore is now acquirable if (sem->u.used.count > 0) notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE); }
void WakeUpThread(bool waitingForZero) { InterruptsSpinLocker schedulerLocker(gSchedulerLock); if (waitingForZero) { // Wake up all threads waiting on zero while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { entry->queued = false; fThreadsWaitingToBeZero--; thread_unblock_locked(entry->thread, 0); } } else { // Wake up all threads even though they might go back to sleep while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { entry->queued = false; fThreadsWaitingToIncrease--; thread_unblock_locked(entry->thread, 0); } } }
~XsiSemaphore() { // For some reason the semaphore is getting destroyed. // Wake up any remaing awaiting threads InterruptsSpinLocker schedulerLocker(gSchedulerLock); while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); } while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); } // No need to remove any sem_undo request still // hanging. When the process exit and doesn't found // the semaphore set, it'll just ignore the sem_undo // request. That's better than iterating trough the // whole sUndoList. Beside we don't know our semaphore // number nor our semaphore set id. }
status_t release_sem_etc(sem_id id, int32 count, uint32 flags) { int32 slot = id % sMaxSems; if (gKernelStartup) return B_OK; if (sSemsActive == false) return B_NO_MORE_SEMS; if (id < 0) return B_BAD_SEM_ID; if (count <= 0 && (flags & B_RELEASE_ALL) == 0) return B_BAD_VALUE; InterruptsLocker _; SpinLocker semLocker(sSems[slot].lock); if (sSems[slot].id != id) { TRACE(("sem_release_etc: invalid sem_id %ld\n", id)); return B_BAD_SEM_ID; } // ToDo: the B_CHECK_PERMISSION flag should be made private, as it // doesn't have any use outside the kernel if ((flags & B_CHECK_PERMISSION) != 0 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { dprintf("thread %ld tried to release kernel semaphore.\n", thread_get_current_thread_id()); return B_NOT_ALLOWED; } KTRACE("release_sem_etc(sem: %ld, count: %ld, flags: 0x%lx)", id, count, flags); sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer; #if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_releaser = thread_get_current_thread_id(); sSems[slot].u.used.last_release_count = count; #endif if (flags & B_RELEASE_ALL) { count = sSems[slot].u.used.net_count - sSems[slot].u.used.count; // is there anything to do for us at all? if (count == 0) return B_OK; // Don't release more than necessary -- there might be interrupted/ // timed out threads in the queue. flags |= B_RELEASE_IF_WAITING_ONLY; } // Grab the scheduler lock, so thread_is_blocked() is reliable (due to // possible interruptions or timeouts, it wouldn't be otherwise). SpinLocker schedulerLocker(gSchedulerLock); while (count > 0) { queued_thread* entry = sSems[slot].queue.Head(); if (entry == NULL) { if ((flags & B_RELEASE_IF_WAITING_ONLY) == 0) { sSems[slot].u.used.count += count; sSems[slot].u.used.net_count += count; } break; } if (thread_is_blocked(entry->thread)) { // The thread is still waiting. If its count is satisfied, // unblock it. Otherwise we can't unblock any other thread. if (entry->count > sSems[slot].u.used.net_count + count) { sSems[slot].u.used.count += count; sSems[slot].u.used.net_count += count; break; } thread_unblock_locked(entry->thread, B_OK); int delta = min_c(count, entry->count); sSems[slot].u.used.count += delta; sSems[slot].u.used.net_count += delta - entry->count; count -= delta; } else { // The thread is no longer waiting, but still queued, which // means acquiration failed and we can just remove it. sSems[slot].u.used.count += entry->count; } sSems[slot].queue.Remove(entry); entry->queued = false; } schedulerLocker.Unlock(); if (sSems[slot].u.used.count > 0) notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE); // If we've unblocked another thread reschedule, if we've not explicitly // been told not to. if ((flags & B_DO_NOT_RESCHEDULE) == 0) { semLocker.Unlock(); schedulerLocker.Lock(); scheduler_reschedule_if_necessary_locked(); } return B_OK; }
status_t switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, uint32 flags, bigtime_t timeout) { int slot = id % sMaxSems; int state; status_t status = B_OK; if (gKernelStartup) return B_OK; if (sSemsActive == false) return B_NO_MORE_SEMS; if (!are_interrupts_enabled()) { panic("switch_sem_etc: called with interrupts disabled for sem %ld\n", id); } if (id < 0) return B_BAD_SEM_ID; if (count <= 0 || (flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) { return B_BAD_VALUE; } state = disable_interrupts(); GRAB_SEM_LOCK(sSems[slot]); if (sSems[slot].id != id) { TRACE(("switch_sem_etc: bad sem %ld\n", id)); status = B_BAD_SEM_ID; goto err; } // TODO: the B_CHECK_PERMISSION flag should be made private, as it // doesn't have any use outside the kernel if ((flags & B_CHECK_PERMISSION) != 0 && sSems[slot].u.used.owner == team_get_kernel_team_id()) { dprintf("thread %ld tried to acquire kernel semaphore %ld.\n", thread_get_current_thread_id(), id); status = B_NOT_ALLOWED; goto err; } if (sSems[slot].u.used.count - count < 0) { if ((flags & B_RELATIVE_TIMEOUT) != 0 && timeout <= 0) { // immediate timeout status = B_WOULD_BLOCK; goto err; } else if ((flags & B_ABSOLUTE_TIMEOUT) != 0 && timeout < 0) { // absolute negative timeout status = B_TIMED_OUT; goto err; } } KTRACE("switch_sem_etc(semToBeReleased: %ld, sem: %ld, count: %ld, " "flags: 0x%lx, timeout: %lld)", semToBeReleased, id, count, flags, timeout); if ((sSems[slot].u.used.count -= count) < 0) { // we need to block Thread *thread = thread_get_current_thread(); TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p," " name = %s\n", id, sSems[slot].u.used.name, thread, thread->name)); // do a quick check to see if the thread has any pending signals // this should catch most of the cases where the thread had a signal SpinLocker schedulerLocker(gSchedulerLock); if (thread_is_interrupted(thread, flags)) { schedulerLocker.Unlock(); sSems[slot].u.used.count += count; status = B_INTERRUPTED; // the other semaphore will be released later goto err; } if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0) timeout = B_INFINITE_TIMEOUT; // enqueue in the semaphore queue and get ready to wait queued_thread queueEntry(thread, count); sSems[slot].queue.Add(&queueEntry); queueEntry.queued = true; thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SEMAPHORE, (void*)(addr_t)id); RELEASE_SEM_LOCK(sSems[slot]); // release the other semaphore, if any if (semToBeReleased >= 0) { release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE); semToBeReleased = -1; } schedulerLocker.Lock(); status_t acquireStatus = timeout == B_INFINITE_TIMEOUT ? thread_block_locked(thread) : thread_block_with_timeout_locked(flags, timeout); schedulerLocker.Unlock(); GRAB_SEM_LOCK(sSems[slot]); // If we're still queued, this means the acquiration failed, and we // need to remove our entry and (potentially) wake up other threads. if (queueEntry.queued) remove_thread_from_sem(&queueEntry, &sSems[slot]); if (acquireStatus >= B_OK) { sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); #if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_acquire_count = count; #endif } RELEASE_SEM_LOCK(sSems[slot]); restore_interrupts(state); TRACE(("switch_sem_etc(sem %ld): exit block name %s, " "thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id, thread->name)); KTRACE("switch_sem_etc() done: 0x%lx", acquireStatus); return acquireStatus; } else { sSems[slot].u.used.net_count -= count; sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); #if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_acquire_count = count; #endif } err: RELEASE_SEM_LOCK(sSems[slot]); restore_interrupts(state); if (status == B_INTERRUPTED && semToBeReleased >= B_OK) { // depending on when we were interrupted, we need to still // release the semaphore to always leave in a consistent // state release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE); } #if 0 if (status == B_NOT_ALLOWED) _user_debugger("Thread tried to acquire kernel semaphore."); #endif KTRACE("switch_sem_etc() done: 0x%lx", status); return status; }