Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
/*
 * Copyright (c) 1993-1995, 1999-2008 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

/*!
 *  @header thread_call.h
 *  @discussion Facilities for executing work asynchronously.
 */

#ifndef _KERN_THREAD_CALL_H_
#define _KERN_THREAD_CALL_H_

#include <mach/mach_types.h>

#include <kern/clock.h>

#include <sys/cdefs.h>

struct thread_call;
typedef struct thread_call *thread_call_t;

typedef void *thread_call_param_t;
typedef void (*thread_call_func_t)(
	thread_call_param_t     param0,
	thread_call_param_t     param1);
/*!
 *  @enum thread_call_priority_t
 *  @discussion Thread call priorities should not be assumed to have any specific
 *  numerical value; they should be interpreted as importances or roles for work
 *  items, priorities for which will be reasonably managed by the subsystem.
 *  @constant THREAD_CALL_PRIORITY_HIGH Importance above everything but realtime.
 *  Thread calls allocated with this priority execute at extremely high priority,
 *  above everything but realtime threads.  They are generally executed  in serial.
 *  Though they may execute concurrently under some circumstances, no fan-out is implied.
 *  These work items should do very small amounts of work or risk disrupting system
 *  responsiveness.
 *  @constant THREAD_CALL_PRIORITY_KERNEL Importance similar to that of normal kernel
 *  threads.
 *  @constant THREAD_CALL_PRIORITY_USER Importance similar to that of normal user threads.
 *  @constant THREAD_CALL_PRIORITY_LOW Very low importance.
 *  @constant THREAD_CALL_PRIORITY_KERNEL_HIGH Importance higher than most kernel
 *  threads.
 */
typedef enum {
	THREAD_CALL_PRIORITY_HIGH        = 0,
	THREAD_CALL_PRIORITY_KERNEL      = 1,
	THREAD_CALL_PRIORITY_USER        = 2,
	THREAD_CALL_PRIORITY_LOW         = 3,
	THREAD_CALL_PRIORITY_KERNEL_HIGH = 4
} thread_call_priority_t;

enum {
	/* if call is re-submitted while the call is executing on a call thread, then delay the re-enqueue until it returns */
	THREAD_CALL_OPTIONS_ONCE   = 0x00000001,
#ifdef XNU_KERNEL_PRIVATE
	/* execute call from the timer interrupt instead of from the thread call thread, private interface for IOTES workloop signaling */
	THREAD_CALL_OPTIONS_SIGNAL = 0x00000002,
#endif /* XNU_KERNEL_PRIVATE */
};
typedef uint32_t thread_call_options_t;

__BEGIN_DECLS

/*!
 *  @function thread_call_enter
 *  @abstract Submit a thread call work item for immediate execution.
 *  @discussion If the work item is already scheduled for delayed execution, and it has
 *  not yet begun to run, that delayed invocation will be cancelled.  Note that if a
 *  thread call is rescheduled from its own callback, then multiple invocations of the
 *  callback may be in flight at the same time.
 *  @result TRUE if the call was already pending for either delayed or immediate
 *  execution, FALSE otherwise.
 *  @param call The thread call to execute.
 */
extern boolean_t        thread_call_enter(
	thread_call_t           call);
/*!
 *  @function thread_call_enter1
 *  @abstract Submit a thread call work item for immediate execution, with an extra parameter.
 *  @discussion This routine is identical to thread_call_enter(), except that
 *  the second parameter to the callback is specified.
 *  @result TRUE if the call was already pending for either delayed or immediate
 *  execution, FALSE otherwise.
 *  @param call The thread call to execute.
 *  @param param1 Parameter to pass callback.
 */
extern boolean_t        thread_call_enter1(
	thread_call_t           call,
	thread_call_param_t     param1);

/*!
 *  @function thread_call_enter_delayed
 *  @abstract Submit a thread call to be executed at some point in the future.
 *  @discussion If the work item is already scheduled for delayed or immediate execution,
 *  and it has not yet begun to run, that invocation will be cancelled in favor of execution
 *  at the newly specified time.  Note that if a thread call is rescheduled from its own callback,
 *  then multiple invocations of the callback may be in flight at the same time.
 *  @result TRUE if the call was already pending for either delayed or immediate
 *  execution, FALSE otherwise.
 *  @param call The thread call to execute.
 *  @param deadline Time, in absolute time units, at which to execute callback.
 */
extern boolean_t        thread_call_enter_delayed(
	thread_call_t           call,
	uint64_t                deadline);
/*!
 *  @function thread_call_enter1_delayed
 *  @abstract Submit a thread call to be executed at some point in the future, with an extra parameter.
 *  @discussion This routine is identical to thread_call_enter_delayed(),
 *  except that a second parameter to the callback is specified.
 *  @result TRUE if the call was already pending for either delayed or immediate
 *  execution, FALSE otherwise.
 *  @param call The thread call to execute.
 *  @param param1 Second parameter to callback.
 *  @param deadline Time, in absolute time units, at which to execute callback.
 */
