1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
18 #define INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
19 
20 #include "perfetto/base/build_config.h"
21 
22 #include <stdio.h>
23 
24 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
25 #include <dirent.h>  // For DIR* / opendir().
26 #endif
27 
28 #include <string>
29 
30 #include "perfetto/base/export.h"
31 #include "perfetto/base/logging.h"
32 #include "perfetto/base/platform_handle.h"
33 
34 namespace perfetto {
35 namespace base {
36 
37 namespace internal {
38 // Used for the most common cases of ScopedResource where there is only one
39 // invalid value.
40 template <typename T, T InvalidValue>
41 struct DefaultValidityChecker {
IsValidDefaultValidityChecker42   static bool IsValid(T t) { return t != InvalidValue; }
43 };
44 }  // namespace internal
45 
46 // RAII classes for auto-releasing fds and dirs.
47 // if T is a pointer type, InvalidValue must be nullptr. Doing otherwise
48 // causes weird unexpected behaviors (See https://godbolt.org/z/5nGMW4).
49 template <typename T,
50           int (*CloseFunction)(T),
51           T InvalidValue,
52           bool CheckClose = true,
53           class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
54 class PERFETTO_EXPORT ScopedResource {
55  public:
56   using ValidityChecker = Checker;
57   static constexpr T kInvalid = InvalidValue;
58 
t_(t)59   explicit ScopedResource(T t = InvalidValue) : t_(t) {}
ScopedResource(ScopedResource && other)60   ScopedResource(ScopedResource&& other) noexcept {
61     t_ = other.t_;
62     other.t_ = InvalidValue;
63   }
64   ScopedResource& operator=(ScopedResource&& other) {
65     reset(other.t_);
66     other.t_ = InvalidValue;
67     return *this;
68   }
get()69   T get() const { return t_; }
70   T operator*() const { return t_; }
71   explicit operator bool() const { return Checker::IsValid(t_); }
72   void reset(T r = InvalidValue) {
73     if (Checker::IsValid(t_)) {
74       int res = CloseFunction(t_);
75       if (CheckClose)
76         PERFETTO_CHECK(res == 0);
77     }
78     t_ = r;
79   }
release()80   T release() {
81     T t = t_;
82     t_ = InvalidValue;
83     return t;
84   }
~ScopedResource()85   ~ScopedResource() { reset(InvalidValue); }
86 
87  private:
88   ScopedResource(const ScopedResource&) = delete;
89   ScopedResource& operator=(const ScopedResource&) = delete;
90   T t_;
91 };
92 
93 // Declared in file_utils.h. Forward declared to avoid #include cycles.
94 int PERFETTO_EXPORT CloseFile(int fd);
95 
96 // Use this for file resources obtained via open() and similar APIs.
97 using ScopedFile = ScopedResource<int, CloseFile, -1>;
98 using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;
99 
100 // Use this for resources that are HANDLE on Windows. See comments in
101 // platform_handle.h
102 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
103 using ScopedPlatformHandle = ScopedResource<PlatformHandle,
104                                             ClosePlatformHandle,
105                                             /*InvalidValue=*/nullptr,
106                                             /*CheckClose=*/true,
107                                             PlatformHandleChecker>;
108 #else
109 // On non-windows systems we alias ScopedPlatformHandle to ScopedFile because
110 // they are really the same. This is to allow assignments between the two in
111 // Linux-specific code paths that predate ScopedPlatformHandle.
112 static_assert(std::is_same<int, PlatformHandle>::value, "");
113 using ScopedPlatformHandle = ScopedFile;
114 
115 // DIR* does not exist on Windows.
116 using ScopedDir = ScopedResource<DIR*, closedir, nullptr>;
117 #endif
118 
119 }  // namespace base
120 }  // namespace perfetto
121 
122 #endif  // INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
123