1326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams/* 2326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Copyright (C) 2009 The Android Open Source Project 3326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 4326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Licensed under the Apache License, Version 2.0 (the "License"); 5326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * you may not use this file except in compliance with the License. 6326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * You may obtain a copy of the License at 7326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 8326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * http://www.apache.org/licenses/LICENSE-2.0 9326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * 10326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * Unless required by applicable law or agreed to in writing, software 11326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * distributed under the License is distributed on an "AS IS" BASIS, 12326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * See the License for the specific language governing permissions and 14326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams * limitations under the License. 15326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams */ 16326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 17326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include "rsContext.h" 18326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams#include "rsThreadIO.h" 194edf030cbb7c6ac08dc563335c2af73c20f6e2e5Alex Sakhartchouk#include "rsgApiStructs.h" 20326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 215f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams#include <unistd.h> 225f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams#include <sys/types.h> 235f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams#include <sys/socket.h> 245f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 255f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams#include <fcntl.h> 265f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams#include <poll.h> 275f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 285f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 29326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsusing namespace android; 30326e0ddf89e8df2837752fbfd7a014814b32082cJason Samsusing namespace android::renderscript; 31326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 325f27d6fb0b0b9184ba9820c629fc1354a635e515Jason SamsThreadIO::ThreadIO() { 335f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mRunning = true; 34bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams mPureFifo = false; 35bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams mMaxInlineSize = 1024; 36326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams} 37326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 38afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex SakhartchoukThreadIO::~ThreadIO() { 39326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams} 40326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 415f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Samsvoid ThreadIO::init() { 425f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToClient.init(); 435f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToCore.init(); 441a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 451a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 46afb743aca56c18beb7ab924e75cb6e070ef3e55aAlex Sakhartchoukvoid ThreadIO::shutdown() { 475f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mRunning = false; 488c0ee6567b3c874dd472843eb7918ae68d1b9739Jason Sams mToCore.shutdown(); 491a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 501a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 511a4efa363916977ef9aeab756725b3bdc880a15bJason Samsvoid * ThreadIO::coreHeader(uint32_t cmdID, size_t dataLen) { 52af12ac6a08651464f8d823add667c706f993b587Steve Block //ALOGE("coreHeader %i %i", cmdID, dataLen); 535f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams CoreCmdHeader *hdr = (CoreCmdHeader *)&mSendBuffer[0]; 545f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams hdr->bytes = dataLen; 555f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams hdr->cmdID = cmdID; 565f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mSendLen = dataLen + sizeof(CoreCmdHeader); 575f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //mToCoreSocket.writeAsync(&hdr, sizeof(hdr)); 585f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("coreHeader ret "); 595f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams return &mSendBuffer[sizeof(CoreCmdHeader)]; 601a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 611a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 621a4efa363916977ef9aeab756725b3bdc880a15bJason Samsvoid ThreadIO::coreCommit() { 635f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToCore.writeAsync(&mSendBuffer, mSendLen); 641a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 651a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 661a4efa363916977ef9aeab756725b3bdc880a15bJason Samsvoid ThreadIO::clientShutdown() { 671a4efa363916977ef9aeab756725b3bdc880a15bJason Sams mToClient.shutdown(); 681a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 691a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 70bda75a977726835d74b2380d7e92360ed2a1ff7aJason Samsvoid ThreadIO::coreWrite(const void *data, size_t len) { 71bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams //ALOGV("core write %p %i", data, (int)len); 72bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams mToCore.writeAsync(data, len, true); 73bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams} 74bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams 75bda75a977726835d74b2380d7e92360ed2a1ff7aJason Samsvoid ThreadIO::coreRead(void *data, size_t len) { 76bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams //ALOGV("core read %p %i", data, (int)len); 77bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams mToCore.read(data, len); 78bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams} 79bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams 801a4efa363916977ef9aeab756725b3bdc880a15bJason Samsvoid ThreadIO::coreSetReturn(const void *data, size_t dataLen) { 815f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams uint32_t buf; 825f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (data == NULL) { 835f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams data = &buf; 845f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams dataLen = sizeof(buf); 855f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 865f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 875f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToCore.readReturn(data, dataLen); 881a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 891a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 901a4efa363916977ef9aeab756725b3bdc880a15bJason Samsvoid ThreadIO::coreGetReturn(void *data, size_t dataLen) { 915f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams uint32_t buf; 925f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (data == NULL) { 935f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams data = &buf; 945f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams dataLen = sizeof(buf); 955f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 961a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 975f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToCore.writeWaitReturn(data, dataLen); 982382aba4a55c6ae74789c478eead8fbd96593321Jason Sams} 992382aba4a55c6ae74789c478eead8fbd96593321Jason Sams 1005f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Samsvoid ThreadIO::setTimeoutCallback(void (*cb)(void *), void *dat, uint64_t timeout) { 1015f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //mToCore.setTimeoutCallback(cb, dat, timeout); 1025f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams} 1031a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 104963a2fb9f03c88633bc67c4a1789429b9a482091Jason Samsbool ThreadIO::playCoreCommands(Context *con, int waitFd) { 105a44cb29164726cd9d812117819abdd7b60dfdd93Jason Sams bool ret = false; 106bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams const bool isLocal = !isPureFifo(); 107e0aab4a8ff1cffd8cfaedc2623db94072549e0e5Jason Sams 1085f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams uint8_t buf[2 * 1024]; 1095f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams const CoreCmdHeader *cmd = (const CoreCmdHeader *)&buf[0]; 1105f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams const void * data = (const void *)&buf[sizeof(CoreCmdHeader)]; 1115f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 1125f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams struct pollfd p[2]; 1135f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[0].fd = mToCore.getReadFd(); 1145f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[0].events = POLLIN; 1155f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[0].revents = 0; 1165f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[1].fd = waitFd; 1175f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[1].events = POLLIN; 1185f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams p[1].revents = 0; 1195f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams int pollCount = 1; 1205f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (waitFd >= 0) { 1215f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams pollCount = 2; 1225f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 123e0aab4a8ff1cffd8cfaedc2623db94072549e0e5Jason Sams 1245f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (con->props.mLogTimes) { 1255f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams con->timerSet(Context::RS_TIMER_IDLE); 1265f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 12736838469a96b984fa27b61288ca1043b664af370Stephen Hines 1285f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams int waitTime = -1; 1295f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams while (mRunning) { 1305f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams int pr = poll(p, pollCount, waitTime); 1315f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (pr <= 0) { 13236838469a96b984fa27b61288ca1043b664af370Stephen Hines break; 13336838469a96b984fa27b61288ca1043b664af370Stephen Hines } 13436838469a96b984fa27b61288ca1043b664af370Stephen Hines 1355f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (p[0].revents) { 136bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams size_t r = 0; 137bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams if (isLocal) { 138bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams r = mToCore.read(&buf[0], sizeof(CoreCmdHeader)); 139bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes); 140bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams if (r != sizeof(CoreCmdHeader)) { 141bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams // exception or timeout occurred. 142bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams break; 143bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams } 144bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams } else { 145bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams r = mToCore.read((void *)&cmd->cmdID, sizeof(cmd->cmdID)); 1465f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 1475f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 148bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams 1495f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams ret = true; 1505f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (con->props.mLogTimes) { 1515f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams con->timerSet(Context::RS_TIMER_INTERNAL); 1525f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 1535f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGV("playCoreCommands 3 %i %i", cmd->cmdID, cmd->bytes); 1545f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 1555f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (cmd->cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) { 1565f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams rsAssert(cmd->cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *))); 1575f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams ALOGE("playCoreCommands error con %p, cmd %i", con, cmd->cmdID); 1585f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 159bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams 160bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams if (isLocal) { 161bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes); 162bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams } else { 163bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams gPlaybackRemoteFuncs[cmd->cmdID](con, this); 164bda75a977726835d74b2380d7e92360ed2a1ff7aJason Sams } 1655f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 1665f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (con->props.mLogTimes) { 1675f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams con->timerSet(Context::RS_TIMER_IDLE); 1685f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 1695f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 1705f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (waitFd < 0) { 1715f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams // If we don't have a secondary wait object we should stop blocking now 1725f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams // that at least one command has been processed. 1735f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams waitTime = 0; 1745f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams } 17576371fff76412fd020e24ddb8bf1ddb5c75f0ed1Joe Onorato } 176326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 1775f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (p[1].revents && !p[0].revents) { 1785f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams // We want to finish processing fifo events before processing the vsync. 1795f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams // Otherwise we can end up falling behind and having tremendous lag. 1805f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams break; 181185b8b01f417488e2fbf6e6c00dfbd3d1d43d98aJason Sams } 182326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams } 183a44cb29164726cd9d812117819abdd7b60dfdd93Jason Sams return ret; 184326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams} 185326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 1861a4efa363916977ef9aeab756725b3bdc880a15bJason SamsRsMessageToClientType ThreadIO::getClientHeader(size_t *receiveLen, uint32_t *usrID) { 1875f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("getClientHeader"); 1885f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToClient.read(&mLastClientHeader, sizeof(mLastClientHeader)); 1895f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 1901a4efa363916977ef9aeab756725b3bdc880a15bJason Sams receiveLen[0] = mLastClientHeader.bytes; 1911a4efa363916977ef9aeab756725b3bdc880a15bJason Sams usrID[0] = mLastClientHeader.userID; 1925f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("getClientHeader %i %i %i", mLastClientHeader.cmdID, usrID[0], receiveLen[0]); 1931a4efa363916977ef9aeab756725b3bdc880a15bJason Sams return (RsMessageToClientType)mLastClientHeader.cmdID; 1941a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 1951a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 1961a4efa363916977ef9aeab756725b3bdc880a15bJason SamsRsMessageToClientType ThreadIO::getClientPayload(void *data, size_t *receiveLen, 1971a4efa363916977ef9aeab756725b3bdc880a15bJason Sams uint32_t *usrID, size_t bufferLen) { 1985f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("getClientPayload"); 1991a4efa363916977ef9aeab756725b3bdc880a15bJason Sams receiveLen[0] = mLastClientHeader.bytes; 2001a4efa363916977ef9aeab756725b3bdc880a15bJason Sams usrID[0] = mLastClientHeader.userID; 2011a4efa363916977ef9aeab756725b3bdc880a15bJason Sams if (bufferLen < mLastClientHeader.bytes) { 2021a4efa363916977ef9aeab756725b3bdc880a15bJason Sams return RS_MESSAGE_TO_CLIENT_RESIZE; 2031a4efa363916977ef9aeab756725b3bdc880a15bJason Sams } 2045f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (receiveLen[0]) { 2055f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToClient.read(data, receiveLen[0]); 2061a4efa363916977ef9aeab756725b3bdc880a15bJason Sams } 2075f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("getClientPayload x"); 2085f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams return (RsMessageToClientType)mLastClientHeader.cmdID; 2091a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 2101a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 2111a4efa363916977ef9aeab756725b3bdc880a15bJason Samsbool ThreadIO::sendToClient(RsMessageToClientType cmdID, uint32_t usrID, const void *data, 2121a4efa363916977ef9aeab756725b3bdc880a15bJason Sams size_t dataLen, bool waitForSpace) { 2135f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 2145f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("sendToClient %i %i %i", cmdID, usrID, (int)dataLen); 2151a4efa363916977ef9aeab756725b3bdc880a15bJason Sams ClientCmdHeader hdr; 2161a4efa363916977ef9aeab756725b3bdc880a15bJason Sams hdr.bytes = dataLen; 2171a4efa363916977ef9aeab756725b3bdc880a15bJason Sams hdr.cmdID = cmdID; 2181a4efa363916977ef9aeab756725b3bdc880a15bJason Sams hdr.userID = usrID; 2191a4efa363916977ef9aeab756725b3bdc880a15bJason Sams 2205f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToClient.writeAsync(&hdr, sizeof(hdr)); 2215f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams if (dataLen) { 2225f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams mToClient.writeAsync(data, dataLen); 2231a4efa363916977ef9aeab756725b3bdc880a15bJason Sams } 2245f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams 2255f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams //ALOGE("sendToClient x"); 2265f27d6fb0b0b9184ba9820c629fc1354a635e515Jason Sams return true; 2271a4efa363916977ef9aeab756725b3bdc880a15bJason Sams} 228326e0ddf89e8df2837752fbfd7a014814b32082cJason Sams 229