blob: 876b704b51aeb08a10eff790f8d945415ec84210 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/****************************************************************************
* Simple mutex functions ;)
****************************************************************************/
#include "kernel-internal.h"
#include "mutex.h"
/* Initialize a mutex object - call before any use and do not call again once
* the object is available to other threads */
void mutex_init(struct mutex *m)
{
wait_queue_init(&m->queue);
m->recursion = 0;
blocker_init(&m->blocker);
#ifdef HAVE_PRIORITY_SCHEDULING
m->no_preempt = false;
#endif
corelock_init(&m->cl);
}
/* Gain ownership of a mutex object or block until it becomes free */
void mutex_lock(struct mutex *m)
{
struct thread_entry *current = __running_self_entry();
if(current == m->blocker.thread)
{
/* current thread already owns this mutex */
m->recursion++;
return;
}
/* lock out other cores */
corelock_lock(&m->cl);
/* must read thread again inside cs (a multiprocessor concern really) */
if(LIKELY(m->blocker.thread == NULL))
{
/* lock is open */
m->blocker.thread = current;
corelock_unlock(&m->cl);
return;
}
/* block until the lock is open... */
disable_irq();
block_thread(current, TIMEOUT_BLOCK, &m->queue, &m->blocker);
corelock_unlock(&m->cl);
/* ...and turn control over to next thread */
switch_thread();
}
/* Release ownership of a mutex object - only owning thread must call this */
void mutex_unlock(struct mutex *m)
{
/* unlocker not being the owner is an unlocking violation */
KERNEL_ASSERT(m->blocker.thread == __running_self_entry(),
"mutex_unlock->wrong thread (%s != %s)\n",
m->blocker.thread->name,
__running_self_entry()->name);
if(m->recursion > 0)
{
/* this thread still owns lock */
m->recursion--;
return;
}
/* lock out other cores */
corelock_lock(&m->cl);
/* transfer to next queued thread if any */
struct thread_entry *thread = WQ_THREAD_FIRST(&m->queue);
if(LIKELY(thread == NULL))
{
/* no threads waiting - open the lock */
m->blocker.thread = NULL;
corelock_unlock(&m->cl);
return;
}
const int oldlevel = disable_irq_save();
/* Tranfer of owning thread is handled in the wakeup protocol
* if priorities are enabled otherwise just set it from the
* queue head. */
#ifndef HAVE_PRIORITY_SCHEDULING
m->blocker.thread = thread;
#endif
unsigned int result = wakeup_thread(thread, WAKEUP_TRANSFER);
restore_irq(oldlevel);
corelock_unlock(&m->cl);
#ifdef HAVE_PRIORITY_SCHEDULING
if((result & THREAD_SWITCH) && !m->no_preempt)
switch_thread();
#endif
(void)result;
}
|