-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathc11_cond.c
195 lines (175 loc) · 8.82 KB
/
c11_cond.c
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//#include <cmsis_os2.h>
#include <threads.h>
#include <sys/thread.h>
#include <svc.h>
typedef struct _thread osThread_t;
#define THREAD_PTR(x) ((osThread_t*)(x))
/*! \defgroup _cond_ C11 Condition Variables
\ingroup _system _libc
\brief Функции управлениея некондицией процессов.
Некондиция представляет собой спсоб управления синхронизацией процессов,
когда один процесс управляет оживлением множества некондиционных процессов.
Процесс может, заблокировать себя используя собственную блокировку (мьютекс) и впасть в некондиционное состояние
идентифицируемое, как абстрактная некондиция.
Кондиция - это оживление некондиционных процессов - переменная типа \b Cond_t, пожет иметь только идентификатор или адрес,
который служит идентификатором, без ассоциированных данных.
Выход из некондиции происходит либо по истечение заданного интервала времени, либо по сигналу
от процесса выполняющего управление некондицией. Некондиция самоблокированных процессов скапливается
в форме списка. Список некондиции связанных с идентификатором кондиции существует неявно и обрабатывается операционной системой.
Методы управлениея некондицией включают возможность оживить и заставить работать один процесс из списка некондиции или все сразу.
С точки зрения поведения процессов в системе, те что самоблокировались и ожидают кондиции, по сути ждут либо освобождения какого-то ресурса,
но тогда следует использовать ожидание семафора, либо ждут наступления какого-то события.
\{
*/
/*! \see ARM® Synchronization Primitives */
#include "semaphore.h"
#include "r3_slice.h"
struct _list_mtx {
struct _list_mtx *next;
mtx_t *mutex;
};
struct os_mutex_cb {
volatile int count;
};
/*! \brief Инициализация некондиции
The \b cnd_init function creates a condition variable. If it succeeds it sets the variable pointed to by
cond to a value that uniquely identifies the newly created condition variable. A thread that calls
\b cnd_wait on a newly created condition variable will block.
\return \b thrd_success on success, or \b thrd_nomem if no memory could be
allocated for the newly created condition, or \b thrd_error if the request could not be honored.
*/
int cnd_init(cnd_t *cond)
{
*(volatile void**)cond=NULL;
return thrd_success;
}
/*! \brief удаление некондиции
The \b cnd_destroy function releases all resources used by the condition variable pointed to by cond.
The \b cnd_destroy function requires that no threads be blocked waiting for the condition variable
pointed to by cond.
*/
void cnd_destroy(cnd_t *cond)
{
/* struct _list_mtx */
volatile void**head = (volatile void**)cond;
struct _list_mtx *node;
while (1) {
do{// атомарно выталкиваем из списка элемент
node = atomic_pointer_get(head);
if (node==NULL) {
//atomic_free();
return;
}
} while(!atomic_pointer_compare_and_exchange(head, node, node->next));
__DMB();
if (node->mutex) {
semaphore_leave(&node->mutex->count);// mtx_unlock(node->mutex);
}
g_slice_free(struct _list_mtx, node);
}
return;
}
/*! \brief Оповещение о кондиции одного процесса
The \b cnd_signal function unblocks one of the threads that are blocked on the condition variable
pointed to by cond at the time of the call. If no threads are blocked on the condition variable at the
time of the call, the function does nothing and returns success.
\return \b thrd_success on success or \b thrd_error if the request could not be honored.
*/
int cnd_signal(cnd_t *cond)
{
volatile void** head = (volatile void**)cond;
struct _list_mtx *node;
do{// атомарно выталкиваем из списка элемент
node = atomic_pointer_get(head);
if (node==NULL) {
//atomic_free();
return thrd_success;
}
} while(!atomic_pointer_compare_and_exchange(head, node, node->next));
__DMB();
if (node->mutex) {
semaphore_leave(&node->mutex->count);// mtx_unlock(node->mutex);
}
g_slice_free(struct _list_mtx, node);
return thrd_success;
}
/*! \brief Оповестить все процессы в сипске о наступлении события
The cnd_broadcast function unblocks all of the threads that are blocked on the condition variable
pointed to by cond at the time of the call. If no threads are blocked on the condition variable pointed
to by cond at the time of the call, the function does nothing.
\return \b thrd_success on success, or \b thrd_error if the request could not be honored
*/
int cnd_broadcast(cnd_t *cond)
{
volatile void** head = (volatile void**)cond;
while (1) {
struct _list_mtx *node;
do{// атомарно выталкиваем из списка элемент
node = atomic_pointer_get(head);
if (node==NULL) {
//atomic_free();
return thrd_success;
}
} while(!atomic_pointer_compare_and_exchange(head, node, node->next));
__DMB();
if (node->mutex) {
semaphore_leave(&node->mutex->count);// mtx_unlock(node->mutex);
}
g_slice_free(struct _list_mtx, node);
}
return thrd_success;
}
/*! \brief Ускорение операций дления на константу */
static inline uint32_t div1000(uint32_t v) {
return (v*0x83126E98ULL)>>41;
}
static inline uint32_t div1M(uint32_t v) {
#if defined(__ARM_ARCH_8M_BASE__)
return v/1000000UL;
#else
return (v*0x8637BD06ULL)>>51;
#endif
}
/*! \brief Ожидать состояние кондиции
\param [in] cond - переммнная/идентификатор группы некондиции
\param [in] mtx - собственная блокировка, мьютекс должна быть заблокирована до выфзова
\param [in] ts - штам времени, абсолютное значение в TIME_UTC
Процесс совершает акт само блокировки и переходит в некондиционное состояние на неопределенное время,
помещается в список некондиции, связанный с идентификатором группы некондиции \b cond.
Рекомендуемый способ вызова:
timespec_get(&ts, TIME_UTC);
ts.tv_nsec += Nanosec(timeout);
if (ts.tv_nsec>=1`000`000`000) {
ts.tv_sec += ts.tv_nsec / 1`000`000`000;
ts.tv_nsec = ts.tv_nsec % 1`000`000`000;
}
cnd_timedwait(cond, mtx, &ts);
The \b cnd_timedwait function atomically unlocks the mutex pointed to by \b mtx and
endeavors to block until the condition variable pointed to by \b cond is signaled by a call to
\b cnd_signal or to \b cnd_broadcast, or until after the TIME_UTC-based calendar
time pointed to by \b ts.
The cnd_timedwait function requires that the mutex pointed to by \b mtx be locked by the calling thread.
*/
int cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx, const struct timespec *restrict ts)
{
volatile void**head = (volatile void**)cond;
struct _list_mtx *node = g_slice_new(struct _list_mtx);
node->mutex = mtx;
//int count = semaphore_enter(&mtx->count);
mtx->count = 0; // lock
do {// атомарно добавляем в список
node->next = atomic_pointer_get(head);
atomic_mb();
} while (!atomic_pointer_compare_and_exchange(head, node->next, node));
__DMB();
svc4(SVC_CLOCK_WAIT, osEventSemaphore, (void*)&mtx->count, TIME_UTC, ts);
thrd_t thr = thrd_current();
int status = THREAD_PTR(thr)->process.event.status;
return (status & osEventSemaphore)?thrd_success:thrd_timedout;
// osEvent_t event = {.status = osEventSemaphore,.value ={.p = (void*)&mtx->count}};
// return osEventTimedWait(&event, ts);// абсолютное время
}
int cnd_wait(cnd_t *cond, mtx_t *mtx) {
return cnd_timedwait(cond, mtx, NULL);
}
//! \}