bool CheckSendThreads() { SortSendThreads(); bool wokeThreads = false; bool filledSpace = false; while (!sendWaitingThreads.empty() && nmp.freeSize > 0) { MsgPipeWaitingThread *thread = &sendWaitingThreads.front(); u32 bytesToSend = std::min(thread->freeSize, (u32) nmp.freeSize); thread->ReadBuffer(buffer + GetUsedSize(), bytesToSend); nmp.freeSize -= bytesToSend; filledSpace = true; if (thread->waitMode == SCE_KERNEL_MPW_ASAP || thread->freeSize == 0) { thread->Complete(GetUID(), 0); sendWaitingThreads.erase(sendWaitingThreads.begin()); wokeThreads = true; thread = NULL; } // Unlike receives, we don't do partial sends. Stop at first blocked thread. else break; } if (filledSpace) wokeThreads |= CheckReceiveThreads(); return wokeThreads; }
static int __KernelReceiveMsgPipe(MsgPipe *m, u32 receiveBufAddr, u32 receiveSize, int waitMode, u32 resultAddr, u32 timeoutPtr, bool cbEnabled, bool poll, bool &needsResched, bool &needsWait) { u32 curReceiveAddr = receiveBufAddr; SceUID uid = m->GetUID(); // MsgPipe buffer size is 0, receiving directly from waiting send threads if (m->nmp.bufSize == 0) { m->SortSendThreads(); // While they're still sending waiting threads (which can send data) while (!m->sendWaitingThreads.empty() && receiveSize != 0) { MsgPipeWaitingThread *thread = &m->sendWaitingThreads.front(); // For send threads, "freeSize" is "free to be read". u32 bytesToReceive = std::min(thread->freeSize, receiveSize); if (bytesToReceive > 0) { thread->ReadBuffer(curReceiveAddr, bytesToReceive); receiveSize -= bytesToReceive; curReceiveAddr += bytesToReceive; if (thread->freeSize == 0 || thread->waitMode == SCE_KERNEL_MPW_ASAP) { thread->Complete(uid, 0); m->sendWaitingThreads.erase(m->sendWaitingThreads.begin()); needsResched = true; thread = NULL; } } } // All data hasn't been received and (mode isn't ASAP or nothing was received) if (receiveSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curReceiveAddr == receiveBufAddr)) { 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(curReceiveAddr - receiveBufAddr, resultAddr); return SCE_KERNEL_ERROR_MPP_EMPTY; } else { m->AddReceiveWaitingThread(__KernelGetCurThread(), curReceiveAddr, receiveSize, waitMode, resultAddr); needsWait = true; return 0; } } } // Getting data from the MsgPipe buffer else { if (receiveSize > (u32) m->nmp.bufSize) { ERROR_LOG(SCEKERNEL, "__KernelReceiveMsgPipe(%d): size %d too large for buffer", uid, receiveSize); return SCE_KERNEL_ERROR_ILLEGAL_SIZE; } while (m->GetUsedSize() > 0) { u32 bytesToReceive = std::min(receiveSize, m->GetUsedSize()); if (bytesToReceive != 0) { Memory::Memcpy(curReceiveAddr, m->buffer, bytesToReceive); m->nmp.freeSize += bytesToReceive; memmove(Memory::GetPointer(m->buffer), Memory::GetPointer(m->buffer) + bytesToReceive, m->GetUsedSize()); curReceiveAddr += bytesToReceive; receiveSize -= bytesToReceive; m->CheckSendThreads(); } else break; } if (receiveSize != 0 && (waitMode != SCE_KERNEL_MPW_ASAP || curReceiveAddr == receiveBufAddr)) { if (poll) return SCE_KERNEL_ERROR_MPP_EMPTY; else { m->AddReceiveWaitingThread(__KernelGetCurThread(), curReceiveAddr, receiveSize, waitMode, resultAddr); needsWait = true; return 0; } } } if (Memory::IsValidAddress(resultAddr)) Memory::Write_U32(curReceiveAddr - receiveBufAddr, resultAddr); return 0; }