-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathThread.cpp
More file actions
274 lines (246 loc) · 6.83 KB
/
Thread.cpp
File metadata and controls
274 lines (246 loc) · 6.83 KB
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
// sonst geht clock_gettime am ESP32 nicht
#define _POSIX_TIMERS
#include <time.h>
#include <stdio.h>
#include <stdexcept>
#include <sys/errno.h>
#include <stdlib.h>
#include <cxxabi.h>
#include <string.h>
#include <assert.h>
#include "utils.h"
#include "Thread.h"
#if defined ESP32
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
// für arduino sdk 1.0.6
#define TO_LONG_FORMAT (unsigned long)
#else
#define TO_LONG_FORMAT
#endif
#define TAG "Thread"
void *Thread::startupThread(void *ptr) {
Thread *t=(Thread *) ptr;
// %lu => linux ist pthread_ unsigned long
NOTICEF("Thread::[%x] @%p startupThread name=%s ========================================", t->getMyId(), t, t->which());
PRINT_FREE_HEAP("startupThread()");
t->exited=false;
try {
t->run();
// !!!!! looks as if ESP32 idf 4.4 can only handle 2 different exceptions, 202203: all exceptions should be std::exception
#ifndef ESP32
} catch(const char *e) {
ERRORF("?: exception %s - client thread killed", e);
/*
} catch(const std::RuntimeExceptionWithBacktrace &e) {
ERRORF("?: Runtime Exception with Backtrace %s - client thread killed", e.what());
*/
} catch(const std::runtime_error &e) {
ERRORF("?: Runtime Exception %s - client thread killed", e.what());
#endif
} catch(const std::exception &e) {
ERRORF("?: exception \"%s\" - client thread finished with an exception", e.what());
#ifndef ESP32
} catch (abi::__forced_unwind&) { // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=28145
ERRORF("Thread::[%d] done: forced unwind exception - client thread killed", t->getMyId());
// copy &paste:
// printf("%d:client exit\n",startupData->clientID);
// pthread_cleanup_pop(true);
t->exited=true;
if(t->autodelete) {
delete t;
}
throw; // rethrow exeption bis zum pthread_create, dort isses dann aus
#endif
} catch(...) {
ERRORF("?: unknown exception caught!"); // crash otherwise
}
NOTICEF("[%x] %s done ====================================", t->getMyId(), t->which() );
t->exited=true;
if(t->autodelete) {
DEBUGF("deleting object %p", t);
delete t;
}
PRINT_FREE_HEAP("startupThread() done");
return NULL;
}
void Thread::start() {
if(this->isRunning()) {
throw std::runtime_error("Thread::start() => thread already running!");
}
pthread_attr_t attr;
pthread_attr_init(&attr);
#ifdef ESP32
DEBUGF("set stack size to 10000");
pthread_attr_setstacksize(&attr,10000);
this->cancelstate=0;
#endif
int s = pthread_create(&this->thread, &attr, &Thread::startupThread, (void *) this);
if (s != 0) {
perror("pthread_create");
throw std::runtime_error("error pthread_create");
}
pthread_attr_destroy(&attr);
}
void Thread::cancel(bool join) {
NOTICEF("Thread::cancel(join=%d) thread=%0lx", join, TO_LONG_FORMAT this->thread);
if(this->thread) {
#ifdef ESP32
// ESP lib kennt kein pthread_cancel
this->cancelstate=1;
#else
int s = pthread_cancel(this->thread);
if (s != 0) {
perror("pthread_cancel");
throw std::runtime_error("pthread_cancel failed");
}
#endif
if(join) {
void *ret=NULL;
// %lu => linux ist pthread_ unsigned long
DEBUGF("Thread::[%0lx] cancel() waiting to exit", TO_LONG_FORMAT this->thread);
int rc = pthread_join(this->thread, &ret);
if(ret == PTHREAD_CANCELED) {
NOTICEF("Thread::[%0lx] cancel() join=true thread was canceled", TO_LONG_FORMAT this->thread);
} else if(ret != NULL) { // if this is malloced value we create a memory leak
ERRORF("Thread::[%0lx] cancel() join=true, ret=%p", TO_LONG_FORMAT this->thread, ret);
abort();
}
if(rc != 0) {
throw std::runtime_error("error pthread_join");
}
}
this->thread=0;
} else {
// already killed/never started
throw std::runtime_error("thread not started");
}
}
bool Thread::isRunning() {
return this->thread != 0 && ! this->exited;
}
void Thread::testcancel() {
#ifdef ESP32
if(this->cancelstate) {
DEBUGF("Thread::[%0lx] quitting thread with an exception", TO_LONG_FORMAT this->getMyId());
throw std::runtime_error("thread canceled from testcancel()"); // pthread_testcancel macht auch nur exception
}
#else
pthread_testcancel();
#endif
}
int Thread::self() {
// 2022 workaround for esp idf 4.4 -> crash if called from main loop() thread ESP_ARDUINO_VERSION_MINOR
// getName returns "loop"
#if defined ESP32 && ESP_IDF_VERSION_MAJOR >= 4
if(! STREQ(pcTaskGetName(NULL), "pthread")) {
return 0;
}
#endif
return pthread_self();
}
Mutex::Mutex() {
if(pthread_mutex_init(&this->m, NULL) != 0) {
throw std::runtime_error("error creating mutex");
}
#ifdef DEBUG_MUTEX
this->lockedby=0;
#endif
}
Mutex::~Mutex() {
if(! this->tryLock()) {
ERRORF("error destroying mutex - mutex is locked");
abort();
}
this->unlock();
if(pthread_mutex_destroy(&this->m)) {
ERRORF("error destroying mutex");
abort();
}
}
void Mutex::lock() {
#ifdef DEBUG_MUTEX
if(this->lockedby) {
DEBUGF("mutex already locked by %0x", this->lockedby);
}
DEBUGF("try mutex lock by %0x", Thread::self());
#endif
if(pthread_mutex_lock(&this->m) != 0 ) {
throw std::runtime_error("error Mutex::lock()");
}
#ifdef DEBUG_MUTEX
DEBUGF("mutex locked by %0x", Thread::self());
this->lockedby=Thread::self();
#endif
}
void Mutex::unlock() {
if(pthread_mutex_unlock(&this->m) != 0 ) {
throw std::runtime_error("error Mutex::unlock()");
}
#ifdef DEBUG_MUTEX
DEBUGF("mutex unlocked by %0x", Thread::self());
this->lockedby=0;
#endif
}
bool Mutex::tryLock() {
#ifdef DEBUG_MUTEX
if(this->lockedby) {
DEBUGF("mutex locked by %0x", this->lockedby);
}
#endif
// 0 on success
int ret = pthread_mutex_trylock(&this->m);
if(ret == 0) return true;
if(ret == EBUSY) return false;
throw std::runtime_error("error Mutex::trylock()");
}
void ThreadSpecific_Descructor(void *ptr) {
}
ThreadSpecific::ThreadSpecific() {
pthread_key_create(&this->key,ThreadSpecific_Descructor);
}
ThreadSpecific::~ThreadSpecific() {
pthread_key_delete(this->key);
}
void *ThreadSpecific::get() {
return pthread_getspecific(this->key);
}
void ThreadSpecific::set(void *ptr) {
pthread_setspecific(this->key, ptr);
}
void ThreadSpecific::del() {
pthread_setspecific(this->key, NULL);
}
Condition::Condition() {
pthread_cond_init(&this->cond, NULL);
}
void Condition::wait() {
Lock lock(this->mutex);
}
/**
* @param timeout
* timeout in seconds
* rc
* true=signal, false=timeout
*/
bool Condition::timeoutWait(int timeout) {
Lock lock(this->mutex);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += timeout;
int rc = pthread_cond_timedwait(&this->cond, &this->mutex.m, &ts);
if(rc == 0) {
return true;
}
if(rc == ETIMEDOUT) {
return false;
}
throw std::runtime_error(utils::format("Condition::timeoutWait() error %s", strerror(rc)));
}
void Condition::signal() {
Lock lock(this->mutex);
const int signal_rv = pthread_cond_signal(&(this->cond));
if (signal_rv) {
throw std::runtime_error(utils::format("Condition::signal() error %s", strerror(signal_rv)));
}
}