1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3
4 #include <rudiments/thread.h>
5 #include <rudiments/error.h>
6 #include <rudiments/stdio.h>
7 #include <rudiments/snooze.h>
8
9 // for pthread_kill
10 #include <signal.h>
11 #ifdef RUDIMENTS_HAVE_SYS_SIGNAL_H
12 #include <sys/signal.h>
13 #endif
14
15 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
16 #include <pthread.h>
17 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
18 #include <rudiments/sys.h>
19 #ifdef RUDIMENTS_HAVE_WINDOWS_H
20 #include <windows.h>
21 #endif
22 #endif
23
24 class threadprivate {
25 friend class thread;
26 private:
27 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
28 pthread_t _thr;
29 pthread_attr_t _attr;
30 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
31 HANDLE _thr;
32 size_t _stacksize;
33 #endif
34 bool _needtowait;
35 bool _retry;
36 };
37
thread()38 thread::thread() {
39 pvt=new threadprivate;
40 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
41 pvt->_thr=0;
42 pthread_attr_init(&pvt->_attr);
43 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
44 pvt->_thr=INVALID_HANDLE_VALUE;
45 pvt->_stacksize=sys::getMinThreadStackSize();
46 #endif
47 pvt->_needtowait=false;
48 pvt->_retry=true;
49 }
50
~thread()51 thread::~thread() {
52 wait(NULL);
53 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
54 pthread_attr_destroy(&pvt->_attr);
55 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
56 if (pvt->_thr!=INVALID_HANDLE_VALUE) {
57 CloseHandle(pvt->_thr);
58 }
59 #endif
60 delete pvt;
61 }
62
setStackSize(size_t stacksize)63 bool thread::setStackSize(size_t stacksize) {
64 #if defined(RUDIMENTS_HAVE_PTHREAD_ATTR_SETSTACKSIZE)
65 error::clearError();
66 int result=pthread_attr_setstacksize(&pvt->_attr,stacksize);
67 if (!result) {
68 return true;
69 }
70 error::setErrorNumber(result);
71 return false;
72 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
73 pvt->_stacksize=stacksize;
74 return true;
75 #else
76 RUDIMENTS_SET_ENOSYS
77 return false;
78 #endif
79 }
80
getStackSize(size_t * stacksize)81 bool thread::getStackSize(size_t *stacksize) {
82 #if defined(RUDIMENTS_HAVE_PTHREAD_ATTR_SETSTACKSIZE)
83 error::clearError();
84 int result=pthread_attr_getstacksize(&pvt->_attr,stacksize);
85 if (!result) {
86 return true;
87 }
88 error::setErrorNumber(result);
89 return false;
90 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
91 *stacksize=pvt->_stacksize;
92 return true;
93 #else
94 RUDIMENTS_SET_ENOSYS
95 return false;
96 #endif
97 }
98
spawn(void * (* function)(void *),void * arg,bool detached)99 bool thread::spawn(void *(*function)(void *), void *arg, bool detached) {
100 pvt->_needtowait=false;
101 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
102 pvt->_thr=0;
103 error::clearError();
104 if (detached && pthread_attr_setdetachstate(&pvt->_attr,
105 PTHREAD_CREATE_DETACHED)) {
106 return false;
107 }
108 int result=0;
109 do {
110 result=pthread_create(&pvt->_thr,
111 &pvt->_attr,
112 function,arg);
113 if (!result) {
114 pvt->_needtowait=!detached;
115 return true;
116 }
117 snooze::macrosnooze(1);
118 pvt->_thr=0;
119 } while (result==EAGAIN && pvt->_retry);
120 error::setErrorNumber(result);
121 return false;
122 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
123 pvt->_thr=INVALID_HANDLE_VALUE;
124 pvt->_thr=CreateThread(NULL,pvt->_stacksize,
125 (LPTHREAD_START_ROUTINE)function,
126 arg,0,NULL);
127 if (pvt->_thr==NULL || pvt->_thr==INVALID_HANDLE_VALUE) {
128 pvt->_thr=NULL;
129 return false;
130 }
131 if (detached && !detach()) {
132 int32_t status=1;
133 exit(&status);
134 pvt->_thr=NULL;
135 return false;
136 }
137 pvt->_needtowait=!detached;
138 return true;
139 #else
140 RUDIMENTS_SET_ENOSYS
141 return false;
142 #endif
143 }
144
exit(int32_t * status)145 void thread::exit(int32_t *status) {
146 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
147 pthread_exit((void *)status);
148 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
149 ExitThread((DWORD)*status);
150 #else
151 RUDIMENTS_SET_ENOSYS
152 #endif
153 }
154
wait(int32_t * status)155 bool thread::wait(int32_t *status) {
156 if (!pvt->_needtowait) {
157 return true;
158 }
159 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
160 error::clearError();
161 if (pvt->_thr) {
162 int32_t *st=NULL;
163 int result=pthread_join(pvt->_thr,(void **)&st);
164 if (result) {
165 error::setErrorNumber(result);
166 return false;
167 }
168 if (status) {
169 *status=*st;
170 }
171 } else {
172 if (status) {
173 *status=0;
174 }
175 }
176 pvt->_needtowait=false;
177 return true;
178 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
179 if (pvt->_thr!=INVALID_HANDLE_VALUE) {
180 if (WaitForSingleObject(pvt->_thr,INFINITE)==
181 WAIT_FAILED) {
182 return false;
183 }
184 DWORD stat=0;
185 if (GetExitCodeThread(pvt->_thr,&stat)==FALSE) {
186 return false;
187 }
188 if (status) {
189 *status=(int32_t)stat;
190 }
191 } else {
192 if (status) {
193 *status=0;
194 }
195 }
196 pvt->_needtowait=false;
197 return true;
198 #else
199 RUDIMENTS_SET_ENOSYS
200 return false;
201 #endif
202 }
203
detach()204 bool thread::detach() {
205 pvt->_needtowait=false;
206 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
207 error::clearError();
208 if (pvt->_thr) {
209 int result=pthread_detach(pvt->_thr);
210 if (result) {
211 error::setErrorNumber(result);
212 return false;
213 }
214 }
215 return true;
216 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
217 bool result=true;
218 if (pvt->_thr!=INVALID_HANDLE_VALUE) {
219 result=(CloseHandle(pvt->_thr)==TRUE);
220 }
221 pvt->_thr=INVALID_HANDLE_VALUE;
222 return result;
223 #else
224 RUDIMENTS_SET_ENOSYS
225 return false;
226 #endif
227 }
228
raiseSignal(int32_t signum)229 bool thread::raiseSignal(int32_t signum) {
230 #if defined(RUDIMENTS_HAVE_PTHREAD_T)
231 if (pvt->_thr) {
232 return !pthread_kill(pvt->_thr,signum);
233 }
234 return true;
235 #elif defined(RUDIMENTS_HAVE_CREATETHREAD)
236 // FIXME: implement this for windows
237 RUDIMENTS_SET_ENOSYS
238 return false;
239 #else
240 RUDIMENTS_SET_ENOSYS
241 return false;
242 #endif
243 }
244
retryFailedSpawn()245 void thread::retryFailedSpawn() {
246 pvt->_retry=true;
247 }
248
dontRetryFailedSpawn()249 void thread::dontRetryFailedSpawn() {
250 pvt->_retry=false;
251 }
252
getRetryFailedSpawn()253 bool thread::getRetryFailedSpawn() {
254 return pvt->_retry;
255 }
256
supported()257 bool thread::supported() {
258 #if defined(RUDIMENTS_HAVE_PTHREAD_T) || \
259 defined(RUDIMENTS_HAVE_CREATETHREAD)
260 return true;
261 #else
262 return false;
263 #endif
264 }
265