foundationdb/fdbrpc/AsyncFileWinASIO.actor.h

216 lines
7.4 KiB
C++

/*
* AsyncFileWinASIO.actor.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 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 <Windows.h>
#include <boost/bind/bind.hpp>
#undef min
#undef max
class AsyncFileWinASIO final : public IAsyncFile, public ReferenceCounted<AsyncFileWinASIO> {
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<Reference<IAsyncFile>> 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<IAsyncFile>(new AsyncFileWinASIO(*ios, h, flags, filename));
}
static Future<Void> 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<std::time_t> 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<AsyncFileWinASIO>::addref(); }
void delref() override { ReferenceCounted<AsyncFileWinASIO>::delref(); }
int64_t debugFD() const override { return (int64_t)(const_cast<decltype(file)&>(file).native_handle()); }
static void onReadReady(Promise<int> 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<Void> 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<int> 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<int> 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<Void> renameFile(std::string const& from, std::string const& to) {
::renameFile(from, to);
return Void();
}
Future<Void> write(void const* data, int length, int64_t offset) override {
/*
FIXME
if ( length + offset >= fileValidData ) {
SetFileValidData( length+offset );
fileValidData = length+offset;
}*/
Promise<Void> 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<Void> 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<Void> 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<int64_t> size() const override {
LARGE_INTEGER s;
if (!GetFileSizeEx(const_cast<decltype(file)&>(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