status_t SystemProfiler::NextBuffer(size_t bytesRead, uint64* _droppedEvents) { InterruptsSpinLocker locker(fLock); if (fWaitingProfilerThread != NULL || !fProfilingActive || bytesRead > fBufferSize) { return B_BAD_VALUE; } fBufferSize -= bytesRead; fBufferStart += bytesRead; if (fBufferStart > fBufferCapacity) fBufferStart -= fBufferCapacity; fHeader->size = fBufferSize; fHeader->start = fBufferStart; // already enough data in the buffer to return? if (fBufferSize > fBufferCapacity / 2) return B_OK; // Wait until the buffer gets too full or an error or a timeout occurs. while (true) { Thread* thread = thread_get_current_thread(); fWaitingProfilerThread = thread; thread_prepare_to_block(thread, B_CAN_INTERRUPT, THREAD_BLOCK_TYPE_OTHER, "system profiler buffer"); locker.Unlock(); status_t error = thread_block_with_timeout(B_RELATIVE_TIMEOUT, 1000000); locker.Lock(); if (error == B_OK) { // the caller has unset fWaitingProfilerThread for us break; } fWaitingProfilerThread = NULL; if (error != B_TIMED_OUT) return error; // just the timeout -- return, if the buffer is not empty if (fBufferSize > 0) break; } if (_droppedEvents != NULL) { *_droppedEvents = fDroppedEvents; fDroppedEvents = 0; } return B_OK; }
status_t _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, bigtime_t timeout) { InterruptsSpinLocker locker(lock->lock); // We might be the writer ourselves. if (lock->holder == thread_get_current_thread_id()) { lock->owner_count++; return B_OK; } // The writer that originally had the lock when we called atomic_add() might // already have gone and another writer could have overtaken us. In this // case the original writer set pending_readers, so we know that we don't // have to wait. if (lock->pending_readers > 0) { lock->pending_readers--; if (lock->count >= RW_LOCK_WRITER_COUNT_BASE) lock->active_readers++; return B_OK; } ASSERT(lock->count >= RW_LOCK_WRITER_COUNT_BASE); // we need to wait // enqueue in waiter list rw_lock_waiter waiter; waiter.thread = thread_get_current_thread(); waiter.next = NULL; waiter.writer = false; if (lock->waiters != NULL) lock->waiters->last->next = &waiter; else lock->waiters = &waiter; lock->waiters->last = &waiter; // block thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_RW_LOCK, lock); locker.Unlock(); status_t error = thread_block_with_timeout(timeoutFlags, timeout); if (error == B_OK || waiter.thread == NULL) { // We were unblocked successfully -- potentially our unblocker overtook // us after we already failed. In either case, we've got the lock, now. return B_OK; } locker.Lock(); // We failed to get the lock -- dequeue from waiter list. rw_lock_waiter* previous = NULL; rw_lock_waiter* other = lock->waiters; while (other != &waiter) { previous = other; other = other->next; } if (previous == NULL) { // we are the first in line lock->waiters = waiter.next; if (lock->waiters != NULL) lock->waiters->last = waiter.last; } else { // one or more other waiters are before us in the queue previous->next = waiter.next; if (lock->waiters->last == &waiter) lock->waiters->last = previous; } // Decrement the count. ATM this is all we have to do. There's at least // one writer ahead of us -- otherwise the last writer would have unblocked // us (writers only manipulate the lock data with thread spinlock being // held) -- so our leaving doesn't make a difference to the ones behind us // in the queue. atomic_add(&lock->count, -1); return error; }
status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout) { #if KDEBUG if (!gKernelStartup && !are_interrupts_enabled()) { panic("_mutex_lock(): called with interrupts disabled for lock %p", lock); } #endif InterruptsSpinLocker locker(lock->lock); // Might have been released after we decremented the count, but before // we acquired the spinlock. #if KDEBUG if (lock->holder < 0) { lock->holder = thread_get_current_thread_id(); return B_OK; } else if (lock->holder == thread_get_current_thread_id()) { panic("_mutex_lock(): double lock of %p by thread %" B_PRId32, lock, lock->holder); } else if (lock->holder == 0) panic("_mutex_lock(): using unitialized lock %p", lock); #else if ((lock->flags & MUTEX_FLAG_RELEASED) != 0) { lock->flags &= ~MUTEX_FLAG_RELEASED; return B_OK; } #endif // enqueue in waiter list mutex_waiter waiter; waiter.thread = thread_get_current_thread(); waiter.next = NULL; if (lock->waiters != NULL) { lock->waiters->last->next = &waiter; } else lock->waiters = &waiter; lock->waiters->last = &waiter; // block thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_MUTEX, lock); locker.Unlock(); status_t error = thread_block_with_timeout(timeoutFlags, timeout); if (error == B_OK) { #if KDEBUG lock->holder = waiter.thread->id; #endif } else { locker.Lock(); // If the timeout occurred, we must remove our waiter structure from // the queue. mutex_waiter* previousWaiter = NULL; mutex_waiter* otherWaiter = lock->waiters; while (otherWaiter != NULL && otherWaiter != &waiter) { previousWaiter = otherWaiter; otherWaiter = otherWaiter->next; } if (otherWaiter == &waiter) { // the structure is still in the list -- dequeue if (&waiter == lock->waiters) { if (waiter.next != NULL) waiter.next->last = waiter.last; lock->waiters = waiter.next; } else { if (waiter.next == NULL) lock->waiters->last = previousWaiter; previousWaiter->next = waiter.next; } #if !KDEBUG // we need to fix the lock count if (atomic_add(&lock->count, 1) == -1) { // This means we were the only thread waiting for the lock and // the lock owner has already called atomic_add() in // mutex_unlock(). That is we probably would get the lock very // soon (if the lock holder has a low priority, that might // actually take rather long, though), but the timeout already // occurred, so we don't try to wait. Just increment the ignore // unlock count. lock->ignore_unlock_count++; } #endif } } return error; }
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 KDEBUG if (!are_interrupts_enabled()) { panic("switch_sem_etc: called with interrupts disabled for sem " "%" B_PRId32 "\n", id); } #endif 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 %" B_PRId32 " tried to acquire kernel semaphore " "%" B_PRId32 ".\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(thread->scheduler_lock); 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; } schedulerLocker.Unlock(); 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; } status_t acquireStatus = timeout == B_INFINITE_TIMEOUT ? thread_block() : thread_block_with_timeout(flags, timeout); 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; }