extern boolean_t        thread_call_enter1_delayed(
	thread_call_t           call,
	thread_call_param_t     param1,
	uint64_t                deadline);
#ifdef XNU_KERNEL_PRIVATE

/*
 * Flags to alter the default timer/timeout coalescing behavior
 * on a per-thread_call basis.
 *
 * The SYS urgency classes indicate that the thread_call is not
 * directly related to the current thread at the time the thread_call
 * is entered, so it is ignored in the calculation entirely (only
 * the subclass specified is used).
 *
 * The USER flags indicate that both the current thread scheduling and QoS
 * attributes, in addition to the per-thread_call urgency specification,
 * are used to establish coalescing behavior.
 */
#define THREAD_CALL_DELAY_SYS_NORMAL            TIMEOUT_URGENCY_SYS_NORMAL
#define THREAD_CALL_DELAY_SYS_CRITICAL          TIMEOUT_URGENCY_SYS_CRITICAL
#define THREAD_CALL_DELAY_SYS_BACKGROUND        TIMEOUT_URGENCY_SYS_BACKGROUND

#define THREAD_CALL_DELAY_USER_MASK             TIMEOUT_URGENCY_USER_MASK
#define THREAD_CALL_DELAY_USER_NORMAL           TIMEOUT_URGENCY_USER_NORMAL
#define THREAD_CALL_DELAY_USER_CRITICAL         TIMEOUT_URGENCY_USER_CRITICAL
#define THREAD_CALL_DELAY_USER_BACKGROUND       TIMEOUT_URGENCY_USER_BACKGROUND

#define THREAD_CALL_DELAY_URGENCY_MASK          TIMEOUT_URGENCY_MASK

/*
 * Indicate that a specific leeway value is being provided (otherwise
 * the leeway parameter is ignored).  The supplied value can currently
 * only be used to extend the leeway calculated internally from the
 * urgency class provided.
 */
#define THREAD_CALL_DELAY_LEEWAY                TIMEOUT_URGENCY_LEEWAY

/*
 * Indicates that the time parameters should be interpreted as
 * mach_continuous_time values, rather than mach_absolute_time and the timer
 * be programmed to fire based on continuous time.
 */
#define THREAD_CALL_CONTINUOUS  0x100

/*!
 *  @function thread_call_enter_delayed_with_leeway
 *  @abstract Submit a thread call to be executed at some point in the future.
 *  @discussion If the work item is already scheduled for delayed or immediate execution,
 *  and it has not yet begun to run, that invocation will be cancelled in favor of execution
 *  at the newly specified time.  Note that if a thread call is rescheduled from its own callback,
 *  then multiple invocations of the callback may be in flight at the same time.
 *  @result TRUE if the call was already pending for either delayed or immediate
 *  execution, FALSE otherwise.
 *  @param call The thread call to execute.
 *  @param param1 Second parameter to callback.
 *  @param deadline Time, in absolute time units, at which to execute callback.
 *  @param leeway Time delta, in absolute time units, which sets range of time allowing kernel
 *       to decide appropriate time to run.
 *  @param flags configuration for timers in kernel.
 */
extern boolean_t        thread_call_enter_delayed_with_leeway(
	thread_call_t           call,
	thread_call_param_t     param1,
	uint64_t                deadline,
	uint64_t                leeway,
	uint32_t                flags);

#endif /* XNU_KERNEL_PRIVATE */

/*!
 *  @function thread_call_cancel
 *  @abstract Attempt to cancel a pending invocation of a thread call.
 *  @discussion Attempt to cancel a thread call which has been scheduled
 *  for execution with a thread_call_enter* variant.  If the call has not
 *  yet begun executing, the pending invocation will be cancelled and TRUE
 *  will be returned.  If the work item has already begun executing,
 *  thread_call_cancel will return FALSE immediately; the callback may be
 *  about to run, currently running, or already done executing.
 *  @result TRUE if the call was successfully cancelled, FALSE otherwise.
 */
extern boolean_t        thread_call_cancel(
	thread_call_t           call);
/*!
 *  @function thread_call_cancel_wait
 *  @abstract Attempt to cancel a pending invocation of a thread call.
 *  If unable to cancel, wait for current invocation to finish.
 *  @discussion Attempt to cancel a thread call which has been scheduled
 *  for execution with a thread_call_enter* variant.  If the call has not
 *  yet begun executing, the pending invocation will be cancelled and TRUE
 *  will be returned.  If the work item has already begun executing,
 *  thread_call_cancel_wait waits for the most recent invocation to finish. When
 *  called on a work item which has already finished, it will return FALSE immediately.
 *  Note that this routine can only be used on thread calls set up with either
 *  thread_call_allocate or thread_call_allocate_with_priority, and that invocations
 *  of the thread call <i>after</i> the current invocation may be in flight when
 *  thread_call_cancel_wait returns.
 *  @result TRUE if the call was successfully cancelled, FALSE otherwise.
 */
