master
h 716 lines 17.6 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #pragma once
3
4 #include <unistd.h>
5 #include <dirent.h>
6 #include <sys/types.h>
7 #include <sstream>
8 #include <optional>
9 #include <assert.h>
10
11 namespace wil {
12
13 #define STRING_TO_WSTRING_IMPL(Str) L##Str
14 #define STRING_TO_WSTRING(Str) STRING_TO_WSTRING_IMPL(Str)
15 #define TEXT(X) X
16 #define FAIL_FAST() raise(SIGABRT);
17 #define FAIL_FAST_CAUGHT_EXCEPTION() FAIL_FAST()
18 #define FAIL_FAST_IF(condition) \
19 do \
20 { \
21 if ((condition)) \
22 { \
23 FAIL_FAST(); \
24 } \
25 } while ((void)0, 0)
26
27 typedef void LogFunction(const char* message, const char* exceptionDescription) noexcept;
28 __declspec(selectany) LogFunction* g_LogExceptionCallback{};
29
30 namespace details {
31 struct FailureInfo
32 {
33 const char* File;
34 int Line;
35 const char* Function;
36 };
37 } // namespace details
38
39 class ResultException : public std::exception
40 {
41 public:
42 ResultException(int result, details::FailureInfo info) noexcept : m_Result{result}, m_Info{info}
43 {
44 }
45
46 ~ResultException() noexcept
47 {
48 delete[] m_What;
49 }
50
51 const char* what() const noexcept override
52 {
53 constexpr size_t bufferSize = 4096;
54 if (m_What == nullptr)
55 {
56 m_What = new (std::nothrow) char[bufferSize]{};
57 if (m_What == nullptr)
58 {
59 return strerror(m_Result);
60 }
61
62 snprintf(m_What, bufferSize, "%s @%s:%d (%s)\n", strerror(m_Result), m_Info.File, m_Info.Line, m_Info.Function);
63 }
64
65 return m_What;
66 }
67
68 int GetErrorCode() const noexcept
69 {
70 return m_Result;
71 }
72
73 private:
74 mutable char* m_What{};
75 int m_Result;
76 details::FailureInfo m_Info;
77 };
78
79 class ExceptionWithUserMessage : public std::exception
80 {
81 public:
82 ExceptionWithUserMessage(std::string&& message) : m_message(std::move(message))
83 {
84 }
85
86 const char* what() const noexcept override
87 {
88 return m_message.c_str();
89 }
90
91 private:
92 std::string m_message;
93 };
94
95 namespace details {
96 inline void ThrowErrorIf(bool condition, int error, FailureInfo info)
97 {
98 if (condition)
99 {
100 throw ::wil::ResultException(error, info);
101 }
102 }
103
104 inline void LogFailure(const char* message, const char* exceptionDescription) noexcept
105 {
106 auto callback = g_LogExceptionCallback;
107 if (callback != nullptr)
108 {
109 callback(message, exceptionDescription);
110 }
111 else
112 {
113 if (message != nullptr)
114 {
115 fputs(message, stderr);
116 fputs("\n", stderr);
117 }
118
119 if (exceptionDescription != nullptr)
120 {
121 fputs("Exception: ", stderr);
122 fputs(exceptionDescription, stderr);
123 fputs("\n", stderr);
124 }
125 }
126 }
127
128 inline void LogCaughtException(const char* message)
129 {
130 try
131 {
132 throw;
133 }
134 catch (const std::exception& ex)
135 {
136 LogFailure(message, ex.what());
137 }
138 catch (...)
139 {
140 LogFailure(message, nullptr);
141 }
142 }
143
144 template <typename TLambda>
145 class lambda_call
146 {
147 public:
148 lambda_call(const lambda_call&) = delete;
149 lambda_call& operator=(const lambda_call&) = delete;
150 lambda_call& operator=(lambda_call&& other) = delete;
151
152 explicit lambda_call(TLambda&& lambda) noexcept : m_lambda(std::move(lambda))
153 {
154 static_assert(std::is_same<decltype(lambda()), void>::value, "scope_exit lambdas must not have a return value");
155 static_assert(
156 !std::is_lvalue_reference<TLambda>::value && !std::is_rvalue_reference<TLambda>::value,
157 "scope_exit should only be directly used with a lambda");
158 }
159
160 lambda_call(lambda_call&& other) noexcept : m_lambda(std::move(other.m_lambda)), m_call(other.m_call)
161 {
162 other.m_call = false;
163 }
164
165 ~lambda_call() noexcept
166 {
167 reset();
168 }
169
170 // Ensures the scope_exit lambda will not be called
171 void release() noexcept
172 {
173 m_call = false;
174 }
175
176 // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again
177 void reset() noexcept
178 {
179 if (m_call)
180 {
181 m_call = false;
182
183 try
184 {
185 m_lambda();
186 }
187 catch (...)
188 {
189 LogCaughtException("Exception thrown from a scope_exit lambda");
190 }
191 }
192 }
193
194 // Returns true if the scope_exit lambda is still going to be executed
195 explicit operator bool() const noexcept
196 {
197 return m_call;
198 }
199
200 protected:
201 TLambda m_lambda;
202 bool m_call = true;
203 };
204
205 } // namespace details
206
207 inline int ResultFromCaughtException()
208 {
209 try
210 {
211 throw;
212 }
213 catch (wil::ResultException& ex)
214 {
215 return ex.GetErrorCode();
216 }
217 catch (std::bad_alloc&)
218 {
219 return ENOMEM;
220 }
221 catch (...)
222 {
223 }
224
225 // Unknown exception type.
226 return EINVAL;
227 }
228
229 #define __WIL_ERROR_INFO {__FILE__, __LINE__, __FUNCTION__}
230
231 #define THROW_ERRNO(Error) throw ::wil::ResultException(Error, __WIL_ERROR_INFO)
232 #define THROW_USER_ERROR(Message) throw ::wil::ExceptionWithUserMessage((Message))
233 #define THROW_ERRNO_IF(Error, Condition) ::wil::details::ThrowErrorIf((Condition), (Error), __WIL_ERROR_INFO)
234 #define THROW_LAST_ERROR_IF(Condition) THROW_ERRNO_IF(errno, (Condition));
235 #define THROW_LAST_ERROR() THROW_ERRNO(errno);
236
237 #define THROW_INVALID() THROW_ERRNO(EINVAL)
238 #define THROW_UNEXPECTED() THROW_ERRNO(EINVAL)
239 #define THROW_INVALID_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))
240 #define THROW_UNEXPECTED_IF(Condition) THROW_ERRNO_IF(EINVAL, (Condition))
241
242 #define LOG_CAUGHT_EXCEPTION() ::wil::details::LogCaughtException(nullptr);
243 #define LOG_CAUGHT_EXCEPTION_MSG(msg) ::wil::details::LogCaughtException(msg);
244 #define RETURN_CAUGHT_EXCEPTION() return -::wil::ResultFromCaughtException()
245 #define CATCH_RETURN() \
246 catch (...) \
247 { \
248 RETURN_CAUGHT_EXCEPTION(); \
249 }
250 #define CATCH_RETURN_ERRNO() \
251 catch (...) \
252 { \
253 LOG_CAUGHT_EXCEPTION(); \
254 errno = ::wil::ResultFromCaughtException(); \
255 return -1; \
256 }
257
258 #define CATCH_LOG() \
259 catch (...) \
260 { \
261 LOG_CAUGHT_EXCEPTION(); \
262 }
263 #define CATCH_LOG_MSG(msg) \
264 catch (...) \
265 { \
266 LOG_CAUGHT_EXCEPTION_MSG(msg); \
267 }
268
269 class unique_dir
270 {
271 public:
272 static constexpr DIR* invalid_dir = nullptr;
273
274 unique_dir(DIR* dir = invalid_dir) noexcept : m_Dir{dir}
275 {
276 }
277
278 ~unique_dir() noexcept
279 {
280 reset();
281 }
282
283 unique_dir(const unique_dir&) = delete;
284 unique_dir& operator=(const unique_dir&) = delete;
285
286 unique_dir(unique_dir&& other) noexcept : m_Dir{other.m_Dir}
287 {
288 other.m_Dir = invalid_dir;
289 }
290
291 unique_dir& operator=(unique_dir&& other) noexcept
292 {
293 std::swap(m_Dir, other.m_Dir);
294 return *this;
295 }
296
297 explicit operator bool() const noexcept
298 {
299 return m_Dir != invalid_dir;
300 }
301
302 DIR* get() const noexcept
303 {
304 return m_Dir;
305 }
306
307 void reset(DIR* dir = invalid_dir) noexcept
308 {
309 if (m_Dir != invalid_dir)
310 {
311 closedir(m_Dir);
312 }
313
314 m_Dir = dir;
315 }
316
317 DIR* release() noexcept
318 {
319 DIR* dir = m_Dir;
320 m_Dir = invalid_dir;
321 return dir;
322 }
323
324 friend void swap(unique_dir& dir1, unique_dir& dir2)
325 {
326 std::swap(dir1.m_Dir, dir2.m_Dir);
327 }
328
329 private:
330 DIR* m_Dir;
331 };
332
333 class unique_fd
334 {
335 public:
336 static constexpr int invalid_fd = -1;
337
338 unique_fd(int fd = invalid_fd) noexcept : m_Fd{fd}
339 {
340 }
341
342 ~unique_fd() noexcept
343 {
344 reset();
345 }
346
347 unique_fd(const unique_fd&) = delete;
348 unique_fd& operator=(const unique_fd&) = delete;
349
350 unique_fd(unique_fd&& other) noexcept : m_Fd{other.m_Fd}
351 {
352 other.m_Fd = invalid_fd;
353 }
354
355 unique_fd& operator=(unique_fd&& other) noexcept
356 {
357 std::swap(m_Fd, other.m_Fd);
358 return *this;
359 }
360
361 explicit operator bool() const noexcept
362 {
363 return m_Fd >= 0;
364 }
365
366 int get() const noexcept
367 {
368 return m_Fd;
369 }
370
371 void reset(int fd = invalid_fd) noexcept
372 {
373 if (m_Fd >= 0)
374 {
375 close(m_Fd);
376 }
377
378 m_Fd = fd;
379 }
380
381 int release() noexcept
382 {
383 int fd = m_Fd;
384 m_Fd = invalid_fd;
385 return fd;
386 }
387
388 int* addressof() noexcept
389 {
390 return &m_Fd;
391 }
392
393 friend void swap(unique_fd& fd1, unique_fd& fd2)
394 {
395 std::swap(fd1.m_Fd, fd2.m_Fd);
396 }
397
398 private:
399 int m_Fd;
400 };
401
402 class unique_pipe
403 {
404 public:
405 unique_pipe() = default;
406
407 unique_pipe(unique_fd&& readFd, unique_fd&& writeFd) noexcept : m_Read(std::move(readFd)), m_Write(std::move(writeFd))
408 {
409 }
410
411 unique_pipe(const unique_pipe&) = delete;
412 unique_pipe& operator=(const unique_pipe&) = delete;
413
414 unique_pipe(unique_pipe&& other) noexcept
415 {
416 m_Read = std::move(other.m_Read);
417 m_Write = std::move(other.m_Write);
418 }
419
420 unique_pipe& operator=(unique_pipe&& other) noexcept
421 {
422 m_Read = std::move(other.m_Read);
423 m_Write = std::move(other.m_Write);
424 return *this;
425 }
426
427 explicit operator bool() const noexcept
428 {
429 return m_Read || m_Write;
430 }
431
432 unique_fd& read()
433 {
434 return m_Read;
435 }
436
437 unique_fd& write()
438 {
439 return m_Write;
440 }
441
442 std::pair<unique_fd, unique_fd> release() noexcept
443 {
444 auto fds = std::make_pair(std::move(m_Read), std::move(m_Write));
445 return fds;
446 }
447
448 friend void swap(unique_pipe& left, unique_pipe& right)
449 {
450 std::swap(left.m_Read, right.m_Read);
451 std::swap(left.m_Write, right.m_Write);
452 }
453
454 static unique_pipe create(int flags)
455 {
456 int pipe[2] = {-1, -1};
457 if (pipe2(pipe, flags) < 0)
458 {
459 THROW_ERRNO(errno);
460 }
461
462 return unique_pipe(unique_fd(pipe[0]), unique_fd(pipe[1]));
463 }
464
465 private:
466 unique_fd m_Read;
467 unique_fd m_Write;
468 };
469
470 class unique_file
471 {
472 public:
473 static constexpr FILE* invalid_file = nullptr;
474
475 unique_file(FILE* file = invalid_file) noexcept : m_File{file}
476 {
477 }
478
479 ~unique_file() noexcept
480 {
481 reset();
482 }
483
484 unique_file(const unique_file&) = delete;
485 unique_file& operator=(const unique_file&) = delete;
486
487 unique_file(unique_file&& other) noexcept : m_File{other.m_File}
488 {
489 other.m_File = invalid_file;
490 }
491
492 unique_file& operator=(unique_file&& other) noexcept
493 {
494 std::swap(m_File, other.m_File);
495 return *this;
496 }
497
498 explicit operator bool() const noexcept
499 {
500 return m_File != invalid_file;
501 }
502
503 FILE* get() const noexcept
504 {
505 return m_File;
506 }
507
508 void reset(FILE* file = invalid_file) noexcept
509 {
510 if (m_File != invalid_file)
511 {
512 fclose(m_File);
513 }
514
515 m_File = file;
516 }
517
518 FILE* release() noexcept
519 {
520 FILE* file = m_File;
521 m_File = invalid_file;
522 return file;
523 }
524
525 friend void swap(unique_file& file1, unique_file& file2)
526 {
527 std::swap(file1.m_File, file2.m_File);
528 }
529
530 private:
531 FILE* m_File;
532 };
533
534 /** Returns an object that executes the given lambda when destroyed.
535 Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid
536 execution. Exceptions thrown in the lambda will fail-fast; use scope_exit_log to avoid. */
537 template <typename TLambda>
538 [[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept
539 {
540 return details::lambda_call<TLambda>(std::forward<TLambda>(lambda));
541 }
542
543 namespace details {
544 template <unsigned long long flag>
545 struct verify_single_flag_helper
546 {
547 static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
548 static const unsigned long long value = flag;
549 };
550
551 // Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
552 #define __WI_MAKE_UNSIGNED(val) \
553 (sizeof(val) == 1 ? static_cast<unsigned char>(val) \
554 : sizeof(val) == 2 ? static_cast<unsigned short>(val) \
555 : sizeof(val) == 4 ? static_cast<unsigned long>(val) \
556 : static_cast<unsigned long long>(val))
557 #define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
558 #define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
559
560 template <typename TVal, typename TFlags>
561 inline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
562 {
563 return ((val & flags) == static_cast<decltype(val & flags)>(flags));
564 }
565
566 template <typename TVal>
567 inline constexpr bool IsSingleFlagSetHelper(TVal val)
568 {
569 return __WI_IS_SINGLE_FLAG_SET(val);
570 }
571
572 template <typename TVal>
573 inline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
574 {
575 return ((val == static_cast<std::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
576 }
577
578 template <typename TVal, typename TMask, typename TFlags>
579 inline constexpr void UpdateFlagsInMaskHelper(TVal& val, TMask mask, TFlags flags)
580 {
581 val = static_cast<std::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
582 }
583
584 template <long>
585 struct variable_size;
586
587 template <>
588 struct variable_size<1>
589 {
590 typedef unsigned char type;
591 };
592
593 template <>
594 struct variable_size<2>
595 {
596 typedef unsigned short type;
597 };
598
599 template <>
600 struct variable_size<4>
601 {
602 typedef unsigned long type;
603 };
604
605 template <>
606 struct variable_size<8>
607 {
608 typedef unsigned long long type;
609 };
610
611 template <typename T>
612 struct variable_size_mapping
613 {
614 typedef typename variable_size<sizeof(T)>::type type;
615 };
616 } // namespace details
617
618 /** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
619 This allows code to generically convert any enum class to it's corresponding underlying type. */
620 template <typename T>
621 using integral_from_enum = typename details::variable_size_mapping<T>::type;
622
623 #define WI_StaticAssertSingleBitSet(flag) \
624 static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
625 #define WI_IsAnyFlagSet(val, flags) \
626 (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
627 #define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
628 #define WI_AreAllFlagsClear(val, flags) \
629 (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
630 #define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
631 #define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
632 #define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
633 //! Evaluates as true if every bitflag specified in `flags` is set within `val`.
634 #define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
635 //! Set zero or more bitflags specified by `flags` in the variable `var`.
636 #define WI_SetAllFlags(var, flags) ((var) |= (flags))
637 //! Set a single compile-time constant `flag` in the variable `var`.
638 #define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
639 //! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
640 #define WI_SetFlagIf(var, flag, condition) \
641 do \
642 { \
643 if (condition) \
644 { \
645 WI_SetFlag(var, flag); \
646 } \
647 } while ((void)0, 0)
648 //! Clear zero or more bitflags specified by `flags` from the variable `var`.
649 #define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
650 //! Clear a single compile-time constant `flag` from the variable `var`.
651 #define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
652
653 #define WI_ASSERT(condition) assert(condition)
654
655 #define EMIT_USER_WARNING(Warning) \
656 do \
657 { \
658 if (::wil::ScopedWarningsCollector::CanCollectWarning()) \
659 { \
660 ::wil::ScopedWarningsCollector::CollectWarning(Warning); \
661 } \
662 } while ((void)0, 0)
663
664 class ScopedWarningsCollector
665 {
666 public:
667 ScopedWarningsCollector()
668 {
669 assert(!g_collectedWarnings.has_value());
670
671 g_collectedWarnings.emplace();
672 }
673
674 ~ScopedWarningsCollector()
675 {
676 assert(g_collectedWarnings.has_value());
677
678 g_collectedWarnings = {};
679 }
680
681 ScopedWarningsCollector(const ScopedWarningsCollector&) = delete;
682 ScopedWarningsCollector(ScopedWarningsCollector&&) = delete;
683 ScopedWarningsCollector& operator=(const ScopedWarningsCollector&) = delete;
684 ScopedWarningsCollector& operator=(ScopedWarningsCollector&&) = delete;
685
686 static bool CanCollectWarning()
687 {
688 return g_collectedWarnings.has_value();
689 }
690
691 static void CollectWarning(std::string&& warning)
692 {
693 assert(g_collectedWarnings.has_value());
694
695 (*g_collectedWarnings) << std::move(warning) << "\n";
696 }
697
698 static std::string ConsumeWarnings()
699 {
700 if (!g_collectedWarnings.has_value())
701 {
702 return {};
703 }
704
705 auto warnings = g_collectedWarnings->str();
706
707 g_collectedWarnings = std::stringstream{};
708
709 return warnings;
710 }
711
712 private:
713 static thread_local std::optional<std::stringstream> g_collectedWarnings;
714 };
715
716 } // namespace wil