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