185 lines
6.5 KiB
C++
185 lines
6.5 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 "fdbrpc/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 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, 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
|