]>
Commit | Line | Data |
---|---|---|
3e170ce0 A |
1 | /* |
2 | * Copyright (c) 2015 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 | #include <sys/cdefs.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/work_interval.h> | |
5ba3f43e A |
26 | |
27 | #include <mach/mach.h> | |
3e170ce0 | 28 | #include <mach/mach_time.h> |
5ba3f43e A |
29 | #include <mach/port.h> |
30 | ||
3e170ce0 A |
31 | #include <sys/errno.h> |
32 | #include <stdlib.h> | |
a39ff7e2 | 33 | #include <strings.h> |
3e170ce0 A |
34 | |
35 | struct work_interval { | |
36 | uint64_t thread_id; | |
37 | uint64_t work_interval_id; | |
5ba3f43e A |
38 | uint32_t create_flags; |
39 | mach_port_t wi_port; | |
3e170ce0 A |
40 | }; |
41 | ||
42 | extern uint64_t __thread_selfid(void); | |
43 | ||
5ba3f43e | 44 | /* Create a new work interval handle (currently for the current thread only). */ |
3e170ce0 | 45 | int |
5ba3f43e | 46 | work_interval_create(work_interval_t *interval_handle, uint32_t create_flags) |
3e170ce0 A |
47 | { |
48 | int ret; | |
3e170ce0 A |
49 | work_interval_t handle; |
50 | ||
5ba3f43e A |
51 | if (interval_handle == NULL) { |
52 | errno = EINVAL; | |
53 | return -1; | |
54 | } | |
55 | ||
56 | struct work_interval_create_params create_params = { | |
57 | .wicp_create_flags = create_flags, | |
58 | }; | |
59 | ||
60 | ret = __work_interval_ctl(WORK_INTERVAL_OPERATION_CREATE2, 0, | |
61 | &create_params, sizeof(create_params)); | |
3e170ce0 A |
62 | if (ret == -1) { |
63 | return ret; | |
64 | } | |
65 | ||
66 | handle = malloc(sizeof(*handle)); | |
67 | if (handle == NULL) { | |
68 | errno = ENOMEM; | |
69 | return -1; | |
70 | } | |
71 | ||
72 | handle->thread_id = __thread_selfid(); | |
5ba3f43e A |
73 | handle->work_interval_id = create_params.wicp_id; |
74 | handle->create_flags = create_params.wicp_create_flags; | |
75 | handle->wi_port = create_params.wicp_port; | |
3e170ce0 A |
76 | |
77 | *interval_handle = handle; | |
78 | return 0; | |
79 | } | |
80 | ||
81 | int | |
5ba3f43e A |
82 | work_interval_notify(work_interval_t interval_handle, uint64_t start, |
83 | uint64_t finish, uint64_t deadline, uint64_t next_start, | |
84 | uint32_t notify_flags) | |
3e170ce0 A |
85 | { |
86 | int ret; | |
87 | uint64_t work_interval_id; | |
88 | struct work_interval_notification notification = { | |
89 | .start = start, | |
90 | .finish = finish, | |
91 | .deadline = deadline, | |
92 | .next_start = next_start, | |
5ba3f43e | 93 | .notify_flags = notify_flags |
3e170ce0 A |
94 | }; |
95 | ||
96 | if (interval_handle == NULL) { | |
97 | errno = EINVAL; | |
98 | return -1; | |
99 | } | |
100 | ||
5ba3f43e | 101 | notification.create_flags = interval_handle->create_flags; |
3e170ce0 A |
102 | work_interval_id = interval_handle->work_interval_id; |
103 | ||
5ba3f43e A |
104 | ret = __work_interval_ctl(WORK_INTERVAL_OPERATION_NOTIFY, work_interval_id, |
105 | ¬ification, sizeof(notification)); | |
3e170ce0 A |
106 | return ret; |
107 | } | |
108 | ||
109 | int | |
5ba3f43e A |
110 | work_interval_notify_simple(work_interval_t interval_handle, uint64_t start, |
111 | uint64_t deadline, uint64_t next_start) | |
3e170ce0 | 112 | { |
5ba3f43e A |
113 | return work_interval_notify(interval_handle, start, mach_absolute_time(), |
114 | deadline, next_start, 0); | |
3e170ce0 A |
115 | } |
116 | ||
a39ff7e2 | 117 | |
3e170ce0 A |
118 | int |
119 | work_interval_destroy(work_interval_t interval_handle) | |
120 | { | |
5ba3f43e A |
121 | if (interval_handle == NULL) { |
122 | errno = EINVAL; | |
123 | return -1; | |
124 | } | |
125 | ||
126 | if (interval_handle->create_flags & WORK_INTERVAL_FLAG_JOINABLE) { | |
127 | mach_port_t wi_port = interval_handle->wi_port; | |
128 | ||
129 | /* | |
130 | * A joinable work interval's lifetime is tied to the port lifetime. | |
131 | * When the last port reference is destroyed, the work interval | |
132 | * is destroyed via no-senders notification. | |
133 | * | |
134 | * Note however that after destroy it can no longer be notified | |
135 | * because the userspace token is gone. | |
136 | * | |
137 | * Additionally, this function does not cause the thread to un-join | |
138 | * the interval. | |
139 | */ | |
140 | kern_return_t kr = mach_port_deallocate(mach_task_self(), wi_port); | |
141 | ||
142 | if (kr != KERN_SUCCESS) { | |
143 | /* | |
144 | * If the deallocate fails, then someone got their port | |
145 | * lifecycle wrong and over-released a port right. | |
146 | * | |
147 | * Return an error so the client can assert on this, | |
148 | * and still find the port name in the interval handle. | |
149 | */ | |
150 | errno = EINVAL; | |
151 | return -1; | |
152 | } | |
153 | ||
154 | interval_handle->wi_port = MACH_PORT_NULL; | |
155 | interval_handle->work_interval_id = 0; | |
156 | ||
157 | free(interval_handle); | |
158 | return 0; | |
159 | } else { | |
160 | uint64_t work_interval_id = interval_handle->work_interval_id; | |
161 | ||
162 | int ret = __work_interval_ctl(WORK_INTERVAL_OPERATION_DESTROY, | |
163 | work_interval_id, NULL, 0); | |
164 | ||
165 | interval_handle->work_interval_id = 0; | |
166 | ||
167 | int saved_errno = errno; | |
168 | free(interval_handle); | |
169 | errno = saved_errno; | |
170 | return ret; | |
171 | } | |
172 | } | |
3e170ce0 | 173 | |
5ba3f43e A |
174 | int |
175 | work_interval_join(work_interval_t interval_handle) | |
176 | { | |
3e170ce0 A |
177 | if (interval_handle == NULL) { |
178 | errno = EINVAL; | |
179 | return -1; | |
180 | } | |
181 | ||
5ba3f43e A |
182 | if ((interval_handle->create_flags & WORK_INTERVAL_FLAG_JOINABLE) == 0) { |
183 | errno = EINVAL; | |
184 | return -1; | |
185 | } | |
3e170ce0 | 186 | |
5ba3f43e | 187 | mach_port_t wi_port = interval_handle->wi_port; |
3e170ce0 | 188 | |
5ba3f43e A |
189 | if (!MACH_PORT_VALID(wi_port)) { |
190 | errno = EINVAL; | |
191 | return -1; | |
192 | } | |
193 | ||
194 | return work_interval_join_port(wi_port); | |
3e170ce0 | 195 | } |
5ba3f43e A |
196 | |
197 | int | |
198 | work_interval_join_port(mach_port_t port) | |
199 | { | |
200 | if (port == MACH_PORT_NULL) { | |
201 | errno = EINVAL; | |
202 | return -1; | |
203 | } | |
204 | ||
205 | return __work_interval_ctl(WORK_INTERVAL_OPERATION_JOIN, | |
206 | (uint64_t)port, NULL, 0); | |
207 | } | |
208 | ||
209 | int | |
210 | work_interval_leave(void) | |
211 | { | |
212 | return __work_interval_ctl(WORK_INTERVAL_OPERATION_JOIN, | |
213 | (uint64_t)MACH_PORT_NULL, NULL, 0); | |
214 | } | |
215 | ||
216 | int | |
217 | work_interval_copy_port(work_interval_t interval_handle, mach_port_t *port) | |
218 | { | |
219 | if (port == NULL) { | |
220 | errno = EINVAL; | |
221 | return -1; | |
222 | } | |
223 | ||
224 | if (interval_handle == NULL) { | |
225 | *port = MACH_PORT_NULL; | |
226 | errno = EINVAL; | |
227 | return -1; | |
228 | } | |
229 | ||
230 | if ((interval_handle->create_flags & WORK_INTERVAL_FLAG_JOINABLE) == 0) { | |
231 | *port = MACH_PORT_NULL; | |
232 | errno = EINVAL; | |
233 | return -1; | |
234 | } | |
235 | ||
236 | mach_port_t wi_port = interval_handle->wi_port; | |
237 | ||
238 | kern_return_t kr = mach_port_mod_refs(mach_task_self(), wi_port, | |
239 | MACH_PORT_RIGHT_SEND, 1); | |
240 | ||
241 | if (kr != KERN_SUCCESS) { | |
242 | *port = MACH_PORT_NULL; | |
243 | errno = EINVAL; | |
244 | return -1; | |
245 | } | |
246 | ||
247 | *port = wi_port; | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | ||
253 | ||
254 |