+static void
+thread_resource_exception(const void *arg0, __unused const void *arg1)
+{
+ thread_t thread = current_thread();
+ int code = (int)((uintptr_t)arg0 & ((int)-1));
+
+ assert(thread->t_threadledger != LEDGER_NULL);
+
+ /*
+ * Disable the exception notification so we don't overwhelm
+ * the listener with an endless stream of redundant exceptions.
+ */
+ ledger_set_action(thread->t_threadledger, thread_ledgers.cpu_time,
+ LEDGER_ACTION_IGNORE);
+ ledger_disable_callback(thread->t_threadledger, thread_ledgers.cpu_time);
+
+ /* XXX code should eventually be a user-exported namespace of resources */
+ (void) task_exception_notify(EXC_RESOURCE, code, 0);
+}
+
+void
+init_thread_ledgers(void) {
+ ledger_template_t t;
+ int idx;
+
+ assert(thread_ledger_template == NULL);
+
+ if ((t = ledger_template_create("Per-thread ledger")) == NULL)
+ panic("couldn't create thread ledger template");
+
+ if ((idx = ledger_entry_add(t, "cpu_time", "sched", "ns")) < 0) {
+ panic("couldn't create cpu_time entry for thread ledger template");
+ }
+
+ if (ledger_set_callback(t, idx, thread_resource_exception,
+ (void *)(uintptr_t)idx, NULL) < 0) {
+ panic("couldn't set thread ledger callback for cpu_time entry");
+ }
+
+ thread_ledgers.cpu_time = idx;
+ thread_ledger_template = t;
+}
+
+/*
+ * Set CPU usage limit on a thread.
+ *
+ * Calling with percentage of 0 will unset the limit for this thread.
+ */
+
+int
+thread_set_cpulimit(int action, uint8_t percentage, uint64_t interval_ns)
+{
+ thread_t thread = current_thread();
+ ledger_t l;
+ uint64_t limittime = 0;
+ uint64_t abstime = 0;
+
+ assert(percentage <= 100);
+
+ if (percentage == 0) {
+ /*
+ * Remove CPU limit, if any exists.
+ */
+ if (thread->t_threadledger != LEDGER_NULL) {
+ /*
+ * The only way to get a per-thread ledger is via CPU limits.
+ */
+ assert(thread->options & (TH_OPT_PROC_CPULIMIT | TH_OPT_PRVT_CPULIMIT));
+ ledger_dereference(thread->t_threadledger);
+ thread->t_threadledger = LEDGER_NULL;
+ thread->options &= ~(TH_OPT_PROC_CPULIMIT | TH_OPT_PRVT_CPULIMIT);
+ }
+
+ return (0);
+ }
+
+ l = thread->t_threadledger;
+ if (l == LEDGER_NULL) {
+ /*
+ * This thread doesn't yet have a per-thread ledger; so create one with the CPU time entry active.
+ */
+ if ((l = ledger_instantiate(thread_ledger_template, LEDGER_CREATE_INACTIVE_ENTRIES)) == LEDGER_NULL)
+ return (KERN_RESOURCE_SHORTAGE);
+
+ /*
+ * We are the first to create this thread's ledger, so only activate our entry.
+ */
+ ledger_entry_setactive(l, thread_ledgers.cpu_time);
+ thread->t_threadledger = l;
+ }
+
+ /*
+ * The limit is specified as a percentage of CPU over an interval in nanoseconds.
+ * Calculate the amount of CPU time that the thread needs to consume in order to hit the limit.
+ */
+ limittime = (interval_ns * percentage) / 100;
+ nanoseconds_to_absolutetime(limittime, &abstime);
+ ledger_set_limit(l, thread_ledgers.cpu_time, abstime);
+ /*
+ * Refill the thread's allotted CPU time every interval_ns nanoseconds.
+ */
+ ledger_set_period(l, thread_ledgers.cpu_time, interval_ns);
+
+ /*
+ * Ledgers supports multiple actions for one ledger entry, so we do too.
+ */
+ if (action == THREAD_CPULIMIT_EXCEPTION) {
+ thread->options |= TH_OPT_PROC_CPULIMIT;
+ ledger_set_action(l, thread_ledgers.cpu_time, LEDGER_ACTION_EXCEPTION);
+ }
+
+ if (action == THREAD_CPULIMIT_BLOCK) {
+ thread->options |= TH_OPT_PRVT_CPULIMIT;
+ /* The per-thread ledger template by default has a callback for CPU time */
+ ledger_disable_callback(l, thread_ledgers.cpu_time);
+ ledger_set_action(l, thread_ledgers.cpu_time, LEDGER_ACTION_BLOCK);
+ }
+
+ thread->t_threadledger = l;
+ return (0);
+}
+