]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/mk_timer.c
0f6850a134290b560e7507f8286ca8c9f2505d17
[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_initialize(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 AbsoluteTime time_of_posting;
140 mk_timer_t timer = p0;
141 ipc_port_t port;
142
143 clock_get_uptime(&time_of_posting);
144
145 simple_lock(&timer->lock);
146
147 if (timer->active > 1) {
148 timer->active--;
149 simple_unlock(&timer->lock);
150 return;
151 }
152
153 port = timer->port;
154 assert(port != IP_NULL);
155
156 while ( timer->is_armed &&
157 !thread_call_is_delayed(&timer->call_entry, NULL) ) {
158 mk_timer_expire_msg_t msg;
159
160 timer->is_armed = FALSE;
161
162 msg.time_of_arming = timer->time_of_arming;
163 msg.armed_time = timer->call_entry.deadline;
164 msg.time_of_posting = time_of_posting;
165
166 simple_unlock(&timer->lock);
167
168 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
169 msg.header.msgh_remote_port = port;
170 msg.header.msgh_local_port = MACH_PORT_NULL;
171 msg.header.msgh_reserved = msg.header.msgh_id = 0;
172
173 (void) mach_msg_send_from_kernel(&msg.header, sizeof (msg));
174
175 simple_lock(&timer->lock);
176 }
177
178 if (--timer->active == 0 && timer->is_dead) {
179 simple_unlock(&timer->lock);
180 zfree(mk_timer_zone, (vm_offset_t)timer);
181
182 ipc_port_release_send(port);
183 return;
184 }
185
186 simple_unlock(&timer->lock);
187 }
188
189 kern_return_t
190 mk_timer_destroy(
191 mach_port_name_t name)
192 {
193 ipc_space_t myspace = current_space();
194 ipc_port_t port;
195 kern_return_t result;
196
197 result = ipc_port_translate_receive(myspace, name, &port);
198 if (result != KERN_SUCCESS)
199 return (result);
200
201 if (ip_kotype(port) == IKOT_TIMER) {
202 ip_unlock(port);
203 result = mach_port_destroy(myspace, name);
204 }
205 else {
206 ip_unlock(port);
207 result = KERN_INVALID_ARGUMENT;
208 }
209
210 return (result);
211 }
212
213 kern_return_t
214 mk_timer_arm(
215 mach_port_name_t name,
216 AbsoluteTime expire_time)
217 {
218 AbsoluteTime time_of_arming;
219 mk_timer_t timer;
220 ipc_space_t myspace = current_space();
221 ipc_port_t port;
222 kern_return_t result;
223
224 clock_get_uptime(&time_of_arming);
225
226 result = ipc_port_translate_receive(myspace, name, &port);
227 if (result != KERN_SUCCESS)
228 return (result);
229
230 if (ip_kotype(port) == IKOT_TIMER) {
231 timer = (mk_timer_t)port->ip_kobject;
232 assert(timer != NULL);
233 simple_lock(&timer->lock);
234 assert(timer->port == port);
235 ip_unlock(port);
236
237 timer->time_of_arming = time_of_arming;
238 timer->is_armed = TRUE;
239
240 if (!thread_call_enter_delayed(&timer->call_entry, expire_time))
241 timer->active++;
242 simple_unlock(&timer->lock);
243 }
244 else {
245 ip_unlock(port);
246 result = KERN_INVALID_ARGUMENT;
247 }
248
249 return (result);
250 }
251
252 kern_return_t
253 mk_timer_cancel(
254 mach_port_name_t name,
255 AbsoluteTime *result_time)
256 {
257 AbsoluteTime armed_time = { 0, 0 };
258 mk_timer_t timer;
259 ipc_space_t myspace = current_space();
260 ipc_port_t port;
261 kern_return_t result;
262
263 result = ipc_port_translate_receive(myspace, name, &port);
264 if (result != KERN_SUCCESS)
265 return (result);
266
267 if (ip_kotype(port) == IKOT_TIMER) {
268 timer = (mk_timer_t)port->ip_kobject;
269 assert(timer != NULL);
270 simple_lock(&timer->lock);
271 assert(timer->port == port);
272 ip_unlock(port);
273
274 if (timer->is_armed) {
275 armed_time = timer->call_entry.deadline;
276 if (thread_call_cancel(&timer->call_entry))
277 timer->active--;
278 timer->is_armed = FALSE;
279 }
280
281 simple_unlock(&timer->lock);
282 }
283 else {
284 ip_unlock(port);
285 result = KERN_INVALID_ARGUMENT;
286 }
287
288 if (result == KERN_SUCCESS)
289 if ( result_time != NULL &&
290 copyout((void *)&armed_time, (void *)result_time,
291 sizeof (armed_time)) != 0 )
292 result = KERN_FAILURE;
293
294 return (result);
295 }