Subversion Repositories freemyipod

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14 theseven 1
//
2
//
3
//    Copyright 2010 TheSeven
4
//
5
//
6
//    This file is part of emBIOS.
7
//
8
//    emBIOS is free software: you can redistribute it and/or
9
//    modify it under the terms of the GNU General Public License as
10
//    published by the Free Software Foundation, either version 2 of the
11
//    License, or (at your option) any later version.
12
//
13
//    emBIOS is distributed in the hope that it will be useful,
14
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
//    See the GNU General Public License for more details.
17
//
18
//    You should have received a copy of the GNU General Public License along
19
//    with emBIOS.  If not, see <http://www.gnu.org/licenses/>.
20
//
21
//
22
 
23
 
24
#include "global.h"
25
#include "thread.h"
26
#include "timer.h"
27
#include "panic.h"
28
#include "util.h"
85 theseven 29
#ifdef HAVE_STORAGE
58 theseven 30
#include "dir.h"
31
#include "file.h"
85 theseven 32
#endif
130 theseven 33
#ifdef HAVE_BUTTON
34
#include "button.h"
35
#endif
14 theseven 36
 
37
 
15 theseven 38
struct scheduler_thread scheduler_threads[MAX_THREADS] IBSS_ATTR;
39
struct scheduler_thread* current_thread IBSS_ATTR;
40
uint32_t last_tick IBSS_ATTR;
34 theseven 41
bool scheduler_frozen IBSS_ATTR;
15 theseven 42
extern struct wakeup dbgwakeup;
14 theseven 43
 
44
 
45
void mutex_init(struct mutex* obj)
46
{
47
    memset(obj, 0, sizeof(struct mutex));
48
}
49
 
50
void mutex_add_to_queue(struct mutex* obj, struct scheduler_thread* thread)
51
{
52
    struct scheduler_thread* t;
15 theseven 53
    if (!obj->waiters || obj->waiters->priority <= thread->priority)
14 theseven 54
    {
55
        thread->queue_next = obj->waiters;
56
        obj->waiters = thread;
57
    }
58
    else
59
    {
60
        t = obj->waiters;
61
        while (t->queue_next && t->queue_next->priority > thread->priority)
62
            t = t->queue_next;
63
        thread->queue_next = t->queue_next;
64
        t->queue_next = thread;
65
    }
66
}
67
 
68
void mutex_remove_from_queue(struct mutex* obj, struct scheduler_thread* thread)
69
{
70
    struct scheduler_thread* t;
71
    if (!obj->waiters) return;
72
    if (obj->waiters == thread) obj->waiters = thread->queue_next;
73
    else
74
    {
75
        t = obj->waiters;
76
        while (t->queue_next)
77
        {
78
            if (t->queue_next == thread) t->queue_next = thread->queue_next;
79
            t = t->queue_next;
80
        }
81
    }
82
}
83
 
84
int mutex_lock(struct mutex* obj, int timeout)
85
{
86
    int ret = THREAD_OK;
87
    struct scheduler_thread* thread;
88
    uint32_t mode = enter_critical_section();
89
 
90
    if (!obj->count)
91
    {
92
        obj->count = 1;
93
        obj->owner = current_thread;
94
    }
95
    else if (obj->owner == current_thread) obj->count++;
96
    else
97
    {
98
        if (timeout)
99
        {
100
            current_thread->state = THREAD_BLOCKED;
101
            current_thread->block_type = THREAD_BLOCK_MUTEX;
102
            current_thread->blocked_by = obj;
103
            current_thread->timeout = timeout;
104
            current_thread->blocked_since = USEC_TIMER;
105
            mutex_add_to_queue(obj, current_thread);
106
            leave_critical_section(mode);
107
            context_switch();
108
            if (obj->owner != current_thread) return THREAD_TIMEOUT;
109
            return THREAD_OK;
110
        }
111
        else ret = THREAD_TIMEOUT;
112
    }
113
 
114
    leave_critical_section(mode);
115
    return ret;
116
}
117
 
118
int mutex_unlock(struct mutex* obj)
119
{
120
    int ret = THREAD_OK;
121
    uint32_t mode = enter_critical_section();
122
 
123
    if (!obj->count)
124
    {
125
        leave_critical_section(mode);
126
        panicf(PANIC_KILLTHREAD, "Trying to unlock non-owned mutex! (%08X)", obj);
127
    }
128
 
129
    if (obj->owner != current_thread)
130
    {
131
        leave_critical_section(mode);
132
        panicf(PANIC_KILLTHREAD, "Trying to unlock mutex owned by different thread! (%08X)", obj);
133
    }
134
 
135
    if (--(obj->count)) ret = obj->count;
136
    else if (obj->waiters)
137
    {
138
        obj->count = 1;
139
        obj->owner = obj->waiters;
140
        obj->waiters->state = THREAD_READY;
141
        obj->waiters->block_type = THREAD_NOT_BLOCKED;
142
        obj->waiters->blocked_by = NULL;
143
        obj->waiters->timeout = 0;
144
        obj->waiters = obj->waiters->queue_next;
145
    }
146
 
147
    leave_critical_section(mode);
148
    return ret;
149
}
150
 
