PageRenderTime 306ms CodeModel.GetById 100ms app.highlight 102ms RepoModel.GetById 100ms app.codeStats 0ms

/mordor/streams/handle.cpp

http://github.com/mozy/mordor
C++ | 338 lines | 317 code | 20 blank | 1 comment | 79 complexity | 9ffe532a450516f01f134f4d7f76e031 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "handle.h"
  4
  5#include "mordor/assert.h"
  6#include "mordor/log.h"
  7#include "mordor/runtime_linking.h"
  8
  9namespace Mordor {
 10
 11static Logger::ptr g_log = Log::lookup("mordor:streams:handle");
 12
 13HandleStream::HandleStream()
 14: m_ioManager(NULL),
 15  m_skipCompletionPortOnSuccess(false),
 16  m_scheduler(NULL),
 17  m_pos(0),
 18  m_hFile(INVALID_HANDLE_VALUE),
 19  m_own(false),
 20  m_cancelRead(false),
 21  m_cancelWrite(false),
 22  m_maxOpSize(0xffffffff)
 23{}
 24
 25void
 26HandleStream::init(HANDLE hFile, IOManager *ioManager, Scheduler *scheduler,
 27                   bool own)
 28{
 29    MORDOR_ASSERT(hFile != NULL);
 30    MORDOR_ASSERT(hFile != INVALID_HANDLE_VALUE);
 31    m_hFile = hFile;
 32    m_own = own;
 33    m_cancelRead = m_cancelWrite = false;
 34    m_ioManager = ioManager;
 35    m_scheduler = scheduler;
 36    DWORD type = GetFileType(hFile);
 37    if (type == FILE_TYPE_CHAR) {
 38        m_ioManager = NULL;
 39        CONSOLE_SCREEN_BUFFER_INFO info;
 40        if (!GetConsoleScreenBufferInfo(hFile, &info))
 41            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("GetConsoleScreenBufferInfo");
 42        m_maxOpSize = info.dwSize.X * info.dwSize.Y / 2;
 43    }
 44    if (m_ioManager) {
 45        try {
 46            m_ioManager->registerFile(m_hFile);
 47            m_skipCompletionPortOnSuccess = !!pSetFileCompletionNotificationModes(m_hFile,
 48                FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE);
 49        } catch(...) {
 50            if (own) {
 51                CloseHandle(m_hFile);
 52                m_hFile = INVALID_HANDLE_VALUE;
 53            }
 54            throw;
 55        }
 56    }
 57}
 58
 59HandleStream::~HandleStream()
 60{
 61    if (m_hFile != INVALID_HANDLE_VALUE && m_own) {
 62        SchedulerSwitcher switcher(m_scheduler);
 63        BOOL result = CloseHandle(m_hFile);
 64        MORDOR_LOG_LEVEL(g_log, result ? Log::VERBOSE : Log::ERROR) << this
 65            << " CloseHandle(" << m_hFile << "): " << result << " ("
 66            << lastError() << ")";
 67    }
 68}
 69
 70static bool g_supportsCancel;
 71static bool g_queriedSupportsCancel;
 72
 73bool
 74HandleStream::supportsCancel()
 75{
 76    if (m_ioManager)
 77        return true;
 78    if (!g_supportsCancel && !g_queriedSupportsCancel) {
 79        BOOL bRet = pCancelIoEx(INVALID_HANDLE_VALUE, NULL);
 80        MORDOR_ASSERT(!bRet);
 81        g_supportsCancel = lastError() != ERROR_CALL_NOT_IMPLEMENTED;
 82        g_queriedSupportsCancel = true;
 83    }
 84    return g_supportsCancel;
 85}
 86
 87void
 88HandleStream::close(CloseType type)
 89{
 90    MORDOR_ASSERT(type == BOTH);
 91    if (m_hFile != INVALID_HANDLE_VALUE && m_own) {
 92        SchedulerSwitcher switcher(m_scheduler);
 93        BOOL result = CloseHandle(m_hFile);
 94        error_t error = lastError();
 95        MORDOR_LOG_LEVEL(g_log, result ? Log::VERBOSE : Log::ERROR) << this
 96            << " CloseHandle(" << m_hFile << "): " << result << " ("
 97            << error << ")";
 98        if (!result)
 99            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "CloseHandle");
100        m_hFile = INVALID_HANDLE_VALUE;
101    }
102}
103
104size_t
105HandleStream::read(void *buffer, size_t length)
106{
107    if (m_cancelRead)
108        MORDOR_THROW_EXCEPTION(OperationAbortedException());
109    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
110    DWORD read;
111    OVERLAPPED *overlapped = NULL;
112    if (m_ioManager) {
113        MORDOR_ASSERT(Scheduler::getThis());
114        m_ioManager->registerEvent(&m_readEvent);
115        overlapped = &m_readEvent.overlapped;
116        if (supportsSeek()) {
117            overlapped->Offset = (DWORD)m_pos;
118            overlapped->OffsetHigh = (DWORD)(m_pos >> 32);
119        }
120    }
121    length = (std::min)(length, m_maxOpSize);
122    BOOL ret = ReadFile(m_hFile, buffer, (DWORD)length, &read, overlapped);
123    Log::Level level = Log::DEBUG;
124    if (!ret) {
125        if (lastError() == ERROR_HANDLE_EOF) {
126        } else if (m_ioManager) {
127            if (lastError() == ERROR_IO_PENDING)
128                level = Log::TRACE;
129            else
130                level = Log::ERROR;
131        } else {
132            level = Log::ERROR;
133        }
134    }
135    error_t error = lastError();
136    MORDOR_LOG_LEVEL(g_log, level) << this << " ReadFile(" << m_hFile << ", "
137        << length << "): " << ret << " - " << read << " (" << error << ")";
138    if (m_ioManager) {
139        if (!ret && error == ERROR_HANDLE_EOF) {
140            m_ioManager->unregisterEvent(&m_readEvent);
141            return 0;
142        }
143        if (!ret && error != ERROR_IO_PENDING) {
144            m_ioManager->unregisterEvent(&m_readEvent);
145            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("ReadFile");
146        }
147        if (m_skipCompletionPortOnSuccess && ret)
148            m_ioManager->unregisterEvent(&m_readEvent);
149        else
150            Scheduler::yieldTo();
151        DWORD error = pRtlNtStatusToDosError((NTSTATUS)m_readEvent.overlapped.Internal);
152        MORDOR_LOG_LEVEL(g_log, error && error != ERROR_HANDLE_EOF ? Log::ERROR : Log::VERBOSE)
153            << this << " ReadFile(" << m_hFile << ", " << length << "): "
154            << m_readEvent.overlapped.InternalHigh << " (" << error << ")";
155        if (error == ERROR_HANDLE_EOF)
156            return 0;
157        if (error)
158            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ReadFile");
159        if (supportsSeek()) {
160            m_pos = ((long long)overlapped->Offset | ((long long)overlapped->OffsetHigh << 32)) +
161                m_readEvent.overlapped.InternalHigh;
162        }
163        return m_readEvent.overlapped.InternalHigh;
164    }
165    if (!ret)
166        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "ReadFile");
167    return read;
168}
169
170void
171HandleStream::cancelRead()
172{
173    m_cancelRead = true;
174    if (m_ioManager) {
175        m_ioManager->cancelEvent(m_hFile, &m_readEvent);
176    } else {
177        if (!pCancelIoEx(m_hFile, NULL))
178            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("CancelIoEx");
179    }
180}
181
182size_t
183HandleStream::write(const void *buffer, size_t length)
184{
185    if (m_cancelWrite)
186        MORDOR_THROW_EXCEPTION(OperationAbortedException());
187    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
188    DWORD written;
189    OVERLAPPED *overlapped = NULL;
190    if (m_ioManager) {
191        MORDOR_ASSERT(Scheduler::getThis());
192        m_ioManager->registerEvent(&m_writeEvent);
193        overlapped = &m_writeEvent.overlapped;
194        if (supportsSeek()) {
195            overlapped->Offset = (DWORD)m_pos;
196            overlapped->OffsetHigh = (DWORD)(m_pos >> 32);
197        }
198    }
199    length = (std::min)(length, m_maxOpSize);
200    BOOL ret = WriteFile(m_hFile, buffer, (DWORD)length, &written, overlapped);
201    Log::Level level = Log::DEBUG;
202    if (!ret) {
203        if (m_ioManager && lastError() == ERROR_IO_PENDING)
204            level = Log::TRACE;
205        else
206            level = Log::ERROR;
207    }
208    error_t error = lastError();
209    MORDOR_LOG_LEVEL(g_log, level) << this << " WriteFile(" << m_hFile << ", "
210        << length << "): " << ret << " - " << written << " (" << error << ")";
211    if (m_ioManager) {
212        if (!ret && error != ERROR_IO_PENDING) {
213            m_ioManager->unregisterEvent(&m_writeEvent);
214            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("WriteFile");
215        }
216        if (m_skipCompletionPortOnSuccess && ret)
217            m_ioManager->unregisterEvent(&m_writeEvent);
218        else
219            Scheduler::yieldTo();
220        DWORD error = pRtlNtStatusToDosError((NTSTATUS)m_writeEvent.overlapped.Internal);
221        MORDOR_LOG_LEVEL(g_log, error ? Log::ERROR : Log::VERBOSE) << this
222            << " WriteFile(" << m_hFile << ", " << length << "): "
223            << m_writeEvent.overlapped.InternalHigh << " (" << error << ")";
224        if (error)
225            MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile");
226        if (supportsSeek()) {
227            m_pos = ((long long)overlapped->Offset | ((long long)overlapped->OffsetHigh << 32)) +
228                m_writeEvent.overlapped.InternalHigh;
229        }
230        return m_writeEvent.overlapped.InternalHigh;
231    }
232    if (!ret)
233        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "WriteFile");
234    return written;
235}
236
237void
238HandleStream::cancelWrite()
239{
240    m_cancelWrite = true;
241    if (m_ioManager) {
242        m_ioManager->cancelEvent(m_hFile, &m_writeEvent);
243    } else {
244        MORDOR_ASSERT(supportsCancel());
245        if (!pCancelIoEx(m_hFile, NULL))
246            MORDOR_THROW_EXCEPTION_FROM_LAST_ERROR_API("CancelIoEx");
247    }
248}
249
250long long
251HandleStream::seek(long long offset, Anchor anchor)
252{
253    SchedulerSwitcher switcher(m_ioManager ? NULL : m_scheduler);
254    if (m_ioManager) {
255        if (supportsSeek()) {
256            switch (anchor) {
257                case BEGIN:
258                    if (offset < 0) {
259                        MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative"));
260                    }
261                    return m_pos = offset;
262                case CURRENT:
263                    if (m_pos + offset < 0) {
264                        MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative"));
265                    }
266                    return m_pos += offset;
267                case END:
268                    {
269                        long long end = size();
270                        if (end + offset < 0) {
271                            MORDOR_THROW_EXCEPTION(std::invalid_argument("resulting offset is negative"));
272                        }
273                        return m_pos = end + offset;
274                    }
275                default:
276                    MORDOR_ASSERT(false);
277            }
278        } else {
279            MORDOR_ASSERT(false);
280        }
281    }
282
283    long long pos;
284    BOOL ret = SetFilePointerEx(m_hFile, *(LARGE_INTEGER*)&offset,
285        (LARGE_INTEGER*)&pos, (DWORD)anchor);
286    error_t error = lastError();
287    MORDOR_LOG_LEVEL(g_log, ret ? Log::VERBOSE : Log::ERROR) << this
288        << " SetFilePointerEx(" << m_hFile << ", " << offset << ", " << pos
289        << ", " << anchor << "): " << ret << " (" << error << ")";
290    if (!ret)
291        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "SetFilePointerEx");
292    return pos;
293}
294
295long long
296HandleStream::size()
297{
298    SchedulerSwitcher switcher(m_scheduler);
299    long long size;
300    BOOL ret = GetFileSizeEx(m_hFile, (LARGE_INTEGER*)&size);
301    error_t error = lastError();
302    MORDOR_LOG_VERBOSE(g_log) << this << " GetFileSizeEx(" << m_hFile << ", "
303        << size << "): " << ret << " (" << error << ")";
304    if (!ret)
305        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "GetFileSizeEx");
306    return size;
307}
308
309void
310HandleStream::truncate(long long size)
311{
312    SchedulerSwitcher switcher(m_scheduler);
313    long long pos = seek(0, CURRENT);
314    seek(size, BEGIN);
315    BOOL ret = SetEndOfFile(m_hFile);
316    error_t error = lastError();
317    MORDOR_LOG_LEVEL(g_log, ret ? Log::VERBOSE : Log::ERROR) << this
318        << " SetEndOfFile(" << m_hFile << "): " << ret << " ("
319        << error << ")";
320    seek(pos, BEGIN);
321    if (!ret)
322        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "SetEndOfFile");
323}
324
325void
326HandleStream::flush(bool flushParent)
327{
328    SchedulerSwitcher switcher(m_scheduler);
329    BOOL ret = FlushFileBuffers(m_hFile);
330    error_t error = lastError();
331    MORDOR_LOG_LEVEL(g_log, ret ? Log::VERBOSE : Log::ERROR) << this
332        << " FlushFileBuffers(" << m_hFile << "): " << ret << " (" << error
333        << ")";
334    if (!ret)
335        MORDOR_THROW_EXCEPTION_FROM_ERROR_API(error, "FlushFileBuffers");
336}
337
338}