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