summaryrefslogtreecommitdiff
path: root/firmware/kernel/mutex.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/kernel/mutex.c')
-rw-r--r--firmware/kernel/mutex.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/firmware/kernel/mutex.c b/firmware/kernel/mutex.c
new file mode 100644
index 0000000000..f1e4b3c722
--- /dev/null
+++ b/firmware/kernel/mutex.c
@@ -0,0 +1,152 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22
23/****************************************************************************
24 * Simple mutex functions ;)
25 ****************************************************************************/
26
27#include <stdbool.h>
28#include "config.h"
29#include "system.h"
30#include "mutex.h"
31#include "corelock.h"
32#include "thread-internal.h"
33#include "kernel-internal.h"
34
35static inline void __attribute__((always_inline))
36mutex_set_thread(struct mutex *mtx, struct thread_entry *td)
37{
38#ifdef HAVE_PRIORITY_SCHEDULING
39 mtx->blocker.thread = td;
40#else
41 mtx->thread = td;
42#endif
43}
44
45static inline struct thread_entry * __attribute__((always_inline))
46mutex_get_thread(volatile struct mutex *mtx)
47{
48#ifdef HAVE_PRIORITY_SCHEDULING
49 return mtx->blocker.thread;
50#else
51 return mtx->thread;
52#endif
53}
54
55/* Initialize a mutex object - call before any use and do not call again once
56 * the object is available to other threads */
57void mutex_init(struct mutex *m)
58{
59 corelock_init(&m->cl);
60 m->queue = NULL;
61 m->recursion = 0;
62 mutex_set_thread(m, NULL);
63#ifdef HAVE_PRIORITY_SCHEDULING
64 m->blocker.priority = PRIORITY_IDLE;
65 m->blocker.wakeup_protocol = wakeup_priority_protocol_transfer;
66 m->no_preempt = false;
67#endif
68}
69
70/* Gain ownership of a mutex object or block until it becomes free */
71void mutex_lock(struct mutex *m)
72{
73 struct thread_entry *current = thread_self_entry();
74
75 if(current == mutex_get_thread(m))
76 {
77 /* current thread already owns this mutex */
78 m->recursion++;
79 return;
80 }
81
82 /* lock out other cores */
83 corelock_lock(&m->cl);
84
85 /* must read thread again inside cs (a multiprocessor concern really) */
86 if(LIKELY(mutex_get_thread(m) == NULL))
87 {
88 /* lock is open */
89 mutex_set_thread(m, current);
90 corelock_unlock(&m->cl);
91 return;
92 }
93
94 /* block until the lock is open... */
95 IF_COP( current->obj_cl = &m->cl; )
96 IF_PRIO( current->blocker = &m->blocker; )
97 current->bqp = &m->queue;
98
99 disable_irq();
100 block_thread(current);
101
102 corelock_unlock(&m->cl);
103
104 /* ...and turn control over to next thread */
105 switch_thread();
106}
107
108/* Release ownership of a mutex object - only owning thread must call this */
109void mutex_unlock(struct mutex *m)
110{
111 /* unlocker not being the owner is an unlocking violation */
112 KERNEL_ASSERT(mutex_get_thread(m) == thread_self_entry(),
113 "mutex_unlock->wrong thread (%s != %s)\n",
114 mutex_get_thread(m)->name,
115 thread_self_entry()->name);
116
117 if(m->recursion > 0)
118 {
119 /* this thread still owns lock */
120 m->recursion--;
121 return;
122 }
123
124 /* lock out other cores */
125 corelock_lock(&m->cl);
126
127 /* transfer to next queued thread if any */
128 if(LIKELY(m->queue == NULL))
129 {
130 /* no threads waiting - open the lock */
131 mutex_set_thread(m, NULL);
132 corelock_unlock(&m->cl);
133 return;
134 }
135 else
136 {
137 const int oldlevel = disable_irq_save();
138 /* Tranfer of owning thread is handled in the wakeup protocol
139 * if priorities are enabled otherwise just set it from the
140 * queue head. */
141 IFN_PRIO( mutex_set_thread(m, m->queue); )
142 IF_PRIO( unsigned int result = ) wakeup_thread(&m->queue);
143 restore_irq(oldlevel);
144
145 corelock_unlock(&m->cl);
146
147#ifdef HAVE_PRIORITY_SCHEDULING
148 if((result & THREAD_SWITCH) && !m->no_preempt)
149 switch_thread();
150#endif
151 }
152}