Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
QoreThreadList.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 QoreThreadList.h
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_QORETHREADLIST_H
33
34#define _QORE_QORETHREADLIST_H
35
36#include <qore/QoreRWLock.h>
37
38// FIXME: move to config.h or something like that
39// not more than this number of threads can be running at the same time
40#ifndef MAX_QORE_THREADS
41#define MAX_QORE_THREADS 0x1000
42#endif
43
44class ThreadData;
45
46#define QTS_AVAIL 0
47#define QTS_NA 1
48#define QTS_ACTIVE 2
49#define QTS_RESERVED 3
50
51#if defined(DARWIN) && MAX_QORE_THREADS > 2560 && !defined(__MAC_10_7)
52// testing has revealed that darwin's pthread_create will not return an error when more than 2560 threads
53// are running, however the threads are not actually started, therefore we set MAX_QORE_THREADS to 2560 on
54// Darwin. This should be much more than any program/script should need (famous last words? :-) )
55// this bug is not present on 10.7.3 at least - in 10.7.3 pthread_create() returns an error after 2047
56// threads have been created and therefore works reliably
57#warning Darwin cannot support more than 2560 threads, MAX_QORE_THREADS set to 2560
58#undef MAX_QORE_THREADS
59#define MAX_QORE_THREADS 2560
60#endif
61
62class tid_node {
63public:
64 int tid;
65 tid_node* next, *prev;
66
67 DLLLOCAL tid_node(int ntid);
68 DLLLOCAL ~tid_node();
69};
70
71// this structure holds all thread data that can be addressed with the qore tid
72class ThreadEntry {
73public:
74 pthread_t ptid;
75 tid_node* tidnode;
76 ThreadData* thread_data;
77 unsigned char status;
78 bool joined; // if set to true then pthread_detach should not be called on exit
79
80 DLLLOCAL void cleanup();
81
82 DLLLOCAL void allocate(tid_node* tn, int stat = QTS_NA);
83
84 DLLLOCAL void activate(int tid, pthread_t n_ptid, QoreProgram* p, bool foreign = false);
85
86 DLLLOCAL bool active() const {
87 return status == QTS_ACTIVE;
88 }
89
90 DLLLOCAL bool available() const {
91 return status == QTS_AVAIL;
92 }
93};
94
95class QoreThreadList {
96friend class QoreThreadListIterator;
97friend class QoreThreadDataHelper;
98friend class tid_node;
99public:
100 // lock for reading / writing call stacks externally
104 mutable QoreRWLock stack_lck;
105
106 DLLLOCAL QoreThreadList() {
107 }
108
109 DLLLOCAL ThreadData* getThreadData(int tid) {
110 return entry[tid].active()
111 ? entry[tid].thread_data
112 : nullptr;
113 }
114
115 DLLLOCAL int get(int status = QTS_NA, bool reuse_last = false) {
116 int tid = -1;
117 AutoLocker al(lck);
118
119 if (current_tid == MAX_QORE_THREADS) {
120 int i = last_tid + 1;
121 while (true) {
122 // never try to assign TID 0
123 if (i == MAX_QORE_THREADS) {
124 if (!last_tid) {
125 break;
126 }
127 i = 1;
128 }
129 assert(i && i < MAX_QORE_THREADS);
130 if (entry[i].available()) {
131 tid = last_tid = i;
132 break;
133 }
134 ++i;
135 if (i == last_tid) {
136 break;
137 }
138 }
139 if (tid == -1) {
140 return -1;
141 }
142 } else if (reuse_last && current_tid && entry[current_tid - 1].available()) {
143 printd(5, "QoreThreadList::get() reusing TID %d\n", current_tid - 1);
144 // re-assign the last assigned TID
145 tid = current_tid - 1;
146 } else {
147 tid = current_tid++;
148 }
149 assert(entry[tid].available());
150
151 entry[tid].allocate(new tid_node(tid), status);
152 ++num_threads;
153 //printf("t%d cs=0\n", tid);
154
155 return tid;
156 }
157
158 DLLLOCAL int getSignalThreadEntry() {
159 AutoLocker al(lck);
160 entry[0].allocate(0);
161 return 0;
162 }
163
164 DLLLOCAL void release(int tid) {
165 AutoLocker al(lck);
166 releaseIntern(tid);
167 }
168
169 DLLLOCAL int releaseReserved(int tid) {
170 AutoLocker al(lck);
171 if (entry[tid].status != QTS_RESERVED) {
172 return -1;
173 }
174
175 releaseIntern(tid);
176 return 0;
177 }
178
179 DLLLOCAL void activate(int tid, pthread_t ptid = pthread_self(), QoreProgram* p = nullptr, bool foreign = false) {
180 AutoLocker al(lck);
181 entry[tid].activate(tid, ptid, p, foreign);
182 }
183
184 DLLLOCAL void setStatus(int tid, int status) {
185 AutoLocker al(lck);
186 assert(entry[tid].status != status);
187 entry[tid].status = status;
188 }
189
190 DLLLOCAL void deleteData(int tid);
191
192 DLLLOCAL void deleteDataRelease(int tid);
193
194 DLLLOCAL void deleteDataReleaseSignalThread();
195
196 DLLLOCAL int activateReserved(int tid) {
197 AutoLocker al(lck);
198
199 if (entry[tid].status != QTS_RESERVED) {
200 return -1;
201 }
202
203 entry[tid].activate(tid, pthread_self(), nullptr, true);
204 return 0;
205 }
206
207 DLLLOCAL unsigned getNumThreads() const {
208 return num_threads;
209 }
210
211 DLLLOCAL unsigned cancelAllActiveThreads();
212
213 DLLLOCAL QoreHashNode* getAllCallStacks();
214
215 DLLLOCAL static QoreHashNode* getCallStackHash(const QoreStackLocation& loc);
216
217 DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const std::string& code,
218 const QoreProgramLocation& loc);
219
220 DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const;
221
222protected:
223 // lock for reading the thread list
224 mutable QoreThreadLock lck;
225 unsigned num_threads = 0;
226 ThreadEntry entry[MAX_QORE_THREADS];
227
228 tid_node* tid_head = nullptr,
229 * tid_tail = nullptr;
230
231 // current TID to be issued next
232 int current_tid = 1;
233
234 // last TID issued to avoid reusing the same TID over and over again
235 int last_tid = 0;
236
237 bool exiting = false;
238
239 DLLLOCAL void releaseIntern(int tid) {
240 // NOTE: cannot safely call printd here, because normally the thread_data has been deleted
241 //printf("DEBUG: ThreadList.releaseIntern() TID %d terminated\n", tid);
242 entry[tid].cleanup();
243 if (tid) {
244 --num_threads;
245 }
246 }
247};
248
249DLLLOCAL extern QoreThreadList thread_list;
250
251class QoreThreadListIterator : public AutoLocker {
252public:
253 DLLLOCAL QoreThreadListIterator(bool access_stack = false) : AutoLocker(thread_list.lck),
254 access_stack(access_stack) {
255 if (access_stack) {
256 // grab the call stack write lock to get exclusive access to all thread stacks
257 thread_list.stack_lck.wrlock();
258 }
259 }
260
261 DLLLOCAL ~QoreThreadListIterator() {
262 if (access_stack) {
263 // release the call stack write lock
264 thread_list.stack_lck.unlock();
265 }
266 }
267
268 DLLLOCAL bool next() {
269 do {
270 w = w ? w->next : thread_list.tid_head;
271 } while (w && (!w->tid || (thread_list.entry[w->tid].status != QTS_ACTIVE)));
272
273 return (bool)w;
274 }
275
276 DLLLOCAL unsigned operator*() const {
277 assert(w);
278 return w->tid;
279 }
280
281protected:
282 tid_node* w = nullptr;
283 bool access_stack;
284};
285
286class QoreThreadDataHelper : public AutoLocker {
287public:
288 DLLLOCAL QoreThreadDataHelper(int tid) : AutoLocker(thread_list.lck), tid(tid) {
289 }
290
291 DLLLOCAL ThreadData* get() {
292 if (tid >= 0 && tid < MAX_QORE_THREADS) {
293 return thread_list.getThreadData(tid);
294 }
295 return nullptr;
296 }
297
298private:
299 int tid;
300};
301#endif
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
QoreThreadLock * lck
the pointer to the lock that will be managed
Definition: QoreThreadLock.h:178
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
supports parsing and executing Qore-language code, reference counted, dynamically-allocated only
Definition: QoreProgram.h:128
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
Stack location element abstract class.
Definition: ExceptionSink.h:418
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49