1 /*
2  * Thread functions
3  *
4  * Copyright (C) 2012-2020, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <memory.h>
24 #include <types.h>
25 
26 #include <errno.h>
27 
28 #if defined( WINAPI ) && ( WINVER >= 0x0602 )
29 #include <Processthreadsapi.h>
30 #include <Synchapi.h>
31 #endif
32 
33 #if defined( HAVE_PTHREAD_H ) && !defined( WINAPI )
34 #include <pthread.h>
35 #endif
36 
37 #include "libcthreads_libcerror.h"
38 #include "libcthreads_thread.h"
39 #include "libcthreads_thread_attributes.h"
40 #include "libcthreads_types.h"
41 
42 #if !defined( HAVE_LOCAL_LIBCTHREADS ) || defined( HAVE_MULTI_THREAD_SUPPORT )
43 
44 #if defined( WINAPI )
45 
46 /* Start function helper function for WINAPI
47  * Returns 0 if successful or 1 on error
48  */
libcthreads_thread_callback_function_helper(void * arguments)49 DWORD WINAPI libcthreads_thread_callback_function_helper(
50               void *arguments )
51 {
52 	libcthreads_internal_thread_t *internal_thread = NULL;
53 	DWORD result                                   = 1;
54 	int callback_function_result                   = 0;
55 
56 	if( arguments != NULL )
57 	{
58 		internal_thread = (libcthreads_internal_thread_t *) arguments;
59 
60 		if( ( internal_thread != NULL )
61 		 && ( internal_thread->callback_function != NULL ) )
62 		{
63 			callback_function_result = internal_thread->callback_function(
64 			                            internal_thread->callback_function_arguments );
65 		}
66 		result = (DWORD) ( callback_function_result != 1 );
67 	}
68 	ExitThread(
69 	 result );
70 }
71 
72 #elif defined( HAVE_PTHREAD_H )
73 
74 /* Start function helper function for pthread
75  * Returns a pointer to a newly allocated int containing 1 if successful or -1 on error
76  * NULL is return if the helper function was unable to run the callback
77  */
libcthreads_thread_callback_function_helper(void * arguments)78 void *libcthreads_thread_callback_function_helper(
79        void *arguments )
80 {
81 	libcthreads_internal_thread_t *internal_thread = NULL;
82 	int *result                                    = NULL;
83 
84 	if( arguments != NULL )
85 	{
86 		internal_thread = (libcthreads_internal_thread_t *) arguments;
87 
88 		if( ( internal_thread != NULL )
89 		 && ( internal_thread->callback_function != NULL ) )
90 		{
91 			result = (int *) memory_allocate(
92 			                  sizeof( int ) );
93 
94 			if( result != NULL )
95 			{
96 				*result = internal_thread->callback_function(
97 				           internal_thread->callback_function_arguments );
98 			}
99 		}
100 	}
101 	pthread_exit(
102 	 (void *) result );
103 }
104 
105 #endif
106 
107 /* Creates a thread
108  * Make sure the value thread is referencing, is set to NULL
109  *
110  * The callback_function should return 1 if successful and -1 on error
111  * Returns 1 if successful or -1 on error
112  */
libcthreads_thread_create(libcthreads_thread_t ** thread,const libcthreads_thread_attributes_t * thread_attributes,int (* callback_function)(void * arguments),void * callback_function_arguments,libcerror_error_t ** error)113 int libcthreads_thread_create(
114      libcthreads_thread_t **thread,
115      const libcthreads_thread_attributes_t *thread_attributes,
116      int (*callback_function)(
117             void *arguments ),
118      void *callback_function_arguments,
119      libcerror_error_t **error )
120 {
121 	libcthreads_internal_thread_t *internal_thread = NULL;
122 	static char *function                          = "libcthreads_thread_create";
123 
124 #if defined( WINAPI )
125 	SECURITY_ATTRIBUTES *security_attributes       = NULL;
126 	HANDLE thread_handle                           = NULL;
127 	DWORD error_code                               = 0;
128 
129 #elif defined( HAVE_PTHREAD_H )
130 	pthread_attr_t *attributes                     = NULL;
131 	int pthread_result                             = 0;
132 #endif
133 
134 	if( thread == NULL )
135 	{
136 		libcerror_error_set(
137 		 error,
138 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
139 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
140 		 "%s: invalid thread.",
141 		 function );
142 
143 		return( -1 );
144 	}
145 	if( *thread != NULL )
146 	{
147 		libcerror_error_set(
148 		 error,
149 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
150 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
151 		 "%s: invalid thread value already set.",
152 		 function );
153 
154 		return( -1 );
155 	}
156 	if( callback_function == NULL )
157 	{
158 		libcerror_error_set(
159 		 error,
160 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
161 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
162 		 "%s: invalid callback function.",
163 		 function );
164 
165 		return( -1 );
166 	}
167 	internal_thread = memory_allocate_structure(
168 	                   libcthreads_internal_thread_t );
169 
170 	if( internal_thread == NULL )
171 	{
172 		libcerror_error_set(
173 		 error,
174 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
175 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
176 		 "%s: unable to create thread.",
177 		 function );
178 
179 		goto on_error;
180 	}
181 	if( memory_set(
182 	     internal_thread,
183 	     0,
184 	     sizeof( libcthreads_internal_thread_t ) ) == NULL )
185 	{
186 		libcerror_error_set(
187 		 error,
188 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
189 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
190 		 "%s: unable to clear thread.",
191 		 function );
192 
193 		memory_free(
194 		 internal_thread );
195 
196 		return( -1 );
197 	}
198 	internal_thread->callback_function           = callback_function;
199 	internal_thread->callback_function_arguments = callback_function_arguments;
200 
201 #if defined( WINAPI )
202 	if( thread_attributes != NULL )
203 	{
204 		security_attributes = &( ( (libcthreads_internal_thread_attributes_t *) thread_attributes )->security_attributes );
205 	}
206 	thread_handle = CreateThread(
207 	                 security_attributes,
208 	                 0, /* stack size */
209 	                 &libcthreads_thread_callback_function_helper,
210 	                 (void *) internal_thread,
211 	                 0, /* creation flags */
212 	                 &( internal_thread->thread_identifier ) );
213 
214 	if( thread_handle == NULL )
215 	{
216 		error_code = GetLastError();
217 
218 		libcerror_system_set_error(
219 		 error,
220 		 error_code,
221 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
222 		 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
223 		 "%s: unable to create thread handle.",
224 		 function );
225 
226 		goto on_error;
227 	}
228 	internal_thread->thread_handle = thread_handle;
229 
230 #elif defined( HAVE_PTHREAD_H )
231 	if( thread_attributes != NULL )
232 	{
233 		attributes = &( ( (libcthreads_internal_thread_attributes_t *) thread_attributes )->attributes );
234 	}
235 	pthread_result = pthread_create(
236 	                  &( internal_thread->thread ),
237 	                  attributes,
238 	                  &libcthreads_thread_callback_function_helper,
239 	                  (void *) internal_thread );
240 
241 	switch( pthread_result )
242 	{
243 		case 0:
244 			break;
245 
246 		case EAGAIN:
247 			libcerror_error_set(
248 			 error,
249 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
250 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
251 			 "%s: unable to create thread with error: Insufficient resources.",
252 			 function );
253 
254 			goto on_error;
255 
256 		default:
257 			libcerror_system_set_error(
258 			 error,
259 			 pthread_result,
260 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
261 			 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
262 			 "%s: unable to create thread.",
263 			 function );
264 
265 			goto on_error;
266 	}
267 #endif
268 	*thread = (libcthreads_thread_t *) internal_thread;
269 
270 	return( 1 );
271 
272 on_error:
273 	if( internal_thread != NULL )
274 	{
275 		memory_free(
276 		 internal_thread );
277 	}
278 	return( -1 );
279 }
280 
281 /* Joins the current with a specified thread
282  * The the thread is freed after join
283  * Returns 1 if successful or -1 on error
284  */
libcthreads_thread_join(libcthreads_thread_t ** thread,libcerror_error_t ** error)285 int libcthreads_thread_join(
286      libcthreads_thread_t **thread,
287      libcerror_error_t **error )
288 {
289 	libcthreads_internal_thread_t *internal_thread = NULL;
290 	static char *function                          = "libcthreads_thread_join";
291 	int result                                     = 1;
292 
293 #if defined( WINAPI )
294 	DWORD error_code                               = 0;
295 	DWORD wait_status                              = 0;
296 
297 #elif defined( HAVE_PTHREAD_H )
298 	int *thread_return_value                       = NULL;
299 	int pthread_result                             = 0;
300 #endif
301 
302 	if( thread == NULL )
303 	{
304 		libcerror_error_set(
305 		 error,
306 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
307 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
308 		 "%s: invalid thread.",
309 		 function );
310 
311 		return( -1 );
312 	}
313 	if( *thread == NULL )
314 	{
315 		libcerror_error_set(
316 		 error,
317 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
318 		 LIBCERROR_RUNTIME_ERROR_VALUE_MISSING,
319 		 "%s: missing thread value.",
320 		 function );
321 
322 		return( -1 );
323 	}
324 	internal_thread = (libcthreads_internal_thread_t *) *thread;
325 	*thread         = NULL;
326 
327 #if defined( WINAPI )
328 	wait_status = WaitForSingleObject(
329 	               internal_thread->thread_handle,
330 	               INFINITE );
331 
332 	if( wait_status == WAIT_FAILED )
333 	{
334 		error_code = GetLastError();
335 
336 		libcerror_system_set_error(
337 		 error,
338 		 error_code,
339 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
340 		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
341 		 "%s: wait for thread failed.",
342 		 function );
343 
344 		result = -1;
345 	}
346 
347 #elif defined( HAVE_PTHREAD_H )
348 	pthread_result = pthread_join(
349 	                  internal_thread->thread,
350 	                  (void **) &thread_return_value );
351 
352 	if( pthread_result == EDEADLK )
353 	{
354 		libcerror_error_set(
355 		 error,
356 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
357 		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
358 		 "%s: unable to join thread with error: Deadlock condition detected.",
359 		 function );
360 
361 		result = -1;
362 	}
363 	else if( pthread_result != 0 )
364 	{
365 		libcerror_system_set_error(
366 		 error,
367 		 pthread_result,
368 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
369 		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
370 		 "%s: unable to join thread.",
371 		 function );
372 
373 		result = -1;
374 	}
375 	/* If the thread returns NULL it never got around to launching the callback function
376 	 */
377 	else if( ( thread_return_value != NULL )
378 	      && ( *thread_return_value != 1 ) )
379 	{
380 		libcerror_error_set(
381 		 error,
382 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
383 		 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
384 		 "%s: thread returned an error status of: %d.",
385 		 function,
386 		 *thread_return_value );
387 
388 		result = -1;
389 	}
390 	if( thread_return_value != NULL )
391 	{
392 		memory_free(
393 		 thread_return_value );
394 
395 		thread_return_value = NULL;
396 	}
397 #endif
398 	memory_free(
399 	 internal_thread );
400 
401 	return( result );
402 }
403 
404 #endif /* !defined( HAVE_LOCAL_LIBCTHREADS ) || defined( HAVE_MULTI_THREAD_SUPPORT ) */
405 
406