151
void wakeup_init(struct wakeup* obj)
152
{
153
    memset(obj, 0, sizeof(struct wakeup));
154
}
155
 
156
int wakeup_wait(struct wakeup* obj, int timeout)
157
{
158
    int ret = THREAD_OK;
159
    uint32_t mode = enter_critical_section();
160
 
161
    if (obj->waiter)
162
    {
163
        leave_critical_section(mode);
164
        panicf(PANIC_KILLTHREAD, "Multiple threads waiting single wakeup! (%08X)", obj);
165
    }
166
 
167
    if (obj->signalled) obj->signalled = false;
168
    else
169
    {
170
        if (timeout)
171
        {
172
            current_thread->state = THREAD_BLOCKED;
173
            current_thread->block_type = THREAD_BLOCK_WAKEUP;
174
            current_thread->blocked_by = obj;
175
            current_thread->timeout = timeout;
176
            current_thread->blocked_since = USEC_TIMER;
177
            obj->waiter = current_thread;
178
            leave_critical_section(mode);
179
            context_switch();
15 theseven 180
            obj->waiter = NULL;
14 theseven 181
            if (!obj->signalled) return THREAD_TIMEOUT;
182
            obj->signalled = false;
183
            return THREAD_OK;
184
        }
185
        else ret = THREAD_TIMEOUT;
186
    }
187
 
188
    leave_critical_section(mode);
189
    return ret;
190
}
191
 
192
int wakeup_signal(struct wakeup* obj)
193
{
194
    int ret = THREAD_OK;
195
    uint32_t mode = enter_critical_section();
196
 
197
    obj->signalled = true;
198
    if (obj->waiter)
199
    {
200
        obj->waiter->state = THREAD_READY;
201
        obj->waiter->block_type = THREAD_NOT_BLOCKED;
202
        obj->waiter->blocked_by = NULL;
203
        obj->waiter->timeout = 0;
204
        ret = THREAD_FOUND;
205
    }
206
 
207
    leave_critical_section(mode);
208
    return ret;
209
}
210
 
211
void sleep(int usecs)
212
{
15 theseven 213
    if (usecs)
214
    {
215
        uint32_t mode = enter_critical_section();
216
        current_thread->state = THREAD_BLOCKED;
217
        current_thread->block_type = THREAD_BLOCK_SLEEP;
218
        current_thread->timeout = usecs;
219
        current_thread->blocked_since = USEC_TIMER;
220
        leave_critical_section(mode);
221
    }
14 theseven 222
    context_switch();
223
}
224
 
225
void scheduler_init(void)
226
{
227
    memset(scheduler_threads, 0, sizeof(scheduler_threads));
34 theseven 228
    scheduler_frozen = false;
14 theseven 229
    last_tick = USEC_TIMER;
230
    current_thread = scheduler_threads;
231
    current_thread->state = THREAD_RUNNING;
232
    current_thread->startusec = last_tick;
233
    current_thread->name = "idle thread";
234
    current_thread->stack = (uint32_t*)-1;
235
    setup_tick();
236
}
237
 
54 theseven 238
bool scheduler_freeze(bool value)
34 theseven 239
{
54 theseven 240
    bool old = scheduler_frozen;
34 theseven 241
    scheduler_frozen = value;
54 theseven 242
    return old;
34 theseven 243
}
244
 
389 theseven 245
void scheduler_pause_accounting()
246
{
247
    uint32_t usec = USEC_TIMER;
248
    current_thread->cputime_total += usec - current_thread->startusec;
249
    current_thread->cputime_current += usec - current_thread->startusec;
250
}
251
 
252
void scheduler_resume_accounting()
253
{
254
    current_thread->startusec = USEC_TIMER;
255
}
256
 
