1 module eventcore.drivers.winapi.files; 2 3 version (Windows): 4 5 import eventcore.driver; 6 import eventcore.drivers.winapi.core; 7 import eventcore.internal.ioworker; 8 import eventcore.internal.win32; 9 10 private extern(Windows) @trusted nothrow @nogc { 11 BOOL SetEndOfFile(HANDLE hFile); 12 } 13 14 final class WinAPIEventDriverFiles : EventDriverFiles { 15 @safe /*@nogc*/ nothrow: 16 private { 17 WinAPIEventDriverCore m_core; 18 IOWorkerPool m_workerPool; 19 } 20 21 this(WinAPIEventDriverCore core) 22 @nogc { 23 m_core = core; 24 } 25 26 override FileFD open(string path, FileOpenMode mode) 27 { 28 import std.utf : toUTF16z; 29 30 auto access = mode == FileOpenMode.readWrite || mode == FileOpenMode.createTrunc ? (GENERIC_WRITE | GENERIC_READ) : 31 mode == FileOpenMode.append ? FILE_APPEND_DATA : GENERIC_READ; 32 auto shareMode = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE; 33 auto creation = mode == FileOpenMode.createTrunc ? CREATE_ALWAYS : mode == FileOpenMode.append? OPEN_ALWAYS : OPEN_EXISTING; 34 35 auto handle = () @trusted { 36 scope (failure) assert(false); 37 return CreateFileW(path.toUTF16z, access, shareMode, null, creation, 38 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, null); 39 } (); 40 auto errorcode = GetLastError(); 41 if (handle == INVALID_HANDLE_VALUE) 42 return FileFD.invalid; 43 44 if (mode == FileOpenMode.createTrunc && errorcode == ERROR_ALREADY_EXISTS) { 45 BOOL ret = SetEndOfFile(handle); 46 if (!ret) { 47 CloseHandle(handle); 48 return FileFD.invalid; 49 } 50 } 51 52 return adoptInternal(handle); 53 } 54 55 override FileFD adopt(int system_handle) 56 { 57 return adoptInternal(() @trusted { return cast(HANDLE)system_handle; } ()); 58 } 59 60 private FileFD adoptInternal(HANDLE handle) 61 { 62 DWORD f; 63 if (!() @trusted { return GetHandleInformation(handle, &f); } ()) 64 return FileFD.invalid; 65 66 auto s = m_core.setupSlot!FileSlot(handle); 67 68 s.read.overlapped.driver = m_core; 69 s.read.overlapped.hEvent = handle; 70 s.write.overlapped.driver = m_core; 71 s.write.overlapped.hEvent = handle; 72 73 return FileFD(cast(size_t)handle, m_core.m_handles[handle].validationCounter); 74 } 75 76 override void close(FileFD file, FileCloseCallback on_closed) 77 { 78 static void doCloseCleanup(CloseParams p) 79 { 80 p.core.removeWaiter(); 81 if (p.callback) p.callback(p.file, p.status); 82 } 83 84 static void doClose(FileFD file, FileCloseCallback on_closed, 85 shared(WinAPIEventDriverCore) core) 86 { 87 CloseParams p; 88 CloseStatus st; 89 p.file = file; 90 p.callback = on_closed; 91 p.core = () @trusted { return cast(WinAPIEventDriverCore)core; } (); 92 if (CloseHandle(idToHandle(file))) p.status = CloseStatus.ok; 93 else p.status = CloseStatus.ioError; 94 95 () @trusted { core.runInOwnerThread(&doCloseCleanup, p); } (); 96 } 97 98 if (!isValid(file)) { 99 on_closed(file, CloseStatus.invalidHandle); 100 return; 101 } 102 103 auto h = idToHandle(file); 104 auto slot = () @trusted { return &m_core.m_handles[h]; } (); 105 if (slot.file.read.overlapped.hEvent != INVALID_HANDLE_VALUE) 106 slot.file.read.overlapped.hEvent = slot.file.write.overlapped.hEvent = INVALID_HANDLE_VALUE; 107 m_core.addWaiter(); 108 m_core.discardEvents(&slot.file.read.overlapped, &slot.file.write.overlapped); 109 m_core.freeSlot(h); 110 workerPool.run!doClose(file, on_closed, () @trusted { return cast(shared)m_core; } ()); 111 } 112 113 override ulong getSize(FileFD file) 114 { 115 if (!isValid(file)) return ulong.max; 116 117 LARGE_INTEGER size; 118 auto succeeded = () @trusted { return GetFileSizeEx(idToHandle(file), &size); } (); 119 if (!succeeded || size.QuadPart < 0) 120 return ulong.max; 121 return size.QuadPart; 122 } 123 124 override void truncate(FileFD file, ulong size, FileIOCallback on_finish) 125 @trusted { 126 if (!isValid(file)) { 127 on_finish(file, IOStatus.invalidHandle, 0); 128 return; 129 } 130 131 auto h = idToHandle(file); 132 133 // FIXME: do this in a separate thread 134 135 LARGE_INTEGER li = {QuadPart: size}; 136 if (!SetFilePointerEx(h, li, null, FILE_BEGIN)) { 137 on_finish(file, IOStatus.error, 0); 138 return; 139 } 140 141 if (!SetEndOfFile(h)) { 142 on_finish(file, IOStatus.error, 0); 143 return; 144 } 145 146 on_finish(file, IOStatus.ok, 0); 147 } 148 149 override void write(FileFD file, ulong offset, const(ubyte)[] buffer, IOMode mode, FileIOCallback on_write_finish) 150 { 151 if (!isValid(file)) { 152 on_write_finish(file, IOStatus.invalidHandle, 0); 153 return; 154 } 155 156 auto h = idToHandle(file); 157 auto slot = &m_core.m_handles[h].file.write; 158 159 if (slot.overlapped.hEvent == INVALID_HANDLE_VALUE) { 160 on_write_finish(file, IOStatus.disconnected, 0); 161 return; 162 } 163 164 if (!buffer.length) { 165 on_write_finish(file, IOStatus.ok, 0); 166 return; 167 } 168 169 slot.bytesTransferred = 0; 170 slot.offset = offset; 171 slot.buffer = buffer; 172 slot.mode = mode; 173 slot.callback = on_write_finish; 174 m_core.addWaiter(); 175 startIO!(WriteFileEx, true)(h, slot); 176 } 177 178 override void read(FileFD file, ulong offset, ubyte[] buffer, IOMode mode, FileIOCallback on_read_finish) 179 { 180 if (!isValid(file)) { 181 on_read_finish(file, IOStatus.invalidHandle, 0); 182 return; 183 } 184 185 auto h = idToHandle(file); 186 auto slot = &m_core.m_handles[h].file.read; 187 188 if (slot.overlapped.hEvent == INVALID_HANDLE_VALUE) { 189 on_read_finish(file, IOStatus.disconnected, 0); 190 return; 191 } 192 193 if (!buffer.length) { 194 on_read_finish(file, IOStatus.ok, 0); 195 return; 196 } 197 198 slot.bytesTransferred = 0; 199 slot.offset = offset; 200 slot.buffer = buffer; 201 slot.mode = mode; 202 slot.callback = on_read_finish; 203 m_core.addWaiter(); 204 startIO!(ReadFileEx, false)(h, slot); 205 } 206 207 override void cancelWrite(FileFD file) 208 { 209 if (!isValid(file)) return; 210 211 auto h = idToHandle(file); 212 cancelIO!true(h, m_core.m_handles[h].file.write); 213 } 214 215 override void cancelRead(FileFD file) 216 { 217 if (!isValid(file)) return; 218 219 auto h = idToHandle(file); 220 cancelIO!false(h, m_core.m_handles[h].file.read); 221 } 222 223 override bool isValid(FileFD handle) 224 const { 225 auto h = idToHandle(handle); 226 if (auto ps = h in m_core.m_handles) 227 return ps.validationCounter == handle.validationCounter; 228 return false; 229 } 230 231 override void addRef(FileFD descriptor) 232 { 233 if (!isValid(descriptor)) return; 234 235 m_core.m_handles[idToHandle(descriptor)].addRef(); 236 } 237 238 override bool releaseRef(FileFD descriptor) 239 { 240 if (!isValid(descriptor)) return true; 241 242 auto h = idToHandle(descriptor); 243 auto slot = &m_core.m_handles[h]; 244 return slot.releaseRef({ close(descriptor, null); }); 245 } 246 247 protected override void* rawUserData(FileFD descriptor, size_t size, DataInitializer initialize, DataInitializer destroy) 248 @system { 249 return m_core.rawUserDataImpl(idToHandle(descriptor), size, initialize, destroy); 250 } 251 252 private static void startIO(alias fun, bool RO)(HANDLE h, FileSlot.Direction!RO* slot) 253 { 254 import std.algorithm.comparison : min; 255 256 with (slot.overlapped.overlapped) { 257 Internal = 0; 258 InternalHigh = 0; 259 Offset = cast(uint)(slot.offset & 0xFFFFFFFF); 260 OffsetHigh = cast(uint)(slot.offset >> 32); 261 hEvent = h; 262 } 263 264 auto nbytes = min(slot.buffer.length, DWORD.max); 265 auto handler = &overlappedIOHandler!(onIOFinished!(fun, RO)); 266 if (!() @trusted { return fun(h, &slot.buffer[0], nbytes, &slot.overlapped.overlapped, handler); } ()) { 267 slot.overlapped.driver.removeWaiter(); 268 slot.invokeCallback(IOStatus.error, slot.bytesTransferred); 269 } 270 } 271 272 private void cancelIO(bool RO)(HANDLE h, ref FileSlot.Direction!RO slot) 273 { 274 if (slot.callback) { 275 m_core.removeWaiter(); 276 () @trusted { CancelIoEx(h, &slot.overlapped.overlapped); } (); 277 slot.callback = null; 278 slot.buffer = null; 279 } 280 } 281 282 private static nothrow 283 void onIOFinished(alias fun, bool RO)(DWORD error, DWORD bytes_transferred, OVERLAPPED_CORE* overlapped) 284 { 285 HANDLE handle = overlapped.hEvent; 286 if (handle == INVALID_HANDLE_VALUE) return; 287 auto cslot = () @trusted { return &overlapped.driver.m_handles[handle]; } (); 288 FileFD id = FileFD(cast(size_t)overlapped.hEvent, cslot.validationCounter); 289 290 static if (RO) 291 auto slot = () @trusted { return &cslot.file.write; } (); 292 else 293 auto slot = () @trusted { return &cslot.file.read; } (); 294 assert(slot !is null); 295 296 if (!slot.callback) { 297 // request was already cancelled 298 return; 299 } 300 301 if (error != 0) { 302 overlapped.driver.removeWaiter(); 303 slot.invokeCallback(IOStatus.error, slot.bytesTransferred + bytes_transferred); 304 return; 305 } 306 307 slot.bytesTransferred += bytes_transferred; 308 slot.offset += bytes_transferred; 309 310 if (slot.bytesTransferred >= slot.buffer.length || slot.mode != IOMode.all) { 311 overlapped.driver.removeWaiter(); 312 slot.invokeCallback(IOStatus.ok, slot.bytesTransferred); 313 } else { 314 startIO!(fun, RO)(handle, slot); 315 } 316 } 317 318 private @property ref IOWorkerPool workerPool() 319 { 320 if (!m_workerPool) 321 m_workerPool = acquireIOWorkerPool(); 322 return m_workerPool; 323 } 324 325 private static HANDLE idToHandle(FileFD id) 326 @trusted @nogc { 327 return cast(HANDLE)cast(size_t)id; 328 } 329 } 330 331 private static struct CloseParams { 332 FileFD file; 333 FileCloseCallback callback; 334 CloseStatus status; 335 WinAPIEventDriverCore core; 336 }