1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
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).
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.
10 #include "port/win/env_win.h"
11 #include "port/win/win_thread.h"
17 #include <process.h> // _getpid
18 #include <io.h> // _access
19 #include <direct.h> // _rmdir, _mkdir, _getcwd
20 #include <sys/types.h>
23 #include "rocksdb/env.h"
24 #include "rocksdb/slice.h"
26 #include "port/port.h"
27 #include "port/port_dirent.h"
28 #include "port/win/win_logger.h"
29 #include "port/win/io_win.h"
31 #include "monitoring/iostats_context_imp.h"
33 #include "monitoring/thread_status_updater.h"
34 #include "monitoring/thread_status_util.h"
36 #include <rpc.h> // for uuid generation
43 namespace ROCKSDB_NAMESPACE
{
45 ThreadStatusUpdater
* CreateThreadStatusUpdater() {
46 return new ThreadStatusUpdater();
51 // Sector size used when physical sector size cannot be obtained from device.
52 static const size_t kSectorSize
= 512;
54 // RAII helpers for HANDLEs
55 const auto CloseHandleFunc
= [](HANDLE h
) { ::CloseHandle(h
); };
56 typedef std::unique_ptr
<void, decltype(CloseHandleFunc
)> UniqueCloseHandlePtr
;
58 const auto FindCloseFunc
= [](HANDLE h
) { ::FindClose(h
); };
59 typedef std::unique_ptr
<void, decltype(FindCloseFunc
)> UniqueFindClosePtr
;
61 void WinthreadCall(const char* label
, std::error_code result
) {
62 if (0 != result
.value()) {
63 fprintf(stderr
, "pthread %s: %s\n", label
, strerror(result
.value()));
72 WinEnvIO::WinEnvIO(Env
* hosted_env
)
73 : hosted_env_(hosted_env
),
75 allocation_granularity_(page_size_
),
76 perf_counter_frequency_(0),
77 nano_seconds_per_period_(0),
78 GetSystemTimePreciseAsFileTime_(NULL
) {
81 GetSystemInfo(&sinfo
);
83 page_size_
= sinfo
.dwPageSize
;
84 allocation_granularity_
= sinfo
.dwAllocationGranularity
;
88 BOOL ret
__attribute__((__unused__
));
89 ret
= QueryPerformanceFrequency(&qpf
);
91 perf_counter_frequency_
= qpf
.QuadPart
;
93 if (std::nano::den
% perf_counter_frequency_
== 0) {
94 nano_seconds_per_period_
= std::nano::den
/ perf_counter_frequency_
;
98 HMODULE module
= GetModuleHandle("kernel32.dll");
100 GetSystemTimePreciseAsFileTime_
=
101 (FnGetSystemTimePreciseAsFileTime
)GetProcAddress(
102 module
, "GetSystemTimePreciseAsFileTime");
106 WinEnvIO::~WinEnvIO() {
109 Status
WinEnvIO::DeleteFile(const std::string
& fname
) {
112 BOOL ret
= RX_DeleteFile(RX_FN(fname
).c_str());
115 auto lastError
= GetLastError();
116 result
= IOErrorFromWindowsError("Failed to delete: " + fname
,
123 Status
WinEnvIO::Truncate(const std::string
& fname
, size_t size
) {
125 int result
= ROCKSDB_NAMESPACE::port::Truncate(fname
, size
);
127 s
= IOError("Failed to truncate: " + fname
, errno
);
132 Status
WinEnvIO::GetCurrentTime(int64_t* unix_time
) {
133 time_t time
= std::time(nullptr);
134 if (time
== (time_t)(-1)) {
135 return Status::NotSupported("Failed to get time");
142 Status
WinEnvIO::NewSequentialFile(const std::string
& fname
,
143 std::unique_ptr
<SequentialFile
>* result
,
144 const EnvOptions
& options
) {
149 // Corruption test needs to rename and delete files of these kind
150 // while they are still open with another handle. For that reason we
151 // allow share_write and delete(allows rename).
152 HANDLE hFile
= INVALID_HANDLE_VALUE
;
154 DWORD fileFlags
= FILE_ATTRIBUTE_READONLY
;
156 if (options
.use_direct_reads
&& !options
.use_mmap_reads
) {
157 fileFlags
|= FILE_FLAG_NO_BUFFERING
;
161 IOSTATS_TIMER_GUARD(open_nanos
);
162 hFile
= RX_CreateFile(
163 RX_FN(fname
).c_str(), GENERIC_READ
,
164 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
165 OPEN_EXISTING
, // Original fopen mode is "rb"
169 if (INVALID_HANDLE_VALUE
== hFile
) {
170 auto lastError
= GetLastError();
171 s
= IOErrorFromWindowsError("Failed to open NewSequentialFile" + fname
,
174 result
->reset(new WinSequentialFile(fname
, hFile
, options
));
179 Status
WinEnvIO::NewRandomAccessFile(const std::string
& fname
,
180 std::unique_ptr
<RandomAccessFile
>* result
,
181 const EnvOptions
& options
) {
185 // Open the file for read-only random access
186 // Random access is to disable read-ahead as the system reads too much data
187 DWORD fileFlags
= FILE_ATTRIBUTE_READONLY
;
189 if (options
.use_direct_reads
&& !options
.use_mmap_reads
) {
190 fileFlags
|= FILE_FLAG_NO_BUFFERING
;
192 fileFlags
|= FILE_FLAG_RANDOM_ACCESS
;
195 /// Shared access is necessary for corruption test to pass
196 // almost all tests would work with a possible exception of fault_injection
199 IOSTATS_TIMER_GUARD(open_nanos
);
200 hFile
= RX_CreateFile(
201 RX_FN(fname
).c_str(), GENERIC_READ
,
202 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
203 NULL
, OPEN_EXISTING
, fileFlags
, NULL
);
206 if (INVALID_HANDLE_VALUE
== hFile
) {
207 auto lastError
= GetLastError();
208 return IOErrorFromWindowsError(
209 "NewRandomAccessFile failed to Create/Open: " + fname
, lastError
);
212 UniqueCloseHandlePtr
fileGuard(hFile
, CloseHandleFunc
);
214 // CAUTION! This will map the entire file into the process address space
215 if (options
.use_mmap_reads
&& sizeof(void*) >= 8) {
216 // Use mmap when virtual address-space is plentiful.
219 s
= GetFileSize(fname
, &fileSize
);
222 // Will not map empty files
225 "NewRandomAccessFile failed to map empty file: " + fname
, EINVAL
);
228 HANDLE hMap
= RX_CreateFileMapping(hFile
, NULL
, PAGE_READONLY
,
229 0, // At its present length
231 NULL
); // Mapping name
234 auto lastError
= GetLastError();
235 return IOErrorFromWindowsError(
236 "Failed to create file mapping for NewRandomAccessFile: " + fname
,
240 UniqueCloseHandlePtr
mapGuard(hMap
, CloseHandleFunc
);
242 const void* mapped_region
=
243 MapViewOfFileEx(hMap
, FILE_MAP_READ
,
244 0, // High DWORD of access start
246 static_cast<SIZE_T
>(fileSize
),
247 NULL
); // Let the OS choose the mapping
249 if (!mapped_region
) {
250 auto lastError
= GetLastError();
251 return IOErrorFromWindowsError(
252 "Failed to MapViewOfFile for NewRandomAccessFile: " + fname
,
256 result
->reset(new WinMmapReadableFile(fname
, hFile
, hMap
, mapped_region
,
257 static_cast<size_t>(fileSize
)));
263 result
->reset(new WinRandomAccessFile(fname
, hFile
,
264 std::max(GetSectorSize(fname
),
272 Status
WinEnvIO::OpenWritableFile(const std::string
& fname
,
273 std::unique_ptr
<WritableFile
>* result
,
274 const EnvOptions
& options
,
277 const size_t c_BufferCapacity
= 64 * 1024;
279 EnvOptions
local_options(options
);
284 DWORD fileFlags
= FILE_ATTRIBUTE_NORMAL
;
286 if (local_options
.use_direct_writes
&& !local_options
.use_mmap_writes
) {
287 fileFlags
= FILE_FLAG_NO_BUFFERING
| FILE_FLAG_WRITE_THROUGH
;
290 // Desired access. We are want to write only here but if we want to memory
292 // the file then there is no write only mode so we have to create it
294 // However, MapViewOfFile specifies only Write only
295 DWORD desired_access
= GENERIC_WRITE
;
296 DWORD shared_mode
= FILE_SHARE_READ
;
298 if (local_options
.use_mmap_writes
) {
299 desired_access
|= GENERIC_READ
;
301 // Adding this solely for tests to pass (fault_injection_test,
302 // wal_manager_test).
303 shared_mode
|= (FILE_SHARE_WRITE
| FILE_SHARE_DELETE
);
306 // This will always truncate the file
307 DWORD creation_disposition
= CREATE_ALWAYS
;
309 creation_disposition
= OPEN_ALWAYS
;
314 IOSTATS_TIMER_GUARD(open_nanos
);
315 hFile
= RX_CreateFile(
316 RX_FN(fname
).c_str(),
317 desired_access
, // Access desired
319 NULL
, // Security attributes
320 // Posix env says (reopen) ? (O_CREATE | O_APPEND) : O_CREAT | O_TRUNC
321 creation_disposition
,
323 NULL
); // Template File
326 if (INVALID_HANDLE_VALUE
== hFile
) {
327 auto lastError
= GetLastError();
328 return IOErrorFromWindowsError(
329 "Failed to create a NewWriteableFile: " + fname
, lastError
);
332 // We will start writing at the end, appending
334 LARGE_INTEGER zero_move
;
335 zero_move
.QuadPart
= 0;
336 BOOL ret
= SetFilePointerEx(hFile
, zero_move
, NULL
, FILE_END
);
338 auto lastError
= GetLastError();
339 return IOErrorFromWindowsError(
340 "Failed to create a ReopenWritableFile move to the end: " + fname
,
345 if (options
.use_mmap_writes
) {
346 // We usually do not use mmmapping on SSD and thus we pass memory
348 result
->reset(new WinMmapFile(fname
, hFile
, page_size_
,
349 allocation_granularity_
, local_options
));
351 // Here we want the buffer allocation to be aligned by the SSD page size
352 // and to be a multiple of it
353 result
->reset(new WinWritableFile(fname
, hFile
,
354 std::max(GetSectorSize(fname
),
356 c_BufferCapacity
, local_options
));
361 Status
WinEnvIO::NewRandomRWFile(const std::string
& fname
,
362 std::unique_ptr
<RandomRWFile
>* result
,
363 const EnvOptions
& options
) {
367 // Open the file for read-only random access
368 // Random access is to disable read-ahead as the system reads too much data
369 DWORD desired_access
= GENERIC_READ
| GENERIC_WRITE
;
370 DWORD shared_mode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
;
371 DWORD creation_disposition
= OPEN_EXISTING
; // Fail if file does not exist
372 DWORD file_flags
= FILE_FLAG_RANDOM_ACCESS
;
374 if (options
.use_direct_reads
&& options
.use_direct_writes
) {
375 file_flags
|= FILE_FLAG_NO_BUFFERING
;
378 /// Shared access is necessary for corruption test to pass
379 // almost all tests would work with a possible exception of fault_injection
382 IOSTATS_TIMER_GUARD(open_nanos
);
384 RX_CreateFile(RX_FN(fname
).c_str(),
387 NULL
, // Security attributes
388 creation_disposition
,
393 if (INVALID_HANDLE_VALUE
== hFile
) {
394 auto lastError
= GetLastError();
395 return IOErrorFromWindowsError(
396 "NewRandomRWFile failed to Create/Open: " + fname
, lastError
);
399 UniqueCloseHandlePtr
fileGuard(hFile
, CloseHandleFunc
);
400 result
->reset(new WinRandomRWFile(fname
, hFile
,
401 std::max(GetSectorSize(fname
),
409 Status
WinEnvIO::NewMemoryMappedFileBuffer(
410 const std::string
& fname
,
411 std::unique_ptr
<MemoryMappedFileBuffer
>* result
) {
415 DWORD fileFlags
= FILE_ATTRIBUTE_READONLY
;
417 HANDLE hFile
= INVALID_HANDLE_VALUE
;
419 IOSTATS_TIMER_GUARD(open_nanos
);
420 hFile
= RX_CreateFile(
421 RX_FN(fname
).c_str(), GENERIC_READ
| GENERIC_WRITE
,
422 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
424 OPEN_EXISTING
, // Open only if it exists
429 if (INVALID_HANDLE_VALUE
== hFile
) {
430 auto lastError
= GetLastError();
431 s
= IOErrorFromWindowsError(
432 "Failed to open NewMemoryMappedFileBuffer: " + fname
, lastError
);
435 UniqueCloseHandlePtr
fileGuard(hFile
, CloseHandleFunc
);
437 uint64_t fileSize
= 0;
438 s
= GetFileSize(fname
, &fileSize
);
442 // Will not map empty files
444 return Status::NotSupported(
445 "NewMemoryMappedFileBuffer can not map zero length files: " + fname
);
448 // size_t is 32-bit with 32-bit builds
449 if (fileSize
> std::numeric_limits
<size_t>::max()) {
450 return Status::NotSupported(
451 "The specified file size does not fit into 32-bit memory addressing: "
455 HANDLE hMap
= RX_CreateFileMapping(hFile
, NULL
, PAGE_READWRITE
,
456 0, // Whole file at its present length
458 NULL
); // Mapping name
461 auto lastError
= GetLastError();
462 return IOErrorFromWindowsError(
463 "Failed to create file mapping for: " + fname
, lastError
);
465 UniqueCloseHandlePtr
mapGuard(hMap
, CloseHandleFunc
);
467 void* base
= MapViewOfFileEx(hMap
, FILE_MAP_WRITE
,
468 0, // High DWORD of access start
470 static_cast<SIZE_T
>(fileSize
),
471 NULL
); // Let the OS choose the mapping
474 auto lastError
= GetLastError();
475 return IOErrorFromWindowsError(
476 "Failed to MapViewOfFile for NewMemoryMappedFileBuffer: " + fname
,
480 result
->reset(new WinMemoryMappedBuffer(hFile
, hMap
, base
,
481 static_cast<size_t>(fileSize
)));
489 Status
WinEnvIO::NewDirectory(const std::string
& name
,
490 std::unique_ptr
<Directory
>* result
) {
492 // Must be nullptr on failure
495 if (!DirExists(name
)) {
496 s
= IOErrorFromWindowsError(
497 "open folder: " + name
, ERROR_DIRECTORY
);
501 HANDLE handle
= INVALID_HANDLE_VALUE
;
502 // 0 - for access means read metadata
504 IOSTATS_TIMER_GUARD(open_nanos
);
505 handle
= RX_CreateFile(
506 RX_FN(name
).c_str(), 0,
507 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
510 FILE_FLAG_BACKUP_SEMANTICS
, // make opening folders possible
514 if (INVALID_HANDLE_VALUE
== handle
) {
515 auto lastError
= GetLastError();
516 s
= IOErrorFromWindowsError("open folder: " + name
, lastError
);
520 result
->reset(new WinDirectory(handle
));
525 Status
WinEnvIO::FileExists(const std::string
& fname
) {
527 // TODO: This does not follow symbolic links at this point
528 // which is consistent with _access() impl on windows
530 WIN32_FILE_ATTRIBUTE_DATA attrs
;
531 if (FALSE
== RX_GetFileAttributesEx(RX_FN(fname
).c_str(),
532 GetFileExInfoStandard
, &attrs
)) {
533 auto lastError
= GetLastError();
535 case ERROR_ACCESS_DENIED
:
536 case ERROR_NOT_FOUND
:
537 case ERROR_FILE_NOT_FOUND
:
538 case ERROR_PATH_NOT_FOUND
:
539 s
= Status::NotFound();
542 s
= IOErrorFromWindowsError("Unexpected error for: " + fname
,
550 Status
WinEnvIO::GetChildren(const std::string
& dir
,
551 std::vector
<std::string
>* result
) {
555 std::vector
<std::string
> output
;
557 RX_WIN32_FIND_DATA data
;
558 memset(&data
, 0, sizeof(data
));
559 std::string
pattern(dir
);
560 pattern
.append("\\").append("*");
562 HANDLE handle
= RX_FindFirstFileEx(RX_FN(pattern
).c_str(),
563 // Do not want alternative name
566 FindExSearchNameMatch
,
567 NULL
, // lpSearchFilter
570 if (handle
== INVALID_HANDLE_VALUE
) {
571 auto lastError
= GetLastError();
573 case ERROR_NOT_FOUND
:
574 case ERROR_ACCESS_DENIED
:
575 case ERROR_FILE_NOT_FOUND
:
576 case ERROR_PATH_NOT_FOUND
:
577 status
= Status::NotFound();
580 status
= IOErrorFromWindowsError(
581 "Failed to GetChhildren for: " + dir
, lastError
);
586 UniqueFindClosePtr
fc(handle
, FindCloseFunc
);
588 if (result
->capacity() > 0) {
589 output
.reserve(result
->capacity());
593 data
.cFileName
[MAX_PATH
- 1] = 0;
596 auto x
= RX_FILESTRING(data
.cFileName
, RX_FNLEN(data
.cFileName
));
597 output
.emplace_back(FN_TO_RX(x
));
598 BOOL ret
=- RX_FindNextFile(handle
, &data
);
599 // If the function fails the return value is zero
600 // and non-zero otherwise. Not TRUE or FALSE.
602 // Posix does not care why we stopped
605 data
.cFileName
[MAX_PATH
- 1] = 0;
607 output
.swap(*result
);
611 Status
WinEnvIO::CreateDir(const std::string
& name
) {
613 BOOL ret
= RX_CreateDirectory(RX_FN(name
).c_str(), NULL
);
615 auto lastError
= GetLastError();
616 result
= IOErrorFromWindowsError(
617 "Failed to create a directory: " + name
, lastError
);
623 Status
WinEnvIO::CreateDirIfMissing(const std::string
& name
) {
626 if (DirExists(name
)) {
630 BOOL ret
= RX_CreateDirectory(RX_FN(name
).c_str(), NULL
);
632 auto lastError
= GetLastError();
633 if (lastError
!= ERROR_ALREADY_EXISTS
) {
634 result
= IOErrorFromWindowsError(
635 "Failed to create a directory: " + name
, lastError
);
638 Status::IOError(name
+ ": exists but is not a directory");
644 Status
WinEnvIO::DeleteDir(const std::string
& name
) {
646 BOOL ret
= RX_RemoveDirectory(RX_FN(name
).c_str());
648 auto lastError
= GetLastError();
649 result
= IOErrorFromWindowsError("Failed to remove dir: " + name
,
655 Status
WinEnvIO::GetFileSize(const std::string
& fname
,
659 WIN32_FILE_ATTRIBUTE_DATA attrs
;
660 if (RX_GetFileAttributesEx(RX_FN(fname
).c_str(), GetFileExInfoStandard
,
662 ULARGE_INTEGER file_size
;
663 file_size
.HighPart
= attrs
.nFileSizeHigh
;
664 file_size
.LowPart
= attrs
.nFileSizeLow
;
665 *size
= file_size
.QuadPart
;
667 auto lastError
= GetLastError();
668 s
= IOErrorFromWindowsError("Can not get size for: " + fname
, lastError
);
673 uint64_t WinEnvIO::FileTimeToUnixTime(const FILETIME
& ftTime
) {
674 const uint64_t c_FileTimePerSecond
= 10000000U;
675 // UNIX epoch starts on 1970-01-01T00:00:00Z
676 // Windows FILETIME starts on 1601-01-01T00:00:00Z
677 // Therefore, we need to subtract the below number of seconds from
678 // the seconds that we obtain from FILETIME with an obvious loss of
680 const uint64_t c_SecondBeforeUnixEpoch
= 11644473600U;
683 li
.HighPart
= ftTime
.dwHighDateTime
;
684 li
.LowPart
= ftTime
.dwLowDateTime
;
687 (li
.QuadPart
/ c_FileTimePerSecond
) - c_SecondBeforeUnixEpoch
;
691 Status
WinEnvIO::GetFileModificationTime(const std::string
& fname
,
692 uint64_t* file_mtime
) {
695 WIN32_FILE_ATTRIBUTE_DATA attrs
;
696 if (RX_GetFileAttributesEx(RX_FN(fname
).c_str(), GetFileExInfoStandard
,
698 *file_mtime
= FileTimeToUnixTime(attrs
.ftLastWriteTime
);
700 auto lastError
= GetLastError();
701 s
= IOErrorFromWindowsError(
702 "Can not get file modification time for: " + fname
, lastError
);
709 Status
WinEnvIO::RenameFile(const std::string
& src
,
710 const std::string
& target
) {
713 // rename() is not capable of replacing the existing file as on Linux
714 // so use OS API directly
715 if (!RX_MoveFileEx(RX_FN(src
).c_str(), RX_FN(target
).c_str(),
716 MOVEFILE_REPLACE_EXISTING
)) {
717 DWORD lastError
= GetLastError();
719 std::string
text("Failed to rename: ");
720 text
.append(src
).append(" to: ").append(target
);
722 result
= IOErrorFromWindowsError(text
, lastError
);
728 Status
WinEnvIO::LinkFile(const std::string
& src
,
729 const std::string
& target
) {
732 if (!RX_CreateHardLink(RX_FN(target
).c_str(), RX_FN(src
).c_str(), NULL
)) {
733 DWORD lastError
= GetLastError();
734 if (lastError
== ERROR_NOT_SAME_DEVICE
) {
735 return Status::NotSupported("No cross FS links allowed");
738 std::string
text("Failed to link: ");
739 text
.append(src
).append(" to: ").append(target
);
741 result
= IOErrorFromWindowsError(text
, lastError
);
747 Status
WinEnvIO::NumFileLinks(const std::string
& fname
, uint64_t* count
) {
749 HANDLE handle
= RX_CreateFile(
750 RX_FN(fname
).c_str(), 0,
751 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
752 NULL
, OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
754 if (INVALID_HANDLE_VALUE
== handle
) {
755 auto lastError
= GetLastError();
756 s
= IOErrorFromWindowsError("NumFileLinks: " + fname
, lastError
);
759 UniqueCloseHandlePtr
handle_guard(handle
, CloseHandleFunc
);
760 FILE_STANDARD_INFO standard_info
;
761 if (0 != GetFileInformationByHandleEx(handle
, FileStandardInfo
,
763 sizeof(standard_info
))) {
764 *count
= standard_info
.NumberOfLinks
;
766 auto lastError
= GetLastError();
767 s
= IOErrorFromWindowsError("GetFileInformationByHandleEx: " + fname
,
773 Status
WinEnvIO::AreFilesSame(const std::string
& first
,
774 const std::string
& second
, bool* res
) {
776 #if (_WIN32_WINNT == _WIN32_WINNT_VISTA)
777 Status s
= Status::NotSupported();
779 assert(res
!= nullptr);
781 if (res
== nullptr) {
782 s
= Status::InvalidArgument("res");
786 // 0 - for access means read metadata
787 HANDLE file_1
= RX_CreateFile(
788 RX_FN(first
).c_str(), 0,
789 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
792 FILE_FLAG_BACKUP_SEMANTICS
, // make opening folders possible
795 if (INVALID_HANDLE_VALUE
== file_1
) {
796 auto lastError
= GetLastError();
797 s
= IOErrorFromWindowsError("open file: " + first
, lastError
);
800 UniqueCloseHandlePtr
g_1(file_1
, CloseHandleFunc
);
802 HANDLE file_2
= RX_CreateFile(
803 RX_FN(second
).c_str(), 0,
804 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
806 FILE_FLAG_BACKUP_SEMANTICS
, // make opening folders possible
809 if (INVALID_HANDLE_VALUE
== file_2
) {
810 auto lastError
= GetLastError();
811 s
= IOErrorFromWindowsError("open file: " + second
, lastError
);
814 UniqueCloseHandlePtr
g_2(file_2
, CloseHandleFunc
);
816 FILE_ID_INFO FileInfo_1
;
817 BOOL result
= GetFileInformationByHandleEx(file_1
, FileIdInfo
, &FileInfo_1
,
821 auto lastError
= GetLastError();
822 s
= IOErrorFromWindowsError("stat file: " + first
, lastError
);
826 FILE_ID_INFO FileInfo_2
;
827 result
= GetFileInformationByHandleEx(file_2
, FileIdInfo
, &FileInfo_2
,
831 auto lastError
= GetLastError();
832 s
= IOErrorFromWindowsError("stat file: " + second
, lastError
);
836 if (FileInfo_1
.VolumeSerialNumber
== FileInfo_2
.VolumeSerialNumber
) {
837 *res
= (0 == memcmp(FileInfo_1
.FileId
.Identifier
,
838 FileInfo_2
.FileId
.Identifier
,
839 sizeof(FileInfo_1
.FileId
.Identifier
)));
847 Status
WinEnvIO::LockFile(const std::string
& lockFname
,
849 assert(lock
!= nullptr);
854 // No-sharing, this is a LOCK file
855 const DWORD ExclusiveAccessON
= 0;
857 // Obtain exclusive access to the LOCK file
858 // Previously, instead of NORMAL attr we set DELETE on close and that worked
859 // well except with fault_injection test that insists on deleting it.
862 IOSTATS_TIMER_GUARD(open_nanos
);
863 hFile
= RX_CreateFile(RX_FN(lockFname
).c_str(),
864 (GENERIC_READ
| GENERIC_WRITE
),
865 ExclusiveAccessON
, NULL
, CREATE_ALWAYS
,
866 FILE_ATTRIBUTE_NORMAL
, NULL
);
869 if (INVALID_HANDLE_VALUE
== hFile
) {
870 auto lastError
= GetLastError();
871 result
= IOErrorFromWindowsError(
872 "Failed to create lock file: " + lockFname
, lastError
);
874 *lock
= new WinFileLock(hFile
);
880 Status
WinEnvIO::UnlockFile(FileLock
* lock
) {
883 assert(lock
!= nullptr);
890 Status
WinEnvIO::GetTestDirectory(std::string
* result
) {
894 const char* env
= getenv("TEST_TMPDIR");
895 if (env
&& env
[0] != '\0') {
900 if (env
&& env
[0] != '\0') {
908 output
.append("\\testrocksdb-");
909 output
.append(std::to_string(_getpid()));
913 output
.swap(*result
);
918 Status
WinEnvIO::NewLogger(const std::string
& fname
,
919 std::shared_ptr
<Logger
>* result
) {
926 IOSTATS_TIMER_GUARD(open_nanos
);
927 hFile
= RX_CreateFile(
928 RX_FN(fname
).c_str(), GENERIC_WRITE
,
929 FILE_SHARE_READ
| FILE_SHARE_DELETE
, // In RocksDb log files are
930 // renamed and deleted before
931 // they are closed. This enables
934 CREATE_ALWAYS
, // Original fopen mode is "w"
935 FILE_ATTRIBUTE_NORMAL
, NULL
);
938 if (INVALID_HANDLE_VALUE
== hFile
) {
939 auto lastError
= GetLastError();
940 s
= IOErrorFromWindowsError("Failed to open LogFile" + fname
, lastError
);
943 // With log files we want to set the true creation time as of now
944 // because the system
945 // for some reason caches the attributes of the previous file that just
947 // this name so auto_roll_logger_test fails
949 GetSystemTimeAsFileTime(&ft
);
950 // Set creation, last access and last write time to the same value
951 SetFileTime(hFile
, &ft
, &ft
, &ft
);
953 result
->reset(new WinLogger(&WinEnvThreads::gettid
, hosted_env_
, hFile
));
958 uint64_t WinEnvIO::NowMicros() {
960 if (GetSystemTimePreciseAsFileTime_
!= NULL
) {
961 // all std::chrono clocks on windows proved to return
962 // values that may repeat that is not good enough for some uses.
963 const int64_t c_UnixEpochStartTicks
= 116444736000000000LL;
964 const int64_t c_FtToMicroSec
= 10;
966 // This interface needs to return system time and not
967 // just any microseconds because it is often used as an argument
968 // to TimedWait() on condition variable
969 FILETIME ftSystemTime
;
970 GetSystemTimePreciseAsFileTime_(&ftSystemTime
);
973 li
.LowPart
= ftSystemTime
.dwLowDateTime
;
974 li
.HighPart
= ftSystemTime
.dwHighDateTime
;
975 // Subtract unix epoch start
976 li
.QuadPart
-= c_UnixEpochStartTicks
;
977 // Convert to microsecs
978 li
.QuadPart
/= c_FtToMicroSec
;
981 using namespace std::chrono
;
982 return duration_cast
<microseconds
>(system_clock::now().time_since_epoch())
986 uint64_t WinEnvIO::NowNanos() {
987 if (nano_seconds_per_period_
!= 0) {
988 // all std::chrono clocks on windows have the same resolution that is only
989 // good enough for microseconds but not nanoseconds
990 // On Windows 8 and Windows 2012 Server
991 // GetSystemTimePreciseAsFileTime(¤t_time) can be used
993 QueryPerformanceCounter(&li
);
994 // Convert performance counter to nanoseconds by precomputed ratio.
995 // Directly multiply nano::den with li.QuadPart causes overflow.
996 // Only do this when nano::den is divisible by perf_counter_frequency_,
997 // which most likely is the case in reality. If it's not, fall back to
998 // high_resolution_clock, which may be less precise under old compilers.
999 li
.QuadPart
*= nano_seconds_per_period_
;
1002 using namespace std::chrono
;
1003 return duration_cast
<nanoseconds
>(
1004 high_resolution_clock::now().time_since_epoch()).count();
1007 Status
WinEnvIO::GetHostName(char* name
, uint64_t len
) {
1009 DWORD nSize
= static_cast<DWORD
>(
1010 std::min
<uint64_t>(len
, std::numeric_limits
<DWORD
>::max()));
1012 if (!::GetComputerNameA(name
, &nSize
)) {
1013 auto lastError
= GetLastError();
1014 s
= IOErrorFromWindowsError("GetHostName", lastError
);
1022 Status
WinEnvIO::GetAbsolutePath(const std::string
& db_path
,
1023 std::string
* output_path
) {
1024 // Check if we already have an absolute path
1025 // For test compatibility we will consider starting slash as an
1027 if ((!db_path
.empty() && (db_path
[0] == '\\' || db_path
[0] == '/')) ||
1028 !RX_PathIsRelative(RX_FN(db_path
).c_str())) {
1029 *output_path
= db_path
;
1030 return Status::OK();
1033 RX_FILESTRING result
;
1034 result
.resize(MAX_PATH
);
1036 // Hopefully no changes the current directory while we do this
1037 // however _getcwd also suffers from the same limitation
1038 DWORD len
= RX_GetCurrentDirectory(MAX_PATH
, &result
[0]);
1040 auto lastError
= GetLastError();
1041 return IOErrorFromWindowsError("Failed to get current working directory",
1046 std::string res
= FN_TO_RX(result
);
1048 res
.swap(*output_path
);
1049 return Status::OK();
1052 std::string
WinEnvIO::TimeToString(uint64_t secondsSince1970
) {
1055 const time_t seconds
= secondsSince1970
;
1056 const int maxsize
= 64;
1059 errno_t ret
= localtime_s(&t
, &seconds
);
1062 result
= std::to_string(seconds
);
1064 result
.resize(maxsize
);
1065 char* p
= &result
[0];
1067 int len
= snprintf(p
, maxsize
, "%04d/%02d/%02d-%02d:%02d:%02d ",
1068 t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
,
1069 t
.tm_min
, t
.tm_sec
);
1078 Status
WinEnvIO::GetFreeSpace(const std::string
& path
, uint64_t* diskfree
) {
1079 assert(diskfree
!= nullptr);
1080 ULARGE_INTEGER freeBytes
;
1081 BOOL f
= RX_GetDiskFreeSpaceEx(RX_FN(path
).c_str(), &freeBytes
, NULL
, NULL
);
1083 *diskfree
= freeBytes
.QuadPart
;
1084 return Status::OK();
1086 DWORD lastError
= GetLastError();
1087 return IOErrorFromWindowsError("Failed to get free space: " + path
,
1092 EnvOptions
WinEnvIO::OptimizeForLogWrite(const EnvOptions
& env_options
,
1093 const DBOptions
& db_options
) const {
1094 EnvOptions
optimized(env_options
);
1095 // These two the same as default optimizations
1096 optimized
.bytes_per_sync
= db_options
.wal_bytes_per_sync
;
1097 optimized
.writable_file_max_buffer_size
=
1098 db_options
.writable_file_max_buffer_size
;
1100 // This adversely affects %999 on windows
1101 optimized
.use_mmap_writes
= false;
1102 // Direct writes will produce a huge perf impact on
1103 // Windows. Pre-allocate space for WAL.
1104 optimized
.use_direct_writes
= false;
1108 EnvOptions
WinEnvIO::OptimizeForManifestWrite(
1109 const EnvOptions
& env_options
) const {
1110 EnvOptions
optimized(env_options
);
1111 optimized
.use_mmap_writes
= false;
1112 optimized
.use_direct_reads
= false;
1116 EnvOptions
WinEnvIO::OptimizeForManifestRead(
1117 const EnvOptions
& env_options
) const {
1118 EnvOptions
optimized(env_options
);
1119 optimized
.use_mmap_writes
= false;
1120 optimized
.use_direct_reads
= false;
1124 // Returns true iff the named directory exists and is a directory.
1125 bool WinEnvIO::DirExists(const std::string
& dname
) {
1126 WIN32_FILE_ATTRIBUTE_DATA attrs
;
1127 if (RX_GetFileAttributesEx(RX_FN(dname
).c_str(),
1128 GetFileExInfoStandard
, &attrs
)) {
1129 return 0 != (attrs
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
);
1134 size_t WinEnvIO::GetSectorSize(const std::string
& fname
) {
1135 size_t sector_size
= kSectorSize
;
1137 if (RX_PathIsRelative(RX_FN(fname
).c_str())) {
1141 // obtain device handle
1142 char devicename
[7] = "\\\\.\\";
1143 int erresult
= strncat_s(devicename
, sizeof(devicename
), fname
.c_str(), 2);
1150 HANDLE hDevice
= CreateFile(devicename
, 0, 0, nullptr, OPEN_EXISTING
,
1151 FILE_ATTRIBUTE_NORMAL
, nullptr);
1153 if (hDevice
== INVALID_HANDLE_VALUE
) {
1157 STORAGE_PROPERTY_QUERY spropertyquery
;
1158 spropertyquery
.PropertyId
= StorageAccessAlignmentProperty
;
1159 spropertyquery
.QueryType
= PropertyStandardQuery
;
1161 BYTE output_buffer
[sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
)];
1162 DWORD output_bytes
= 0;
1164 BOOL ret
= DeviceIoControl(hDevice
, IOCTL_STORAGE_QUERY_PROPERTY
,
1165 &spropertyquery
, sizeof(spropertyquery
),
1167 sizeof(STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
),
1168 &output_bytes
, nullptr);
1171 sector_size
= ((STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR
*)output_buffer
)->BytesPerLogicalSector
;
1173 // many devices do not support StorageProcessAlignmentProperty. Any failure here and we
1174 // fall back to logical alignment
1176 DISK_GEOMETRY_EX geometry
= { 0 };
1177 ret
= DeviceIoControl(hDevice
, IOCTL_DISK_GET_DRIVE_GEOMETRY
,
1178 nullptr, 0, &geometry
, sizeof(geometry
), &output_bytes
, nullptr);
1180 sector_size
= geometry
.Geometry
.BytesPerSector
;
1184 if (hDevice
!= INVALID_HANDLE_VALUE
) {
1185 CloseHandle(hDevice
);
1191 ////////////////////////////////////////////////////////////////////////
1194 WinEnvThreads::WinEnvThreads(Env
* hosted_env
)
1195 : hosted_env_(hosted_env
), thread_pools_(Env::Priority::TOTAL
) {
1197 for (int pool_id
= 0; pool_id
< Env::Priority::TOTAL
; ++pool_id
) {
1198 thread_pools_
[pool_id
].SetThreadPriority(
1199 static_cast<Env::Priority
>(pool_id
));
1200 // This allows later initializing the thread-local-env of each thread.
1201 thread_pools_
[pool_id
].SetHostEnv(hosted_env
);
1205 WinEnvThreads::~WinEnvThreads() {
1209 for (auto& thpool
: thread_pools_
) {
1210 thpool
.JoinAllThreads();
1214 void WinEnvThreads::Schedule(void(*function
)(void*), void* arg
,
1215 Env::Priority pri
, void* tag
,
1216 void(*unschedFunction
)(void* arg
)) {
1217 assert(pri
>= Env::Priority::BOTTOM
&& pri
<= Env::Priority::HIGH
);
1218 thread_pools_
[pri
].Schedule(function
, arg
, tag
, unschedFunction
);
1221 int WinEnvThreads::UnSchedule(void* arg
, Env::Priority pri
) {
1222 return thread_pools_
[pri
].UnSchedule(arg
);
1227 struct StartThreadState
{
1228 void(*user_function
)(void*);
1232 void* StartThreadWrapper(void* arg
) {
1233 std::unique_ptr
<StartThreadState
> state(
1234 reinterpret_cast<StartThreadState
*>(arg
));
1235 state
->user_function(state
->arg
);
1241 void WinEnvThreads::StartThread(void(*function
)(void* arg
), void* arg
) {
1242 std::unique_ptr
<StartThreadState
> state(new StartThreadState
);
1243 state
->user_function
= function
;
1246 ROCKSDB_NAMESPACE::port::WindowsThread
th(&StartThreadWrapper
, state
.get());
1249 std::lock_guard
<std::mutex
> lg(mu_
);
1250 threads_to_join_
.push_back(std::move(th
));
1252 } catch (const std::system_error
& ex
) {
1253 WinthreadCall("start thread", ex
.code());
1257 void WinEnvThreads::WaitForJoin() {
1258 for (auto& th
: threads_to_join_
) {
1261 threads_to_join_
.clear();
1264 unsigned int WinEnvThreads::GetThreadPoolQueueLen(Env::Priority pri
) const {
1265 assert(pri
>= Env::Priority::BOTTOM
&& pri
<= Env::Priority::HIGH
);
1266 return thread_pools_
[pri
].GetQueueLen();
1269 uint64_t WinEnvThreads::gettid() {
1270 uint64_t thread_id
= GetCurrentThreadId();
1274 uint64_t WinEnvThreads::GetThreadID() const { return gettid(); }
1276 void WinEnvThreads::SleepForMicroseconds(int micros
) {
1277 std::this_thread::sleep_for(std::chrono::microseconds(micros
));
1280 void WinEnvThreads::SetBackgroundThreads(int num
, Env::Priority pri
) {
1281 assert(pri
>= Env::Priority::BOTTOM
&& pri
<= Env::Priority::HIGH
);
1282 thread_pools_
[pri
].SetBackgroundThreads(num
);
1285 int WinEnvThreads::GetBackgroundThreads(Env::Priority pri
) {
1286 assert(pri
>= Env::Priority::BOTTOM
&& pri
<= Env::Priority::HIGH
);
1287 return thread_pools_
[pri
].GetBackgroundThreads();
1290 void WinEnvThreads::IncBackgroundThreadsIfNeeded(int num
, Env::Priority pri
) {
1291 assert(pri
>= Env::Priority::BOTTOM
&& pri
<= Env::Priority::HIGH
);
1292 thread_pools_
[pri
].IncBackgroundThreadsIfNeeded(num
);
1295 /////////////////////////////////////////////////////////////////////////
1298 WinEnv::WinEnv() : winenv_io_(this), winenv_threads_(this) {
1299 // Protected member of the base class
1300 thread_status_updater_
= CreateThreadStatusUpdater();
1305 // All threads must be joined before the deletion of
1306 // thread_status_updater_.
1307 delete thread_status_updater_
;
1310 Status
WinEnv::GetThreadList(std::vector
<ThreadStatus
>* thread_list
) {
1311 assert(thread_status_updater_
);
1312 return thread_status_updater_
->GetThreadList(thread_list
);
1315 Status
WinEnv::DeleteFile(const std::string
& fname
) {
1316 return winenv_io_
.DeleteFile(fname
);
1319 Status
WinEnv::Truncate(const std::string
& fname
, size_t size
) {
1320 return winenv_io_
.Truncate(fname
, size
);
1323 Status
WinEnv::GetCurrentTime(int64_t* unix_time
) {
1324 return winenv_io_
.GetCurrentTime(unix_time
);
1327 Status
WinEnv::NewSequentialFile(const std::string
& fname
,
1328 std::unique_ptr
<SequentialFile
>* result
,
1329 const EnvOptions
& options
) {
1330 return winenv_io_
.NewSequentialFile(fname
, result
, options
);
1333 Status
WinEnv::NewRandomAccessFile(const std::string
& fname
,
1334 std::unique_ptr
<RandomAccessFile
>* result
,
1335 const EnvOptions
& options
) {
1336 return winenv_io_
.NewRandomAccessFile(fname
, result
, options
);
1339 Status
WinEnv::NewWritableFile(const std::string
& fname
,
1340 std::unique_ptr
<WritableFile
>* result
,
1341 const EnvOptions
& options
) {
1342 return winenv_io_
.OpenWritableFile(fname
, result
, options
, false);
1345 Status
WinEnv::ReopenWritableFile(const std::string
& fname
,
1346 std::unique_ptr
<WritableFile
>* result
,
1347 const EnvOptions
& options
) {
1348 return winenv_io_
.OpenWritableFile(fname
, result
, options
, true);
1351 Status
WinEnv::NewRandomRWFile(const std::string
& fname
,
1352 std::unique_ptr
<RandomRWFile
>* result
,
1353 const EnvOptions
& options
) {
1354 return winenv_io_
.NewRandomRWFile(fname
, result
, options
);
1357 Status
WinEnv::NewMemoryMappedFileBuffer(
1358 const std::string
& fname
,
1359 std::unique_ptr
<MemoryMappedFileBuffer
>* result
) {
1360 return winenv_io_
.NewMemoryMappedFileBuffer(fname
, result
);
1363 Status
WinEnv::NewDirectory(const std::string
& name
,
1364 std::unique_ptr
<Directory
>* result
) {
1365 return winenv_io_
.NewDirectory(name
, result
);
1368 Status
WinEnv::FileExists(const std::string
& fname
) {
1369 return winenv_io_
.FileExists(fname
);
1372 Status
WinEnv::GetChildren(const std::string
& dir
,
1373 std::vector
<std::string
>* result
) {
1374 return winenv_io_
.GetChildren(dir
, result
);
1377 Status
WinEnv::CreateDir(const std::string
& name
) {
1378 return winenv_io_
.CreateDir(name
);
1381 Status
WinEnv::CreateDirIfMissing(const std::string
& name
) {
1382 return winenv_io_
.CreateDirIfMissing(name
);
1385 Status
WinEnv::DeleteDir(const std::string
& name
) {
1386 return winenv_io_
.DeleteDir(name
);
1389 Status
WinEnv::GetFileSize(const std::string
& fname
,
1391 return winenv_io_
.GetFileSize(fname
, size
);
1394 Status
WinEnv::GetFileModificationTime(const std::string
& fname
,
1395 uint64_t* file_mtime
) {
1396 return winenv_io_
.GetFileModificationTime(fname
, file_mtime
);
1399 Status
WinEnv::RenameFile(const std::string
& src
,
1400 const std::string
& target
) {
1401 return winenv_io_
.RenameFile(src
, target
);
1404 Status
WinEnv::LinkFile(const std::string
& src
,
1405 const std::string
& target
) {
1406 return winenv_io_
.LinkFile(src
, target
);
1409 Status
WinEnv::NumFileLinks(const std::string
& fname
, uint64_t* count
) {
1410 return winenv_io_
.NumFileLinks(fname
, count
);
1413 Status
WinEnv::AreFilesSame(const std::string
& first
,
1414 const std::string
& second
, bool* res
) {
1415 return winenv_io_
.AreFilesSame(first
, second
, res
);
1418 Status
WinEnv::LockFile(const std::string
& lockFname
,
1420 return winenv_io_
.LockFile(lockFname
, lock
);
1423 Status
WinEnv::UnlockFile(FileLock
* lock
) {
1424 return winenv_io_
.UnlockFile(lock
);
1427 Status
WinEnv::GetTestDirectory(std::string
* result
) {
1428 return winenv_io_
.GetTestDirectory(result
);
1431 Status
WinEnv::NewLogger(const std::string
& fname
,
1432 std::shared_ptr
<Logger
>* result
) {
1433 return winenv_io_
.NewLogger(fname
, result
);
1436 uint64_t WinEnv::NowMicros() {
1437 return winenv_io_
.NowMicros();
1440 uint64_t WinEnv::NowNanos() {
1441 return winenv_io_
.NowNanos();
1444 Status
WinEnv::GetHostName(char* name
, uint64_t len
) {
1445 return winenv_io_
.GetHostName(name
, len
);
1448 Status
WinEnv::GetAbsolutePath(const std::string
& db_path
,
1449 std::string
* output_path
) {
1450 return winenv_io_
.GetAbsolutePath(db_path
, output_path
);
1453 std::string
WinEnv::TimeToString(uint64_t secondsSince1970
) {
1454 return winenv_io_
.TimeToString(secondsSince1970
);
1457 void WinEnv::Schedule(void(*function
)(void*), void* arg
, Env::Priority pri
,
1459 void(*unschedFunction
)(void* arg
)) {
1460 return winenv_threads_
.Schedule(function
, arg
, pri
, tag
, unschedFunction
);
1463 int WinEnv::UnSchedule(void* arg
, Env::Priority pri
) {
1464 return winenv_threads_
.UnSchedule(arg
, pri
);
1467 void WinEnv::StartThread(void(*function
)(void* arg
), void* arg
) {
1468 return winenv_threads_
.StartThread(function
, arg
);
1471 void WinEnv::WaitForJoin() {
1472 return winenv_threads_
.WaitForJoin();
1475 unsigned int WinEnv::GetThreadPoolQueueLen(Env::Priority pri
) const {
1476 return winenv_threads_
.GetThreadPoolQueueLen(pri
);
1479 uint64_t WinEnv::GetThreadID() const {
1480 return winenv_threads_
.GetThreadID();
1483 Status
WinEnv::GetFreeSpace(const std::string
& path
, uint64_t* diskfree
) {
1484 return winenv_io_
.GetFreeSpace(path
, diskfree
);
1487 void WinEnv::SleepForMicroseconds(int micros
) {
1488 return winenv_threads_
.SleepForMicroseconds(micros
);
1491 // Allow increasing the number of worker threads.
1492 void WinEnv::SetBackgroundThreads(int num
, Env::Priority pri
) {
1493 return winenv_threads_
.SetBackgroundThreads(num
, pri
);
1496 int WinEnv::GetBackgroundThreads(Env::Priority pri
) {
1497 return winenv_threads_
.GetBackgroundThreads(pri
);
1500 void WinEnv::IncBackgroundThreadsIfNeeded(int num
, Env::Priority pri
) {
1501 return winenv_threads_
.IncBackgroundThreadsIfNeeded(num
, pri
);
1504 EnvOptions
WinEnv::OptimizeForManifestRead(
1505 const EnvOptions
& env_options
) const {
1506 return winenv_io_
.OptimizeForManifestRead(env_options
);
1509 EnvOptions
WinEnv::OptimizeForLogWrite(const EnvOptions
& env_options
,
1510 const DBOptions
& db_options
) const {
1511 return winenv_io_
.OptimizeForLogWrite(env_options
, db_options
);
1514 EnvOptions
WinEnv::OptimizeForManifestWrite(
1515 const EnvOptions
& env_options
) const {
1516 return winenv_io_
.OptimizeForManifestWrite(env_options
);
1521 std::string
Env::GenerateUniqueId() {
1525 UuidCreateSequential(&uuid
);
1528 auto status
= UuidToStringA(&uuid
, &rpc_str
);
1530 assert(status
== RPC_S_OK
);
1532 result
= reinterpret_cast<char*>(rpc_str
);
1534 status
= RpcStringFreeA(&rpc_str
);
1535 assert(status
== RPC_S_OK
);
1540 } // namespace ROCKSDB_NAMESPACE