extern boolean_t        thread_call_cancel_wait(
	thread_call_t           call);

/*!
 *  @function thread_call_allocate
 *  @abstract Allocate a thread call to execute with default (high) priority.
 *  @discussion  Allocates a thread call that will run with properties of
 *  THREAD_CALL_PRIORITY_HIGH, binding the first parameter to the callback.
 *  @param func Callback to invoke when thread call is scheduled.
 *  @param param0 First argument ot pass to callback.
 *  @result Thread call which can be passed to thread_call_enter variants.
 */
extern thread_call_t    thread_call_allocate(
	thread_call_func_t      func,
	thread_call_param_t     param0);

/*!
 *  @function thread_call_allocate_with_priority
 *  @abstract Allocate a thread call to execute with a specified priority.
 *  @discussion Identical to thread_call_allocate, except that priority
 *  is specified by caller.
 *  @param func Callback to invoke when thread call is scheduled.
 *  @param param0 First argument to pass to callback.
 *  @param pri Priority of item.
 *  @result Thread call which can be passed to thread_call_enter variants.
 */
extern thread_call_t    thread_call_allocate_with_priority(
	thread_call_func_t      func,
	thread_call_param_t     param0,
	thread_call_priority_t  pri);

/*!
 *  @function thread_call_allocate_with_options
 *  @abstract Allocate a thread call to execute with a specified priority.
 *  @discussion Identical to thread_call_allocate, except that priority
 *  and options are specified by caller.
 *  @param func Callback to invoke when thread call is scheduled.
 *  @param param0 First argument to pass to callback.
 *  @param pri Priority of item.
 *  @param options Options for item.
 *  @result Thread call which can be passed to thread_call_enter variants.
 */
extern thread_call_t    thread_call_allocate_with_options(
	thread_call_func_t      func,
	thread_call_param_t     param0,
	thread_call_priority_t  pri,
	thread_call_options_t   options);

#ifdef KERNEL_PRIVATE
/*!
 *  @function thread_call_allocate_with_qos
 *  @abstract Allocate a thread call to execute with a specified QoS.
 *  @discussion Identical to thread_call_allocate_with_options, except it uses the QoS namespace.
 *      Private interface for pthread kext.
 *  @param func Callback to invoke when thread call is scheduled.
 *  @param param0 First argument to pass to callback.
 *  @param qos_tier QoS tier to execute callback at (as in THREAD_QOS_POLICY)
 *  @param options flags from thread_call_options_t to influence the thread call behavior
 *  @result Thread call which can be passed to thread_call_enter variants.
 */
extern thread_call_t
thread_call_allocate_with_qos(thread_call_func_t        func,
    thread_call_param_t       param0,
    int                       qos_tier,
    thread_call_options_t     options);

/*!
 *  @function thread_call_wait_once
 *  @abstract Wait for a THREAD_CALL_OPTIONS_ONCE call to finish executing if it is executing
 *  @discussion Only works on THREAD_CALL_OPTIONS_ONCE calls
 *  @param call The thread call to wait for
 *  @result True if it waited, false if it did not wait
 */
extern boolean_t
thread_call_wait_once(thread_call_t call);
#endif /* KERNEL_PRIVATE */

/*!
 *  @function thread_call_free
 *  @abstract Release a thread call.
 *  @discussion Should only be used on thread calls allocated with thread_call_allocate
 *  or thread_call_allocate_with_priority.  Once thread_call_free has been called,
 *  no other operations may be performed on a thread call.  If the thread call is
 *  currently pending, thread_call_free will return FALSE and will have no effect.
 *  Calling thread_call_free from a thread call's own callback is safe; the work
 *  item is not considering "pending" at that point.
 *  @result TRUE if the thread call has been successfully released, else FALSE.
 *  @param call The thread call to release.
 */
extern boolean_t        thread_call_free(
	thread_call_t           call);

/*!
 *  @function thread_call_isactive
 *  @abstract Determine whether a thread call is pending or currently executing.
 *  @param call Thread call to examine.
 *  @result TRUE if the thread call is either scheduled for execution (immediately
 *  or at some point in the future) or is currently executing.
 */
boolean_t               thread_call_isactive(
	thread_call_t call);
__END_DECLS

#ifdef  MACH_KERNEL_PRIVATE

#include <kern/queue.h>
#include <kern/priority_queue.h>

