]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | // |
6 | // Copyright (c) 2011 The LevelDB Authors. All rights reserved. | |
7 | // Use of this source code is governed by a BSD-style license that can be | |
8 | // found in the LICENSE file. See the AUTHORS file for names of contributors. | |
9 | ||
20effc67 TL |
10 | #if defined(OS_WIN) |
11 | ||
7c673cae | 12 | #include "port/win/env_win.h" |
7c673cae | 13 | |
1e59de90 | 14 | #include <direct.h> // _rmdir, _mkdir, _getcwd |
7c673cae | 15 | #include <errno.h> |
1e59de90 TL |
16 | #include <io.h> // _access |
17 | #include <rpc.h> // for uuid generation | |
18 | #include <shlwapi.h> | |
7c673cae | 19 | #include <sys/stat.h> |
1e59de90 TL |
20 | #include <sys/types.h> |
21 | #include <windows.h> | |
22 | #include <winioctl.h> | |
7c673cae | 23 | |
1e59de90 TL |
24 | #include <algorithm> |
25 | #include <ctime> | |
26 | #include <thread> | |
7c673cae FG |
27 | |
28 | #include "monitoring/iostats_context_imp.h" | |
7c673cae FG |
29 | #include "monitoring/thread_status_updater.h" |
30 | #include "monitoring/thread_status_util.h" | |
1e59de90 TL |
31 | #include "port/lang.h" |
32 | #include "port/port.h" | |
33 | #include "port/port_dirent.h" | |
34 | #include "port/win/io_win.h" | |
35 | #include "port/win/win_logger.h" | |
36 | #include "rocksdb/env.h" | |
37 | #include "rocksdb/slice.h" | |
11fdf7f2 | 38 | #include "strsafe.h" |
1e59de90 | 39 | #include "util/string_util.h" |
11fdf7f2 | 40 | |
1e59de90 TL |
41 | // Undefine the functions windows might use (again)... |
42 | #undef GetCurrentTime | |
43 | #undef DeleteFile | |
44 | #undef LoadLibrary | |
7c673cae | 45 | |
f67539c2 | 46 | namespace ROCKSDB_NAMESPACE { |
7c673cae FG |
47 | |
48 | ThreadStatusUpdater* CreateThreadStatusUpdater() { | |
49 | return new ThreadStatusUpdater(); | |
50 | } | |
51 | ||
52 | namespace { | |
53 | ||
494da23a TL |
54 | // Sector size used when physical sector size cannot be obtained from device. |
55 | static const size_t kSectorSize = 512; | |
11fdf7f2 | 56 | |
7c673cae FG |
57 | // RAII helpers for HANDLEs |
58 | const auto CloseHandleFunc = [](HANDLE h) { ::CloseHandle(h); }; | |
1e59de90 | 59 | using UniqueCloseHandlePtr = std::unique_ptr<void, decltype(CloseHandleFunc)>; |
7c673cae | 60 | |
11fdf7f2 | 61 | const auto FindCloseFunc = [](HANDLE h) { ::FindClose(h); }; |
1e59de90 | 62 | using UniqueFindClosePtr = std::unique_ptr<void, decltype(FindCloseFunc)>; |
11fdf7f2 | 63 | |
7c673cae FG |
64 | void WinthreadCall(const char* label, std::error_code result) { |
65 | if (0 != result.value()) { | |
1e59de90 TL |
66 | fprintf(stderr, "Winthread %s: %s\n", label, |
67 | errnoStr(result.value()).c_str()); | |
7c673cae FG |
68 | abort(); |
69 | } | |
70 | } | |
71 | ||
1e59de90 | 72 | } // namespace |
7c673cae FG |
73 | |
74 | namespace port { | |
1e59de90 TL |
75 | WinClock::WinClock() |
76 | : perf_counter_frequency_(0), | |
494da23a | 77 | nano_seconds_per_period_(0), |
7c673cae | 78 | GetSystemTimePreciseAsFileTime_(NULL) { |
7c673cae FG |
79 | { |
80 | LARGE_INTEGER qpf; | |
11fdf7f2 TL |
81 | BOOL ret __attribute__((__unused__)); |
82 | ret = QueryPerformanceFrequency(&qpf); | |
7c673cae FG |
83 | assert(ret == TRUE); |
84 | perf_counter_frequency_ = qpf.QuadPart; | |
494da23a TL |
85 | |
86 | if (std::nano::den % perf_counter_frequency_ == 0) { | |
87 | nano_seconds_per_period_ = std::nano::den / perf_counter_frequency_; | |
88 | } | |
7c673cae FG |
89 | } |
90 | ||
91 | HMODULE module = GetModuleHandle("kernel32.dll"); | |
92 | if (module != NULL) { | |
1e59de90 TL |
93 | GetSystemTimePreciseAsFileTime_ = (FnGetSystemTimePreciseAsFileTime)( |
94 | void*)GetProcAddress(module, "GetSystemTimePreciseAsFileTime"); | |
7c673cae FG |
95 | } |
96 | } | |
97 | ||
1e59de90 TL |
98 | void WinClock::SleepForMicroseconds(int micros) { |
99 | std::this_thread::sleep_for(std::chrono::microseconds(micros)); | |
7c673cae FG |
100 | } |
101 | ||
1e59de90 TL |
102 | std::string WinClock::TimeToString(uint64_t secondsSince1970) { |
103 | std::string result; | |
7c673cae | 104 | |
1e59de90 TL |
105 | const time_t seconds = secondsSince1970; |
106 | const int maxsize = 64; | |
494da23a | 107 | |
1e59de90 TL |
108 | struct tm t; |
109 | errno_t ret = localtime_s(&t, &seconds); | |
110 | ||
111 | if (ret) { | |
112 | result = std::to_string(seconds); | |
113 | } else { | |
114 | result.resize(maxsize); | |
115 | char* p = &result[0]; | |
116 | ||
117 | int len = | |
118 | snprintf(p, maxsize, "%04d/%02d/%02d-%02d:%02d:%02d ", t.tm_year + 1900, | |
119 | t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); | |
120 | assert(len > 0); | |
121 | ||
122 | result.resize(len); | |
7c673cae FG |
123 | } |
124 | ||
125 | return result; | |
126 | } | |
127 | ||
1e59de90 TL |
128 | uint64_t WinClock::NowMicros() { |
129 | if (GetSystemTimePreciseAsFileTime_ != NULL) { | |
130 | // all std::chrono clocks on windows proved to return | |
131 | // values that may repeat that is not good enough for some uses. | |
132 | const int64_t c_UnixEpochStartTicks = 116444736000000000LL; | |
133 | const int64_t c_FtToMicroSec = 10; | |
134 | ||
135 | // This interface needs to return system time and not | |
136 | // just any microseconds because it is often used as an argument | |
137 | // to TimedWait() on condition variable | |
138 | FILETIME ftSystemTime; | |
139 | GetSystemTimePreciseAsFileTime_(&ftSystemTime); | |
140 | ||
141 | LARGE_INTEGER li; | |
142 | li.LowPart = ftSystemTime.dwLowDateTime; | |
143 | li.HighPart = ftSystemTime.dwHighDateTime; | |
144 | // Subtract unix epoch start | |
145 | li.QuadPart -= c_UnixEpochStartTicks; | |
146 | // Convert to microsecs | |
147 | li.QuadPart /= c_FtToMicroSec; | |
148 | return li.QuadPart; | |
11fdf7f2 | 149 | } |
1e59de90 TL |
150 | return std::chrono::duration_cast<std::chrono::microseconds>( |
151 | std::chrono::system_clock::now().time_since_epoch()) | |
152 | .count(); | |
153 | } | |
154 | ||
155 | uint64_t WinClock::NowNanos() { | |
156 | if (nano_seconds_per_period_ != 0) { | |
157 | // all std::chrono clocks on windows have the same resolution that is only | |
158 | // good enough for microseconds but not nanoseconds | |
159 | // On Windows 8 and Windows 2012 Server | |
160 | // GetSystemTimePreciseAsFileTime(¤t_time) can be used | |
161 | LARGE_INTEGER li; | |
162 | QueryPerformanceCounter(&li); | |
163 | // Convert performance counter to nanoseconds by precomputed ratio. | |
164 | // Directly multiply nano::den with li.QuadPart causes overflow. | |
165 | // Only do this when nano::den is divisible by perf_counter_frequency_, | |
166 | // which most likely is the case in reality. If it's not, fall back to | |
167 | // high_resolution_clock, which may be less precise under old compilers. | |
168 | li.QuadPart *= nano_seconds_per_period_; | |
169 | return li.QuadPart; | |
170 | } | |
171 | return std::chrono::duration_cast<std::chrono::nanoseconds>( | |
172 | std::chrono::high_resolution_clock::now().time_since_epoch()) | |
173 | .count(); | |
11fdf7f2 TL |
174 | } |
175 | ||
1e59de90 | 176 | Status WinClock::GetCurrentTime(int64_t* unix_time) { |
7c673cae FG |
177 | time_t time = std::time(nullptr); |
178 | if (time == (time_t)(-1)) { | |
179 | return Status::NotSupported("Failed to get time"); | |
180 | } | |
181 | ||
182 | *unix_time = time; | |
183 | return Status::OK(); | |
184 | } | |
185 | ||
1e59de90 TL |
186 | WinFileSystem::WinFileSystem(const std::shared_ptr<SystemClock>& clock) |
187 | : clock_(clock), page_size_(4 * 1024), allocation_granularity_(page_size_) { | |
188 | SYSTEM_INFO sinfo; | |
189 | GetSystemInfo(&sinfo); | |
190 | ||
191 | page_size_ = sinfo.dwPageSize; | |
192 | allocation_granularity_ = sinfo.dwAllocationGranularity; | |
193 | } | |
194 | ||
195 | const std::shared_ptr<WinFileSystem>& WinFileSystem::Default() { | |
196 | STATIC_AVOID_DESTRUCTION(std::shared_ptr<WinFileSystem>, fs) | |
197 | (std::make_shared<WinFileSystem>(WinClock::Default())); | |
198 | return fs; | |
199 | } | |
200 | ||
201 | WinEnvIO::WinEnvIO(Env* hosted_env) : hosted_env_(hosted_env) {} | |
202 | ||
203 | WinEnvIO::~WinEnvIO() {} | |
204 | ||
205 | IOStatus WinFileSystem::DeleteFile(const std::string& fname, | |
206 | const IOOptions& /*options*/, | |
207 | IODebugContext* /*dbg*/) { | |
208 | IOStatus result; | |
209 | ||
210 | BOOL ret = RX_DeleteFile(RX_FN(fname).c_str()); | |
211 | ||
212 | if (!ret) { | |
213 | auto lastError = GetLastError(); | |
214 | result = IOErrorFromWindowsError("Failed to delete: " + fname, lastError); | |
215 | } | |
216 | ||
217 | return result; | |
218 | } | |
219 | ||
220 | IOStatus WinFileSystem::Truncate(const std::string& fname, size_t size, | |
221 | const IOOptions& /*options*/, | |
222 | IODebugContext* /*dbg*/) { | |
223 | IOStatus s; | |
224 | int result = ROCKSDB_NAMESPACE::port::Truncate(fname, size); | |
225 | if (result != 0) { | |
226 | s = IOError("Failed to truncate: " + fname, errno); | |
227 | } | |
228 | return s; | |
229 | } | |
230 | ||
231 | IOStatus WinFileSystem::NewSequentialFile( | |
232 | const std::string& fname, const FileOptions& options, | |
233 | std::unique_ptr<FSSequentialFile>* result, IODebugContext* /*dbg*/) { | |
234 | IOStatus s; | |
7c673cae FG |
235 | |
236 | result->reset(); | |
237 | ||
238 | // Corruption test needs to rename and delete files of these kind | |
239 | // while they are still open with another handle. For that reason we | |
240 | // allow share_write and delete(allows rename). | |
241 | HANDLE hFile = INVALID_HANDLE_VALUE; | |
242 | ||
243 | DWORD fileFlags = FILE_ATTRIBUTE_READONLY; | |
244 | ||
245 | if (options.use_direct_reads && !options.use_mmap_reads) { | |
246 | fileFlags |= FILE_FLAG_NO_BUFFERING; | |
247 | } | |
248 | ||
249 | { | |
250 | IOSTATS_TIMER_GUARD(open_nanos); | |
494da23a TL |
251 | hFile = RX_CreateFile( |
252 | RX_FN(fname).c_str(), GENERIC_READ, | |
253 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, | |
254 | OPEN_EXISTING, // Original fopen mode is "rb" | |
255 | fileFlags, NULL); | |
7c673cae FG |
256 | } |
257 | ||
258 | if (INVALID_HANDLE_VALUE == hFile) { | |
259 | auto lastError = GetLastError(); | |
260 | s = IOErrorFromWindowsError("Failed to open NewSequentialFile" + fname, | |
494da23a | 261 | lastError); |
7c673cae FG |
262 | } else { |
263 | result->reset(new WinSequentialFile(fname, hFile, options)); | |
264 | } | |
265 | return s; | |
266 | } | |
267 | ||
1e59de90 TL |
268 | IOStatus WinFileSystem::NewRandomAccessFile( |
269 | const std::string& fname, const FileOptions& options, | |
270 | std::unique_ptr<FSRandomAccessFile>* result, IODebugContext* dbg) { | |
7c673cae | 271 | result->reset(); |
1e59de90 | 272 | IOStatus s; |
7c673cae FG |
273 | |
274 | // Open the file for read-only random access | |
275 | // Random access is to disable read-ahead as the system reads too much data | |
276 | DWORD fileFlags = FILE_ATTRIBUTE_READONLY; | |
277 | ||
278 | if (options.use_direct_reads && !options.use_mmap_reads) { | |
279 | fileFlags |= FILE_FLAG_NO_BUFFERING; | |
280 | } else { | |
281 | fileFlags |= FILE_FLAG_RANDOM_ACCESS; | |
282 | } | |
283 | ||
284 | /// Shared access is necessary for corruption test to pass | |
285 | // almost all tests would work with a possible exception of fault_injection | |
286 | HANDLE hFile = 0; | |
287 | { | |
288 | IOSTATS_TIMER_GUARD(open_nanos); | |
1e59de90 TL |
289 | hFile = |
290 | RX_CreateFile(RX_FN(fname).c_str(), GENERIC_READ, | |
291 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | |
292 | NULL, OPEN_EXISTING, fileFlags, NULL); | |
7c673cae FG |
293 | } |
294 | ||
295 | if (INVALID_HANDLE_VALUE == hFile) { | |
296 | auto lastError = GetLastError(); | |
297 | return IOErrorFromWindowsError( | |
494da23a | 298 | "NewRandomAccessFile failed to Create/Open: " + fname, lastError); |
7c673cae FG |
299 | } |
300 | ||
301 | UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc); | |
302 | ||
1e59de90 TL |
303 | // CAUTION! This will map the entire file into the process address space. |
304 | // Not recommended for 32-bit platforms. | |
305 | if (options.use_mmap_reads) { | |
7c673cae FG |
306 | uint64_t fileSize; |
307 | ||
1e59de90 | 308 | s = GetFileSize(fname, IOOptions(), &fileSize, dbg); |
7c673cae FG |
309 | |
310 | if (s.ok()) { | |
311 | // Will not map empty files | |
312 | if (fileSize == 0) { | |
1e59de90 TL |
313 | return IOError("NewRandomAccessFile failed to map empty file: " + fname, |
314 | EINVAL); | |
7c673cae FG |
315 | } |
316 | ||
494da23a TL |
317 | HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READONLY, |
318 | 0, // At its present length | |
319 | 0, | |
320 | NULL); // Mapping name | |
7c673cae FG |
321 | |
322 | if (!hMap) { | |
323 | auto lastError = GetLastError(); | |
324 | return IOErrorFromWindowsError( | |
494da23a TL |
325 | "Failed to create file mapping for NewRandomAccessFile: " + fname, |
326 | lastError); | |
7c673cae FG |
327 | } |
328 | ||
329 | UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc); | |
330 | ||
331 | const void* mapped_region = | |
1e59de90 TL |
332 | MapViewOfFileEx(hMap, FILE_MAP_READ, |
333 | 0, // High DWORD of access start | |
334 | 0, // Low DWORD | |
335 | static_cast<SIZE_T>(fileSize), | |
336 | NULL); // Let the OS choose the mapping | |
7c673cae FG |
337 | |
338 | if (!mapped_region) { | |
339 | auto lastError = GetLastError(); | |
340 | return IOErrorFromWindowsError( | |
494da23a TL |
341 | "Failed to MapViewOfFile for NewRandomAccessFile: " + fname, |
342 | lastError); | |
7c673cae FG |
343 | } |
344 | ||
345 | result->reset(new WinMmapReadableFile(fname, hFile, hMap, mapped_region, | |
494da23a | 346 | static_cast<size_t>(fileSize))); |
7c673cae FG |
347 | |
348 | mapGuard.release(); | |
349 | fileGuard.release(); | |
350 | } | |
351 | } else { | |
1e59de90 | 352 | result->reset(new WinRandomAccessFile(fname, hFile, page_size_, options)); |
7c673cae FG |
353 | fileGuard.release(); |
354 | } | |
355 | return s; | |
356 | } | |
357 | ||
1e59de90 TL |
358 | IOStatus WinFileSystem::OpenWritableFile( |
359 | const std::string& fname, const FileOptions& options, | |
360 | std::unique_ptr<FSWritableFile>* result, bool reopen) { | |
7c673cae FG |
361 | const size_t c_BufferCapacity = 64 * 1024; |
362 | ||
363 | EnvOptions local_options(options); | |
364 | ||
365 | result->reset(); | |
1e59de90 | 366 | IOStatus s; |
7c673cae FG |
367 | |
368 | DWORD fileFlags = FILE_ATTRIBUTE_NORMAL; | |
369 | ||
370 | if (local_options.use_direct_writes && !local_options.use_mmap_writes) { | |
11fdf7f2 | 371 | fileFlags = FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; |
7c673cae FG |
372 | } |
373 | ||
374 | // Desired access. We are want to write only here but if we want to memory | |
375 | // map | |
376 | // the file then there is no write only mode so we have to create it | |
377 | // Read/Write | |
378 | // However, MapViewOfFile specifies only Write only | |
379 | DWORD desired_access = GENERIC_WRITE; | |
380 | DWORD shared_mode = FILE_SHARE_READ; | |
381 | ||
382 | if (local_options.use_mmap_writes) { | |
383 | desired_access |= GENERIC_READ; | |
384 | } else { | |
385 | // Adding this solely for tests to pass (fault_injection_test, | |
386 | // wal_manager_test). | |
387 | shared_mode |= (FILE_SHARE_WRITE | FILE_SHARE_DELETE); | |
388 | } | |
389 | ||
11fdf7f2 TL |
390 | // This will always truncate the file |
391 | DWORD creation_disposition = CREATE_ALWAYS; | |
392 | if (reopen) { | |
393 | creation_disposition = OPEN_ALWAYS; | |
394 | } | |
395 | ||
7c673cae FG |
396 | HANDLE hFile = 0; |
397 | { | |
398 | IOSTATS_TIMER_GUARD(open_nanos); | |
494da23a TL |
399 | hFile = RX_CreateFile( |
400 | RX_FN(fname).c_str(), | |
401 | desired_access, // Access desired | |
402 | shared_mode, | |
1e59de90 | 403 | NULL, // Security attributes |
494da23a TL |
404 | // Posix env says (reopen) ? (O_CREATE | O_APPEND) : O_CREAT | O_TRUNC |
405 | creation_disposition, | |
1e59de90 TL |
406 | fileFlags, // Flags |
407 | NULL); // Template File | |
7c673cae FG |
408 | } |
409 | ||
410 | if (INVALID_HANDLE_VALUE == hFile) { | |
411 | auto lastError = GetLastError(); | |
412 | return IOErrorFromWindowsError( | |
1e59de90 | 413 | "Failed to create a NewWritableFile: " + fname, lastError); |
7c673cae FG |
414 | } |
415 | ||
11fdf7f2 TL |
416 | // We will start writing at the end, appending |
417 | if (reopen) { | |
418 | LARGE_INTEGER zero_move; | |
419 | zero_move.QuadPart = 0; | |
420 | BOOL ret = SetFilePointerEx(hFile, zero_move, NULL, FILE_END); | |
421 | if (!ret) { | |
422 | auto lastError = GetLastError(); | |
423 | return IOErrorFromWindowsError( | |
494da23a TL |
424 | "Failed to create a ReopenWritableFile move to the end: " + fname, |
425 | lastError); | |
11fdf7f2 TL |
426 | } |
427 | } | |
428 | ||
7c673cae FG |
429 | if (options.use_mmap_writes) { |
430 | // We usually do not use mmmapping on SSD and thus we pass memory | |
431 | // page_size | |
432 | result->reset(new WinMmapFile(fname, hFile, page_size_, | |
494da23a | 433 | allocation_granularity_, local_options)); |
7c673cae FG |
434 | } else { |
435 | // Here we want the buffer allocation to be aligned by the SSD page size | |
436 | // and to be a multiple of it | |
1e59de90 | 437 | result->reset(new WinWritableFile(fname, hFile, GetPageSize(), |
494da23a | 438 | c_BufferCapacity, local_options)); |
7c673cae FG |
439 | } |
440 | return s; | |
441 | } | |
442 | ||
1e59de90 TL |
443 | IOStatus WinFileSystem::NewWritableFile(const std::string& fname, |
444 | const FileOptions& options, | |
445 | std::unique_ptr<FSWritableFile>* result, | |
446 | IODebugContext* /*dbg*/) { | |
447 | return OpenWritableFile(fname, options, result, false); | |
448 | } | |
7c673cae | 449 | |
1e59de90 TL |
450 | IOStatus WinFileSystem::ReopenWritableFile( |
451 | const std::string& fname, const FileOptions& options, | |
452 | std::unique_ptr<FSWritableFile>* result, IODebugContext* /*dbg*/) { | |
453 | return OpenWritableFile(fname, options, result, true); | |
454 | } | |
455 | ||
456 | IOStatus WinFileSystem::NewRandomRWFile(const std::string& fname, | |
457 | const FileOptions& options, | |
458 | std::unique_ptr<FSRandomRWFile>* result, | |
459 | IODebugContext* /*dbg*/) { | |
460 | IOStatus s; | |
7c673cae FG |
461 | |
462 | // Open the file for read-only random access | |
463 | // Random access is to disable read-ahead as the system reads too much data | |
464 | DWORD desired_access = GENERIC_READ | GENERIC_WRITE; | |
465 | DWORD shared_mode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; | |
1e59de90 | 466 | DWORD creation_disposition = OPEN_EXISTING; // Fail if file does not exist |
7c673cae FG |
467 | DWORD file_flags = FILE_FLAG_RANDOM_ACCESS; |
468 | ||
469 | if (options.use_direct_reads && options.use_direct_writes) { | |
470 | file_flags |= FILE_FLAG_NO_BUFFERING; | |
471 | } | |
472 | ||
473 | /// Shared access is necessary for corruption test to pass | |
474 | // almost all tests would work with a possible exception of fault_injection | |
475 | HANDLE hFile = 0; | |
476 | { | |
477 | IOSTATS_TIMER_GUARD(open_nanos); | |
1e59de90 TL |
478 | hFile = RX_CreateFile(RX_FN(fname).c_str(), desired_access, shared_mode, |
479 | NULL, // Security attributes | |
480 | creation_disposition, file_flags, NULL); | |
7c673cae FG |
481 | } |
482 | ||
483 | if (INVALID_HANDLE_VALUE == hFile) { | |
484 | auto lastError = GetLastError(); | |
485 | return IOErrorFromWindowsError( | |
1e59de90 | 486 | "NewRandomRWFile failed to Create/Open: " + fname, lastError); |
7c673cae FG |
487 | } |
488 | ||
489 | UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc); | |
1e59de90 | 490 | result->reset(new WinRandomRWFile(fname, hFile, GetPageSize(), options)); |
11fdf7f2 TL |
491 | fileGuard.release(); |
492 | ||
493 | return s; | |
494 | } | |
495 | ||
1e59de90 TL |
496 | IOStatus WinFileSystem::NewMemoryMappedFileBuffer( |
497 | const std::string& fname, std::unique_ptr<MemoryMappedFileBuffer>* result) { | |
498 | IOStatus s; | |
11fdf7f2 TL |
499 | result->reset(); |
500 | ||
501 | DWORD fileFlags = FILE_ATTRIBUTE_READONLY; | |
502 | ||
503 | HANDLE hFile = INVALID_HANDLE_VALUE; | |
504 | { | |
505 | IOSTATS_TIMER_GUARD(open_nanos); | |
494da23a TL |
506 | hFile = RX_CreateFile( |
507 | RX_FN(fname).c_str(), GENERIC_READ | GENERIC_WRITE, | |
1e59de90 | 508 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, |
494da23a | 509 | OPEN_EXISTING, // Open only if it exists |
1e59de90 | 510 | fileFlags, NULL); |
11fdf7f2 TL |
511 | } |
512 | ||
513 | if (INVALID_HANDLE_VALUE == hFile) { | |
514 | auto lastError = GetLastError(); | |
494da23a TL |
515 | s = IOErrorFromWindowsError( |
516 | "Failed to open NewMemoryMappedFileBuffer: " + fname, lastError); | |
11fdf7f2 TL |
517 | return s; |
518 | } | |
519 | UniqueCloseHandlePtr fileGuard(hFile, CloseHandleFunc); | |
520 | ||
521 | uint64_t fileSize = 0; | |
1e59de90 | 522 | s = GetFileSize(fname, IOOptions(), &fileSize, nullptr); |
11fdf7f2 TL |
523 | if (!s.ok()) { |
524 | return s; | |
525 | } | |
526 | // Will not map empty files | |
527 | if (fileSize == 0) { | |
1e59de90 | 528 | return IOStatus::NotSupported( |
494da23a | 529 | "NewMemoryMappedFileBuffer can not map zero length files: " + fname); |
11fdf7f2 TL |
530 | } |
531 | ||
532 | // size_t is 32-bit with 32-bit builds | |
533 | if (fileSize > std::numeric_limits<size_t>::max()) { | |
1e59de90 TL |
534 | return IOStatus::NotSupported( |
535 | "The specified file size does not fit into 32-bit memory addressing: " + | |
536 | fname); | |
11fdf7f2 TL |
537 | } |
538 | ||
494da23a TL |
539 | HANDLE hMap = RX_CreateFileMapping(hFile, NULL, PAGE_READWRITE, |
540 | 0, // Whole file at its present length | |
541 | 0, | |
542 | NULL); // Mapping name | |
11fdf7f2 TL |
543 | |
544 | if (!hMap) { | |
545 | auto lastError = GetLastError(); | |
546 | return IOErrorFromWindowsError( | |
494da23a | 547 | "Failed to create file mapping for: " + fname, lastError); |
11fdf7f2 TL |
548 | } |
549 | UniqueCloseHandlePtr mapGuard(hMap, CloseHandleFunc); | |
550 | ||
551 | void* base = MapViewOfFileEx(hMap, FILE_MAP_WRITE, | |
494da23a TL |
552 | 0, // High DWORD of access start |
553 | 0, // Low DWORD | |
554 | static_cast<SIZE_T>(fileSize), | |
555 | NULL); // Let the OS choose the mapping | |
11fdf7f2 TL |
556 | |
557 | if (!base) { | |
558 | auto lastError = GetLastError(); | |
559 | return IOErrorFromWindowsError( | |
494da23a TL |
560 | "Failed to MapViewOfFile for NewMemoryMappedFileBuffer: " + fname, |
561 | lastError); | |
11fdf7f2 TL |
562 | } |
563 | ||
494da23a TL |
564 | result->reset(new WinMemoryMappedBuffer(hFile, hMap, base, |
565 | static_cast<size_t>(fileSize))); | |
11fdf7f2 TL |
566 | |
567 | mapGuard.release(); | |
7c673cae FG |
568 | fileGuard.release(); |
569 | ||
570 | return s; | |
571 | } | |
572 | ||
1e59de90 TL |
573 | IOStatus WinFileSystem::NewDirectory(const std::string& name, |
574 | const IOOptions& /*options*/, | |
575 | std::unique_ptr<FSDirectory>* result, | |
576 | IODebugContext* /*dbg*/) { | |
577 | IOStatus s; | |
7c673cae FG |
578 | // Must be nullptr on failure |
579 | result->reset(); | |
11fdf7f2 | 580 | |
7c673cae | 581 | if (!DirExists(name)) { |
1e59de90 | 582 | s = IOErrorFromWindowsError("open folder: " + name, ERROR_DIRECTORY); |
11fdf7f2 TL |
583 | return s; |
584 | } | |
585 | ||
586 | HANDLE handle = INVALID_HANDLE_VALUE; | |
587 | // 0 - for access means read metadata | |
588 | { | |
7c673cae | 589 | IOSTATS_TIMER_GUARD(open_nanos); |
494da23a TL |
590 | handle = RX_CreateFile( |
591 | RX_FN(name).c_str(), 0, | |
1e59de90 | 592 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
494da23a | 593 | OPEN_EXISTING, |
1e59de90 | 594 | FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible |
494da23a | 595 | NULL); |
11fdf7f2 TL |
596 | } |
597 | ||
598 | if (INVALID_HANDLE_VALUE == handle) { | |
599 | auto lastError = GetLastError(); | |
494da23a | 600 | s = IOErrorFromWindowsError("open folder: " + name, lastError); |
11fdf7f2 | 601 | return s; |
7c673cae | 602 | } |
11fdf7f2 | 603 | |
1e59de90 | 604 | result->reset(new WinDirectory(name, handle)); |
11fdf7f2 | 605 | |
7c673cae FG |
606 | return s; |
607 | } | |
608 | ||
1e59de90 TL |
609 | IOStatus WinFileSystem::FileExists(const std::string& fname, |
610 | const IOOptions& /*opts*/, | |
611 | IODebugContext* /*dbg*/) { | |
612 | IOStatus s; | |
11fdf7f2 TL |
613 | // TODO: This does not follow symbolic links at this point |
614 | // which is consistent with _access() impl on windows | |
615 | // but can be added | |
616 | WIN32_FILE_ATTRIBUTE_DATA attrs; | |
494da23a TL |
617 | if (FALSE == RX_GetFileAttributesEx(RX_FN(fname).c_str(), |
618 | GetFileExInfoStandard, &attrs)) { | |
11fdf7f2 TL |
619 | auto lastError = GetLastError(); |
620 | switch (lastError) { | |
1e59de90 TL |
621 | case ERROR_ACCESS_DENIED: |
622 | case ERROR_NOT_FOUND: | |
623 | case ERROR_FILE_NOT_FOUND: | |
624 | case ERROR_PATH_NOT_FOUND: | |
625 | s = IOStatus::NotFound(); | |
626 | break; | |
627 | default: | |
628 | s = IOErrorFromWindowsError("Unexpected error for: " + fname, | |
629 | lastError); | |
630 | break; | |
11fdf7f2 TL |
631 | } |
632 | } | |
633 | return s; | |
7c673cae FG |
634 | } |
635 | ||
1e59de90 TL |
636 | IOStatus WinFileSystem::GetChildren(const std::string& dir, |
637 | const IOOptions& /*opts*/, | |
638 | std::vector<std::string>* result, | |
639 | IODebugContext* /*dbg*/) { | |
640 | IOStatus status; | |
7c673cae | 641 | result->clear(); |
7c673cae | 642 | |
494da23a TL |
643 | RX_WIN32_FIND_DATA data; |
644 | memset(&data, 0, sizeof(data)); | |
11fdf7f2 TL |
645 | std::string pattern(dir); |
646 | pattern.append("\\").append("*"); | |
7c673cae | 647 | |
1e59de90 TL |
648 | HANDLE handle = |
649 | RX_FindFirstFileEx(RX_FN(pattern).c_str(), | |
650 | // Do not want alternative name | |
651 | FindExInfoBasic, &data, FindExSearchNameMatch, | |
652 | NULL, // lpSearchFilter | |
653 | 0); | |
7c673cae | 654 | |
11fdf7f2 TL |
655 | if (handle == INVALID_HANDLE_VALUE) { |
656 | auto lastError = GetLastError(); | |
657 | switch (lastError) { | |
1e59de90 TL |
658 | case ERROR_NOT_FOUND: |
659 | case ERROR_ACCESS_DENIED: | |
660 | case ERROR_FILE_NOT_FOUND: | |
661 | case ERROR_PATH_NOT_FOUND: | |
662 | status = IOStatus::NotFound(); | |
663 | break; | |
664 | default: | |
665 | status = IOErrorFromWindowsError("Failed to GetChhildren for: " + dir, | |
666 | lastError); | |
7c673cae | 667 | } |
11fdf7f2 | 668 | return status; |
7c673cae FG |
669 | } |
670 | ||
11fdf7f2 TL |
671 | UniqueFindClosePtr fc(handle, FindCloseFunc); |
672 | ||
11fdf7f2 TL |
673 | // For safety |
674 | data.cFileName[MAX_PATH - 1] = 0; | |
675 | ||
676 | while (true) { | |
1e59de90 TL |
677 | // filter out '.' and '..' directory entries |
678 | // which appear only on some platforms | |
679 | const bool ignore = | |
680 | ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) && | |
681 | (RX_FNCMP(data.cFileName, ".") == 0 || | |
682 | RX_FNCMP(data.cFileName, "..") == 0); | |
683 | if (!ignore) { | |
684 | auto x = RX_FILESTRING(data.cFileName, RX_FNLEN(data.cFileName)); | |
685 | result->push_back(FN_TO_RX(x)); | |
686 | } | |
687 | ||
688 | BOOL ret = -RX_FindNextFile(handle, &data); | |
11fdf7f2 TL |
689 | // If the function fails the return value is zero |
690 | // and non-zero otherwise. Not TRUE or FALSE. | |
691 | if (ret == FALSE) { | |
692 | // Posix does not care why we stopped | |
693 | break; | |
694 | } | |
695 | data.cFileName[MAX_PATH - 1] = 0; | |
696 | } | |
7c673cae FG |
697 | return status; |
698 | } | |
699 | ||
1e59de90 TL |
700 | IOStatus WinFileSystem::CreateDir(const std::string& name, |
701 | const IOOptions& /*opts*/, | |
702 | IODebugContext* /*dbg*/) { | |
703 | IOStatus result; | |
494da23a | 704 | BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL); |
11fdf7f2 TL |
705 | if (!ret) { |
706 | auto lastError = GetLastError(); | |
1e59de90 TL |
707 | result = IOErrorFromWindowsError("Failed to create a directory: " + name, |
708 | lastError); | |
7c673cae FG |
709 | } |
710 | ||
711 | return result; | |
712 | } | |
713 | ||
1e59de90 TL |
714 | IOStatus WinFileSystem::CreateDirIfMissing(const std::string& name, |
715 | const IOOptions& /*opts*/, | |
716 | IODebugContext* /*dbg*/) { | |
717 | IOStatus result; | |
7c673cae FG |
718 | |
719 | if (DirExists(name)) { | |
720 | return result; | |
721 | } | |
722 | ||
494da23a | 723 | BOOL ret = RX_CreateDirectory(RX_FN(name).c_str(), NULL); |
11fdf7f2 TL |
724 | if (!ret) { |
725 | auto lastError = GetLastError(); | |
726 | if (lastError != ERROR_ALREADY_EXISTS) { | |
1e59de90 TL |
727 | result = IOErrorFromWindowsError("Failed to create a directory: " + name, |
728 | lastError); | |
7c673cae | 729 | } else { |
1e59de90 | 730 | result = IOStatus::IOError(name + ": exists but is not a directory"); |
7c673cae FG |
731 | } |
732 | } | |
7c673cae FG |
733 | return result; |
734 | } | |
735 | ||
1e59de90 TL |
736 | IOStatus WinFileSystem::DeleteDir(const std::string& name, |
737 | const IOOptions& /*options*/, | |
738 | IODebugContext* /*dbg*/) { | |
739 | IOStatus result; | |
494da23a | 740 | BOOL ret = RX_RemoveDirectory(RX_FN(name).c_str()); |
11fdf7f2 TL |
741 | if (!ret) { |
742 | auto lastError = GetLastError(); | |
1e59de90 TL |
743 | result = |
744 | IOErrorFromWindowsError("Failed to remove dir: " + name, lastError); | |
7c673cae FG |
745 | } |
746 | return result; | |
747 | } | |
748 | ||
1e59de90 TL |
749 | IOStatus WinFileSystem::GetFileSize(const std::string& fname, |
750 | const IOOptions& /*opts*/, uint64_t* size, | |
751 | IODebugContext* /*dbg*/) { | |
752 | IOStatus s; | |
7c673cae FG |
753 | |
754 | WIN32_FILE_ATTRIBUTE_DATA attrs; | |
494da23a TL |
755 | if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard, |
756 | &attrs)) { | |
7c673cae FG |
757 | ULARGE_INTEGER file_size; |
758 | file_size.HighPart = attrs.nFileSizeHigh; | |
759 | file_size.LowPart = attrs.nFileSizeLow; | |
760 | *size = file_size.QuadPart; | |
761 | } else { | |
762 | auto lastError = GetLastError(); | |
763 | s = IOErrorFromWindowsError("Can not get size for: " + fname, lastError); | |
764 | } | |
765 | return s; | |
766 | } | |
767 | ||
1e59de90 | 768 | uint64_t WinFileSystem::FileTimeToUnixTime(const FILETIME& ftTime) { |
7c673cae FG |
769 | const uint64_t c_FileTimePerSecond = 10000000U; |
770 | // UNIX epoch starts on 1970-01-01T00:00:00Z | |
771 | // Windows FILETIME starts on 1601-01-01T00:00:00Z | |
772 | // Therefore, we need to subtract the below number of seconds from | |
773 | // the seconds that we obtain from FILETIME with an obvious loss of | |
774 | // precision | |
775 | const uint64_t c_SecondBeforeUnixEpoch = 11644473600U; | |
776 | ||
777 | ULARGE_INTEGER li; | |
778 | li.HighPart = ftTime.dwHighDateTime; | |
779 | li.LowPart = ftTime.dwLowDateTime; | |
780 | ||
781 | uint64_t result = | |
1e59de90 | 782 | (li.QuadPart / c_FileTimePerSecond) - c_SecondBeforeUnixEpoch; |
7c673cae FG |
783 | return result; |
784 | } | |
785 | ||
1e59de90 TL |
786 | IOStatus WinFileSystem::GetFileModificationTime(const std::string& fname, |
787 | const IOOptions& /*opts*/, | |
788 | uint64_t* file_mtime, | |
789 | IODebugContext* /*dbg*/) { | |
790 | IOStatus s; | |
7c673cae FG |
791 | |
792 | WIN32_FILE_ATTRIBUTE_DATA attrs; | |
494da23a | 793 | if (RX_GetFileAttributesEx(RX_FN(fname).c_str(), GetFileExInfoStandard, |
1e59de90 | 794 | &attrs)) { |
7c673cae FG |
795 | *file_mtime = FileTimeToUnixTime(attrs.ftLastWriteTime); |
796 | } else { | |
797 | auto lastError = GetLastError(); | |
798 | s = IOErrorFromWindowsError( | |
1e59de90 | 799 | "Can not get file modification time for: " + fname, lastError); |
7c673cae FG |
800 | *file_mtime = 0; |
801 | } | |
802 | ||
803 | return s; | |
804 | } | |
805 | ||
1e59de90 TL |
806 | IOStatus WinFileSystem::RenameFile(const std::string& src, |
807 | const std::string& target, | |
808 | const IOOptions& /*opts*/, | |
809 | IODebugContext* /*dbg*/) { | |
810 | IOStatus result; | |
7c673cae FG |
811 | |
812 | // rename() is not capable of replacing the existing file as on Linux | |
813 | // so use OS API directly | |
494da23a TL |
814 | if (!RX_MoveFileEx(RX_FN(src).c_str(), RX_FN(target).c_str(), |
815 | MOVEFILE_REPLACE_EXISTING)) { | |
7c673cae FG |
816 | DWORD lastError = GetLastError(); |
817 | ||
818 | std::string text("Failed to rename: "); | |
819 | text.append(src).append(" to: ").append(target); | |
820 | ||
821 | result = IOErrorFromWindowsError(text, lastError); | |
822 | } | |
823 | ||
824 | return result; | |
825 | } | |
826 | ||
1e59de90 TL |
827 | IOStatus WinFileSystem::LinkFile(const std::string& src, |
828 | const std::string& target, | |
829 | const IOOptions& /*opts*/, | |
830 | IODebugContext* /*dbg*/) { | |
831 | IOStatus result; | |
7c673cae | 832 | |
1e59de90 | 833 | if (!RX_CreateHardLink(RX_FN(target).c_str(), RX_FN(src).c_str(), NULL)) { |
7c673cae | 834 | DWORD lastError = GetLastError(); |
11fdf7f2 | 835 | if (lastError == ERROR_NOT_SAME_DEVICE) { |
1e59de90 | 836 | return IOStatus::NotSupported("No cross FS links allowed"); |
11fdf7f2 | 837 | } |
7c673cae FG |
838 | |
839 | std::string text("Failed to link: "); | |
840 | text.append(src).append(" to: ").append(target); | |
841 | ||
842 | result = IOErrorFromWindowsError(text, lastError); | |
843 | } | |
844 | ||
845 | return result; | |
846 | } | |
847 | ||
1e59de90 TL |
848 | IOStatus WinFileSystem::NumFileLinks(const std::string& fname, |
849 | const IOOptions& /*opts*/, uint64_t* count, | |
850 | IODebugContext* /*dbg*/) { | |
851 | IOStatus s; | |
852 | HANDLE handle = | |
853 | RX_CreateFile(RX_FN(fname).c_str(), 0, | |
854 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, | |
855 | NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
11fdf7f2 TL |
856 | |
857 | if (INVALID_HANDLE_VALUE == handle) { | |
858 | auto lastError = GetLastError(); | |
859 | s = IOErrorFromWindowsError("NumFileLinks: " + fname, lastError); | |
860 | return s; | |
861 | } | |
862 | UniqueCloseHandlePtr handle_guard(handle, CloseHandleFunc); | |
863 | FILE_STANDARD_INFO standard_info; | |
864 | if (0 != GetFileInformationByHandleEx(handle, FileStandardInfo, | |
865 | &standard_info, | |
866 | sizeof(standard_info))) { | |
867 | *count = standard_info.NumberOfLinks; | |
868 | } else { | |
869 | auto lastError = GetLastError(); | |
870 | s = IOErrorFromWindowsError("GetFileInformationByHandleEx: " + fname, | |
871 | lastError); | |
872 | } | |
873 | return s; | |
874 | } | |
875 | ||
1e59de90 TL |
876 | IOStatus WinFileSystem::AreFilesSame(const std::string& first, |
877 | const std::string& second, | |
878 | const IOOptions& /*opts*/, bool* res, | |
879 | IODebugContext* /*dbg*/) { | |
11fdf7f2 TL |
880 | // For MinGW builds |
881 | #if (_WIN32_WINNT == _WIN32_WINNT_VISTA) | |
1e59de90 | 882 | IOStatus s = IOStatus::NotSupported(); |
11fdf7f2 TL |
883 | #else |
884 | assert(res != nullptr); | |
1e59de90 | 885 | IOStatus s; |
11fdf7f2 | 886 | if (res == nullptr) { |
1e59de90 | 887 | s = IOStatus::InvalidArgument("res"); |
11fdf7f2 TL |
888 | return s; |
889 | } | |
890 | ||
891 | // 0 - for access means read metadata | |
494da23a TL |
892 | HANDLE file_1 = RX_CreateFile( |
893 | RX_FN(first).c_str(), 0, | |
1e59de90 | 894 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
494da23a | 895 | OPEN_EXISTING, |
1e59de90 | 896 | FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible |
494da23a | 897 | NULL); |
11fdf7f2 TL |
898 | |
899 | if (INVALID_HANDLE_VALUE == file_1) { | |
900 | auto lastError = GetLastError(); | |
494da23a | 901 | s = IOErrorFromWindowsError("open file: " + first, lastError); |
11fdf7f2 TL |
902 | return s; |
903 | } | |
904 | UniqueCloseHandlePtr g_1(file_1, CloseHandleFunc); | |
905 | ||
494da23a TL |
906 | HANDLE file_2 = RX_CreateFile( |
907 | RX_FN(second).c_str(), 0, | |
1e59de90 TL |
908 | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
909 | OPEN_EXISTING, | |
910 | FILE_FLAG_BACKUP_SEMANTICS, // make opening folders possible | |
494da23a | 911 | NULL); |
11fdf7f2 TL |
912 | |
913 | if (INVALID_HANDLE_VALUE == file_2) { | |
914 | auto lastError = GetLastError(); | |
494da23a | 915 | s = IOErrorFromWindowsError("open file: " + second, lastError); |
11fdf7f2 TL |
916 | return s; |
917 | } | |
918 | UniqueCloseHandlePtr g_2(file_2, CloseHandleFunc); | |
919 | ||
920 | FILE_ID_INFO FileInfo_1; | |
921 | BOOL result = GetFileInformationByHandleEx(file_1, FileIdInfo, &FileInfo_1, | |
494da23a | 922 | sizeof(FileInfo_1)); |
11fdf7f2 TL |
923 | |
924 | if (!result) { | |
925 | auto lastError = GetLastError(); | |
494da23a | 926 | s = IOErrorFromWindowsError("stat file: " + first, lastError); |
11fdf7f2 TL |
927 | return s; |
928 | } | |
929 | ||
1e59de90 TL |
930 | FILE_ID_INFO FileInfo_2; |
931 | result = GetFileInformationByHandleEx(file_2, FileIdInfo, &FileInfo_2, | |
932 | sizeof(FileInfo_2)); | |
11fdf7f2 TL |
933 | |
934 | if (!result) { | |
935 | auto lastError = GetLastError(); | |
494da23a | 936 | s = IOErrorFromWindowsError("stat file: " + second, lastError); |
11fdf7f2 TL |
937 | return s; |
938 | } | |
939 | ||
940 | if (FileInfo_1.VolumeSerialNumber == FileInfo_2.VolumeSerialNumber) { | |
1e59de90 TL |
941 | *res = |
942 | (0 == memcmp(FileInfo_1.FileId.Identifier, FileInfo_2.FileId.Identifier, | |
943 | sizeof(FileInfo_1.FileId.Identifier))); | |
11fdf7f2 TL |
944 | } else { |
945 | *res = false; | |
946 | } | |
947 | #endif | |
948 | return s; | |
949 | } | |
950 | ||
1e59de90 TL |
951 | IOStatus WinFileSystem::LockFile(const std::string& lockFname, |
952 | const IOOptions& /*opts*/, FileLock** lock, | |
953 | IODebugContext* /*dbg*/) { | |
7c673cae FG |
954 | assert(lock != nullptr); |
955 | ||
956 | *lock = NULL; | |
1e59de90 | 957 | IOStatus result; |
7c673cae FG |
958 | |
959 | // No-sharing, this is a LOCK file | |
960 | const DWORD ExclusiveAccessON = 0; | |
961 | ||
962 | // Obtain exclusive access to the LOCK file | |
963 | // Previously, instead of NORMAL attr we set DELETE on close and that worked | |
964 | // well except with fault_injection test that insists on deleting it. | |
965 | HANDLE hFile = 0; | |
966 | { | |
967 | IOSTATS_TIMER_GUARD(open_nanos); | |
494da23a | 968 | hFile = RX_CreateFile(RX_FN(lockFname).c_str(), |
1e59de90 TL |
969 | (GENERIC_READ | GENERIC_WRITE), ExclusiveAccessON, |
970 | NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | |
7c673cae FG |
971 | } |
972 | ||
973 | if (INVALID_HANDLE_VALUE == hFile) { | |
974 | auto lastError = GetLastError(); | |
1e59de90 TL |
975 | result = IOErrorFromWindowsError("Failed to create lock file: " + lockFname, |
976 | lastError); | |
7c673cae FG |
977 | } else { |
978 | *lock = new WinFileLock(hFile); | |
979 | } | |
980 | ||
981 | return result; | |
982 | } | |
983 | ||
1e59de90 TL |
984 | IOStatus WinFileSystem::UnlockFile(FileLock* lock, const IOOptions& /*opts*/, |
985 | IODebugContext* /*dbg*/) { | |
986 | IOStatus result; | |
7c673cae FG |
987 | |
988 | assert(lock != nullptr); | |
989 | ||
990 | delete lock; | |
991 | ||
992 | return result; | |
993 | } | |
994 | ||
1e59de90 TL |
995 | IOStatus WinFileSystem::GetTestDirectory(const IOOptions& opts, |
996 | std::string* result, | |
997 | IODebugContext* dbg) { | |
7c673cae FG |
998 | std::string output; |
999 | ||
1000 | const char* env = getenv("TEST_TMPDIR"); | |
1001 | if (env && env[0] != '\0') { | |
1002 | output = env; | |
7c673cae FG |
1003 | } else { |
1004 | env = getenv("TMP"); | |
1005 | ||
1006 | if (env && env[0] != '\0') { | |
1007 | output = env; | |
1008 | } else { | |
1009 | output = "c:\\tmp"; | |
1010 | } | |
7c673cae | 1011 | } |
1e59de90 | 1012 | CreateDir(output, opts, dbg); |
7c673cae FG |
1013 | |
1014 | output.append("\\testrocksdb-"); | |
20effc67 | 1015 | output.append(std::to_string(GetCurrentProcessId())); |
7c673cae | 1016 | |
1e59de90 | 1017 | CreateDir(output, opts, dbg); |
7c673cae FG |
1018 | |
1019 | output.swap(*result); | |
1020 | ||
1e59de90 | 1021 | return IOStatus::OK(); |
7c673cae FG |
1022 | } |
1023 | ||
1e59de90 TL |
1024 | IOStatus WinFileSystem::NewLogger(const std::string& fname, |
1025 | const IOOptions& /*opts*/, | |
1026 | std::shared_ptr<Logger>* result, | |
1027 | IODebugContext* /*dbg*/) { | |
1028 | IOStatus s; | |
7c673cae FG |
1029 | |
1030 | result->reset(); | |
1031 | ||
1032 | HANDLE hFile = 0; | |
1033 | { | |
1034 | IOSTATS_TIMER_GUARD(open_nanos); | |
494da23a TL |
1035 | hFile = RX_CreateFile( |
1036 | RX_FN(fname).c_str(), GENERIC_WRITE, | |
1037 | FILE_SHARE_READ | FILE_SHARE_DELETE, // In RocksDb log files are | |
1038 | // renamed and deleted before | |
1039 | // they are closed. This enables | |
1040 | // doing so. | |
1041 | NULL, | |
1042 | CREATE_ALWAYS, // Original fopen mode is "w" | |
1043 | FILE_ATTRIBUTE_NORMAL, NULL); | |
7c673cae FG |
1044 | } |
1045 | ||
1046 | if (INVALID_HANDLE_VALUE == hFile) { | |
1047 | auto lastError = GetLastError(); | |
1048 | s = IOErrorFromWindowsError("Failed to open LogFile" + fname, lastError); | |
1049 | } else { | |
1050 | { | |
1051 | // With log files we want to set the true creation time as of now | |
1052 | // because the system | |
1053 | // for some reason caches the attributes of the previous file that just | |
1054 | // been renamed from | |
1055 | // this name so auto_roll_logger_test fails | |
1056 | FILETIME ft; | |
1057 | GetSystemTimeAsFileTime(&ft); | |
1058 | // Set creation, last access and last write time to the same value | |
1059 | SetFileTime(hFile, &ft, &ft, &ft); | |
1060 | } | |
1e59de90 | 1061 | result->reset(new WinLogger(&WinEnvThreads::gettid, clock_.get(), hFile)); |
7c673cae FG |
1062 | } |
1063 | return s; | |
1064 | } | |
1065 | ||
1e59de90 TL |
1066 | IOStatus WinFileSystem::IsDirectory(const std::string& path, |
1067 | const IOOptions& /*opts*/, bool* is_dir, | |
1068 | IODebugContext* /*dbg*/) { | |
20effc67 TL |
1069 | BOOL ret = RX_PathIsDirectory(RX_FN(path).c_str()); |
1070 | if (is_dir) { | |
1071 | *is_dir = ret ? true : false; | |
1072 | } | |
1e59de90 | 1073 | return IOStatus::OK(); |
7c673cae FG |
1074 | } |
1075 | ||
1076 | Status WinEnvIO::GetHostName(char* name, uint64_t len) { | |
1077 | Status s; | |
1078 | DWORD nSize = static_cast<DWORD>( | |
1e59de90 | 1079 | std::min<uint64_t>(len, std::numeric_limits<DWORD>::max())); |
7c673cae FG |
1080 | |
1081 | if (!::GetComputerNameA(name, &nSize)) { | |
1082 | auto lastError = GetLastError(); | |
1083 | s = IOErrorFromWindowsError("GetHostName", lastError); | |
1084 | } else { | |
1085 | name[nSize] = 0; | |
1086 | } | |
1087 | ||
1088 | return s; | |
1089 | } | |
1090 | ||
1e59de90 TL |
1091 | IOStatus WinFileSystem::GetAbsolutePath(const std::string& db_path, |
1092 | const IOOptions& /*options*/, | |
1093 | std::string* output_path, | |
1094 | IODebugContext* dbg) { | |
7c673cae | 1095 | // Check if we already have an absolute path |
11fdf7f2 TL |
1096 | // For test compatibility we will consider starting slash as an |
1097 | // absolute path | |
1098 | if ((!db_path.empty() && (db_path[0] == '\\' || db_path[0] == '/')) || | |
1e59de90 | 1099 | !RX_PathIsRelative(RX_FN(db_path).c_str())) { |
7c673cae | 1100 | *output_path = db_path; |
1e59de90 | 1101 | return IOStatus::OK(); |
7c673cae FG |
1102 | } |
1103 | ||
494da23a | 1104 | RX_FILESTRING result; |
11fdf7f2 | 1105 | result.resize(MAX_PATH); |
7c673cae | 1106 | |
11fdf7f2 TL |
1107 | // Hopefully no changes the current directory while we do this |
1108 | // however _getcwd also suffers from the same limitation | |
494da23a | 1109 | DWORD len = RX_GetCurrentDirectory(MAX_PATH, &result[0]); |
11fdf7f2 TL |
1110 | if (len == 0) { |
1111 | auto lastError = GetLastError(); | |
1112 | return IOErrorFromWindowsError("Failed to get current working directory", | |
494da23a | 1113 | lastError); |
7c673cae FG |
1114 | } |
1115 | ||
11fdf7f2 | 1116 | result.resize(len); |
494da23a | 1117 | std::string res = FN_TO_RX(result); |
7c673cae | 1118 | |
494da23a | 1119 | res.swap(*output_path); |
1e59de90 | 1120 | return IOStatus::OK(); |
7c673cae FG |
1121 | } |
1122 | ||
1e59de90 TL |
1123 | IOStatus WinFileSystem::GetFreeSpace(const std::string& path, |
1124 | const IOOptions& /*options*/, | |
1125 | uint64_t* diskfree, | |
1126 | IODebugContext* /*dbg*/) { | |
f67539c2 TL |
1127 | assert(diskfree != nullptr); |
1128 | ULARGE_INTEGER freeBytes; | |
1129 | BOOL f = RX_GetDiskFreeSpaceEx(RX_FN(path).c_str(), &freeBytes, NULL, NULL); | |
1130 | if (f) { | |
1131 | *diskfree = freeBytes.QuadPart; | |
1e59de90 | 1132 | return IOStatus::OK(); |
f67539c2 TL |
1133 | } else { |
1134 | DWORD lastError = GetLastError(); | |
1135 | return IOErrorFromWindowsError("Failed to get free space: " + path, | |
1136 | lastError); | |
1137 | } | |
1138 | } | |
1139 | ||
1e59de90 TL |
1140 | FileOptions WinFileSystem::OptimizeForLogWrite( |
1141 | const FileOptions& file_options, const DBOptions& db_options) const { | |
1142 | FileOptions optimized(file_options); | |
11fdf7f2 | 1143 | // These two the same as default optimizations |
7c673cae | 1144 | optimized.bytes_per_sync = db_options.wal_bytes_per_sync; |
11fdf7f2 TL |
1145 | optimized.writable_file_max_buffer_size = |
1146 | db_options.writable_file_max_buffer_size; | |
1147 | ||
1148 | // This adversely affects %999 on windows | |
7c673cae | 1149 | optimized.use_mmap_writes = false; |
11fdf7f2 TL |
1150 | // Direct writes will produce a huge perf impact on |
1151 | // Windows. Pre-allocate space for WAL. | |
7c673cae | 1152 | optimized.use_direct_writes = false; |
7c673cae FG |
1153 | return optimized; |
1154 | } | |
1155 | ||
1e59de90 TL |
1156 | FileOptions WinFileSystem::OptimizeForManifestWrite( |
1157 | const FileOptions& options) const { | |
1158 | FileOptions optimized(options); | |
7c673cae | 1159 | optimized.use_mmap_writes = false; |
11fdf7f2 TL |
1160 | optimized.use_direct_reads = false; |
1161 | return optimized; | |
1162 | } | |
1163 | ||
1e59de90 TL |
1164 | FileOptions WinFileSystem::OptimizeForManifestRead( |
1165 | const FileOptions& file_options) const { | |
1166 | FileOptions optimized(file_options); | |
11fdf7f2 TL |
1167 | optimized.use_mmap_writes = false; |
1168 | optimized.use_direct_reads = false; | |
7c673cae FG |
1169 | return optimized; |
1170 | } | |
1171 | ||
1172 | // Returns true iff the named directory exists and is a directory. | |
1e59de90 | 1173 | bool WinFileSystem::DirExists(const std::string& dname) { |
7c673cae | 1174 | WIN32_FILE_ATTRIBUTE_DATA attrs; |
1e59de90 TL |
1175 | if (RX_GetFileAttributesEx(RX_FN(dname).c_str(), GetFileExInfoStandard, |
1176 | &attrs)) { | |
7c673cae FG |
1177 | return 0 != (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); |
1178 | } | |
1179 | return false; | |
1180 | } | |
1181 | ||
1e59de90 | 1182 | size_t WinFileSystem::GetSectorSize(const std::string& fname) { |
11fdf7f2 TL |
1183 | size_t sector_size = kSectorSize; |
1184 | ||
11fdf7f2 TL |
1185 | // obtain device handle |
1186 | char devicename[7] = "\\\\.\\"; | |
1e59de90 TL |
1187 | int erresult = 0; |
1188 | if (RX_PathIsRelative(RX_FN(fname).c_str())) { | |
1189 | RX_FILESTRING rx_current_dir; | |
1190 | rx_current_dir.resize(MAX_PATH); | |
1191 | DWORD len = RX_GetCurrentDirectory(MAX_PATH, &rx_current_dir[0]); | |
1192 | if (len == 0) { | |
1193 | return sector_size; | |
1194 | } | |
1195 | rx_current_dir.resize(len); | |
1196 | std::string current_dir = FN_TO_RX(rx_current_dir); | |
1197 | erresult = | |
1198 | strncat_s(devicename, sizeof(devicename), current_dir.c_str(), 2); | |
1199 | } else { | |
1200 | erresult = strncat_s(devicename, sizeof(devicename), fname.c_str(), 2); | |
1201 | } | |
11fdf7f2 TL |
1202 | |
1203 | if (erresult) { | |
1204 | assert(false); | |
1205 | return sector_size; | |
1206 | } | |
1207 | ||
494da23a TL |
1208 | HANDLE hDevice = CreateFile(devicename, 0, 0, nullptr, OPEN_EXISTING, |
1209 | FILE_ATTRIBUTE_NORMAL, nullptr); | |
11fdf7f2 TL |
1210 | |
1211 | if (hDevice == INVALID_HANDLE_VALUE) { | |
1212 | return sector_size; | |
1213 | } | |
1214 | ||
1215 | STORAGE_PROPERTY_QUERY spropertyquery; | |
1216 | spropertyquery.PropertyId = StorageAccessAlignmentProperty; | |
1217 | spropertyquery.QueryType = PropertyStandardQuery; | |
1218 | ||
1219 | BYTE output_buffer[sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR)]; | |
1220 | DWORD output_bytes = 0; | |
1221 | ||
1e59de90 TL |
1222 | BOOL ret = DeviceIoControl( |
1223 | hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &spropertyquery, | |
1224 | sizeof(spropertyquery), output_buffer, | |
1225 | sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR), &output_bytes, nullptr); | |
11fdf7f2 TL |
1226 | |
1227 | if (ret) { | |
1e59de90 TL |
1228 | sector_size = ((STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR*)output_buffer) |
1229 | ->BytesPerLogicalSector; | |
11fdf7f2 | 1230 | } else { |
1e59de90 TL |
1231 | // many devices do not support StorageProcessAlignmentProperty. Any failure |
1232 | // here and we fall back to logical alignment | |
11fdf7f2 | 1233 | |
1e59de90 TL |
1234 | DISK_GEOMETRY_EX geometry = {0}; |
1235 | ret = DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0, | |
1236 | &geometry, sizeof(geometry), &output_bytes, nullptr); | |
11fdf7f2 TL |
1237 | if (ret) { |
1238 | sector_size = geometry.Geometry.BytesPerSector; | |
1239 | } | |
1240 | } | |
1241 | ||
1242 | if (hDevice != INVALID_HANDLE_VALUE) { | |
1243 | CloseHandle(hDevice); | |
1244 | } | |
1245 | ||
1246 | return sector_size; | |
1247 | } | |
1248 | ||
7c673cae FG |
1249 | //////////////////////////////////////////////////////////////////////// |
1250 | // WinEnvThreads | |
1251 | ||
494da23a TL |
1252 | WinEnvThreads::WinEnvThreads(Env* hosted_env) |
1253 | : hosted_env_(hosted_env), thread_pools_(Env::Priority::TOTAL) { | |
7c673cae FG |
1254 | for (int pool_id = 0; pool_id < Env::Priority::TOTAL; ++pool_id) { |
1255 | thread_pools_[pool_id].SetThreadPriority( | |
1e59de90 | 1256 | static_cast<Env::Priority>(pool_id)); |
7c673cae FG |
1257 | // This allows later initializing the thread-local-env of each thread. |
1258 | thread_pools_[pool_id].SetHostEnv(hosted_env); | |
1259 | } | |
1260 | } | |
1261 | ||
1262 | WinEnvThreads::~WinEnvThreads() { | |
7c673cae FG |
1263 | WaitForJoin(); |
1264 | ||
1265 | for (auto& thpool : thread_pools_) { | |
1266 | thpool.JoinAllThreads(); | |
1267 | } | |
1268 | } | |
1269 | ||
1e59de90 | 1270 | void WinEnvThreads::Schedule(void (*function)(void*), void* arg, |
494da23a | 1271 | Env::Priority pri, void* tag, |
1e59de90 | 1272 | void (*unschedFunction)(void* arg)) { |
11fdf7f2 | 1273 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
7c673cae FG |
1274 | thread_pools_[pri].Schedule(function, arg, tag, unschedFunction); |
1275 | } | |
1276 | ||
1277 | int WinEnvThreads::UnSchedule(void* arg, Env::Priority pri) { | |
1278 | return thread_pools_[pri].UnSchedule(arg); | |
1279 | } | |
1280 | ||
1281 | namespace { | |
1282 | ||
1e59de90 TL |
1283 | struct StartThreadState { |
1284 | void (*user_function)(void*); | |
1285 | void* arg; | |
1286 | }; | |
7c673cae | 1287 | |
1e59de90 TL |
1288 | void* StartThreadWrapper(void* arg) { |
1289 | std::unique_ptr<StartThreadState> state( | |
7c673cae | 1290 | reinterpret_cast<StartThreadState*>(arg)); |
1e59de90 TL |
1291 | state->user_function(state->arg); |
1292 | return nullptr; | |
7c673cae FG |
1293 | } |
1294 | ||
1e59de90 TL |
1295 | } // namespace |
1296 | ||
1297 | void WinEnvThreads::StartThread(void (*function)(void* arg), void* arg) { | |
7c673cae FG |
1298 | std::unique_ptr<StartThreadState> state(new StartThreadState); |
1299 | state->user_function = function; | |
1300 | state->arg = arg; | |
1301 | try { | |
1e59de90 | 1302 | Thread th(&StartThreadWrapper, state.get()); |
7c673cae FG |
1303 | state.release(); |
1304 | ||
1305 | std::lock_guard<std::mutex> lg(mu_); | |
1306 | threads_to_join_.push_back(std::move(th)); | |
1307 | ||
1308 | } catch (const std::system_error& ex) { | |
1309 | WinthreadCall("start thread", ex.code()); | |
1310 | } | |
1311 | } | |
1312 | ||
1313 | void WinEnvThreads::WaitForJoin() { | |
1314 | for (auto& th : threads_to_join_) { | |
1315 | th.join(); | |
1316 | } | |
1317 | threads_to_join_.clear(); | |
1318 | } | |
1319 | ||
1320 | unsigned int WinEnvThreads::GetThreadPoolQueueLen(Env::Priority pri) const { | |
11fdf7f2 | 1321 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
7c673cae FG |
1322 | return thread_pools_[pri].GetQueueLen(); |
1323 | } | |
1324 | ||
1e59de90 TL |
1325 | int WinEnvThreads::ReserveThreads(int threads_to_reserved, Env::Priority pri) { |
1326 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); | |
1327 | return thread_pools_[pri].ReserveThreads(threads_to_reserved); | |
1328 | } | |
1329 | ||
1330 | int WinEnvThreads::ReleaseThreads(int threads_to_released, Env::Priority pri) { | |
1331 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); | |
1332 | return thread_pools_[pri].ReleaseThreads(threads_to_released); | |
1333 | } | |
1334 | ||
7c673cae FG |
1335 | uint64_t WinEnvThreads::gettid() { |
1336 | uint64_t thread_id = GetCurrentThreadId(); | |
1337 | return thread_id; | |
1338 | } | |
1339 | ||
1340 | uint64_t WinEnvThreads::GetThreadID() const { return gettid(); } | |
1341 | ||
7c673cae | 1342 | void WinEnvThreads::SetBackgroundThreads(int num, Env::Priority pri) { |
11fdf7f2 | 1343 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
7c673cae FG |
1344 | thread_pools_[pri].SetBackgroundThreads(num); |
1345 | } | |
1346 | ||
11fdf7f2 TL |
1347 | int WinEnvThreads::GetBackgroundThreads(Env::Priority pri) { |
1348 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); | |
1349 | return thread_pools_[pri].GetBackgroundThreads(); | |
1350 | } | |
1351 | ||
7c673cae | 1352 | void WinEnvThreads::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) { |
11fdf7f2 | 1353 | assert(pri >= Env::Priority::BOTTOM && pri <= Env::Priority::HIGH); |
7c673cae FG |
1354 | thread_pools_[pri].IncBackgroundThreadsIfNeeded(num); |
1355 | } | |
1356 | ||
1357 | ///////////////////////////////////////////////////////////////////////// | |
1358 | // WinEnv | |
1359 | ||
1e59de90 TL |
1360 | WinEnv::WinEnv() |
1361 | : CompositeEnv(WinFileSystem::Default(), WinClock::Default()), | |
1362 | winenv_io_(this), | |
1363 | winenv_threads_(this) { | |
7c673cae FG |
1364 | // Protected member of the base class |
1365 | thread_status_updater_ = CreateThreadStatusUpdater(); | |
1366 | } | |
1367 | ||
7c673cae FG |
1368 | WinEnv::~WinEnv() { |
1369 | // All threads must be joined before the deletion of | |
1370 | // thread_status_updater_. | |
1371 | delete thread_status_updater_; | |
1372 | } | |
1373 | ||
494da23a | 1374 | Status WinEnv::GetThreadList(std::vector<ThreadStatus>* thread_list) { |
7c673cae FG |
1375 | assert(thread_status_updater_); |
1376 | return thread_status_updater_->GetThreadList(thread_list); | |
1377 | } | |
1378 | ||
7c673cae FG |
1379 | Status WinEnv::GetHostName(char* name, uint64_t len) { |
1380 | return winenv_io_.GetHostName(name, len); | |
1381 | } | |
1382 | ||
1e59de90 TL |
1383 | void WinEnv::Schedule(void (*function)(void*), void* arg, Env::Priority pri, |
1384 | void* tag, void (*unschedFunction)(void* arg)) { | |
7c673cae FG |
1385 | return winenv_threads_.Schedule(function, arg, pri, tag, unschedFunction); |
1386 | } | |
1387 | ||
1388 | int WinEnv::UnSchedule(void* arg, Env::Priority pri) { | |
1389 | return winenv_threads_.UnSchedule(arg, pri); | |
1390 | } | |
1391 | ||
1e59de90 | 1392 | void WinEnv::StartThread(void (*function)(void* arg), void* arg) { |
7c673cae FG |
1393 | return winenv_threads_.StartThread(function, arg); |
1394 | } | |
1395 | ||
1e59de90 | 1396 | void WinEnv::WaitForJoin() { return winenv_threads_.WaitForJoin(); } |
7c673cae | 1397 | |
1e59de90 | 1398 | unsigned int WinEnv::GetThreadPoolQueueLen(Env::Priority pri) const { |
7c673cae FG |
1399 | return winenv_threads_.GetThreadPoolQueueLen(pri); |
1400 | } | |
1e59de90 TL |
1401 | int WinEnv::ReserveThreads(int threads_to_reserved, Env::Priority pri) { |
1402 | return winenv_threads_.ReserveThreads(threads_to_reserved, pri); | |
7c673cae FG |
1403 | } |
1404 | ||
1e59de90 TL |
1405 | int WinEnv::ReleaseThreads(int threads_to_released, Env::Priority pri) { |
1406 | return winenv_threads_.ReleaseThreads(threads_to_released, pri); | |
f67539c2 TL |
1407 | } |
1408 | ||
1e59de90 | 1409 | uint64_t WinEnv::GetThreadID() const { return winenv_threads_.GetThreadID(); } |
7c673cae FG |
1410 | |
1411 | // Allow increasing the number of worker threads. | |
1e59de90 | 1412 | void WinEnv::SetBackgroundThreads(int num, Env::Priority pri) { |
7c673cae FG |
1413 | return winenv_threads_.SetBackgroundThreads(num, pri); |
1414 | } | |
1415 | ||
11fdf7f2 TL |
1416 | int WinEnv::GetBackgroundThreads(Env::Priority pri) { |
1417 | return winenv_threads_.GetBackgroundThreads(pri); | |
1418 | } | |
1419 | ||
1e59de90 | 1420 | void WinEnv::IncBackgroundThreadsIfNeeded(int num, Env::Priority pri) { |
7c673cae FG |
1421 | return winenv_threads_.IncBackgroundThreadsIfNeeded(num, pri); |
1422 | } | |
1423 | ||
7c673cae FG |
1424 | } // namespace port |
1425 | ||
1e59de90 TL |
1426 | std::shared_ptr<FileSystem> FileSystem::Default() { |
1427 | return port::WinFileSystem::Default(); | |
7c673cae FG |
1428 | } |
1429 | ||
1e59de90 TL |
1430 | const std::shared_ptr<SystemClock>& SystemClock::Default() { |
1431 | STATIC_AVOID_DESTRUCTION(std::shared_ptr<SystemClock>, clock) | |
1432 | (std::make_shared<port::WinClock>()); | |
1433 | return clock; | |
1434 | } | |
f67539c2 | 1435 | } // namespace ROCKSDB_NAMESPACE |
20effc67 TL |
1436 | |
1437 | #endif |