foundationdb/fdbrpc/AsyncFileWinASIO.actor.h

186 lines
6.6 KiB
C++

/*
* 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 "AsyncFileWinASIO.actor.g.h"
#elif !defined(FLOW_ASYNCFILEWINASIO_ACTOR_H)
#define FLOW_ASYNCFILEWINASIO_ACTOR_H
#include <Windows.h>
#undef min
#undef max
class AsyncFileWinASIO : public IAsyncFile, public ReferenceCounted<AsyncFileWinASIO> {
public:
static void init() {}
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 ) {
if (!(flags & OPEN_UNBUFFERED)) {
TraceEvent(SevError, "FileOpenError").detail("Reason", "Must be unbuffered").detail("Flags", flags).detail("File", filename);
return io_error();
}
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, NULL,
(flags&OPEN_EXCLUSIVE) ? CREATE_NEW :
(flags&OPEN_CREATE) ? OPEN_ALWAYS :
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
NULL );
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;
}
virtual void addref() { ReferenceCounted<AsyncFileWinASIO>::addref(); }
virtual void delref() { ReferenceCounted<AsyncFileWinASIO>::delref(); }
virtual int64_t debugFD() { return (int64_t)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() );
}
}
virtual Future<int> read( void* data, int length, int64_t offset ) {
// 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();
}
virtual Future<Void> write( void const* data, int length, int64_t offset ) {
/*
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();
}
virtual Future<Void> truncate( int64_t size ) {
// FIXME: Possibly use SetFileInformationByHandle( file.native_handle(), FileEndOfFileInfo, ... ) instead
if (!SetFilePointerEx( file.native_handle(), *(LARGE_INTEGER*)&size, NULL, FILE_BEGIN ))
throw io_error();
if (!SetEndOfFile(file.native_handle()))
throw io_error();
return Void();
}
virtual Future<Void> sync() {
// 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();
}
virtual Future<int64_t> size() {
LARGE_INTEGER s;
if (!GetFileSizeEx(file.native_handle(), &s)) throw io_error();
return *(int64_t*)&s;
}
virtual std::string getFilename() {
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