__enum_closed_decl(thread_call_index_t, uint16_t, {
	THREAD_CALL_INDEX_INVALID       = 0,    /* make sure zero tc_index is detected as invalid */
	THREAD_CALL_INDEX_HIGH          = 1,
	THREAD_CALL_INDEX_KERNEL        = 2,
	THREAD_CALL_INDEX_USER          = 3,
	THREAD_CALL_INDEX_LOW           = 4,
	THREAD_CALL_INDEX_KERNEL_HIGH   = 5,
	THREAD_CALL_INDEX_QOS_UI        = 6,
	THREAD_CALL_INDEX_QOS_IN        = 7,
	THREAD_CALL_INDEX_QOS_UT        = 8,
	THREAD_CALL_INDEX_MAX           = 9,    /* count of thread call indexes */
});

__options_closed_decl(thread_call_flags_t, uint16_t, {
	THREAD_CALL_ALLOC               = 0x0001,       /* memory owned by thread_call.c */
	THREAD_CALL_WAIT                = 0x0002,       /* thread waiting for call to finish running */
	THREAD_CALL_DELAYED             = 0x0004,       /* deadline based */
	THREAD_CALL_RUNNING             = 0x0008,       /* currently executing on a thread */
	THREAD_CALL_SIGNAL              = 0x0010,       /* call from timer interrupt instead of thread */
	THREAD_CALL_ONCE                = 0x0020,       /* pend the enqueue if re-armed while running */
	THREAD_CALL_RESCHEDULE          = 0x0040,       /* enqueue is pending due to re-arm while running */
	THREAD_CALL_RATELIMITED         = 0x0080,       /* timer doesn't fire until slop+deadline */
	THREAD_CALL_FLAG_CONTINUOUS     = 0x0100,       /* deadline is in continuous time */
	THREAD_CALL_INITIALIZED         = 0x0200,       /* thread call is initialized */
});

struct thread_call {
	/* Originally requested deadline */
	uint64_t                                tc_soft_deadline;
	/* Deadline presented to hardware (post-leeway) stored in tc_pqlink.deadline */
	struct priority_queue_entry_deadline    tc_pqlink;
	/* Which queue head is this call enqueued on */
	queue_head_t                            *tc_queue;
	queue_chain_t                           tc_qlink;
	thread_call_index_t                     tc_index;
	thread_call_flags_t                     tc_flags;
	int32_t                                 tc_refs;
	/* Time to deadline at creation */
	uint64_t                                tc_ttd;
	/* Timestamp of enqueue on pending queue */
	uint64_t                                tc_pending_timestamp;
	thread_call_func_t                      tc_func;
	thread_call_param_t                     tc_param0;
	thread_call_param_t                     tc_param1;
	uint64_t                                tc_submit_count;
	uint64_t                                tc_finish_count;
};

typedef struct thread_call thread_call_data_t;

extern void             thread_call_setup(
	thread_call_t                   call,
	thread_call_func_t              func,
	thread_call_param_t             param0);

extern void             thread_call_setup_with_options(
	thread_call_t                   call,
	thread_call_func_t              func,
	thread_call_param_t             param0,
	thread_call_priority_t          pri,
	thread_call_options_t           options);

extern void             thread_call_delayed_timer_rescan_all(void);
extern uint64_t         thread_call_get_armed_deadline(thread_call_t call);

struct thread_call_thread_state;

#endif  /* MACH_KERNEL_PRIVATE */

#ifdef  XNU_KERNEL_PRIVATE

__BEGIN_DECLS

/*
 * These routines are equivalent to their thread_call_enter_XXX
 * variants, only the thread_call_t is allocated out of a
 * fixed preallocated pool of memory, and will panic if the pool
 * is exhausted.
 */

extern void             thread_call_func_delayed(
	thread_call_func_t              func,
	thread_call_param_t             param,
	uint64_t                        deadline);

extern void             thread_call_func_delayed_with_leeway(
	thread_call_func_t              func,
	thread_call_param_t             param,
	uint64_t                deadline,
	uint64_t                leeway,
	uint32_t                flags);

/*
 * This iterates all of the pending or delayed thread calls in the group,
 * which is really inefficient.
 *
 * This is deprecated, switch to an allocated thread call instead.
 */
extern boolean_t        thread_call_func_cancel(
	thread_call_func_t      func,
	thread_call_param_t     param,
	boolean_t               cancel_all);

/*
 * Called on the wake path to adjust the thread callouts running in mach_continuous_time
 */
extern void adjust_cont_time_thread_calls(void);

/* called by IOTimerEventSource to track when the workloop lock has been taken */
extern void thread_call_start_iotes_invocation(thread_call_t call);

__END_DECLS

#endif  /* XNU_KERNEL_PRIVATE */

#endif  /* _KERN_THREAD_CALL_H_ */