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