/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rsContext.h"
#include "rsThreadIO.h"
#include "rsgApiStructs.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <poll.h>
namespace android {
namespace renderscript {
ThreadIO::ThreadIO() {
mRunning = true;
mMaxInlineSize = 1024;
}
ThreadIO::~ThreadIO() {
}
bool ThreadIO::init() {
return mToClient.init() && mToCore.init();
}
void ThreadIO::shutdown() {
mRunning = false;
mToCore.shutdown();
}
void * ThreadIO::coreHeader(uint32_t cmdID, size_t dataLen) {
//ALOGE("coreHeader %i %i", cmdID, dataLen);
CoreCmdHeader *hdr = (CoreCmdHeader *)&mSendBuffer[0];
hdr->bytes = dataLen;
hdr->cmdID = cmdID;
mSendLen = dataLen + sizeof(CoreCmdHeader);
//mToCoreSocket.writeAsync(&hdr, sizeof(hdr));
//ALOGE("coreHeader ret ");
return &mSendBuffer[sizeof(CoreCmdHeader)];
}
void ThreadIO::coreCommit() {
mToCore.writeAsync(&mSendBuffer, mSendLen);
}
void ThreadIO::clientShutdown() {
mToClient.shutdown();
}
void ThreadIO::coreWrite(const void *data, size_t len) {
//ALOGV("core write %p %i", data, (int)len);
mToCore.writeAsync(data, len, true);
}
void ThreadIO::coreRead(void *data, size_t len) {
//ALOGV("core read %p %i", data, (int)len);
mToCore.read(data, len);
}
void ThreadIO::coreSetReturn(const void *data, size_t dataLen) {
uint32_t buf;
if (data == nullptr) {
data = &buf;
dataLen = sizeof(buf);
}
mToCore.readReturn(data, dataLen);
}
void ThreadIO::coreGetReturn(void *data, size_t dataLen) {
uint32_t buf;
if (data == nullptr) {
data = &buf;
dataLen = sizeof(buf);
}
mToCore.writeWaitReturn(data, dataLen);
}
void ThreadIO::setTimeoutCallback(void (*cb)(void *), void *dat, uint64_t timeout) {
//mToCore.setTimeoutCallback(cb, dat, timeout);
}
bool ThreadIO::playCoreCommands(Context *con, int waitFd) {
bool ret = false;
uint8_t buf[2 * 1024];
const CoreCmdHeader *cmd = (const CoreCmdHeader *)&buf[0];
const void * data = (const void *)&buf[sizeof(CoreCmdHeader)];
struct pollfd p[2];
p[0].fd = mToCore.getReadFd();
p[0].events = POLLIN;
p[0].revents = 0;
p[1].fd = waitFd;
p[1].events = POLLIN;
p[1].revents = 0;
int pollCount = 1;
if (waitFd >= 0) {
pollCount = 2;
}
if (con->props.mLogTimes) {
con->timerSet(Context::RS_TIMER_IDLE);
}
int waitTime = -1;
while (mRunning) {
int pr = poll(p, pollCount, waitTime);
if (pr <= 0) {
break;
}
if (p[0].revents) {
size_t r = 0;
r = mToCore.read(&buf[0], sizeof(CoreCmdHeader));
mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes);
if (r != sizeof(CoreCmdHeader)) {
// exception or timeout occurred.
break;
}
ret = true;
if (con->props.mLogTimes) {
con->timerSet(Context::RS_TIMER_INTERNAL);
}
//ALOGV("playCoreCommands 3 %i %i", cmd->cmdID, cmd->bytes);
if (cmd->cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) {
rsAssert(cmd->cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *)));
ALOGE("playCoreCommands error con %p, cmd %i", con, cmd->cmdID);
}
gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes);
if (con->props.mLogTimes) {
con->timerSet(Context::RS_TIMER_IDLE);
}
if (waitFd < 0) {
// If we don't have a secondary wait object we should stop blocking now
// that at least one command has been processed.
waitTime = 0;
}
}
if (p[1].revents && !p[0].revents) {
// We want to finish processing fifo events before processing the vsync.
// Otherwise we can end up falling behind and having tremendous lag.
break;
}
}
return ret;
}
RsMessageToClientType ThreadIO::getClientHeader(size_t *receiveLen, uint32_t *usrID) {
//ALOGE("getClientHeader");
mToClient.read(&mLastClientHeader, sizeof(mLastClientHeader));
receiveLen[0] = mLastClientHeader.bytes;
usrID[0] = mLastClientHeader.userID;
//ALOGE("getClientHeader %i %i %i", mLastClientHeader.cmdID, usrID[0], receiveLen[0]);
return (RsMessageToClientType)mLastClientHeader.cmdID;
}
RsMessageToClientType ThreadIO::getClientPayload(void *data, size_t *receiveLen,
uint32_t *usrID, size_t bufferLen) {
//ALOGE("getClientPayload");
receiveLen[0] = mLastClientHeader.bytes;
usrID[0] = mLastClientHeader.userID;
if (bufferLen < mLastClientHeader.bytes) {
return RS_MESSAGE_TO_CLIENT_RESIZE;
}
if (receiveLen[0]) {
mToClient.read(data, receiveLen[0]);
}
//ALOGE("getClientPayload x");
return (RsMessageToClientType)mLastClientHeader.cmdID;
}
bool ThreadIO::sendToClient(RsMessageToClientType cmdID, uint32_t usrID, const void *data,
size_t dataLen, bool waitForSpace) {
//ALOGE("sendToClient %i %i %i", cmdID, usrID, (int)dataLen);
ClientCmdHeader hdr;
hdr.bytes = (uint32_t)dataLen;
hdr.cmdID = cmdID;
hdr.userID = usrID;
mToClient.writeAsync(&hdr, sizeof(hdr));
if (dataLen) {
mToClient.writeAsync(data, dataLen);
}
//ALOGE("sendToClient x");
return true;
}
} // namespace renderscript
} // namespace android