PageRenderTime 104ms CodeModel.GetById 40ms app.highlight 38ms RepoModel.GetById 23ms app.codeStats 0ms

/mordor/streams/fd.cpp

http://github.com/mozy/mordor
C++ | 235 lines | 214 code | 20 blank | 1 comment | 39 complexity | ed32d96e4eff3a1ecf1f6ce0e8f2da9e MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "mordor/pch.h"
  4
  5#include "fd.h"
  6
  7#include <algorithm>
  8#include <limits>
  9
 10#include <sys/fcntl.h>
 11#include <sys/stat.h>
 12#include <sys/uio.h>
 13
 14#include "buffer.h"
 15#include "mordor/assert.h"
 16#include "mordor/iomanager.h"
 17
 18namespace Mordor {
 19
 20static Logger::ptr g_log = Log::lookup("mordor:streams:fd");
 21
 22FDStream::FDStream()
 23: m_ioManager(NULL),
 24  m_scheduler(NULL),
 25  m_fd(-1),
 26  m_own(false)
 27{}
 28
 29void
 30FDStream::init(int fd, IOManager *ioManager, Scheduler *scheduler, bool own)
 31{
 32    MORDOR_ASSERT(fd >= 0);
 33    m_ioManager = ioManager;
 34    m_scheduler = scheduler;
 35    m_fd = fd;
 36    m_own = own;
 37    if (m_ioManager) {
 38        if (fcntl(m_fd, F_SETFL, O_NONBLOCK)) {
 39            error_t error = lastError();
 40            if (own) {
 41                ::close(m_fd);
 42                m_fd = -1;
 43            }
 44            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "fcntl");
 45        }
 46    }
 47}
 48
 49FDStream::~FDStream()
 50{
 51    if (m_own && m_fd >= 0) {
 52        SchedulerSwitcher switcher(m_scheduler);
 53        int rc = ::close(m_fd);
 54        MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this
 55            << " close(" << m_fd << "): " << rc << " (" << lastError() << ")";
 56    }
 57}
 58
 59void
 60FDStream::close(CloseType type)
 61{
 62    MORDOR_ASSERT(type == BOTH);
 63    if (m_fd > 0 && m_own) {
 64        SchedulerSwitcher switcher(m_scheduler);
 65        int rc = ::close(m_fd);
 66        error_t error = lastError();
 67        MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this
 68            << " close(" << m_fd << "): " << rc << " (" << error << ")";
 69        if (rc)
 70            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "close");
 71        m_fd = -1;
 72    }
 73}
 74
 75size_t
 76FDStream::read(Buffer &buffer, size_t length)
 77{
 78    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
 79    MORDOR_ASSERT(m_fd >= 0);
 80    if (length > 0xfffffffe)
 81        length = 0xfffffffe;
 82    std::vector<iovec> iovs = buffer.writeBuffers(length);
 83    int rc = readv(m_fd, &iovs[0], iovs.size());
 84    while (rc < 0 && errno == EAGAIN && m_ioManager) {
 85        MORDOR_LOG_TRACE(g_log) << this << " readv(" << m_fd << ", " << length
 86            << "): " << rc << " (EAGAIN)";
 87        m_ioManager->registerEvent(m_fd, IOManager::READ);
 88        Scheduler::yieldTo();
 89        rc = readv(m_fd, &iovs[0], iovs.size());
 90    }
 91    error_t error = lastError();
 92    MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::DEBUG) << this
 93        << " readv(" << m_fd << ", " << length << "): " << rc << " (" << error
 94        << ")";
 95    if (rc < 0)
 96        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "readv");
 97    buffer.produce(rc);
 98    return rc;
 99}
