]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/test_mpsc_queue.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / test_mpsc_queue.c
CommitLineData
cb323159
A
1/*
2 * Copyright (c) 2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <machine/machine_cpu.h>
30#include <kern/locks.h>
31#include <kern/mpsc_queue.h>
32#include <kern/thread.h>
33
34#if !DEBUG && !DEVELOPMENT
35#error "Test only file"
36#endif
37
38#include <sys/errno.h>
39
40struct mpsc_test_pingpong_queue {
41 struct mpsc_daemon_queue queue;
42 struct mpsc_queue_chain link;
43 struct mpsc_test_pingpong_queue *other;
44 uint64_t *count, *end;
45};
46
47static void
48mpsc_test_pingpong_invoke(mpsc_queue_chain_t elm, mpsc_daemon_queue_t dq)
49{
50 struct mpsc_test_pingpong_queue *q;
51 q = mpsc_queue_element(elm, struct mpsc_test_pingpong_queue, link);
52 assert(&q->queue == dq);
53
54 if (*q->count % 10000 == 0) {
55 printf("mpsc_test_pingpong: %lld asyncs left\n", *q->count);
56 }
57 if ((*q->count)-- > 0) {
58 mpsc_daemon_enqueue(&q->other->queue, &q->other->link,
59 MPSC_QUEUE_DISABLE_PREEMPTION);
60 } else {
61 *q->end = mach_absolute_time();
62 thread_wakeup(&mpsc_test_pingpong_invoke);
63 }
64}
65
66/*
67 * The point of this test is to exercise the enqueue/unlock-drain race
68 * since the MPSC queue tries to mimize wakeups when it knows it's useless.
69 *
70 * It also ensures basic enqueue properties,
71 * and will panic if anything goes wrong to help debugging state.
72 *
73 * Performance wise, we will always go through the wakeup codepath,
74 * hence this is mostly a benchmark of
75 * assert_wait()/clear_wait()/thread_block()/thread_wakeup()
76 * rather than a benchmark of the MPSC queues.
77 */
78int
79mpsc_test_pingpong(uint64_t count, uint64_t *out)
80{
81 struct mpsc_test_pingpong_queue ping, pong;
82 kern_return_t kr;
83 wait_result_t wr;
84
85 if (count < 1000 || count > 1000 * 1000) {
86 return EINVAL;
87 }
88
89 printf("mpsc_test_pingpong: START\n");
90
91 kr = mpsc_daemon_queue_init_with_thread(&ping.queue,
92 mpsc_test_pingpong_invoke, MINPRI_KERNEL, "ping");
93 if (kr != KERN_SUCCESS) {
94 panic("mpsc_test_pingpong: unable to create pong: %x", kr);
95 }
96
97 kr = mpsc_daemon_queue_init_with_thread(&pong.queue,
98 mpsc_test_pingpong_invoke, MINPRI_KERNEL, "pong");
99 if (kr != KERN_SUCCESS) {
100 panic("mpsc_test_pingpong: unable to create ping: %x", kr);
101 }
102
103 uint64_t n = count, start, end;
104 ping.count = pong.count = &n;
105 ping.end = pong.end = &end;
106 ping.other = &pong;
107 pong.other = &ping;
108
109 assert_wait_timeout(&mpsc_test_pingpong_invoke, THREAD_UNINT,
110 5000, 1000 * NSEC_PER_USEC);
111 start = mach_absolute_time();
112 mpsc_daemon_enqueue(&ping.queue, &ping.link, MPSC_QUEUE_DISABLE_PREEMPTION);
113
114 wr = thread_block(THREAD_CONTINUE_NULL);
115 if (wr == THREAD_TIMED_OUT) {
116 panic("mpsc_test_pingpong: timed out: ping:%p pong:%p", &ping, &pong);
117 }
118
119 printf("mpsc_test_pingpong: CLEANUP\n");
120
121 mpsc_daemon_queue_cancel_and_wait(&ping.queue);
122 mpsc_daemon_queue_cancel_and_wait(&pong.queue);
123 absolutetime_to_nanoseconds(end - start, out);
124
125 printf("mpsc_test_pingpong: %lld ping-pongs in %lld ns (%lld.%03lld us/async)\n",
126 count, *out, (*out / count) / 1000, (*out / count) % 1000);
127 return 0;
128}