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