14 theseven 257
void scheduler_switch(int thread)
258
{
259
    int i;
260
    uint32_t score, best;
261
    uint32_t usec = USEC_TIMER;
262
    if (current_thread->state == THREAD_RUNNING) current_thread->state = THREAD_READY;
263
    if ((int)current_thread->stack != -1 && *current_thread->stack != 0xaffebeaf)
15 theseven 264
    {
265
        for (i = 0; i < MAX_THREADS; i++)
345 theseven 266
            if (scheduler_threads[i].state != THREAD_FREE)
267
                if (scheduler_threads[i].type == USER_THREAD)
268
                    scheduler_threads[i].state = THREAD_SUSPENDED;
15 theseven 269
        current_thread->state = THREAD_DEFUNCT;
270
        current_thread->block_type = THREAD_DEFUNCT_STKOV;
271
        wakeup_signal(&dbgwakeup);
272
    }
14 theseven 273
 
274
    if (usec - last_tick > SCHEDULER_TICK)
275
    {
392 theseven 276
        uint32_t diff = usec - last_tick;
15 theseven 277
        last_tick = usec;
14 theseven 278
        for (i = 0; i < MAX_THREADS; i++)
279
        {
392 theseven 280
            scheduler_threads[i].cpuload = 255 * scheduler_threads[i].cputime_current / diff;
14 theseven 281
            scheduler_threads[i].cputime_current = 0;
282
        }
283
    }
284
 
34 theseven 285
    if (scheduler_frozen) thread = 0;
14 theseven 286
    else
287
    {
288
        for (i = 0; i < MAX_THREADS; i++)
34 theseven 289
            if (scheduler_threads[i].state == THREAD_BLOCKED
290
             && scheduler_threads[i].timeout != -1
291
             && TIME_AFTER(usec, scheduler_threads[i].blocked_since
292
                               + scheduler_threads[i].timeout))
14 theseven 293
            {
34 theseven 294
                if (scheduler_threads[i].block_type == THREAD_BLOCK_MUTEX)
295
                    mutex_remove_from_queue((struct mutex*)scheduler_threads[i].blocked_by,
296
                                            &scheduler_threads[i]);
297
                scheduler_threads[i].state = THREAD_READY;
298
                scheduler_threads[i].block_type = THREAD_NOT_BLOCKED;
299
                scheduler_threads[i].blocked_by = NULL;
300
                scheduler_threads[i].timeout = 0;
301
            }
302
 
303
        if (thread >= 0 && thread < MAX_THREADS && scheduler_threads[thread].state == THREAD_READY)
304
            current_thread = &scheduler_threads[thread];
305
        else
306
        {
307
            thread = 0;
308
            best = 0xffffffff;
309
            for (i = 0; i < MAX_THREADS; i++)
310
                if (scheduler_threads[i].state == THREAD_READY && scheduler_threads[i].priority)
14 theseven 311
                {
34 theseven 312
                    score = scheduler_threads[i].cputime_current / scheduler_threads[i].priority;
313
                    if (score < best)
314
                    {
315
                        best = score;
316
                        thread = i;
317
                    }
14 theseven 318
                }
34 theseven 319
        }
14 theseven 320
    }
321
 
322
    current_thread = &scheduler_threads[thread];
323
    current_thread->state = THREAD_RUNNING;
324
}
325
 
326
int thread_create(const char* name, const void* code, void* stack,
15 theseven 327
                  int stacksize, enum thread_type type, int priority, bool run)
14 theseven 328
{
329
    int ret = NO_MORE_THREADS;
330
    int i;
331
 
332
    for (i = 0; i < stacksize >> 2; i ++) ((uint32_t*)stack)[i] = 0xaffebeaf;
333
 
334
    uint32_t mode = enter_critical_section();
335
 
336
    for (i = 0; i < MAX_THREADS; i++)
337
        if (scheduler_threads[i].state == THREAD_FREE)
338
        {
339
            ret = i;
340
            memset(&scheduler_threads[i], 0, sizeof(struct scheduler_thread));
341
            scheduler_threads[i].state = run ? THREAD_READY : THREAD_SUSPENDED;
15 theseven 342
            scheduler_threads[i].type = type;
14 theseven 343
            scheduler_threads[i].name = name;
344
            scheduler_threads[i].priority = priority;
43 theseven 345
            scheduler_threads[i].cpsr = 0x1f;
14 theseven 346
            scheduler_threads[i].regs[15] = (uint32_t)code;
347
            scheduler_threads[i].regs[14] = (uint32_t)thread_exit;
348
            scheduler_threads[i].regs[13] = (uint32_t)stack + stacksize;
349
            scheduler_threads[i].stack = stack;
350
            break;
351
        }
352
 
353
    leave_critical_section(mode);
354
    return ret;
355
}
356
 
