/* * AsyncFileWinASIO.actor.h * * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors * * 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. */ #pragma once #ifdef WIN32 #define Net2AsyncFile AsyncFileWinASIO // When actually compiled (NO_INTELLISENSE), include the generated version of this file. In intellisense use the source // version. #if defined(NO_INTELLISENSE) && !defined(FLOW_ASYNCFILEWINASIO_ACTOR_G_H) #define FLOW_ASYNCFILEWINASIO_ACTOR_G_H #include "fdbrpc/AsyncFileWinASIO.actor.g.h" #elif !defined(FLOW_ASYNCFILEWINASIO_ACTOR_H) #define FLOW_ASYNCFILEWINASIO_ACTOR_H #include #undef min #undef max class AsyncFileWinASIO final : public IAsyncFile, public ReferenceCounted { public: static void init() {} static void stop() {} static bool should_poll() { return false; } // FIXME: This implementation isn't actually asynchronous - it just does operations synchronously! static Future> open(std::string filename, int flags, int mode, boost::asio::io_service* ios) { ASSERT(flags & OPEN_UNBUFFERED); std::string open_filename = filename; if (flags & OPEN_ATOMIC_WRITE_AND_CREATE) { ASSERT((flags & OPEN_CREATE) && (flags & OPEN_READWRITE) && !(flags & OPEN_EXCLUSIVE)); open_filename = filename + ".part"; } HANDLE h = CreateFile(open_filename.c_str(), GENERIC_READ | ((flags & OPEN_READWRITE) ? GENERIC_WRITE : 0), FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, (flags & OPEN_EXCLUSIVE) ? CREATE_NEW : (flags & OPEN_CREATE) ? OPEN_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, nullptr); if (h == INVALID_HANDLE_VALUE) { bool notFound = GetLastError() == ERROR_FILE_NOT_FOUND; Error e = notFound ? file_not_found() : io_error(); TraceEvent(notFound ? SevWarn : SevWarnAlways, "FileOpenError") .error(e) .GetLastError() .detail("File", filename) .detail("Flags", flags) .detail("Mode", mode); return e; } return Reference(new AsyncFileWinASIO(*ios, h, flags, filename)); } static Future deleteFile(std::string filename, bool mustBeDurable) { ::deleteFile(filename); // SOMEDAY: What is necessary to implement mustBeDurable on Windows? Does DeleteFile take care of it? // DeleteFileTransacted? return Void(); } static Future lastWriteTime(std::string filename) { // TODO(alexmiller): I have no idea about windows struct _stat buf; if (_stat(filename.c_str(), &buf) != 0) { throw io_error(); } return buf.st_mtime; } void addref() override { ReferenceCounted::addref(); } void delref() override { ReferenceCounted::delref(); } int64_t debugFD() const override { return (int64_t)(const_cast(file).native_handle()); } static void onReadReady(Promise onReady, const boost::system::error_code& error, size_t bytesRead) { if (error) { Error e = io_error(); TraceEvent("AsyncReadError") .error(e) .GetLastError() .detail("ASIOCode", error.value()) .detail("ASIOMessage", error.message()); onReady.sendError(e); } else { onReady.send(bytesRead); } } static void onWriteReady(Promise onReady, size_t bytesExpected, const boost::system::error_code& error, size_t bytesWritten) { if (error) { Error e = io_error(); TraceEvent("AsyncWriteError") .error(e) .GetLastError() .detail("ASIOCode", error.value()) .detail("ASIOMessage", error.message()); onReady.sendError(e); } else if (bytesWritten != bytesExpected) { Error e = io_error(); TraceEvent("AsyncWriteError").detail("BytesExpected", bytesExpected).detail("BytesWritten", bytesWritten); onReady.sendError(io_error()); } else { onReady.send(Void()); } } Future read(void* data, int length, int64_t offset) override { // the size call is set inline auto end = this->size().get(); //TraceEvent("WinAsyncRead").detail("Offset", offset).detail("Length", length).detail("FileSize", end).detail("FileName", filename); if (offset >= end) return 0; Promise result; file.async_read_some_at( offset, boost::asio::mutable_buffers_1(data, length), boost::bind( &onReadReady, result, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); return result.getFuture(); } static Future renameFile(std::string const& from, std::string const& to) { ::renameFile(from, to); return Void(); } Future write(void const* data, int length, int64_t offset) override { /* FIXME if ( length + offset >= fileValidData ) { SetFileValidData( length+offset ); fileValidData = length+offset; }*/ Promise result; boost::asio::async_write_at(file, offset, boost::asio::const_buffers_1(data, length), boost::bind(&onWriteReady, result, length, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); return result.getFuture(); } Future truncate(int64_t size) override { // FIXME: Possibly use SetFileInformationByHandle( file.native_handle(), FileEndOfFileInfo, ... ) instead if (!SetFilePointerEx(file.native_handle(), *(LARGE_INTEGER*)&size, nullptr, FILE_BEGIN)) throw io_error(); if (!SetEndOfFile(file.native_handle())) throw io_error(); return Void(); } Future sync() override { // FIXME: Do FlushFileBuffers in a worker thread (using g_network->createThreadPool)? if (!FlushFileBuffers(file.native_handle())) throw io_error(); if (flags & OPEN_ATOMIC_WRITE_AND_CREATE) { flags &= ~OPEN_ATOMIC_WRITE_AND_CREATE; // FIXME: MoveFileEx(..., MOVEFILE_WRITE_THROUGH) in thread? MoveFile((filename + ".part").c_str(), filename.c_str()); } return Void(); } Future size() const override { LARGE_INTEGER s; if (!GetFileSizeEx(const_cast(file).native_handle(), &s)) throw io_error(); return *(int64_t*)&s; } std::string getFilename() const override { return filename; } ~AsyncFileWinASIO() {} private: boost::asio::windows::random_access_handle file; int flags; std::string filename; AsyncFileWinASIO(boost::asio::io_service& ios, HANDLE h, int flags, std::string filename) : file(ios, h), flags(flags), filename(filename) {} }; #endif #endif