diff options
Diffstat (limited to 'libs/utils/Pipe.cpp')
-rw-r--r-- | libs/utils/Pipe.cpp | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp new file mode 100644 index 0000000..613906b --- /dev/null +++ b/libs/utils/Pipe.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Unidirectional pipe. +// + +#include <utils/Pipe.h> +#include <utils/Log.h> + +#if defined(HAVE_WIN32_IPC) +# include <windows.h> +#else +# include <fcntl.h> +# include <unistd.h> +# include <errno.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> + +using namespace android; + +const unsigned long kInvalidHandle = (unsigned long) -1; + + +/* + * Constructor. Do little. + */ +Pipe::Pipe(void) + : mReadNonBlocking(false), mReadHandle(kInvalidHandle), + mWriteHandle(kInvalidHandle) +{ +} + +/* + * Destructor. Use the system-appropriate close call. + */ +Pipe::~Pipe(void) +{ +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) + LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", + mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + FlushFileBuffers((HANDLE)mWriteHandle); + if (!CloseHandle((HANDLE)mWriteHandle)) + LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", + mWriteHandle); + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", + (int) mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", + (int) mWriteHandle); + } +#endif +} + +/* + * Create the pipe. + * + * Use the POSIX stuff for everything but Windows. + */ +bool Pipe::create(void) +{ + assert(mReadHandle == kInvalidHandle); + assert(mWriteHandle == kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + /* we use this across processes, so they need to be inheritable */ + HANDLE handles[2]; + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = (unsigned long) handles[0]; + mWriteHandle = (unsigned long) handles[1]; + return true; +#else + int fds[2]; + + if (pipe(fds) != 0) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = fds[0]; + mWriteHandle = fds[1]; + return true; +#endif +} + +/* + * Create a "half pipe". Please, no Segway riding. + */ +bool Pipe::createReader(unsigned long handle) +{ + mReadHandle = handle; + assert(mWriteHandle == kInvalidHandle); + return true; +} + +/* + * Create a "half pipe" for writing. + */ +bool Pipe::createWriter(unsigned long handle) +{ + mWriteHandle = handle; + assert(mReadHandle == kInvalidHandle); + return true; +} + +/* + * Return "true" if create() has been called successfully. + */ +bool Pipe::isCreated(void) +{ + // one or the other should be open + return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); +} + + +/* + * Read data from the pipe. + * + * For Linux and Darwin, just call read(). For Windows, implement + * non-blocking reads by calling PeekNamedPipe first. + */ +int Pipe::read(void* buf, int count) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail = count; + DWORD bytesRead; + + if (mReadNonBlocking) { + // use PeekNamedPipe to adjust read count expectations + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return -1; + } + + if (totalBytesAvail == 0) + return 0; + } + + if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, + NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) + return 0; + LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); + return -1; + } + + return (int) bytesRead; +#else + int cc; + cc = ::read(mReadHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Write data to the pipe. + * + * POSIX systems are trivial, Windows uses a different call and doesn't + * handle non-blocking writes. + * + * If we add non-blocking support here, we probably want to make it an + * all-or-nothing write. + * + * DO NOT use LOG() here, we could be writing a log message. + */ +int Pipe::write(const void* buf, int count) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD bytesWritten; + + if (mWriteNonBlocking) { + // BUG: can't use PeekNamedPipe() to get the amount of space + // left. Looks like we need to use "overlapped I/O" functions. + // I just don't care that much. + } + + if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { + // can't LOG, use stderr + fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); + return -1; + } + + return (int) bytesWritten; +#else + int cc; + cc = ::write(mWriteHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Figure out if there is data available on the read fd. + * + * We return "true" on error because we want the caller to try to read + * from the pipe. They'll notice the read failure and do something + * appropriate. + */ +bool Pipe::readReady(void) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail; + + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return true; + } + + return (totalBytesAvail != 0); +#else + errno = 0; + fd_set readfds; + struct timeval tv = { 0, 0 }; + int cc; + + FD_ZERO(&readfds); + FD_SET(mReadHandle, &readfds); + + cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); + if (cc < 0) { + LOG(LOG_ERROR, "pipe", "select() failed\n"); + return true; + } else if (cc == 0) { + /* timed out, nothing available */ + return false; + } else if (cc == 1) { + /* our fd is ready */ + return true; + } else { + LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); + return true; + } +#endif +} + +/* + * Enable or disable non-blocking mode for the read descriptor. + * + * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to + * actually be in non-blocking mode. If this matters -- i.e. you're not + * using a select() call -- put a call to readReady() in front of the + * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for + * Darwin. + */ +bool Pipe::setReadNonBlocking(bool val) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); + return false; + } +#endif + + mReadNonBlocking = val; + return true; +} + +/* + * Enable or disable non-blocking mode for the write descriptor. + * + * As with setReadNonBlocking(), this does not work on the Mac. + */ +bool Pipe::setWriteNonBlocking(bool val) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't get flags for pipe write fd (errno=%d)\n", + errno); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't set flags for pipe write fd (errno=%d)\n", + errno); + return false; + } +#endif + + mWriteNonBlocking = val; + return true; +} + +/* + * Specify whether a file descriptor can be inherited by a child process. + * Under Linux this means setting the close-on-exec flag, under Windows + * this is SetHandleInformation(HANDLE_FLAG_INHERIT). + */ +bool Pipe::disallowReadInherit(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} +bool Pipe::disallowWriteInherit(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} + +/* + * Close read descriptor. + */ +bool Pipe::closeRead(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) { + LOG(LOG_WARN, "pipe", "failed closing read handle\n"); + return false; + } + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing read fd\n"); + return false; + } + } +#endif + mReadHandle = kInvalidHandle; + return true; +} + +/* + * Close write descriptor. + */ +bool Pipe::closeWrite(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mWriteHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mWriteHandle)) { + LOG(LOG_WARN, "pipe", "failed closing write handle\n"); + return false; + } + } +#else + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing write fd\n"); + return false; + } + } +#endif + mWriteHandle = kInvalidHandle; + return true; +} + +/* + * Get the read handle. + */ +unsigned long Pipe::getReadHandle(void) +{ + assert(mReadHandle != kInvalidHandle); + + return mReadHandle; +} + +/* + * Get the write handle. + */ +unsigned long Pipe::getWriteHandle(void) +{ + assert(mWriteHandle != kInvalidHandle); + + return mWriteHandle; +} + |