]>
Commit | Line | Data |
---|---|---|
70ad1dc8 A |
1 | /* |
2 | * Copyright (c) 2018 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /*! | |
25 | * @header | |
26 | * Attributes to handle automatic clean-up of certain types of variables when | |
27 | * they go out of scope. | |
28 | * | |
29 | * IMPORTANT: These attributes will NOT cause a variable to be cleaned up when | |
30 | * its value changes. For example, this pattern would leak: | |
31 | * | |
32 | * void *__os_free ptr = malloc(10); | |
33 | * ptr = somewhere_else; | |
34 | * return; | |
35 | * | |
36 | * You should only use these attributes for very well-scoped, temporary | |
37 | * allocations. | |
38 | */ | |
39 | #ifndef __DARWIN_CLEANUP_H | |
40 | #define __DARWIN_CLEANUP_H | |
41 | ||
42 | #include <os/base.h> | |
43 | #include <os/api.h> | |
44 | #include <os/assumes.h> | |
45 | #include <os/object_private.h> | |
507116e3 | 46 | #include <os/lock.h> |
70ad1dc8 A |
47 | |
48 | #include <sys/errno.h> | |
49 | #include <sys/cdefs.h> | |
50 | ||
51 | #include <stdlib.h> | |
52 | #include <stdio.h> | |
53 | #include <unistd.h> | |
54 | #include <mach/mach_init.h> | |
55 | #include <mach/port.h> | |
56 | #include <mach/mach_port.h> | |
57 | #include <mach/kern_return.h> | |
507116e3 | 58 | #include <mach/mach_right.h> |
70ad1dc8 A |
59 | |
60 | __BEGIN_DECLS; | |
61 | ||
62 | #if __has_attribute(cleanup) | |
63 | /*! | |
64 | * @define __os_free | |
65 | * An attribute that may be applied to a variable's type. This attribute causes | |
66 | * the variable to be passed to free(3) when it goes out of scope. Applying this | |
67 | * attribute to variables that do not reference heap allocations will result in | |
68 | * undefined behavior. | |
69 | */ | |
70 | #define __os_free __attribute__((cleanup(__os_cleanup_free))) | |
71 | static inline void | |
72 | __os_cleanup_free(void *__p) | |
73 | { | |
74 | void **tp = (void **)__p; | |
75 | void *p = *tp; | |
76 | free(p); | |
77 | } | |
78 | ||
79 | /*! | |
80 | * @define __os_close | |
81 | * An attribute that may be applied to a variable's type. This attribute causes | |
82 | * the variable to be passed to close(2) when it goes out of scope. Applying | |
83 | * this attribute to variables that do not reference a valid file descriptor | |
84 | * will result in undefined behavior. If the variable's value is -1 upon going | |
85 | * out-of-scope, no cleanup is performed. | |
86 | */ | |
87 | #define __os_close __attribute__((cleanup(__os_cleanup_close))) | |
88 | static inline void | |
89 | __os_cleanup_close(int *__fd) | |
90 | { | |
91 | int fd = *__fd; | |
92 | if (fd == -1) { | |
93 | return; | |
94 | } | |
95 | posix_assert_zero(close(fd)); | |
96 | } | |
97 | ||
98 | /*! | |
99 | * @define __os_fclose | |
100 | * An attribute that may be applied to a variable's type. This attribute causes | |
101 | * the variable to be passed to fclose(3) when it goes out of scope. Applying | |
102 | * this attribute to variables that do not reference a valid FILE* will result | |
103 | * in undefined behavior. If the variable's value is NULL upon going out-of- | |
104 | * scope, no cleanup is performed. | |
105 | */ | |
106 | #define __os_fclose __attribute__((cleanup(__os_cleanup_fclose))) | |
107 | static inline void | |
108 | __os_cleanup_fclose(FILE **__fp) | |
109 | { | |
110 | FILE *f = *__fp; | |
111 | int ret = -1; | |
112 | ||
113 | if (!f) { | |
114 | return; | |
115 | } | |
116 | ||
117 | ret = fclose(f); | |
118 | if (ret == EOF) { | |
119 | os_assert_zero(errno); | |
120 | } | |
121 | } | |
122 | ||
123 | /*! | |
124 | * @define __os_close_mach_recv | |
125 | * An attribute that may be applied to a variable's type. This attribute causes | |
507116e3 A |
126 | * the variable to be wrapped in a mach receive right object and passed to |
127 | * {@link mach_right_recv_destruct} when it goes out of scope. Applying this | |
128 | * attribute to variables that do not reference a valid Mach port receive right | |
129 | * will result in undefined behavior. If the variable's value is MACH_PORT_NULL | |
130 | * or MACH_PORT_DEAD upon going out-of-scope, no cleanup is performed. | |
70ad1dc8 A |
131 | */ |
132 | #define __os_close_mach_recv \ | |
133 | __attribute__((cleanup(__os_cleanup_close_mach_recv))) | |
134 | static inline void | |
135 | __os_cleanup_close_mach_recv(mach_port_t *__p) | |
136 | { | |
137 | mach_port_t p = *__p; | |
507116e3 | 138 | mach_right_recv_t mr = mach_right_recv(p); |
70ad1dc8 A |
139 | |
140 | if (!MACH_PORT_VALID(p)) { | |
141 | return; | |
142 | } | |
143 | ||
507116e3 | 144 | mach_right_recv_destruct(mr, NULL, 0); |
70ad1dc8 A |
145 | } |
146 | ||
147 | /*! | |
148 | * @define __os_release_mach_send | |
149 | * An attribute that may be applied to a variable's type. This attribute causes | |
507116e3 A |
150 | * the variable to be wrapped in a mach send right object and passed to |
151 | * {@link mach_right_send_release} when it goes out of scope. Applying this | |
152 | * attribute to variables that do not reference a valid Mach port send right or | |
153 | * MACH_PORT_NULL or MACH_PORT_DEAD will result in undefined behavior. If the | |
154 | * variable's value is MACH_PORT_NULL or MACH_PORT_DEAD upon going out-of-scope, | |
155 | * no cleanup is performed. | |
70ad1dc8 A |
156 | */ |
157 | #define __os_release_mach_send \ | |
158 | __attribute__((cleanup(__os_cleanup_release_mach_send))) | |
159 | static inline void | |
160 | __os_cleanup_release_mach_send(mach_port_t *__p) | |
161 | { | |
162 | mach_port_t p = *__p; | |
507116e3 | 163 | mach_right_send_t ms = mach_right_send(p); |
70ad1dc8 A |
164 | |
165 | if (!MACH_PORT_VALID(p)) { | |
166 | return; | |
167 | } | |
168 | ||
507116e3 | 169 | mach_right_send_release(ms); |
70ad1dc8 A |
170 | } |
171 | ||
172 | /*! | |
173 | * @define __os_preserve_errno | |
174 | * An attribute that may be applied to a variable's type. This attribute sets | |
175 | * the global errno to the value of the variable when the variable goes out of | |
176 | * scope. This attribute is useful for preserving the value of errno upon entry | |
177 | * to a function and guaranteeing that it is restored upon exit. | |
178 | */ | |
179 | #define __os_preserve_errno \ | |
180 | __unused __attribute__((cleanup(__os_cleanup_errno))) | |
181 | static inline void | |
182 | __os_cleanup_errno(int *__e) | |
183 | { | |
184 | errno = *__e; | |
185 | } | |
186 | ||
187 | /*! | |
188 | * @define __os_release | |
189 | * An attribute that may be applied to a variable's type. This attribute causes | |
190 | * the variable to be passed to os_release() when it goes out of scope. Applying | |
191 | * this attribute to a variable which does not reference a valid os_object_t | |
192 | * object will result in undefined behavior. If the variable's value is NULL | |
193 | * upon going out-of-scope, no cleanup is performed. | |
194 | * | |
195 | * This attribute may be applied to dispatch and XPC objects. | |
507116e3 A |
196 | * |
197 | * When compiling with ARC, this attribute does nothing. | |
70ad1dc8 | 198 | */ |
507116e3 A |
199 | #if __has_feature(objc_arc) |
200 | #define __os_release | |
201 | #else | |
70ad1dc8 A |
202 | #define __os_release __attribute__((cleanup(__os_cleanup_os_release))) |
203 | static inline void | |
204 | __os_cleanup_os_release(void *__p) | |
205 | { | |
206 | _os_object_t *tp = (_os_object_t *)__p; | |
207 | _os_object_t o = *tp; | |
208 | if (!o) { | |
209 | return; | |
210 | } | |
211 | os_release(o); | |
212 | } | |
507116e3 | 213 | #endif |
70ad1dc8 A |
214 | |
215 | #if __COREFOUNDATION__ | |
216 | /*! | |
217 | * @define __os_cfrelease | |
218 | * An attribute that may be applied to a variable's type. This attribute causes | |
219 | * the variable to be passed to CFRelease() when it goes out of scope. Applying | |
220 | * this attribute to a variable which does not reference a valid CoreFoundation | |
221 | * object will result in undefined behavior. If the variable's value is NULL | |
222 | * upon going out-of-scope, no cleanup is performed. | |
223 | */ | |
224 | #define __os_cfrelease __attribute__((cleanup(__os_cleanup_cfrelease))) | |
225 | static inline void | |
226 | __os_cleanup_cfrelease(void *__p) | |
227 | { | |
228 | CFTypeRef *tp = (CFTypeRef *)__p; | |
229 | CFTypeRef cf = *tp; | |
230 | if (!cf) { | |
231 | return; | |
232 | } | |
233 | CFRelease(cf); | |
234 | } | |
235 | #endif // __COREFOUNDATION__ | |
236 | ||
507116e3 A |
237 | /*! |
238 | * @define __os_unfair_unlock | |
239 | * An attribute that may be applied to a variable's type. This attribute causes | |
240 | * the variable to be passed to os_unfair_lock_unlock() when it goes out of | |
241 | * scope. Applying this attribute to a variable which does not reference a valid | |
242 | * os_unfair_lock_t object will result in undefined behavior. If the variable's | |
243 | * value is NULL upon going out-of-scope, no cleanup is performed. | |
244 | * | |
245 | * This attribute is useful even when the target lock is taken conditionally via | |
246 | * the following pattern: | |
247 | * | |
248 | * os_unfair_lock lock = OS_UNFAIR_LOCK_INIT; | |
249 | * os_unfair_lock_t __os_unfair_unlock l2un = NULL; | |
250 | * | |
251 | * if (take_the_lock) { | |
252 | * os_unfair_lock_lock(&lock); | |
253 | * | |
254 | * // Guarantee that 'lock' will be unconditionally released when the | |
255 | * // scope containing 'l2un' ends. | |
256 | * l2un = &lock; | |
257 | * } | |
258 | */ | |
259 | #define __os_unfair_unlock __attribute__((cleanup(__os_cleanup_unfair_unlock))) | |
260 | static inline void | |
261 | __os_cleanup_unfair_unlock(void *__p) | |
262 | { | |
263 | os_unfair_lock_t *tp = (os_unfair_lock_t *)__p; | |
264 | os_unfair_lock_t ufl = *tp; | |
265 | if (!ufl) { | |
266 | return; | |
267 | } | |
268 | os_unfair_lock_assert_owner(ufl); | |
269 | os_unfair_lock_unlock(ufl); | |
270 | } | |
271 | ||
70ad1dc8 | 272 | #else // __has_attribute(cleanup) |
507116e3 A |
273 | #define __os_cleanup_unsupported \ |
274 | _Pragma("GCC error \"automatic cleanup not supported\"") | |
275 | #define __os_free __os_cleanup_unsupported | |
276 | #define __os_close __os_cleanup_unsupported | |
277 | #define __os_fclose __os_cleanup_unsupported | |
278 | #define __os_close_mach_recv __os_cleanup_unsupported | |
279 | #define __os_release_mach_send __os_cleanup_unsupported | |
280 | #define __os_preserve_errno __os_cleanup_unsupported | |
281 | #define __os_release __os_cleanup_unsupported | |
282 | #define __os_cfrelease __os_cleanup_unsupported | |
283 | #define __os_unfair_unlock __os_cleanup_unsupported | |
70ad1dc8 A |
284 | #endif // __has_attribute(cleanup) |
285 | ||
286 | __END_DECLS; | |
287 | ||
288 | #endif // __DARWIN_CLEANUP_H |