1 /*
2  * Copyright (c) 2008-2015 Travis Geiselbrecht
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include <debug.h>
24 #include <trace.h>
25 #include <rand.h>
26 #include <err.h>
27 #include <assert.h>
28 #include <string.h>
29 #include <app/tests.h>
30 #include <kernel/thread.h>
31 #include <kernel/mutex.h>
32 #include <kernel/semaphore.h>
33 #include <kernel/event.h>
34 #include <platform.h>
35 
sleep_thread(void * arg)36 static int sleep_thread(void *arg)
37 {
38     for (;;) {
39         printf("sleeper %p\n", get_current_thread());
40         thread_sleep(rand() % 500);
41     }
42     return 0;
43 }
44 
sleep_test(void)45 int sleep_test(void)
46 {
47     int i;
48     for (i=0; i < 16; i++)
49         thread_detach_and_resume(thread_create("sleeper", &sleep_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
50     return 0;
51 }
52 
53 static semaphore_t sem;
54 static const int sem_total_its = 10000;
55 static const int sem_thread_max_its = 1000;
56 static const int sem_start_value = 10;
57 static int sem_remaining_its = 0;
58 static int sem_threads = 0;
59 static mutex_t sem_test_mutex;
60 
semaphore_producer(void * unused)61 static int semaphore_producer(void *unused)
62 {
63     printf("semaphore producer %p starting up, running for %d iterations\n", get_current_thread(), sem_total_its);
64 
65     for (int x = 0; x < sem_total_its; x++) {
66         sem_post(&sem, true);
67     }
68 
69     return 0;
70 }
71 
semaphore_consumer(void * unused)72 static int semaphore_consumer(void *unused)
73 {
74     unsigned int iterations = 0;
75 
76     mutex_acquire(&sem_test_mutex);
77     if (sem_remaining_its >= sem_thread_max_its) {
78         iterations = rand();
79         iterations %= sem_thread_max_its;
80     } else {
81         iterations = sem_remaining_its;
82     }
83     sem_remaining_its -= iterations;
84     mutex_release(&sem_test_mutex);
85 
86     printf("semaphore consumer %p starting up, running for %u iterations\n", get_current_thread(), iterations);
87     for (unsigned int x = 0; x < iterations; x++)
88         sem_wait(&sem);
89     printf("semaphore consumer %p done\n", get_current_thread());
90     atomic_add(&sem_threads, -1);
91     return 0;
92 }
93 
semaphore_test(void)94 static int semaphore_test(void)
95 {
96     static semaphore_t isem = SEMAPHORE_INITIAL_VALUE(isem, 99);
97     printf("preinitialized semaphore:\n");
98     hexdump(&isem, sizeof(isem));
99 
100     sem_init(&sem, sem_start_value);
101     mutex_init(&sem_test_mutex);
102 
103     sem_remaining_its = sem_total_its;
104     while (1) {
105         mutex_acquire(&sem_test_mutex);
106         if (sem_remaining_its) {
107             thread_detach_and_resume(thread_create("semaphore consumer", &semaphore_consumer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
108             atomic_add(&sem_threads, 1);
109         } else {
110             mutex_release(&sem_test_mutex);
111             break;
112         }
113         mutex_release(&sem_test_mutex);
114     }
115 
116     thread_detach_and_resume(thread_create("semaphore producer", &semaphore_producer, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
117 
118     while (sem_threads)
119         thread_yield();
120 
121     if (sem.count == sem_start_value)
122         printf("semaphore tests successfully complete\n");
123     else
124         printf("semaphore tests failed: %d != %d\n", sem.count, sem_start_value);
125 
126     sem_destroy(&sem);
127     mutex_destroy(&sem_test_mutex);
128 
129     return 0;
130 }
131 
mutex_thread(void * arg)132 static int mutex_thread(void *arg)
133 {
134     int i;
135     const int iterations = 1000000;
136 
137     static volatile int shared = 0;
138 
139     mutex_t *m = (mutex_t *)arg;
140 
141     printf("mutex tester thread %p starting up, will go for %d iterations\n", get_current_thread(), iterations);
142 
143     for (i = 0; i < iterations; i++) {
144         mutex_acquire(m);
145 
146         if (shared != 0)
147             panic("someone else has messed with the shared data\n");
148 
149         shared = (intptr_t)get_current_thread();
150         thread_yield();
151         shared = 0;
152 
153         mutex_release(m);
154         thread_yield();
155     }
156 
157     return 0;
158 }
159 
mutex_timeout_thread(void * arg)160 static int mutex_timeout_thread(void *arg)
161 {
162     mutex_t *timeout_mutex = (mutex_t *)arg;
163     status_t err;
164 
165     printf("mutex_timeout_thread acquiring mutex %p with 1 second timeout\n", timeout_mutex);
166     err = mutex_acquire_timeout(timeout_mutex, 1000);
167     if (err == ERR_TIMED_OUT)
168         printf("mutex_acquire_timeout returns with TIMEOUT\n");
169     else
170         printf("mutex_acquire_timeout returns %d\n", err);
171 
172     return err;
173 }
174 
mutex_zerotimeout_thread(void * arg)175 static int mutex_zerotimeout_thread(void *arg)
176 {
177     mutex_t *timeout_mutex = (mutex_t *)arg;
178     status_t err;
179 
180     printf("mutex_zerotimeout_thread acquiring mutex %p with zero second timeout\n", timeout_mutex);
181     err = mutex_acquire_timeout(timeout_mutex, 0);
182     if (err == ERR_TIMED_OUT)
183         printf("mutex_acquire_timeout returns with TIMEOUT\n");
184     else
185         printf("mutex_acquire_timeout returns %d\n", err);
186 
187     return err;
188 }
189 
mutex_test(void)190 int mutex_test(void)
191 {
192     static mutex_t imutex = MUTEX_INITIAL_VALUE(imutex);
193     printf("preinitialized mutex:\n");
194     hexdump(&imutex, sizeof(imutex));
195 
196     mutex_t m;
197     mutex_init(&m);
198 
199     thread_t *threads[5];
200 
201     for (uint i=0; i < countof(threads); i++) {
202         threads[i] = thread_create("mutex tester", &mutex_thread, &m, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
203         thread_resume(threads[i]);
204     }
205 
206     for (uint i=0; i < countof(threads); i++) {
207         thread_join(threads[i], NULL, INFINITE_TIME);
208     }
209 
210     printf("done with simple mutex tests\n");
211 
212     printf("testing mutex timeout\n");
213 
214     mutex_t timeout_mutex;
215 
216     mutex_init(&timeout_mutex);
217     mutex_acquire(&timeout_mutex);
218 
219     for (uint i=0; i < 2; i++) {
220         threads[i] = thread_create("mutex timeout tester", &mutex_timeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
221         thread_resume(threads[i]);
222     }
223 
224     for (uint i=2; i < 4; i++) {
225         threads[i] = thread_create("mutex timeout tester", &mutex_zerotimeout_thread, (void *)&timeout_mutex, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
226         thread_resume(threads[i]);
227     }
228 
229     thread_sleep(5000);
230     mutex_release(&timeout_mutex);
231 
232     for (uint i=0; i < 4; i++) {
233         thread_join(threads[i], NULL, INFINITE_TIME);
234     }
235 
236     printf("done with mutex tests\n");
237 
238     mutex_destroy(&timeout_mutex);
239 
240     return 0;
241 }
242 
243 static event_t e;
244 
event_signaler(void * arg)245 static int event_signaler(void *arg)
246 {
247     printf("event signaler pausing\n");
248     thread_sleep(1000);
249 
250 //  for (;;) {
251     printf("signaling event\n");
252     event_signal(&e, true);
253     printf("done signaling event\n");
254     thread_yield();
255 //  }
256 
257     return 0;
258 }
259 
event_waiter(void * arg)260 static int event_waiter(void *arg)
261 {
262     int count = (intptr_t)arg;
263 
264     printf("event waiter starting\n");
265 
266     while (count > 0) {
267         printf("%p: waiting on event...\n", get_current_thread());
268         if (event_wait(&e) < 0) {
269             printf("%p: event_wait() returned error\n", get_current_thread());
270             return -1;
271         }
272         printf("%p: done waiting on event...\n", get_current_thread());
273         thread_yield();
274         count--;
275     }
276 
277     return 0;
278 }
279 
event_test(void)280 void event_test(void)
281 {
282     thread_t *threads[5];
283 
284     static event_t ievent = EVENT_INITIAL_VALUE(ievent, true, 0x1234);
285     printf("preinitialized event:\n");
286     hexdump(&ievent, sizeof(ievent));
287 
288     printf("event tests starting\n");
289 
290     /* make sure signaling the event wakes up all the threads */
291     event_init(&e, false, 0);
292     threads[0] = thread_create("event signaler", &event_signaler, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
293     threads[1] = thread_create("event waiter 0", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
294     threads[2] = thread_create("event waiter 1", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
295     threads[3] = thread_create("event waiter 2", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
296     threads[4] = thread_create("event waiter 3", &event_waiter, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
297 
298     for (uint i = 0; i < countof(threads); i++)
299         thread_resume(threads[i]);
300 
301     thread_sleep(2000);
302     printf("destroying event\n");
303     event_destroy(&e);
304 
305     for (uint i = 0; i < countof(threads); i++)
306         thread_join(threads[i], NULL, INFINITE_TIME);
307 
308     /* make sure signaling the event wakes up precisely one thread */
309     event_init(&e, false, EVENT_FLAG_AUTOUNSIGNAL);
310     threads[0] = thread_create("event signaler", &event_signaler, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
311     threads[1] = thread_create("event waiter 0", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
312     threads[2] = thread_create("event waiter 1", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
313     threads[3] = thread_create("event waiter 2", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
314     threads[4] = thread_create("event waiter 3", &event_waiter, (void *)99, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
315 
316     for (uint i = 0; i < countof(threads); i++)
317         thread_resume(threads[i]);
318 
319     thread_sleep(2000);
320     event_destroy(&e);
321 
322     for (uint i = 0; i < countof(threads); i++)
323         thread_join(threads[i], NULL, INFINITE_TIME);
324 
325     printf("event tests done\n");
326 }
327 
quantum_tester(void * arg)328 static int quantum_tester(void *arg)
329 {
330     for (;;) {
331         printf("%p: in this thread. rq %d\n", get_current_thread(), get_current_thread()->remaining_quantum);
332     }
333     return 0;
334 }
335 
quantum_test(void)336 void quantum_test(void)
337 {
338     thread_detach_and_resume(thread_create("quantum tester 0", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
339     thread_detach_and_resume(thread_create("quantum tester 1", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
340     thread_detach_and_resume(thread_create("quantum tester 2", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
341     thread_detach_and_resume(thread_create("quantum tester 3", &quantum_tester, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
342 }
343 
344 static event_t context_switch_event;
345 static event_t context_switch_done_event;
346 
context_switch_tester(void * arg)347 static int context_switch_tester(void *arg)
348 {
349     int i;
350     uint total_count = 0;
351     const int iter = 100000;
352     int thread_count = (intptr_t)arg;
353 
354     event_wait(&context_switch_event);
355 
356     uint count = arch_cycle_count();
357     for (i = 0; i < iter; i++) {
358         thread_yield();
359     }
360     total_count += arch_cycle_count() - count;
361     thread_sleep(1000);
362     printf("took %u cycles to yield %d times, %u per yield, %u per yield per thread\n",
363            total_count, iter, total_count / iter, total_count / iter / thread_count);
364 
365     event_signal(&context_switch_done_event, true);
366 
367     return 0;
368 }
369 
context_switch_test(void)370 void context_switch_test(void)
371 {
372     event_init(&context_switch_event, false, 0);
373     event_init(&context_switch_done_event, false, 0);
374 
375     thread_detach_and_resume(thread_create("context switch idle", &context_switch_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
376     thread_sleep(100);
377     event_signal(&context_switch_event, true);
378     event_wait(&context_switch_done_event);
379     thread_sleep(100);
380 
381     event_unsignal(&context_switch_event);
382     event_unsignal(&context_switch_done_event);
383     thread_detach_and_resume(thread_create("context switch 2a", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
384     thread_detach_and_resume(thread_create("context switch 2b", &context_switch_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
385     thread_sleep(100);
386     event_signal(&context_switch_event, true);
387     event_wait(&context_switch_done_event);
388     thread_sleep(100);
389 
390     event_unsignal(&context_switch_event);
391     event_unsignal(&context_switch_done_event);
392     thread_detach_and_resume(thread_create("context switch 4a", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
393     thread_detach_and_resume(thread_create("context switch 4b", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
394     thread_detach_and_resume(thread_create("context switch 4c", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
395     thread_detach_and_resume(thread_create("context switch 4d", &context_switch_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
396     thread_sleep(100);
397     event_signal(&context_switch_event, true);
398     event_wait(&context_switch_done_event);
399     thread_sleep(100);
400 }
401 
402 static volatile int atomic;
403 static volatile int atomic_count;
404 
atomic_tester(void * arg)405 static int atomic_tester(void *arg)
406 {
407     int add = (intptr_t)arg;
408     int i;
409 
410     const int iter = 10000000;
411 
412     TRACEF("add %d, %d iterations\n", add, iter);
413 
414     for (i=0; i < iter; i++) {
415         atomic_add(&atomic, add);
416     }
417 
418     int old = atomic_add(&atomic_count, -1);
419     TRACEF("exiting, old count %d\n", old);
420 
421     return 0;
422 }
423 
atomic_test(void)424 static void atomic_test(void)
425 {
426     atomic = 0;
427     atomic_count = 8;
428 
429     printf("testing atomic routines\n");
430 
431     thread_t *threads[8];
432     threads[0] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
433     threads[1] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
434     threads[2] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
435     threads[3] = thread_create("atomic tester 1", &atomic_tester, (void *)1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
436     threads[4] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
437     threads[5] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
438     threads[6] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
439     threads[7] = thread_create("atomic tester 2", &atomic_tester, (void *)-1, LOW_PRIORITY, DEFAULT_STACK_SIZE);
440 
441     /* start all the threads */
442     for (uint i = 0; i < countof(threads); i++)
443         thread_resume(threads[i]);
444 
445     /* wait for them to all stop */
446     for (uint i = 0; i < countof(threads); i++) {
447         thread_join(threads[i], NULL, INFINITE_TIME);
448     }
449 
450     printf("atomic count == %d (should be zero)\n", atomic);
451 }
452 
453 static volatile int preempt_count;
454 
preempt_tester(void * arg)455 static int preempt_tester(void *arg)
456 {
457     spin(1000000);
458 
459     printf("exiting ts %lld\n", current_time_hires());
460 
461     atomic_add(&preempt_count, -1);
462 #undef COUNT
463 
464     return 0;
465 }
466 
preempt_test(void)467 static void preempt_test(void)
468 {
469     /* create 5 threads, let them run. If the system is properly timer preempting,
470      * the threads should interleave each other at a fine enough granularity so
471      * that they complete at roughly the same time. */
472     printf("testing preemption\n");
473 
474     preempt_count = 5;
475 
476     for (int i = 0; i < preempt_count; i++)
477         thread_detach_and_resume(thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE));
478 
479     while (preempt_count > 0) {
480         thread_sleep(1000);
481     }
482 
483     printf("done with preempt test, above time stamps should be very close\n");
484 
485     /* do the same as above, but mark the threads as real time, which should
486      * effectively disable timer based preemption for them. They should
487      * complete in order, about a second apart. */
488     printf("testing real time preemption\n");
489 
490     preempt_count = 5;
491 
492     for (int i = 0; i < preempt_count; i++) {
493         thread_t *t = thread_create("preempt tester", &preempt_tester, NULL, LOW_PRIORITY, DEFAULT_STACK_SIZE);
494         thread_set_real_time(t);
495         thread_detach_and_resume(t);
496     }
497 
498     while (preempt_count > 0) {
499         thread_sleep(1000);
500     }
501 
502     printf("done with real-time preempt test, above time stamps should be 1 second apart\n");
503 }
504 
join_tester(void * arg)505 static int join_tester(void *arg)
506 {
507     long val = (long)arg;
508 
509     printf("\t\tjoin tester starting\n");
510     thread_sleep(500);
511     printf("\t\tjoin tester exiting with result %ld\n", val);
512 
513     return val;
514 }
515 
join_tester_server(void * arg)516 static int join_tester_server(void *arg)
517 {
518     int ret;
519     status_t err;
520     thread_t *t;
521 
522     printf("\ttesting thread_join/thread_detach\n");
523 
524     printf("\tcreating and waiting on thread to exit with thread_join\n");
525     t = thread_create("join tester", &join_tester, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
526     thread_resume(t);
527     ret = 99;
528     printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
529     err = thread_join(t, &ret, INFINITE_TIME);
530     printf("\tthread_join returns err %d, retval %d\n", err, ret);
531     printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
532 
533     printf("\tcreating and waiting on thread to exit with thread_join, after thread has exited\n");
534     t = thread_create("join tester", &join_tester, (void *)2, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
535     thread_resume(t);
536     thread_sleep(1000); // wait until thread is already dead
537     ret = 99;
538     printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
539     err = thread_join(t, &ret, INFINITE_TIME);
540     printf("\tthread_join returns err %d, retval %d\n", err, ret);
541     printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
542 
543     printf("\tcreating a thread, detaching it, let it exit on its own\n");
544     t = thread_create("join tester", &join_tester, (void *)3, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
545     thread_detach(t);
546     thread_resume(t);
547     thread_sleep(1000); // wait until the thread should be dead
548     printf("\tthread magic is 0x%x (should be 0)\n", t->magic);
549 
550     printf("\tcreating a thread, detaching it after it should be dead\n");
551     t = thread_create("join tester", &join_tester, (void *)4, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
552     thread_resume(t);
553     thread_sleep(1000); // wait until thread is already dead
554     printf("\tthread magic is 0x%x (should be 0x%x)\n", t->magic, THREAD_MAGIC);
555     thread_detach(t);
556     printf("\tthread magic is 0x%x\n", t->magic);
557 
558     printf("\texiting join tester server\n");
559 
560     return 55;
561 }
562 
join_test(void)563 static void join_test(void)
564 {
565     int ret;
566     status_t err;
567     thread_t *t;
568 
569     printf("testing thread_join/thread_detach\n");
570 
571     printf("creating thread join server thread\n");
572     t = thread_create("join tester server", &join_tester_server, (void *)1, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
573     thread_resume(t);
574     ret = 99;
575     err = thread_join(t, &ret, INFINITE_TIME);
576     printf("thread_join returns err %d, retval %d (should be 0 and 55)\n", err, ret);
577 }
578 
spinlock_test(void)579 static void spinlock_test(void)
580 {
581     spin_lock_saved_state_t state;
582     spin_lock_t lock;
583 
584     spin_lock_init(&lock);
585 
586     // verify basic functionality (single core)
587     printf("testing spinlock:\n");
588     ASSERT(!spin_lock_held(&lock));
589     ASSERT(!arch_ints_disabled());
590     spin_lock_irqsave(&lock, state);
591     ASSERT(arch_ints_disabled());
592     ASSERT(spin_lock_held(&lock));
593     spin_unlock_irqrestore(&lock, state);
594     ASSERT(!spin_lock_held(&lock));
595     ASSERT(!arch_ints_disabled());
596     printf("seems to work\n");
597 
598 #define COUNT (1024*1024)
599     uint32_t c = arch_cycle_count();
600     for (uint i = 0; i < COUNT; i++) {
601         spin_lock(&lock);
602         spin_unlock(&lock);
603     }
604     c = arch_cycle_count() - c;
605 
606     printf("%u cycles to acquire/release lock %u times (%u cycles per)\n", c, COUNT, c / COUNT);
607 
608     c = arch_cycle_count();
609     for (uint i = 0; i < COUNT; i++) {
610         spin_lock_irqsave(&lock, state);
611         spin_unlock_irqrestore(&lock, state);
612     }
613     c = arch_cycle_count() - c;
614 
615     printf("%u cycles to acquire/release lock w/irqsave %u times (%u cycles per)\n", c, COUNT, c / COUNT);
616 #undef COUNT
617 }
618 
thread_tests(void)619 int thread_tests(void)
620 {
621     mutex_test();
622     semaphore_test();
623     event_test();
624 
625     spinlock_test();
626     atomic_test();
627 
628     thread_sleep(200);
629     context_switch_test();
630 
631     preempt_test();
632 
633     join_test();
634 
635     return 0;
636 }
637 
spinner_thread(void * arg)638 static int spinner_thread(void *arg)
639 {
640     for (;;)
641         ;
642 
643     return 0;
644 }
645 
spinner(int argc,const cmd_args * argv)646 int spinner(int argc, const cmd_args *argv)
647 {
648     if (argc < 2) {
649         printf("not enough args\n");
650         printf("usage: %s <priority> <rt>\n", argv[0].str);
651         return -1;
652     }
653 
654     thread_t *t = thread_create("spinner", spinner_thread, NULL, argv[1].u, DEFAULT_STACK_SIZE);
655     if (!t)
656         return ERR_NO_MEMORY;
657 
658     if (argc >= 3 && !strcmp(argv[2].str, "rt")) {
659         thread_set_real_time(t);
660     }
661     thread_resume(t);
662 
663     return 0;
664 }
665