// This function should be only ran when the temporary buffer size is not 0 (otherwise, data is copied directly to the threads) bool CheckReceiveThreads() { SortReceiveThreads(); bool wokeThreads = false; bool freedSpace = false; while (!receiveWaitingThreads.empty() && GetUsedSize() > 0) { MsgPipeWaitingThread *thread = &receiveWaitingThreads.front(); // Receive as much as possible, even if it's not enough to wake up. u32 bytesToSend = std::min(thread->freeSize, GetUsedSize()); thread->WriteBuffer(Memory::GetPointer(buffer), bytesToSend); // Put the unused data at the start of the buffer. nmp.freeSize += bytesToSend; memmove(Memory::GetPointer(buffer), Memory::GetPointer(buffer) + bytesToSend, GetUsedSize()); freedSpace = true; if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0) { thread->Complete(GetUID(), 0); receiveWaitingThreads.erase(receiveWaitingThreads.begin()); wokeThreads = true; thread = NULL; } // Stop at the first that can't wake up. else break; } if (freedSpace) wokeThreads |= CheckSendThreads(); return wokeThreads; }
static int __KernelSendMsgPipe(MsgPipe *m, u32 sendBufAddr, u32 sendSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll, bool &needsResched, bool &needsWait) { u32 curSendAddr = sendBufAddr; SceUID uid = m->GetUID(); // If the buffer size is 0, nothing is buffered and all operations wait. if (m->nmp.bufSize == 0) { m->SortReceiveThreads(); while (!m->receiveWaitingThreads.empty() && sendSize != 0) { MsgPipeWaitingThread *thread = &m->receiveWaitingThreads.front(); u32 bytesToSend = std::min(thread->freeSize, sendSize); if (bytesToSend > 0) { thread->WriteBuffer(curSendAddr, bytesToSend); sendSize -= bytesToSend; curSendAddr += bytesToSend; if (thread->freeSize == 0 || thread->waitMode == SCE_KERNEL_MPW_ASAP) { thread->Complete(uid, 0); m->receiveWaitingThreads.erase(m->receiveWaitingThreads.begin()); needsResched = true; thread = NULL; } } } // If there is still data to send and (we want to send all of it or we didn't send anything) if (sendSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curSendAddr == sendBufAddr)) { if (poll) { // Generally, result is not updated in this case. But for a 0 size buffer in ASAP mode, it is. if (Memory::IsValidAddress(resultAddr) && waitMode == SCE_KERNEL_MPW_ASAP) Memory::Write_U32(curSendAddr - sendBufAddr, resultAddr); return SCE_KERNEL_ERROR_MPP_FULL; } else { m->AddSendWaitingThread(__KernelGetCurThread(), curSendAddr, sendSize, waitMode, resultAddr); needsWait = true; return 0; } } } else { if (sendSize > (u32) m->nmp.bufSize) { ERROR_LOG(SCEKERNEL, "__KernelSendMsgPipe(%d): size %d too large for buffer", uid, sendSize); return SCE_KERNEL_ERROR_ILLEGAL_SIZE; } u32 bytesToSend = 0; // If others are already waiting, space or not, we have to get in line. m->SortSendThreads(); if (m->sendWaitingThreads.empty()) { if (sendSize <= (u32) m->nmp.freeSize) bytesToSend = sendSize; else if (waitMode == SCE_KERNEL_MPW_ASAP) bytesToSend = m->nmp.freeSize; } if (bytesToSend != 0) { Memory::Memcpy(m->buffer + (m->nmp.bufSize - m->nmp.freeSize), sendBufAddr, bytesToSend); m->nmp.freeSize -= bytesToSend; curSendAddr += bytesToSend; sendSize -= bytesToSend; if (m->CheckReceiveThreads()) needsResched = true; } else if (sendSize != 0) { if (poll) return SCE_KERNEL_ERROR_MPP_FULL; else { m->AddSendWaitingThread(__KernelGetCurThread(), curSendAddr, sendSize, waitMode, resultAddr); needsWait = true; return 0; } } } // We didn't wait, so update the number of bytes transferred now. if (Memory::IsValidAddress(resultAddr)) Memory::Write_U32(curSendAddr - sendBufAddr, resultAddr); return 0; }