]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
xnu-517.7.7.tar.gz
[apple/xnu.git] / osfmk / kern / mk_timer.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
24 *
25 * HISTORY
26 *
27 * 29 June 2000 (debo)
28 * Created.
29 */
30
31 #include <mach/mach_types.h>
32 #include <mach/mach_port_server.h>
33
34 #include <mach/mk_timer.h>
35
36 #include <ipc/ipc_space.h>
37
38 #include <kern/mk_timer.h>
39 #include <kern/thread_call.h>
40
41 static zone_t mk_timer_zone;
42
43 static mach_port_qos_t mk_timer_qos = {
44 FALSE, TRUE, 0, sizeof (mk_timer_expire_msg_t)
45 };
46
47 static void mk_timer_expire(
48 void *p0,
49 void *p1);
50
51 mach_port_name_t
52 mk_timer_create(void)
53 {
54 mk_timer_t timer;
55 ipc_space_t myspace = current_space();
56 mach_port_name_t name = MACH_PORT_NULL;
57 ipc_port_t port;
58 kern_return_t result;
59
60 timer = (mk_timer_t)zalloc(mk_timer_zone);
61 if (timer == NULL)
62 return (MACH_PORT_NULL);
63
64 result = mach_port_allocate_qos(myspace, MACH_PORT_RIGHT_RECEIVE,
65 &mk_timer_qos, &name);
66 if (result == KERN_SUCCESS)
67 result = ipc_port_translate_receive(myspace, name, &port);
68
69 if (result != KERN_SUCCESS) {
70 zfree(mk_timer_zone, (vm_offset_t)timer);
71
72 return (MACH_PORT_NULL);
73 }
74
75 simple_lock_init(&timer->lock, ETAP_MISC_TIMER);
76 call_entry_setup(&timer->call_entry, mk_timer_expire, timer);
77 timer->is_armed = timer->is_dead = FALSE;
78 timer->active = 0;
79
80 timer->port = port;
81 ipc_kobject_set_atomically(port, (ipc_kobject_t)timer, IKOT_TIMER);
82
83 port->ip_srights++;
84 ip_reference(port);
85 ip_unlock(port);
86
87 return (name);
88 }
89
90 void
91 mk_timer_port_destroy(
92 ipc_port_t port)
93 {
94 mk_timer_t timer = NULL;
95
96 ip_lock(port);
97 if (ip_kotype(port) == IKOT_TIMER) {
98 timer = (mk_timer_t)port->ip_kobject;
99 assert(timer != NULL);
100 ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE);
101 simple_lock(&timer->lock);
102 assert(timer->port == port);
103 }
104 ip_unlock(port);
105
106 if (timer != NULL) {
107 if (thread_call_cancel(&timer->call_entry))
108 timer->active--;
109 timer->is_armed = FALSE;
110
111 timer->is_dead = TRUE;
112 if (timer->active == 0) {
113 simple_unlock(&timer->lock);
114 zfree(mk_timer_zone, (vm_offset_t)timer);
115
116 ipc_port_release_send(port);
117 return;
118 }
119
120 simple_unlock(&timer->lock);
121 }
122 }
123
124 void
125 mk_timer_init(void)
126 {
127 int s = sizeof (mk_timer_data_t);
128
129 assert(!(mk_timer_zone != NULL));
130
131 mk_timer_zone = zinit(s, (4096 * s), (16 * s), "mk_timer");
132 }
133
134 static void
135 mk_timer_expire(
136 void *p0,
137 void *p1)
138 {
139 mk_timer_t timer = p0;
140 ipc_port_t port;
141
142 simple_lock(&timer->lock);
143
144 if (timer->active > 1) {
145 timer->active--;
146 simple_unlock(&timer->lock);
147 return;
148 }
149
150 port = timer->port;
151 assert(port != IP_NULL);
152 assert(timer->active == 1);
153
154 while (timer->is_armed && timer->active == 1) {
155 mk_timer_expire_msg_t msg;
156
157 timer->is_armed = FALSE;
158 simple_unlock(&timer->lock);
159
160 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
161 msg.header.msgh_remote_port = port;
162 msg.header.msgh_local_port = MACH_PORT_NULL;
163 msg.header.msgh_reserved = msg.header.msgh_id = 0;
164
165 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0;
166
167 (void) mach_msg_send_from_kernel(&msg.header, sizeof (msg));
168
169 simple_lock(&timer->lock);
170 }
171
172 if (--timer->active == 0 && timer->is_dead) {
173 simple_unlock(&timer->lock);
174 zfree(mk_timer_zone, (vm_offset_t)timer);
175
176 ipc_port_release_send(port);
177 return;
178 }
179
180 simple_unlock(&timer->lock);
181 }
182
183 kern_return_t
184 mk_timer_destroy(
185 mach_port_name_t name)
186 {
187 ipc_space_t myspace = current_space();
188 ipc_port_t port;
189 kern_return_t result;
190
191 result = ipc_port_translate_receive(myspace, name, &port);
192 if (result != KERN_SUCCESS)
193 return (result);
194
195 if (ip_kotype(port) == IKOT_TIMER) {
196 ip_unlock(port);
197 result = mach_port_destroy(myspace, name);
198 }
199 else {
200 ip_unlock(port);
201 result = KERN_INVALID_ARGUMENT;
202 }
203
204 return (result);
205 }
206
207 kern_return_t
208 mk_timer_arm(
209 mach_port_name_t name,
210 uint64_t expire_time)
211 {
212 uint64_t time_of_arming;
213 mk_timer_t timer;
214 ipc_space_t myspace = current_space();
215 ipc_port_t port;
216 kern_return_t result;
217
218 clock_get_uptime(&time_of_arming);
219
220 result = ipc_port_translate_receive(myspace, name, &port);
221 if (result != KERN_SUCCESS)
222 return (result);
223
224 if (ip_kotype(port) == IKOT_TIMER) {
225 timer = (mk_timer_t)port->ip_kobject;
226 assert(timer != NULL);
227 simple_lock(&timer->lock);
228 assert(timer->port == port);
229 ip_unlock(port);
230
231 if (!timer->is_dead) {
232 timer->time_of_arming = time_of_arming;
233 timer->is_armed = TRUE;
234
235 if (!thread_call_enter_delayed(&timer->call_entry, expire_time))
236 timer->active++;
237 }
238
239 simple_unlock(&timer->lock);
240 }
241 else {
242 ip_unlock(port);
243 result = KERN_INVALID_ARGUMENT;
244 }
245
246 return (result);
247 }
248
249 kern_return_t
250 mk_timer_cancel(
251 mach_port_name_t name,
252 uint64_t *result_time)
253 {
254 uint64_t armed_time = 0;
255 mk_timer_t timer;
256 ipc_space_t myspace = current_space();
257 ipc_port_t port;
258 kern_return_t result;
259
260 result = ipc_port_translate_receive(myspace, name, &port);
261 if (result != KERN_SUCCESS)
262 return (result);
263
264 if (ip_kotype(port) == IKOT_TIMER) {
265 timer = (mk_timer_t)port->ip_kobject;
266 assert(timer != NULL);
267 simple_lock(&timer->lock);
268 assert(timer->port == port);
269 ip_unlock(port);
270
271 if (timer->is_armed) {
272 armed_time = timer->call_entry.deadline;
273 if (thread_call_cancel(&timer->call_entry))
274 timer->active--;
275 timer->is_armed = FALSE;
276 }
277
278 simple_unlock(&timer->lock);
279 }
280 else {
281 ip_unlock(port);
282 result = KERN_INVALID_ARGUMENT;
283 }
284
285 if (result == KERN_SUCCESS)
286 if ( result_time != NULL &&
287 copyout((void *)&armed_time, (void *)result_time,
288 sizeof (armed_time)) != 0 )
289 result = KERN_FAILURE;
290
291 return (result);
292 }