-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathAsyncLoadOperation_WinAPI.cpp
More file actions
151 lines (123 loc) · 5.68 KB
/
AsyncLoadOperation_WinAPI.cpp
File metadata and controls
151 lines (123 loc) · 5.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2015 XLGAMES Inc.
//
// Distributed under the MIT License (See
// accompanying file "LICENSE" or the website
// http://www.opensource.org/licenses/mit-license.php)
#include "AsyncLoadOperation.h"
#include "IFileSystem.h"
#include "../Utility/Threading/CompletionThreadPool.h"
#include "../Utility/StringUtils.h"
#include "../Core/SelectConfiguration.h"
#if PLATFORMOS_ACTIVE != PLATFORMOS_WINDOWS
#error AsyncLoadOperation.cpp only implemented for Windows (or Microsoft API targets)
#endif
#include "../Core/WinAPI/IncludeWindows.h"
namespace Assets
{
class AsyncLoadOperation::SpecialOverlapped : public OVERLAPPED
{
public:
std::shared_ptr<AsyncLoadOperation> _returnPointer;
HANDLE _fileHandle;
static void CALLBACK CompletionRoutine(
DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped);
};
void CALLBACK AsyncLoadOperation::SpecialOverlapped::CompletionRoutine(
DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped)
{
auto* o = (SpecialOverlapped*)lpOverlapped;
assert(o && o->_returnPointer);
if (o->_fileHandle != INVALID_HANDLE_VALUE)
CloseHandle(o->_fileHandle);
std::weak_ptr<AsyncLoadOperation> weakToThis = o->_returnPointer;
// We can reset the "_returnPointer", which will also decrease the reference
// count on the marker object (and so could destroy this).
// If o->_returnPointer is the only reference on this object, then we can
// consider it a cancel, and we can skip calling Complete().
// To check, we hold a weak ptr, release o->_returnPointer, and then check
// the status of the weak ptr.
o->_returnPointer.reset();
auto obj = weakToThis.lock();
if (!obj) return; // this operation was cancelled. No clients held their references
// Someone is still waiting on our results...
// Call the Compete() method to finish all processing
obj->Complete(obj->GetBuffer(), obj->GetBufferSize());
}
void AsyncLoadOperation::Enqueue(const std::shared_ptr<AsyncLoadOperation>& op, StringSection<ResChar> filename, CompletionThreadPool& pool)
{
assert(!op->_hasBeenQueued);
op->_hasBeenQueued = true;
XlCopyString(op->_filename, filename);
// We will hold an extra reference to this object
// during the queueing processing and while the background
// load is occurring.
//
// Before the file open is started, it will be just a
// weak reference. If all client references are destroyed
// before we open the file, then we will cancel.
// However, once the file has been opened we need to
// hold a strong reference at least until the read has
// completed
std::weak_ptr<AsyncLoadOperation> weakToThis = op;
pool.Enqueue(
[weakToThis]()
{
auto thisOp = weakToThis.lock();
// if all other references to this object have been released
// then the weak_ptr::lock() will fail, and we can consider
// it a cancel
if (!thisOp) return;
auto translated = MainFileSystem::TryGetDesc(thisOp->_filename);
if (translated._state != FileDesc::State::Normal || translated._naturalName.empty()) {
thisOp->OnFailure();
return;
}
// if we got to this point, we cannot cancel until the load
// level read operation has been completed
auto h = CreateFileA(
(const char*)translated._naturalName.c_str(), GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
nullptr);
if (h == INVALID_HANDLE_VALUE) {
// failed to load the file -- probably because it's missing
thisOp->OnFailure();
return;
}
auto fileSize = GetFileSize(h, nullptr);
if (!fileSize || fileSize == INVALID_FILE_SIZE) {
CloseHandle(h);
thisOp->OnFailure();
return;
}
thisOp->_buffer.reset((uint8*)XlMemAlign(fileSize, 16));
thisOp->_bufferLength = fileSize;
thisOp->_overlapped = std::make_unique<SpecialOverlapped>();
XlSetMemory(thisOp->_overlapped.get(), 0, sizeof(OVERLAPPED));
thisOp->_overlapped->_fileHandle = INVALID_HANDLE_VALUE;
thisOp->_overlapped->_returnPointer = thisOp;
auto readResult = ReadFileEx(
h, thisOp->_buffer.get(), fileSize,
thisOp->_overlapped.get(), &SpecialOverlapped::CompletionRoutine);
if (!readResult) {
CloseHandle(h);
thisOp->_overlapped->_returnPointer.reset();
thisOp->OnFailure();
return;
}
thisOp->_overlapped->_fileHandle = h;
// execution will pass to AsyncLoadOperation::CompletionRoutine, which
// will complete the load operation
});
}
const uint8* AsyncLoadOperation::GetBuffer() const { return AsPointer(_buffer.get()); }
size_t AsyncLoadOperation::GetBufferSize() const { return _bufferLength; }
AsyncLoadOperation::AsyncLoadOperation()
{
_filename[0] = '\0';
_bufferLength = 0;
_hasBeenQueued = false;
}
AsyncLoadOperation::~AsyncLoadOperation() {}
}