Fixes leak of native File objects.

Also employs the same reference counting technique as secure sockets to
avoid the IO Service touching dangling pointers.

R=iposva@google.com

Review URL: https://codereview.chromium.org/1892623002 .
This commit is contained in:
Zachary Anderson 2016-04-20 10:08:37 -07:00
parent 8f9defbb50
commit 218545ba10
17 changed files with 310 additions and 257 deletions

View file

@ -222,7 +222,8 @@ void DartUtils::WriteFile(const void* buffer,
void DartUtils::CloseFile(void* stream) {
delete reinterpret_cast<File*>(stream);
File* file = reinterpret_cast<File*>(stream);
file->Release();
}

View file

@ -18,14 +18,66 @@
namespace dart {
namespace bin {
static const int kFileNativeFieldIndex = 0;
static const int kMSPerSecond = 1000;
// The file pointer has been passed into Dart as an intptr_t and it is safe
// to pull it out of Dart as a 64-bit integer, cast it to an intptr_t and
// from there to a File pointer.
static File* GetFilePointer(Dart_Handle handle) {
intptr_t value = DartUtils::GetIntptrValue(handle);
return reinterpret_cast<File*>(value);
static File* GetFile(Dart_NativeArguments args) {
File* file;
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
ASSERT(Dart_IsInstance(dart_this));
ThrowIfError(Dart_GetNativeInstanceField(
dart_this,
kFileNativeFieldIndex,
reinterpret_cast<intptr_t*>(&file)));
return file;
}
static void SetFile(Dart_Handle dart_this, intptr_t file_pointer) {
Dart_Handle result = Dart_SetNativeInstanceField(
dart_this,
kFileNativeFieldIndex,
file_pointer);
if (Dart_IsError(result)) {
Log::PrintErr("SetNativeInstanceField in SetFile() failed\n");
Dart_PropagateError(result);
}
}
void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) {
File* file = GetFile(args);
// If the file is already closed, GetFile() will return NULL.
if (file != NULL) {
// Increment file's reference count. File_GetPointer() should only be called
// when we are about to send the File* to the IO Service.
file->Retain();
}
intptr_t file_pointer = reinterpret_cast<intptr_t>(file);
Dart_SetReturnValue(args, Dart_NewInteger(file_pointer));
}
static void ReleaseFile(void* isolate_callback_data,
Dart_WeakPersistentHandle handle,
void* peer) {
File* file = reinterpret_cast<File*>(peer);
file->Release();
}
void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) {
Dart_Handle dart_this = ThrowIfError(Dart_GetNativeArgument(args, 0));
intptr_t file_pointer =
DartUtils::GetIntptrValue(Dart_GetNativeArgument(args, 1));
File* file = reinterpret_cast<File*>(file_pointer);
Dart_WeakPersistentHandle handle = Dart_NewWeakPersistentHandle(
dart_this, reinterpret_cast<void*>(file), sizeof(*file), ReleaseFile);
file->SetWeakHandle(handle);
SetFile(dart_this, file_pointer);
}
@ -40,8 +92,7 @@ void FUNCTION_NAME(File_Open)(Dart_NativeArguments args) {
// reading. This is to prevent the opening of directories as
// files. Directories can be opened for reading using the posix
// 'open' call.
File* file = NULL;
file = File::ScopedOpen(filename, file_mode);
File* file = File::ScopedOpen(filename, file_mode);
if (file != NULL) {
Dart_SetReturnValue(args,
Dart_NewInteger(reinterpret_cast<intptr_t>(file)));
@ -60,22 +111,20 @@ void FUNCTION_NAME(File_Exists)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
delete file;
file->DeleteWeakHandle(Dart_CurrentIsolate());
file->Release();
// NULL-out the now potentially dangling pointer.
Dart_Handle dart_this = Dart_GetNativeArgument(args, 0);
SetFile(dart_this, 0);
Dart_SetReturnValue(args, Dart_NewInteger(0));
}
void FUNCTION_NAME(File_GetFD)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
ASSERT(file != NULL);
Dart_SetReturnValue(args, Dart_NewInteger(file->GetFD()));
}
void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
uint8_t buffer;
int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1);
@ -90,7 +139,7 @@ void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
int64_t byte = 0;
if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &byte)) {
@ -109,7 +158,7 @@ void FUNCTION_NAME(File_WriteByte)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Read)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
Dart_Handle length_object = Dart_GetNativeArgument(args, 1);
int64_t length = 0;
@ -150,7 +199,7 @@ void FUNCTION_NAME(File_Read)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_ReadInto)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
ASSERT(Dart_IsList(buffer_obj));
@ -185,7 +234,7 @@ void FUNCTION_NAME(File_ReadInto)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_WriteFrom)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
Dart_Handle buffer_obj = Dart_GetNativeArgument(args, 1);
@ -233,7 +282,7 @@ void FUNCTION_NAME(File_WriteFrom)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
intptr_t return_value = file->Position();
if (return_value >= 0) {
@ -245,7 +294,7 @@ void FUNCTION_NAME(File_Position)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_SetPosition)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
int64_t position = 0;
if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &position)) {
@ -262,7 +311,7 @@ void FUNCTION_NAME(File_SetPosition)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Truncate)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
int64_t length = 0;
if (DartUtils::GetInt64Value(Dart_GetNativeArgument(args, 1), &length)) {
@ -279,7 +328,7 @@ void FUNCTION_NAME(File_Truncate)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Length)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
int64_t return_value = file->Length();
if (return_value >= 0) {
@ -315,7 +364,7 @@ void FUNCTION_NAME(File_LastModified)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
if (file->Flush()) {
Dart_SetReturnValue(args, Dart_True());
@ -326,7 +375,7 @@ void FUNCTION_NAME(File_Flush)(Dart_NativeArguments args) {
void FUNCTION_NAME(File_Lock)(Dart_NativeArguments args) {
File* file = GetFilePointer(Dart_GetNativeArgument(args, 0));
File* file = GetFile(args);
ASSERT(file != NULL);
int64_t lock;
int64_t start;
@ -704,9 +753,16 @@ CObject* File::CloseRequest(const CObjectArray& request) {
intptr_t return_value = -1;
if ((request.Length() == 1) && request[0]->IsIntptr()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
delete file;
RefCntReleaseScope<File> rs(file);
return_value = 0;
// We have retained a reference to the file here. Therefore the file's
// destructor can't be running. Since no further requests are dispatched by
// the Dart code after an async close call, this Close() can't be racing
// with any other call on the file. We don't do an extra Release(), and we
// don't delete the weak persistent handle. The file is closed here, but the
// memory will be cleaned up when the finalizer runs.
ASSERT(!file->IsClosed());
file->Close();
}
return new CObjectIntptr(CObject::NewIntptr(return_value));
}
@ -715,7 +771,7 @@ CObject* File::CloseRequest(const CObjectArray& request) {
CObject* File::PositionRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsIntptr()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
intptr_t return_value = file->Position();
if (return_value >= 0) {
@ -736,7 +792,7 @@ CObject* File::SetPositionRequest(const CObjectArray& request) {
request[0]->IsIntptr() &&
request[1]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t position = CObjectInt32OrInt64ToInt64(request[1]);
if (file->SetPosition(position)) {
@ -757,7 +813,7 @@ CObject* File::TruncateRequest(const CObjectArray& request) {
request[0]->IsIntptr() &&
request[1]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
if (file->Truncate(length)) {
@ -776,7 +832,7 @@ CObject* File::TruncateRequest(const CObjectArray& request) {
CObject* File::LengthRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsIntptr()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t return_value = file->Length();
if (return_value >= 0) {
@ -823,7 +879,7 @@ CObject* File::LastModifiedRequest(const CObjectArray& request) {
CObject* File::FlushRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsIntptr()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
if (file->Flush()) {
return CObject::True();
@ -841,7 +897,7 @@ CObject* File::FlushRequest(const CObjectArray& request) {
CObject* File::ReadByteRequest(const CObjectArray& request) {
if ((request.Length() == 1) && request[0]->IsIntptr()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
uint8_t buffer;
int64_t bytes_read = file->Read(reinterpret_cast<void*>(&buffer), 1);
@ -865,7 +921,7 @@ CObject* File::WriteByteRequest(const CObjectArray& request) {
request[0]->IsIntptr() &&
request[1]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t byte = CObjectInt32OrInt64ToInt64(request[1]);
uint8_t buffer = static_cast<uint8_t>(byte & 0xff);
@ -888,7 +944,7 @@ CObject* File::ReadRequest(const CObjectArray& request) {
request[0]->IsIntptr() &&
request[1]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
@ -920,7 +976,7 @@ CObject* File::ReadIntoRequest(const CObjectArray& request) {
request[0]->IsIntptr() &&
request[1]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t length = CObjectInt32OrInt64ToInt64(request[1]);
Dart_CObject* io_buffer = CObject::NewIOBuffer(length);
@ -980,7 +1036,7 @@ CObject* File::WriteFromRequest(const CObjectArray& request) {
request[2]->IsInt32OrInt64() &&
request[3]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t start = CObjectInt32OrInt64ToInt64(request[2]);
int64_t end = CObjectInt32OrInt64ToInt64(request[3]);
@ -1143,7 +1199,7 @@ CObject* File::LockRequest(const CObjectArray& request) {
request[2]->IsInt32OrInt64() &&
request[3]->IsInt32OrInt64()) {
File* file = CObjectToFilePointer(request[0]);
ASSERT(file != NULL);
RefCntReleaseScope<File> rs(file);
if (!file->IsClosed()) {
int64_t lock = CObjectInt32OrInt64ToInt64(request[1]);
int64_t start = CObjectInt32OrInt64ToInt64(request[2]);

View file

@ -12,6 +12,8 @@
#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/log.h"
#include "bin/reference_counting.h"
namespace dart {
namespace bin {
@ -19,7 +21,7 @@ namespace bin {
// Forward declaration.
class FileHandle;
class File {
class File : public ReferenceCounted<File> {
public:
enum FileOpenMode {
kRead = 0,
@ -82,8 +84,6 @@ class File {
kLockMax = 2
};
~File();
intptr_t GetFD();
// Read/Write attempt to transfer num_bytes to/from buffer. It returns
@ -124,6 +124,26 @@ class File {
// Returns whether the file has been closed.
bool IsClosed();
// Calls the platform-specific functions to close the file.
void Close();
// Returns the weak persistent handle for the File's Dart wrapper.
Dart_WeakPersistentHandle WeakHandle() const { return weak_handle_; }
// Set the weak persistent handle for the File's Dart wrapper.
void SetWeakHandle(Dart_WeakPersistentHandle handle) {
ASSERT(weak_handle_ == NULL);
weak_handle_ = handle;
}
// Deletes the weak persistent handle for the File's Dart wrapper. Call
// when the file is explicitly closed and the finalizer is no longer
// needed.
void DeleteWeakHandle(Dart_Isolate isolate) {
Dart_DeleteWeakPersistentHandle(isolate, weak_handle_);
weak_handle_ = NULL;
}
// Open the file with the given path. The file is always opened for
// reading. If mode contains kWrite the file is opened for both
// reading and writing. If mode contains kWrite and the file does
@ -190,8 +210,12 @@ class File {
static CObject* LockRequest(const CObjectArray& request);
private:
explicit File(FileHandle* handle) : handle_(handle) { }
void Close();
explicit File(FileHandle* handle) :
ReferenceCounted(),
handle_(handle),
weak_handle_(NULL) {}
~File();
static File* FileOpenW(const wchar_t* system_name, FileOpenMode mode);
@ -200,6 +224,12 @@ class File {
// FileHandle is an OS specific class which stores data about the file.
FileHandle* handle_; // OS specific handle for the file.
// We retain the weak handle because we can do cleanup eagerly when Dart code
// calls closeSync(). In that case, we delete the weak handle so that the
// finalizer doesn't run.
Dart_WeakPersistentHandle weak_handle_;
friend class ReferenceCounted<File>;
DISALLOW_COPY_AND_ASSIGN(File);
};

View file

@ -39,7 +39,9 @@ class FileHandle {
File::~File() {
Close();
if (!IsClosed()) {
Close();
}
delete handle_;
}
@ -197,10 +199,7 @@ File* File::Open(const char* path, FileOpenMode mode) {
File* File::OpenStdio(int fd) {
if ((fd < 0) || (2 < fd)) {
return NULL;
}
return new File(new FileHandle(fd));
return ((fd < 0) || (2 < fd)) ? NULL : new File(new FileHandle(fd));
}

View file

@ -38,7 +38,9 @@ class FileHandle {
File::~File() {
Close();
if (!IsClosed()) {
Close();
}
delete handle_;
}
@ -196,10 +198,7 @@ File* File::Open(const char* path, FileOpenMode mode) {
File* File::OpenStdio(int fd) {
if ((fd < 0) || (2 < fd)) {
return NULL;
}
return new File(new FileHandle(fd));
return ((fd < 0) || (2 < fd)) ? NULL : new File(new FileHandle(fd));
}

View file

@ -40,7 +40,9 @@ class FileHandle {
File::~File() {
Close();
if (!IsClosed()) {
Close();
}
delete handle_;
}
@ -199,10 +201,7 @@ File* File::Open(const char* path, FileOpenMode mode) {
File* File::OpenStdio(int fd) {
if ((fd < 0) || (2 < fd)) {
return NULL;
}
return new File(new FileHandle(fd));
return ((fd < 0) || (2 < fd)) ? NULL : new File(new FileHandle(fd));
}

View file

@ -22,24 +22,34 @@ patch class _File {
}
patch class _RandomAccessFile {
/* patch */ static int _close(int id) native "File_Close";
/* patch */ static int _getFD(int id) native "File_GetFD";
/* patch */ static _readByte(int id) native "File_ReadByte";
/* patch */ static _read(int id, int bytes) native "File_Read";
/* patch */ static _readInto(int id, List<int> buffer, int start, int end)
native "File_ReadInto";
/* patch */ static _writeByte(int id, int value) native "File_WriteByte";
/* patch */ static _writeFrom(int id, List<int> buffer, int start, int end)
native "File_WriteFrom";
/* patch */ static _position(int id) native "File_Position";
/* patch */ static _setPosition(int id, int position)
native "File_SetPosition";
/* patch */ static _truncate(int id, int length) native "File_Truncate";
/* patch */ static _length(int id) native "File_Length";
/* patch */ static _flush(int id) native "File_Flush";
/* patch */ static _lock(int id, int lock, int start, int end)
native "File_Lock";
patch class _RandomAccessFileOps {
/* patch */ factory _RandomAccessFileOps(int pointer)
=> new _RandomAccessFileOpsImpl(pointer);
}
class _RandomAccessFileOpsImpl extends NativeFieldWrapperClass1
implements _RandomAccessFileOps {
_RandomAccessFileOpsImpl._();
factory _RandomAccessFileOpsImpl(int pointer)
=> new _RandomAccessFileOpsImpl._().._setPointer(pointer);
void _setPointer(int pointer) native "File_SetPointer";
int getPointer() native "File_GetPointer";
int close() native "File_Close";
readByte() native "File_ReadByte";
read(int bytes) native "File_Read";
readInto(List<int> buffer, int start, int end) native "File_ReadInto";
writeByte(int value) native "File_WriteByte";
writeFrom(List<int> buffer, int start, int end) native "File_WriteFrom";
position() native "File_Position";
setPosition(int position) native "File_SetPosition";
truncate(int length) native "File_Truncate";
length() native "File_Length";
flush() native "File_Flush";
lock(int lock, int start, int end) native "File_Lock";
}

View file

@ -32,7 +32,7 @@ TEST_CASE(Read) {
buffer[13] = '\0';
EXPECT_STREQ("// Copyright ", buffer);
EXPECT(!file->WriteByte(1)); // Cannot write to a read-only file.
delete file;
file->Release();
}
@ -42,7 +42,7 @@ TEST_CASE(FileLength) {
File* file = File::Open(kFilename, File::kRead);
EXPECT(file != NULL);
EXPECT_EQ(42, file->Length());
delete file;
file->Release();
}
@ -56,7 +56,7 @@ TEST_CASE(FilePosition) {
EXPECT_EQ(12, file->Position());
EXPECT(file->ReadFully(buf, 6));
EXPECT_EQ(18, file->Position());
delete file;
file->Release();
}
} // namespace bin

View file

@ -11,6 +11,19 @@
namespace dart {
namespace bin {
void FUNCTION_NAME(File_GetPointer)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"File is not supported on this platform"));
}
void FUNCTION_NAME(File_SetPointer)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"File is not supported on this platform"));
}
void FUNCTION_NAME(File_Open)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"File is not supported on this platform"));
@ -29,12 +42,6 @@ void FUNCTION_NAME(File_Close)(Dart_NativeArguments args) {
}
void FUNCTION_NAME(File_GetFD)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"File is not supported on this platform"));
}
void FUNCTION_NAME(File_ReadByte)(Dart_NativeArguments args) {
Dart_ThrowException(DartUtils::NewInternalError(
"File is not supported on this platform"));

View file

@ -38,7 +38,9 @@ class FileHandle {
File::~File() {
Close();
if (!IsClosed()) {
Close();
}
delete handle_;
}

View file

@ -214,7 +214,7 @@ static void WriteSnapshotFile(const char* filename,
if (!file->WriteFully(buffer, size)) {
Log::PrintErr("Error: Failed to write snapshot file.\n\n");
}
delete file;
file->Release();
}

View file

@ -31,9 +31,10 @@ namespace bin {
V(Directory_List, 3) \
V(EventHandler_SendData, 3) \
V(EventHandler_TimerMillisecondClock, 0) \
V(File_GetPointer, 1) \
V(File_SetPointer, 2) \
V(File_Open, 2) \
V(File_Exists, 1) \
V(File_GetFD, 1) \
V(File_Close, 1) \
V(File_ReadByte, 1) \
V(File_WriteByte, 2) \

View file

@ -1099,7 +1099,7 @@ static void WriteSnapshotFile(const char* snapshot_directory,
"Unable to open file %s for writing snapshot\n",
qualified_filename);
}
delete file;
file->Release();
if (concat != NULL) {
delete concat;
}

View file

@ -135,58 +135,10 @@ class _File {
}
@patch
class _RandomAccessFile {
class _RandomAccessFileOps {
@patch
static int _close(int id) {
throw new UnsupportedError("RandomAccessFile._close");
}
@patch
static int _getFD(int id) {
throw new UnsupportedError("RandomAccessFile._getFD");
}
@patch
static _readByte(int id) {
throw new UnsupportedError("RandomAccessFile._readByte");
}
@patch
static _read(int id, int bytes) {
throw new UnsupportedError("RandomAccessFile._read");
}
@patch
static _readInto(int id, List<int> buffer, int start, int end) {
throw new UnsupportedError("RandomAccessFile._readInto");
}
@patch
static _writeByte(int id, int value) {
throw new UnsupportedError("RandomAccessFile._writeByte");
}
@patch
static _writeFrom(int id, List<int> buffer, int start, int end) {
throw new UnsupportedError("RandomAccessFile._writeFrom");
}
@patch
static _position(int id) {
throw new UnsupportedError("RandomAccessFile._position");
}
@patch
static _setPosition(int id, int position) {
throw new UnsupportedError("RandomAccessFile._setPosition");
}
@patch
static _truncate(int id, int length) {
throw new UnsupportedError("RandomAccessFile._truncate");
}
@patch
static _length(int id) {
throw new UnsupportedError("RandomAccessFile._length");
}
@patch
static _flush(int id) {
throw new UnsupportedError("RandomAccessFile._flush");
}
@patch
static _lock(int id, int lock, int start, int end) {
throw new UnsupportedError("RandomAccessFile._lock");
factory _RandomAccessFileOps(int pointer) {
throw new UnsupportedError("RandomAccessFile");
}
}

View file

@ -751,7 +751,7 @@ abstract class RandomAccessFile {
* already unlocked".
*/
Future<RandomAccessFile> lock(
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end]);
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]);
/**
* Synchronously locks the file or part of the file.
@ -784,7 +784,8 @@ abstract class RandomAccessFile {
* already unlocked".
*
*/
void lockSync([FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end]);
void lockSync(
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]);
/**
* Unlocks the file or part of the file.
@ -800,7 +801,7 @@ abstract class RandomAccessFile {
*
* See [lock] for more details.
*/
Future<RandomAccessFile> unlock([int start = 0, int end]);
Future<RandomAccessFile> unlock([int start = 0, int end = -1]);
/**
* Synchronously unlocks the file or part of the file.
@ -816,7 +817,7 @@ abstract class RandomAccessFile {
*
* See [lockSync] for more details.
*/
void unlockSync([int start = 0, int end]);
void unlockSync([int start = 0, int end = -1]);
/**
* Returns a human-readable string for this RandomAccessFile instance.

View file

@ -564,19 +564,43 @@ class _File extends FileSystemEntity implements File {
}
}
abstract class _RandomAccessFileOps {
external factory _RandomAccessFileOps(int pointer);
class _RandomAccessFile
implements RandomAccessFile {
int getPointer();
int close();
readByte();
read(int bytes);
readInto(List<int> buffer, int start, int end);
writeByte(int value);
writeFrom(List<int> buffer, int start, int end);
position();
setPosition(int position);
truncate(int length);
length();
flush();
lock(int lock, int start, int end);
}
class _RandomAccessFile implements RandomAccessFile {
static bool _connectedResourceHandler = false;
final String path;
int _id;
// Calling this function will increase the reference count on the native
// object that implements the file operations. It should only be called to
// pass the pointer to the IO Service, which will decrement the reference
// count when it is finished with it.
int _pointer() => _ops.getPointer();
bool _asyncDispatched = false;
SendPort _fileService;
_FileResourceInfo _resourceInfo;
_RandomAccessFileOps _ops;
_RandomAccessFile(this._id, this.path) {
_RandomAccessFile(int pointer, this.path) {
_ops = new _RandomAccessFileOps(pointer);
_resourceInfo = new _FileResourceInfo(this);
_maybeConnectHandler();
}
@ -587,12 +611,10 @@ class _RandomAccessFile
}
}
external static int _getFD(int id);
_maybeConnectHandler() {
if (!_connectedResourceHandler) {
// TODO(ricow): we probably need set these in some initialization code.
// We need to make sure that these are always awailable from the
// TODO(ricow): We probably need to set these in some initialization code.
// We need to make sure that these are always available from the
// observatory even if no files (or sockets for the socket ones) are
// open.
registerExtension('ext.dart.io.getOpenFiles',
@ -604,9 +626,9 @@ class _RandomAccessFile
}
Future<RandomAccessFile> close() {
return _dispatch(_FILE_CLOSE, [_id], markClosed: true).then((result) {
return _dispatch(_FILE_CLOSE, [_pointer()], markClosed: true).then((result) {
if (result != -1) {
_id = result;
closed = closed || (result == 0);
_maybePerformCleanup();
return this;
} else {
@ -615,20 +637,18 @@ class _RandomAccessFile
});
}
external static int _close(int id);
void closeSync() {
_checkAvailable();
var id = _close(_id);
var id = _ops.close();
if (id == -1) {
throw new FileSystemException("Cannot close file", path);
}
_id = id;
closed = closed || (id == 0);
_maybePerformCleanup();
}
Future<int> readByte() {
return _dispatch(_FILE_READ_BYTE, [_id]).then((response) {
return _dispatch(_FILE_READ_BYTE, [_pointer()]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "readByte failed", path);
}
@ -637,11 +657,9 @@ class _RandomAccessFile
});
}
external static _readByte(int id);
int readByteSync() {
_checkAvailable();
var result = _readByte(_id);
var result = _ops.readByte();
if (result is OSError) {
throw new FileSystemException("readByte failed", path, result);
}
@ -653,7 +671,7 @@ class _RandomAccessFile
if (bytes is !int) {
throw new ArgumentError(bytes);
}
return _dispatch(_FILE_READ, [_id, bytes]).then((response) {
return _dispatch(_FILE_READ, [_pointer(), bytes]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "read failed", path);
}
@ -662,14 +680,12 @@ class _RandomAccessFile
});
}
external static _read(int id, int bytes);
List<int> readSync(int bytes) {
_checkAvailable();
if (bytes is !int) {
throw new ArgumentError(bytes);
}
var result = _read(_id, bytes);
var result = _ops.read(bytes);
if (result is OSError) {
throw new FileSystemException("readSync failed", path, result);
}
@ -678,15 +694,17 @@ class _RandomAccessFile
}
Future<int> readInto(List<int> buffer, [int start = 0, int end]) {
if (buffer is !List ||
(start != null && start is !int) ||
(end != null && end is !int)) {
if ((buffer is !List) ||
((start != null) && (start is !int)) ||
((end != null) && (end is !int))) {
throw new ArgumentError();
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) return new Future.value(0);
if (end == start) {
return new Future.value(0);
}
int length = end - start;
return _dispatch(_FILE_READ_INTO, [_id, length]).then((response) {
return _dispatch(_FILE_READ_INTO, [_pointer(), length]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "readInto failed", path);
}
@ -698,18 +716,18 @@ class _RandomAccessFile
});
}
external static _readInto(int id, List<int> buffer, int start, int end);
int readIntoSync(List<int> buffer, [int start = 0, int end]) {
_checkAvailable();
if (buffer is !List ||
(start != null && start is !int) ||
(end != null && end is !int)) {
if ((buffer is !List) ||
((start != null) && (start is !int)) ||
((end != null) && (end is !int))) {
throw new ArgumentError();
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) return 0;
var result = _readInto(_id, buffer, start, end);
if (end == start) {
return 0;
}
var result = _ops.readInto(buffer, start, end);
if (result is OSError) {
throw new FileSystemException("readInto failed", path, result);
}
@ -721,7 +739,7 @@ class _RandomAccessFile
if (value is !int) {
throw new ArgumentError(value);
}
return _dispatch(_FILE_WRITE_BYTE, [_id, value]).then((response) {
return _dispatch(_FILE_WRITE_BYTE, [_pointer(), value]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "writeByte failed", path);
}
@ -730,14 +748,12 @@ class _RandomAccessFile
});
}
external static _writeByte(int id, int value);
int writeByteSync(int value) {
_checkAvailable();
if (value is !int) {
throw new ArgumentError(value);
}
var result = _writeByte(_id, value);
var result = _ops.writeByte(value);
if (result is OSError) {
throw new FileSystemException("writeByte failed", path, result);
}
@ -748,12 +764,14 @@ class _RandomAccessFile
Future<RandomAccessFile> writeFrom(
List<int> buffer, [int start = 0, int end]) {
if ((buffer is !List) ||
(start != null && start is !int) ||
(end != null && end is !int)) {
((start != null) && (start is !int)) ||
((end != null) && (end is !int))) {
throw new ArgumentError("Invalid arguments to writeFrom");
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) return new Future.value(this);
if (end == start) {
return new Future.value(this);
}
_BufferAndStart result;
try {
result = _ensureFastAndSerializableByteData(buffer, start, end);
@ -762,7 +780,7 @@ class _RandomAccessFile
}
List request = new List(4);
request[0] = _id;
request[0] = _pointer();
request[1] = result.buffer;
request[2] = result.start;
request[3] = end - (start - result.start);
@ -775,23 +793,22 @@ class _RandomAccessFile
});
}
external static _writeFrom(int id, List<int> buffer, int start, int end);
void writeFromSync(List<int> buffer, [int start = 0, int end]) {
_checkAvailable();
if (buffer is !List ||
(start != null && start is !int) ||
(end != null && end is !int)) {
if ((buffer is !List) ||
((start != null) && (start is !int)) ||
((end != null) && (end is !int))) {
throw new ArgumentError("Invalid arguments to writeFromSync");
}
end = RangeError.checkValidRange(start, end, buffer.length);
if (end == start) return;
if (end == start) {
return;
}
_BufferAndStart bufferAndStart =
_ensureFastAndSerializableByteData(buffer, start, end);
var result = _writeFrom(_id,
bufferAndStart.buffer,
bufferAndStart.start,
end - (start - bufferAndStart.start));
var result = _ops.writeFrom(bufferAndStart.buffer,
bufferAndStart.start,
end - (start - bufferAndStart.start));
if (result is OSError) {
throw new FileSystemException("writeFrom failed", path, result);
}
@ -816,7 +833,7 @@ class _RandomAccessFile
}
Future<int> position() {
return _dispatch(_FILE_POSITION, [_id]).then((response) {
return _dispatch(_FILE_POSITION, [_pointer()]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "position failed", path);
}
@ -824,11 +841,9 @@ class _RandomAccessFile
});
}
external static _position(int id);
int positionSync() {
_checkAvailable();
var result = _position(_id);
var result = _ops.position();
if (result is OSError) {
throw new FileSystemException("position failed", path, result);
}
@ -836,7 +851,7 @@ class _RandomAccessFile
}
Future<RandomAccessFile> setPosition(int position) {
return _dispatch(_FILE_SET_POSITION, [_id, position])
return _dispatch(_FILE_SET_POSITION, [_pointer(), position])
.then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "setPosition failed", path);
@ -845,18 +860,16 @@ class _RandomAccessFile
});
}
external static _setPosition(int id, int position);
void setPositionSync(int position) {
_checkAvailable();
var result = _setPosition(_id, position);
var result = _ops.setPosition(position);
if (result is OSError) {
throw new FileSystemException("setPosition failed", path, result);
}
}
Future<RandomAccessFile> truncate(int length) {
return _dispatch(_FILE_TRUNCATE, [_id, length]).then((response) {
return _dispatch(_FILE_TRUNCATE, [_pointer(), length]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "truncate failed", path);
}
@ -864,18 +877,16 @@ class _RandomAccessFile
});
}
external static _truncate(int id, int length);
void truncateSync(int length) {
_checkAvailable();
var result = _truncate(_id, length);
var result = _ops.truncate(length);
if (result is OSError) {
throw new FileSystemException("truncate failed", path, result);
}
}
Future<int> length() {
return _dispatch(_FILE_LENGTH, [_id]).then((response) {
return _dispatch(_FILE_LENGTH, [_pointer()]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, "length failed", path);
}
@ -883,11 +894,9 @@ class _RandomAccessFile
});
}
external static _length(int id);
int lengthSync() {
_checkAvailable();
var result = _length(_id);
var result = _ops.length();
if (result is OSError) {
throw new FileSystemException("length failed", path, result);
}
@ -895,7 +904,7 @@ class _RandomAccessFile
}
Future<RandomAccessFile> flush() {
return _dispatch(_FILE_FLUSH, [_id]).then((response) {
return _dispatch(_FILE_FLUSH, [_pointer()]).then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response,
"flush failed",
@ -905,11 +914,9 @@ class _RandomAccessFile
});
}
external static _flush(int id);
void flushSync() {
_checkAvailable();
var result = _flush(_id);
var result = _ops.flush();
if (result is OSError) {
throw new FileSystemException("flush failed", path, result);
}
@ -920,19 +927,15 @@ class _RandomAccessFile
static final int LOCK_EXCLUSIVE = 2;
Future<RandomAccessFile> lock(
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end]) {
if ((start != null && start is !int) ||
(end != null && end is !int) ||
mode is !FileLock) {
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]) {
if ((mode is !FileLock) || (start is !int) || (end is !int)) {
throw new ArgumentError();
}
if (start == null) start = 0;
if (end == null) end = -1;
if (start < 0 || end < -1 || (end != -1 && start >= end)) {
if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) {
throw new ArgumentError();
}
int lock = mode == FileLock.EXCLUSIVE ? LOCK_EXCLUSIVE : LOCK_SHARED;
return _dispatch(_FILE_LOCK, [_id, lock, start, end])
int lock = (mode == FileLock.EXCLUSIVE) ? LOCK_EXCLUSIVE : LOCK_SHARED;
return _dispatch(_FILE_LOCK, [_pointer(), lock, start, end])
.then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, 'lock failed', path);
@ -941,15 +944,14 @@ class _RandomAccessFile
});
}
Future<RandomAccessFile> unlock([int start = 0, int end]) {
if ((start != null && start is !int) ||
(end != null && end is !int)) {
Future<RandomAccessFile> unlock([int start = 0, int end = -1]) {
if ((start is !int) || (end is !int)) {
throw new ArgumentError();
}
if (start == null) start = 0;
if (end == null) end = -1;
if (start == end) throw new ArgumentError();
return _dispatch(_FILE_LOCK, [_id, LOCK_UNLOCK, start, end])
if (start == end) {
throw new ArgumentError();
}
return _dispatch(_FILE_LOCK, [_pointer(), LOCK_UNLOCK, start, end])
.then((response) {
if (_isErrorResponse(response)) {
throw _exceptionFromResponse(response, 'unlock failed', path);
@ -958,43 +960,37 @@ class _RandomAccessFile
});
}
external static _lock(int id, int lock, int start, int end);
void lockSync([FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end]) {
void lockSync(
[FileLock mode = FileLock.EXCLUSIVE, int start = 0, int end = -1]) {
_checkAvailable();
if ((start != null && start is !int) ||
(end != null && end is !int) ||
mode is !FileLock) {
if ((mode is !FileLock) || (start is !int) || (end is !int)) {
throw new ArgumentError();
}
if (start == null) start = 0;
if (end == null) end = -1;
if (start < 0 || end < -1 || (end != -1 && start >= end)) {
if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) {
throw new ArgumentError();
}
int lock = mode == FileLock.EXCLUSIVE ? LOCK_EXCLUSIVE : LOCK_SHARED;
var result = _lock(_id, lock, start, end);
int lock = (mode == FileLock.EXCLUSIVE) ? LOCK_EXCLUSIVE : LOCK_SHARED;
var result = _ops.lock(lock, start, end);
if (result is OSError) {
throw new FileSystemException('lock failed', path, result);
}
}
void unlockSync([int start = 0, int end]) {
void unlockSync([int start = 0, int end = -1]) {
_checkAvailable();
if ((start != null && start is !int) ||
(end != null && end is !int)) {
if ((start is !int) || (end is !int)) {
throw new ArgumentError();
}
if (start == null) start = 0;
if (end == null) end = -1;
if (start == end) throw new ArgumentError();
var result = _lock(_id, LOCK_UNLOCK, start, end);
if (start == end) {
throw new ArgumentError();
}
var result = _ops.lock(LOCK_UNLOCK, start, end);
if (result is OSError) {
throw new FileSystemException('unlock failed', path, result);
}
}
bool get closed => _id == 0;
bool closed = false;
Future _dispatch(int request, List data, { bool markClosed: false }) {
if (closed) {
@ -1005,9 +1001,9 @@ class _RandomAccessFile
return new Future.error(new FileSystemException(msg, path));
}
if (markClosed) {
// Set the id_ to 0 (NULL) to ensure the no more async requests
// can be issued for this file.
_id = 0;
// Set closed to true to ensure that no more async requests can be issued
// for this file.
closed = true;
}
_asyncDispatched = true;
return _IOService._dispatch(request, data)

View file

@ -36,11 +36,11 @@ check(String path, int start, int end, FileLock mode, {bool locked}) {
}
checkLocked(String path,
[int start, int end, FileLock mode = FileLock.EXCLUSIVE]) =>
[int start = 0, int end = -1, FileLock mode = FileLock.EXCLUSIVE]) =>
check(path, start, end, mode, locked: true);
checkNotLocked(String path,
[int start, int end, FileLock mode = FileLock.EXCLUSIVE]) =>
[int start = 0, int end = -1, FileLock mode = FileLock.EXCLUSIVE]) =>
check(path, start, end, mode, locked: false);
void testLockWholeFile() {