357
int thread_suspend(int thread)
358
{
359
    int ret = THREAD_OK;
360
    struct scheduler_thread* t = &scheduler_threads[thread];
361
    bool needsswitch = false;
362
    uint32_t mode = enter_critical_section();
363
 
364
    if (thread == -1) t = current_thread;
365
    else if (thread < 0 || thread >= MAX_THREADS) ret = INVALID_THREAD;
366
    else if (t->state == THREAD_FREE) ret = INVALID_THREAD;
367
    else if (t->state == THREAD_SUSPENDED) ret = ALREADY_SUSPENDED;
368
    if (ret == THREAD_OK)
369
    {
370
        if (t->state == THREAD_RUNNING) needsswitch = true;
371
        else if (t->state == THREAD_BLOCKED)
372
        {
373
            if (t->block_type == THREAD_BLOCK_SLEEP)
15 theseven 374
            {
375
                if (t->timeout != -1) t->timeout -= USEC_TIMER - t->blocked_since;
376
            }
14 theseven 377
            else if (t->block_type == THREAD_BLOCK_MUTEX)
378
            {
379
                mutex_remove_from_queue((struct mutex*)t->blocked_by, t);
15 theseven 380
                if (t->timeout != -1) t->timeout -= USEC_TIMER - t->blocked_since;
14 theseven 381
            }
382
            else if (t->block_type == THREAD_BLOCK_WAKEUP)
15 theseven 383
            {
384
                if (t->timeout != -1) t->timeout -= USEC_TIMER - t->blocked_since;
385
            }
14 theseven 386
        }
387
        t->state = THREAD_SUSPENDED;
388
    }
389
 
390
    leave_critical_section(mode);
391
 
392
    if (needsswitch) context_switch();
393
 
394
    return ret;
395
}
396
 
397
int thread_resume(int thread)
398
{
399
    int ret = THREAD_OK;
400
    struct scheduler_thread* t = &scheduler_threads[thread];
401
    bool needsswitch = false;
402
    uint32_t mode = enter_critical_section();
403
 
404
    if (thread == -1) t = current_thread;
405
    else if (thread < 0 || thread >= MAX_THREADS) ret = INVALID_THREAD;
406
    else if (t->state == THREAD_FREE) ret = INVALID_THREAD;
407
    else if (t->state != THREAD_SUSPENDED) ret = ALREADY_RESUMED;
408
    if (ret == THREAD_OK)
409
    {
410
        if (t->block_type == THREAD_BLOCK_SLEEP)
411
            t->blocked_since = USEC_TIMER;
412
        else if (t->block_type == THREAD_BLOCK_MUTEX)
413
        {
414
            mutex_add_to_queue((struct mutex*)t->blocked_by, t);
415
            t->blocked_since = USEC_TIMER;
416
            t->state = THREAD_BLOCKED;
417
        }
418
        else if (t->block_type == THREAD_BLOCK_WAKEUP)
419
        {
420
            t->blocked_since = USEC_TIMER;
421
            t->state = THREAD_BLOCKED;
422
        }
423
        else t->state = THREAD_READY;
424
    }
425
 
426
    leave_critical_section(mode);
427
    return ret;
428
}
429
 
430
int thread_terminate(int thread)
431
{
432
    int ret = THREAD_OK;
433
    struct scheduler_thread* t = &scheduler_threads[thread];
434
    bool needsswitch = false;
435
    uint32_t mode = enter_critical_section();
436
 
437
    if (thread == -1) t = current_thread;
438
    else if (thread < 0 || thread >= MAX_THREADS) ret = INVALID_THREAD;
439
    else if (t->state == THREAD_FREE) ret = INVALID_THREAD;
440
    if (ret == THREAD_OK)
441
    {
442
        if (t->state == THREAD_RUNNING) needsswitch = true;
443
        else if (t->state == THREAD_BLOCKED)
444
        {
445
            if (t->block_type == THREAD_BLOCK_MUTEX)
446
                mutex_remove_from_queue((struct mutex*)t->blocked_by, t);
447
            else if (t->block_type == THREAD_BLOCK_WAKEUP)
448
                ((struct wakeup*)t->blocked_by)->waiter = NULL;
449
        }
450
        t->state = THREAD_FREE;
85 theseven 451
#ifdef HAVE_STORAGE
58 theseven 452
        close_all_of_process(t);
453
        closedir_all_of_process(t);
85 theseven 454
#endif
130 theseven 455
#ifdef HAVE_BUTTON
456
        button_unregister_all_of_thread(t);
457
#endif
14 theseven 458
    }
459
 
460
    leave_critical_section(mode);
461
 
462
    if (needsswitch) context_switch();
463
 
464
    return ret;
465
}
466
 
249 theseven 467
enum thread_state thread_get_state(int thread)
468
{
469
    return scheduler_threads[thread].state;
470
}
471
 
14 theseven 472
void thread_exit()
473
{
474
    thread_terminate(-1);
475
}
71 theseven 476
 
477
int* __errno()
478
{
479
    return &current_thread->err_no;
480
}