Qore Programming Language  1.12.0
QoreThreadList.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  QoreThreadList.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 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 
44 class 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 
62 class tid_node {
63 public:
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
72 class ThreadEntry {
73 public:
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 
95 class QoreThreadList {
96 friend class QoreThreadListIterator;
97 friend class tid_node;
98 public:
99  // lock for reading / writing call stacks externally
103  mutable QoreRWLock stack_lck;
104 
105  DLLLOCAL QoreThreadList() {
106  }
107 
108  DLLLOCAL int get(int status = QTS_NA, bool reuse_last = false) {
109  int tid = -1;
110  AutoLocker al(lck);
111 
112  if (current_tid == MAX_QORE_THREADS) {
113  int i;
114  // scan thread_list for free entry
115  for (i = 1; i < MAX_QORE_THREADS; i++) {
116  if (entry[i].available()) {
117  tid = i;
118  goto finish;
119  }
120  }
121  if (i == MAX_QORE_THREADS) {
122  return -1;
123  }
124  } else if (reuse_last && current_tid && entry[current_tid - 1].available()) {
125  // re-assign the last assigned TID
126  tid = current_tid - 1;
127  } else {
128  tid = current_tid++;
129  }
130 
131 finish:
132  entry[tid].allocate(new tid_node(tid), status);
133  ++num_threads;
134  //printf("t%d cs=0\n", tid);
135 
136  return tid;
137  }
138 
139  DLLLOCAL int getSignalThreadEntry() {
140  AutoLocker al(lck);
141  entry[0].allocate(0);
142  return 0;
143  }
144 
145  DLLLOCAL void release(int tid) {
146  AutoLocker al(lck);
147  releaseIntern(tid);
148  }
149 
150  DLLLOCAL int releaseReserved(int tid) {
151  AutoLocker al(lck);
152  if (entry[tid].status != QTS_RESERVED) {
153  return -1;
154  }
155 
156  releaseIntern(tid);
157  return 0;
158  }
159 
160  DLLLOCAL void activate(int tid, pthread_t ptid = pthread_self(), QoreProgram* p = nullptr, bool foreign = false) {
161  AutoLocker al(lck);
162  entry[tid].activate(tid, ptid, p, foreign);
163  }
164 
165  DLLLOCAL void setStatus(int tid, int status) {
166  AutoLocker al(lck);
167  assert(entry[tid].status != status);
168  entry[tid].status = status;
169  }
170 
171  DLLLOCAL void deleteData(int tid);
172 
173  DLLLOCAL void deleteDataRelease(int tid);
174 
175  DLLLOCAL void deleteDataReleaseSignalThread();
176 
177  DLLLOCAL int activateReserved(int tid) {
178  AutoLocker al(lck);
179 
180  if (entry[tid].status != QTS_RESERVED) {
181  return -1;
182  }
183 
184  entry[tid].activate(tid, pthread_self(), nullptr, true);
185  return 0;
186  }
187 
188  DLLLOCAL unsigned getNumThreads() const {
189  return num_threads;
190  }
191 
192  DLLLOCAL unsigned cancelAllActiveThreads();
193 
194  DLLLOCAL QoreHashNode* getAllCallStacks();
195 
196  DLLLOCAL static QoreHashNode* getCallStackHash(const QoreStackLocation& loc);
197 
198  DLLLOCAL static QoreHashNode* getCallStackHash(qore_call_t type, const std::string& code,
199  const QoreProgramLocation& loc);
200 
201  DLLLOCAL QoreListNode* getCallStack(const QoreStackLocation* stack_location) const;
202 
203 protected:
204  // lock for reading the thread list
205  mutable QoreThreadLock lck;
206  unsigned num_threads = 0;
207  ThreadEntry entry[MAX_QORE_THREADS];
208 
209  tid_node* tid_head = nullptr,
210  * tid_tail = nullptr;
211 
212  // current TID to be issued next
213  int current_tid = 1;
214 
215  bool exiting = false;
216 
217  DLLLOCAL void releaseIntern(int tid) {
218  // NOTE: cannot safely call printd here, because normally the thread_data has been deleted
219  //printf("DEBUG: ThreadList.releaseIntern() TID %d terminated\n", tid);
220  entry[tid].cleanup();
221  if (tid) {
222  --num_threads;
223  }
224  }
225 };
226 
227 DLLLOCAL extern QoreThreadList thread_list;
228 
229 class QoreThreadListIterator : public AutoLocker {
230 public:
231  DLLLOCAL QoreThreadListIterator(bool access_stack = false) : AutoLocker(thread_list.lck),
232  access_stack(access_stack) {
233  if (access_stack) {
234  // grab the call stack write lock to get exclusive access to all thread stacks
235  thread_list.stack_lck.wrlock();
236  }
237  }
238 
239  DLLLOCAL ~QoreThreadListIterator() {
240  if (access_stack) {
241  // release the call stack write lock
242  thread_list.stack_lck.unlock();
243  }
244  }
245 
246  DLLLOCAL bool next() {
247  do {
248  w = w ? w->next : thread_list.tid_head;
249  } while (w && (!w->tid || (thread_list.entry[w->tid].status != QTS_ACTIVE)));
250 
251  return (bool)w;
252  }
253 
254  DLLLOCAL unsigned operator*() const {
255  assert(w);
256  return w->tid;
257  }
258 
259 protected:
260  tid_node* w = nullptr;
261  bool access_stack;
262 };
263 
264 #endif
provides a safe and exception-safe way to hold locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:136
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:127
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
Stack location element abstract class.
Definition: ExceptionSink.h:409
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49