+/* Lock held */
+static thread_call_group_t
+thread_call_get_group(thread_call_t call)
+{
+ thread_call_index_t index = call->tc_index;
+
+ assert(index >= 0 && index < THREAD_CALL_INDEX_MAX);
+
+ return &thread_call_groups[index];
+}
+
+/* Lock held */
+static thread_call_flavor_t
+thread_call_get_flavor(thread_call_t call)
+{
+ return (call->tc_flags & THREAD_CALL_FLAG_CONTINUOUS) ? TCF_CONTINUOUS : TCF_ABSOLUTE;
+}
+
+/* Lock held */
+static thread_call_flavor_t
+thread_call_set_flavor(thread_call_t call, thread_call_flavor_t flavor)
+{
+ assert(flavor == TCF_CONTINUOUS || flavor == TCF_ABSOLUTE);
+ thread_call_flavor_t old_flavor = thread_call_get_flavor(call);
+
+ if (old_flavor != flavor) {
+ if (flavor == TCF_CONTINUOUS) {
+ call->tc_flags |= THREAD_CALL_FLAG_CONTINUOUS;
+ } else {
+ call->tc_flags &= ~THREAD_CALL_FLAG_CONTINUOUS;
+ }
+ }
+
+ return old_flavor;
+}
+
+/* returns true if it was on a queue */
+static bool
+thread_call_enqueue_tail(
+ thread_call_t call,
+ queue_t new_queue)
+{
+ queue_t old_queue = call->tc_queue;
+
+ thread_call_group_t group = thread_call_get_group(call);
+ thread_call_flavor_t flavor = thread_call_get_flavor(call);
+
+ if (old_queue != NULL &&
+ old_queue != &group->delayed_queues[flavor]) {
+ panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
+ }
+
+ if (old_queue == &group->delayed_queues[flavor]) {
+ priority_queue_remove(&group->delayed_pqueues[flavor], &call->tc_pqlink);
+ }
+
+ if (old_queue == NULL) {
+ enqueue_tail(new_queue, &call->tc_qlink);
+ } else {
+ re_queue_tail(new_queue, &call->tc_qlink);
+ }
+
+ call->tc_queue = new_queue;
+
+ return old_queue != NULL;
+}
+
+static queue_head_t *
+thread_call_dequeue(
+ thread_call_t call)
+{
+ queue_t old_queue = call->tc_queue;
+
+ thread_call_group_t group = thread_call_get_group(call);
+ thread_call_flavor_t flavor = thread_call_get_flavor(call);
+
+ if (old_queue != NULL &&
+ old_queue != &group->pending_queue &&
+ old_queue != &group->delayed_queues[flavor]) {
+ panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
+ }
+
+ if (old_queue == &group->delayed_queues[flavor]) {
+ priority_queue_remove(&group->delayed_pqueues[flavor], &call->tc_pqlink);
+ }
+
+ if (old_queue != NULL) {
+ remqueue(&call->tc_qlink);
+
+ call->tc_queue = NULL;
+ }
+ return old_queue;
+}
+
+static queue_head_t *
+thread_call_enqueue_deadline(
+ thread_call_t call,
+ thread_call_group_t group,
+ thread_call_flavor_t flavor,
+ uint64_t deadline)
+{
+ queue_t old_queue = call->tc_queue;
+ queue_t new_queue = &group->delayed_queues[flavor];
+
+ thread_call_flavor_t old_flavor = thread_call_set_flavor(call, flavor);
+
+ if (old_queue != NULL &&
+ old_queue != &group->pending_queue &&
+ old_queue != &group->delayed_queues[old_flavor]) {
+ panic("thread call (%p) on bad queue (old_queue: %p)", call, old_queue);
+ }
+
+ if (old_queue == new_queue) {
+ /* optimize the same-queue case to avoid a full re-insert */
+ uint64_t old_deadline = call->tc_pqlink.deadline;
+ call->tc_pqlink.deadline = deadline;
+
+ if (old_deadline < deadline) {
+ priority_queue_entry_increased(&group->delayed_pqueues[flavor],
+ &call->tc_pqlink);
+ } else {
+ priority_queue_entry_decreased(&group->delayed_pqueues[flavor],
+ &call->tc_pqlink);
+ }
+ } else {
+ if (old_queue == &group->delayed_queues[old_flavor]) {
+ priority_queue_remove(&group->delayed_pqueues[old_flavor],
+ &call->tc_pqlink);
+ }
+
+ call->tc_pqlink.deadline = deadline;
+
+ priority_queue_insert(&group->delayed_pqueues[flavor], &call->tc_pqlink);
+ }
+
+ if (old_queue == NULL) {
+ enqueue_tail(new_queue, &call->tc_qlink);
+ } else if (old_queue != new_queue) {
+ re_queue_tail(new_queue, &call->tc_qlink);
+ }
+
+ call->tc_queue = new_queue;
+
+ return old_queue;
+}
+
+uint64_t
+thread_call_get_armed_deadline(thread_call_t call)
+{
+ return call->tc_pqlink.deadline;
+}
+
+
+static bool