100
101size_t
102FDStream::read(void *buffer, size_t length)
103{
104    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
105    MORDOR_ASSERT(m_fd >= 0);
106    if (length > 0xfffffffe)
107        length = 0xfffffffe;
108    int rc = ::read(m_fd, buffer, length);
109    while (rc < 0 && errno == EAGAIN && m_ioManager) {
110        MORDOR_LOG_TRACE(g_log) << this << " read(" << m_fd << ", " << length
111            << "): " << rc << " (EAGAIN)";
112        m_ioManager->registerEvent(m_fd, IOManager::READ);
113        Scheduler::yieldTo();
114        rc = ::read(m_fd, buffer, length);
115    }
116    error_t error = lastError();
117    MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::DEBUG) << this
118        << " read(" << m_fd << ", " << length << "): " << rc << " (" << error
119        << ")";
120    if (rc < 0)
121        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "read");
122    return rc;
123}
124
125size_t
126FDStream::write(const Buffer &buffer, size_t length)
127{
128    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
129    MORDOR_ASSERT(m_fd >= 0);
130    length = std::min(length, (size_t)std::numeric_limits<ssize_t>::max());
131    const std::vector<iovec> iovs = buffer.readBuffers(length);
132    ssize_t rc = 0;
133    const int count = std::min(iovs.size(), (size_t)IOV_MAX);
134    while ((rc = writev(m_fd, &iovs[0], count)) < 0 &&
135           errno == EAGAIN && m_ioManager) {
136        MORDOR_LOG_TRACE(g_log) << this << " writev(" << m_fd << ", " << length
137            << "): " << rc << " (EAGAIN)";
138        m_ioManager->registerEvent(m_fd, IOManager::WRITE);
139        Scheduler::yieldTo();
140    }
141    error_t error = lastError();
142    MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::DEBUG) << this
143        << " writev(" << m_fd << ", " << length << "): " << rc << " (" << error
144        << ")";
145    if (rc == 0)
146        MORDOR_THROW_EXCEPTION(std::runtime_error("Zero length write"));
147    if (rc < 0)
148        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "writev");
149    return rc;
150}
151
152size_t
153FDStream::write(const void *buffer, size_t length)
154{
155    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
156    MORDOR_ASSERT(m_fd >= 0);
157    if (length > 0xfffffffe)
158        length = 0xfffffffe;
159    int rc = ::write(m_fd, buffer, length);
160    while (rc < 0 && errno == EAGAIN && m_ioManager) {
161        MORDOR_LOG_TRACE(g_log) << this << " write(" << m_fd << ", " << length
162            << "): " << rc << " (EAGAIN)";
163        m_ioManager->registerEvent(m_fd, IOManager::WRITE);
164        Scheduler::yieldTo();
165        rc = ::write(m_fd, buffer, length);
166    }
167    error_t error = lastError();
168    MORDOR_LOG_LEVEL(g_log, rc < 0 ? Log::ERROR : Log::DEBUG) << this
169        << " write(" << m_fd << ", " << length << "): " << rc << " (" << error
170        << ")";
171    if (rc == 0)
172        MORDOR_THROW_EXCEPTION(std::runtime_error("Zero length write"));
173    if (rc < 0)
174        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "write");
175    return rc;
176}
177
178long long
179FDStream::seek(long long offset, Anchor anchor)
180{
181    SchedulerSwitcher switcher(m_scheduler);
182    MORDOR_ASSERT(m_fd >= 0);
183    long long pos = lseek(m_fd, offset, (int)anchor);
184    error_t error = lastError();
185    MORDOR_LOG_LEVEL(g_log, pos < 0 ? Log::ERROR : Log::VERBOSE) << this
186        << " lseek(" << m_fd << ", " << offset << ", " << anchor << "): "
187        << pos << " (" << error << ")";
188    if (pos < 0)
189        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "lseek");
190    return pos;
191}
192
193long long
194FDStream::size()
195{
196    SchedulerSwitcher switcher(m_scheduler);
197    MORDOR_ASSERT(m_fd >= 0);
198    struct stat statbuf;
199    int rc = fstat(m_fd, &statbuf);
200    error_t error = lastError();
201    MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this
202        << " fstat(" << m_fd << "): " << rc << " (" << error << ")";
203    if (rc)
204        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "fstat");
205    return statbuf.st_size;
206}
207
208void
209FDStream::truncate(long long size)
210{
211    SchedulerSwitcher switcher(m_scheduler);
212    MORDOR_ASSERT(m_fd >= 0);
213    int rc = ftruncate(m_fd, size);
214    error_t error = lastError();
215    MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this
216        << " ftruncate(" << m_fd << ", " << size << "): " << rc
217        << " (" << error << ")";
218    if (rc)
219        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ftruncate");
220}
221
222void
223FDStream::flush(bool flushParent)
224{
225    SchedulerSwitcher switcher(m_scheduler);
226    MORDOR_ASSERT(m_fd >= 0);
227    int rc = fsync(m_fd);
228    error_t error = lastError();
229    MORDOR_LOG_LEVEL(g_log, rc ? Log::ERROR : Log::VERBOSE) << this
230        << " fsync(" << m_fd << "): " << rc << " (" << error << ")";
231    if (rc)
232        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "fsync");
233}
234
235}