mhd_threads.h (19189B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2016-2023 Karlson2k (Evgeny Grin) 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 19 */ 20 21 /** 22 * @file microhttpd/mhd_threads.h 23 * @brief Header for platform-independent threads abstraction 24 * @author Karlson2k (Evgeny Grin) 25 * 26 * Provides basic abstraction for threads. 27 * Any functions can be implemented as macro on some platforms 28 * unless explicitly marked otherwise. 29 * Any function argument can be skipped in macro, so avoid 30 * variable modification in function parameters. 31 * 32 * @warning Unlike pthread functions, most of functions return 33 * nonzero on success. 34 */ 35 36 #ifndef MHD_THREADS_H 37 #define MHD_THREADS_H 1 38 39 #include "mhd_options.h" 40 #ifdef HAVE_STDDEF_H 41 # include <stddef.h> /* for size_t */ 42 #elif defined(HAVE_STDLIB_H) 43 # include <stdlib.h> /* for size_t */ 44 #else /* ! HAVE_STDLIB_H */ 45 # include <stdio.h> /* for size_t */ 46 #endif /* ! HAVE_STDLIB_H */ 47 48 #if defined(MHD_USE_POSIX_THREADS) 49 # undef HAVE_CONFIG_H 50 # include <pthread.h> 51 # define HAVE_CONFIG_H 1 52 # ifndef MHD_USE_THREADS 53 # define MHD_USE_THREADS 1 54 # endif 55 #elif defined(MHD_USE_W32_THREADS) 56 # ifndef WIN32_LEAN_AND_MEAN 57 # define WIN32_LEAN_AND_MEAN 1 58 # endif /* !WIN32_LEAN_AND_MEAN */ 59 # include <windows.h> 60 # ifndef MHD_USE_THREADS 61 # define MHD_USE_THREADS 1 62 # endif 63 #else 64 # error No threading API is available. 65 #endif 66 67 #ifdef HAVE_STDBOOL_H 68 # include <stdbool.h> 69 #endif /* HAVE_STDBOOL_H */ 70 71 #if defined(MHD_USE_POSIX_THREADS) && defined(MHD_USE_W32_THREADS) 72 # error Both MHD_USE_POSIX_THREADS and MHD_USE_W32_THREADS are defined 73 #endif /* MHD_USE_POSIX_THREADS && MHD_USE_W32_THREADS */ 74 75 #ifndef MHD_NO_THREAD_NAMES 76 # if defined(MHD_USE_POSIX_THREADS) 77 # if defined(HAVE_PTHREAD_SETNAME_NP_GNU) || \ 78 defined(HAVE_PTHREAD_SET_NAME_NP_FREEBSD) || \ 79 defined(HAVE_PTHREAD_SETNAME_NP_DARWIN) || \ 80 defined(HAVE_PTHREAD_SETNAME_NP_NETBSD) || \ 81 defined(HAVE_PTHREAD_ATTR_SETNAME_NP_NETBSD) || \ 82 defined(HAVE_PTHREAD_ATTR_SETNAME_NP_IBMI) 83 # define MHD_USE_THREAD_NAME_ 84 # endif /* HAVE_PTHREAD_SETNAME_NP */ 85 # elif defined(MHD_USE_W32_THREADS) 86 # ifdef _MSC_FULL_VER 87 /* Thread names only available with VC compiler */ 88 # define MHD_USE_THREAD_NAME_ 89 # endif /* _MSC_FULL_VER */ 90 # endif 91 #endif 92 93 /* ** Thread handle - used to control the thread ** */ 94 95 #if defined(MHD_USE_POSIX_THREADS) 96 /** 97 * Wait until specified thread is ended and free thread handle on success. 98 * @param thread handle to watch 99 * @return nonzero on success, zero otherwise 100 */ 101 # define MHD_join_thread_(native_handle) \ 102 (! pthread_join ((native_handle), NULL)) 103 #elif defined(MHD_USE_W32_THREADS) 104 /** 105 * Wait until specified thread is ended and free thread handle on success. 106 * @param thread handle to watch 107 * @return nonzero on success, zero otherwise 108 */ 109 # define MHD_join_thread_(native_handle) \ 110 ( (WAIT_OBJECT_0 == WaitForSingleObject ( (native_handle), INFINITE)) ? \ 111 (CloseHandle ( (native_handle)), ! 0) : 0 ) 112 #endif 113 114 #if defined(MHD_USE_POSIX_THREADS) 115 /** 116 * The native type to control the thread from other threads 117 */ 118 typedef pthread_t MHD_thread_handle_native_; 119 #elif defined(MHD_USE_W32_THREADS) 120 /** 121 * The native type to control the thread from other threads 122 */ 123 typedef HANDLE MHD_thread_handle_native_; 124 #endif 125 126 #if defined(MHD_USE_POSIX_THREADS) 127 # if defined(__gnu_linux__) || \ 128 (defined(__linux__) && defined(__GLIBC__)) 129 /* The next part of code is disabled because it relies on undocumented 130 behaviour. 131 It could be enabled for neglectable performance and size improvements. */ 132 # if 0 /* Disabled code */ 133 /** 134 * The native invalid value for native thread handle 135 */ 136 # define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \ 137 ((MHD_thread_handle_native_) 0) 138 # endif /* Disabled code */ 139 # endif /* __gnu_linux__ || (__linux__ && __GLIBC__) */ 140 #elif defined(MHD_USE_W32_THREADS) 141 /** 142 * The native invalid value for native thread handle 143 */ 144 # define MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ \ 145 ((MHD_thread_handle_native_) NULL) 146 #endif /* MHD_USE_W32_THREADS */ 147 148 #if ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) 149 /** 150 * Structure with thread handle and validity flag 151 */ 152 struct MHD_thread_handle_struct_ 153 { 154 bool valid; /**< true if native handle is set */ 155 MHD_thread_handle_native_ native; /**< the native thread handle */ 156 }; 157 /** 158 * Type with thread handle that can be set to invalid value 159 */ 160 typedef struct MHD_thread_handle_struct_ MHD_thread_handle_; 161 162 /** 163 * Set variable pointed by @a handle_ptr to invalid (unset) value 164 */ 165 # define MHD_thread_handle_set_invalid_(handle_ptr) \ 166 ((handle_ptr)->valid = false) 167 /** 168 * Set native handle in variable pointed by @a handle_ptr 169 * to @a native_val value 170 */ 171 # define MHD_thread_handle_set_native_(handle_ptr,native_val) \ 172 ((handle_ptr)->valid = true, (handle_ptr)->native = native_val) 173 /** 174 * Check whether native handle value is set in @a handle_var variable 175 */ 176 # define MHD_thread_handle_is_valid_(handle_var) \ 177 ((handle_var).valid) 178 /** 179 * Get native handle value from @a handle_var variable 180 */ 181 # define MHD_thread_handle_get_native_(handle_var) \ 182 ((handle_var).native) 183 #else /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ 184 /** 185 * Type with thread handle that can be set to invalid value 186 */ 187 typedef MHD_thread_handle_native_ MHD_thread_handle_; 188 189 /** 190 * Set variable pointed by @a handle_ptr to invalid (unset) value 191 */ 192 # define MHD_thread_handle_set_invalid_(handle_ptr) \ 193 ((*(handle_ptr)) = MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) 194 /** 195 * Set native handle in the variable pointed by @a handle_ptr 196 * to @a native_val value 197 */ 198 # define MHD_thread_handle_set_native_(handle_ptr,native_val) \ 199 ((*(handle_ptr)) = native_val) 200 /** 201 * Check whether native handle value is set in @a handle_var variable 202 */ 203 # define MHD_thread_handle_is_valid_(handle_var) \ 204 (MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ != handle_var) 205 /** 206 * Get native handle value from @a handle_var variable 207 */ 208 # define MHD_thread_handle_get_native_(handle_var) \ 209 (handle_var) 210 /** 211 * Get pointer to native handle stored the variable pointed by @a handle_ptr 212 * @note This macro could not available if direct manipulation of 213 * the native handle is not possible 214 */ 215 # define MHD_thread_handle_get_native_ptr_(handle_ptr) \ 216 (handle_ptr) 217 #endif /* MHD_THREAD_HANDLE_NATIVE_INVALID_ */ 218 219 220 /* ** Thread ID - used to check threads match ** */ 221 222 #if defined(MHD_USE_POSIX_THREADS) 223 /** 224 * The native type used to check whether current thread matches expected thread 225 */ 226 typedef pthread_t MHD_thread_ID_native_; 227 228 /** 229 * Function to get the current thread native ID. 230 */ 231 # define MHD_thread_ID_native_current_ pthread_self 232 233 /** 234 * Check whether two native thread IDs are equal. 235 * @return non-zero if equal, zero if not equal 236 */ 237 # define MHD_thread_ID_native_equal_(id1,id2) \ 238 (pthread_equal(id1,id2)) 239 #elif defined(MHD_USE_W32_THREADS) 240 /** 241 * The native type used to check whether current thread matches expected thread 242 */ 243 typedef DWORD MHD_thread_ID_native_; 244 245 /** 246 * Function to get the current thread native ID. 247 */ 248 # define MHD_thread_ID_native_current_ GetCurrentThreadId 249 250 /** 251 * Check whether two native thread IDs are equal. 252 * @return non-zero if equal, zero if not equal 253 */ 254 # define MHD_thread_ID_native_equal_(id1,id2) \ 255 ((id1) == (id2)) 256 #endif 257 258 /** 259 * Check whether specified thread ID matches current thread. 260 * @param id the thread ID to match 261 * @return nonzero on match, zero otherwise 262 */ 263 #define MHD_thread_ID_native_is_current_thread_(id) \ 264 MHD_thread_ID_native_equal_(id, MHD_thread_ID_native_current_()) 265 266 267 #if defined(MHD_USE_POSIX_THREADS) 268 # if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) 269 /** 270 * The native invalid value for native thread ID 271 */ 272 # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ 273 MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ 274 # endif /* MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ */ 275 #elif defined(MHD_USE_W32_THREADS) 276 /** 277 * The native invalid value for native thread ID 278 */ 279 # define MHD_THREAD_ID_NATIVE_VALUE_INVALID_ \ 280 ((MHD_thread_ID_native_) 0) 281 #endif /* MHD_USE_W32_THREADS */ 282 283 #if ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) 284 /** 285 * Structure with thread id and validity flag 286 */ 287 struct MHD_thread_ID_struct_ 288 { 289 bool valid; /**< true if native ID is set */ 290 MHD_thread_ID_native_ native; /**< the native thread ID */ 291 }; 292 /** 293 * Type with thread ID that can be set to invalid value 294 */ 295 typedef struct MHD_thread_ID_struct_ MHD_thread_ID_; 296 297 /** 298 * Set variable pointed by @a ID_ptr to invalid (unset) value 299 */ 300 # define MHD_thread_ID_set_invalid_(ID_ptr) \ 301 ((ID_ptr)->valid = false) 302 /** 303 * Set native ID in variable pointed by @a ID_ptr 304 * to @a native_val value 305 */ 306 # define MHD_thread_ID_set_native_(ID_ptr,native_val) \ 307 ((ID_ptr)->valid = true, (ID_ptr)->native = native_val) 308 /** 309 * Check whether native ID value is set in @a ID_var variable 310 */ 311 # define MHD_thread_ID_is_valid_(ID_var) \ 312 ((ID_var).valid) 313 /** 314 * Get native ID value from @a ID_var variable 315 */ 316 # define MHD_thread_ID_get_native_(ID_var) \ 317 ((ID_var).native) 318 /** 319 * Check whether @a ID_var variable is equal current thread 320 */ 321 # define MHD_thread_ID_is_current_thread_(ID_var) \ 322 (MHD_thread_ID_is_valid_(ID_var) && \ 323 MHD_thread_ID_native_is_current_thread_((ID_var).native)) 324 #else /* MHD_THREAD_ID_NATIVE_INVALID_ */ 325 /** 326 * Type with thread ID that can be set to invalid value 327 */ 328 typedef MHD_thread_ID_native_ MHD_thread_ID_; 329 330 /** 331 * Set variable pointed by @a ID_ptr to invalid (unset) value 332 */ 333 # define MHD_thread_ID_set_invalid_(ID_ptr) \ 334 ((*(ID_ptr)) = MHD_THREAD_ID_NATIVE_VALUE_INVALID_) 335 /** 336 * Set native ID in variable pointed by @a ID_ptr 337 * to @a native_val value 338 */ 339 # define MHD_thread_ID_set_native_(ID_ptr,native_val) \ 340 ((*(ID_ptr)) = native_val) 341 /** 342 * Check whether native ID value is set in @a ID_var variable 343 */ 344 # define MHD_thread_ID_is_valid_(ID_var) \ 345 (MHD_THREAD_ID_NATIVE_VALUE_INVALID_ != ID_var) 346 /** 347 * Get native ID value from @a ID_var variable 348 */ 349 # define MHD_thread_ID_get_native_(ID_var) \ 350 (ID_var) 351 /** 352 * Check whether @a ID_var variable is equal current thread 353 */ 354 # define MHD_thread_ID_is_current_thread_(ID_var) \ 355 MHD_thread_ID_native_is_current_thread_(ID_var) 356 #endif /* MHD_THREAD_ID_NATIVE_INVALID_ */ 357 358 /** 359 * Set current thread ID in variable pointed by @a ID_ptr 360 */ 361 # define MHD_thread_ID_set_current_thread_(ID_ptr) \ 362 MHD_thread_ID_set_native_(ID_ptr,MHD_thread_ID_native_current_()) 363 364 365 #if defined(MHD_USE_POSIX_THREADS) 366 # if defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ 367 ! defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) 368 # error \ 369 MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined 370 # elif ! defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ 371 defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) 372 # error \ 373 MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is not defined, but MHD_THREAD_ID_NATIVE_VALUE_INVALID_ is defined 374 # endif 375 #endif /* MHD_USE_POSIX_THREADS */ 376 377 /* When staring a new thread, the kernel (and thread implementation) may 378 * pause the calling (initial) thread and start the new thread. 379 * If thread identifier is assigned to variable in the initial thread then 380 * the value of the identifier variable will be undefined in the new thread 381 * until the initial thread continue processing. 382 * However, it is also possible that the new thread created, but not executed 383 * for some time while the initial thread continue execution. In this case any 384 * variable assigned in the new thread will be undefined for some time until 385 * they really processed by the new thread. 386 * To avoid data races, a special structure MHD_thread_handle_ID_ is used. 387 * The "handle" is assigned by calling (initial) thread and should be always 388 * defined when checked in the initial thread. 389 * The "ID" is assigned by the new thread and should be always defined when 390 * checked inside the new thread. 391 */ 392 /* Depending on implementation, pthread_create() MAY set thread ID into 393 * provided pointer and after it start thread OR start thread and after 394 * it set thread ID. In the latter case, to avoid data races, additional 395 * pthread_self() call is required in thread routine. If some platform 396 * is known for setting thread ID BEFORE starting thread macro 397 * MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD could be defined 398 * to save some resources. */ 399 /* #define MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 1 */ 400 401 /* * handle - must be valid when other thread knows that particular thread 402 is started. 403 * ID - must be valid when code is executed inside thread */ 404 #if defined(MHD_USE_POSIX_THREADS) && \ 405 defined(MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD) && \ 406 defined(MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_) && \ 407 defined(MHD_THREAD_ID_NATIVE_VALUE_INVALID_) && \ 408 defined(MHD_thread_handle_get_native_ptr_) 409 union _MHD_thread_handle_ID_ 410 { 411 MHD_thread_handle_ handle; /**< To be used in other threads */ 412 MHD_thread_ID_ ID; /**< To be used in the thread itself */ 413 }; 414 typedef union _MHD_thread_handle_ID_ MHD_thread_handle_ID_; 415 # define MHD_THREAD_HANDLE_ID_IS_UNION 1 416 #else /* !MHD_USE_POSIX_THREADS 417 || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 418 || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ 419 || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ 420 || !MHD_thread_handle_get_native_ptr_ */ 421 struct _MHD_thread_handle_ID_ 422 { 423 MHD_thread_handle_ handle; /**< To be used in other threads */ 424 MHD_thread_ID_ ID; /**< To be used in the thread itself */ 425 }; 426 typedef struct _MHD_thread_handle_ID_ MHD_thread_handle_ID_; 427 #endif /* !MHD_USE_POSIX_THREADS 428 || !MHD_PTHREAD_CREATE__SET_ID_BEFORE_START_THREAD 429 || !MHD_THREAD_HANDLE_NATIVE_VALUE_INVALID_ 430 || !MHD_THREAD_ID_NATIVE_VALUE_INVALID_ 431 || !MHD_thread_handle_get_native_ptr_ */ 432 433 /** 434 * Set MHD_thread_handle_ID_ to invalid value 435 */ 436 #define MHD_thread_handle_ID_set_invalid_(hndl_id_ptr) \ 437 (MHD_thread_handle_set_invalid_(&((hndl_id_ptr)->handle)), \ 438 MHD_thread_ID_set_invalid_(&((hndl_id_ptr)->ID))) 439 440 /** 441 * Check whether thread handle is valid. 442 * To be used in threads other then the thread specified by @a hndl_id. 443 */ 444 #define MHD_thread_handle_ID_is_valid_handle_(hndl_id) \ 445 MHD_thread_handle_is_valid_((hndl_id).handle) 446 447 /** 448 * Set native handle in variable pointed by @a hndl_id_ptr 449 * to @a native_val value 450 */ 451 #define MHD_thread_handle_ID_set_native_handle_(hndl_id_ptr,native_val) \ 452 MHD_thread_handle_set_native_(&((hndl_id_ptr)->handle),native_val) 453 454 #if defined(MHD_thread_handle_get_native_ptr_) 455 /** 456 * Get pointer to native handle stored the variable pointed by @a hndl_id_ptr 457 * @note This macro could not available if direct manipulation of 458 * the native handle is not possible 459 */ 460 # define MHD_thread_handle_ID_get_native_handle_ptr_(hndl_id_ptr) \ 461 MHD_thread_handle_get_native_ptr_(&((hndl_id_ptr)->handle)) 462 #endif /* MHD_thread_handle_get_native_ptr_ */ 463 464 /** 465 * Get native thread handle from MHD_thread_handle_ID_ variable. 466 */ 467 #define MHD_thread_handle_ID_get_native_handle_(hndl_id) \ 468 MHD_thread_handle_get_native_((hndl_id).handle) 469 470 /** 471 * Check whether thread ID is valid. 472 * To be used in the thread itself. 473 */ 474 #define MHD_thread_handle_ID_is_valid_ID_(hndl_id) \ 475 MHD_thread_ID_is_valid_((hndl_id).ID) 476 477 #if defined(MHD_THREAD_HANDLE_ID_IS_UNION) 478 # if defined(MHD_USE_W32_THREADS) 479 # error MHD_thread_handle_ID_ cannot be a union with W32 threads 480 # endif /* MHD_USE_W32_THREADS */ 481 /** 482 * Set current thread ID in the variable pointed by @a hndl_id_ptr 483 */ 484 # define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) (void) 0 485 #else /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ 486 /** 487 * Set current thread ID in the variable pointed by @a hndl_id_ptr 488 */ 489 # define MHD_thread_handle_ID_set_current_thread_ID_(hndl_id_ptr) \ 490 MHD_thread_ID_set_current_thread_(&((hndl_id_ptr)->ID)) 491 #endif /* ! MHD_THREAD_HANDLE_ID_IS_UNION */ 492 493 /** 494 * Check whether provided thread ID matches current thread. 495 * @param ID thread ID to match 496 * @return nonzero on match, zero otherwise 497 */ 498 #define MHD_thread_handle_ID_is_current_thread_(hndl_id) \ 499 MHD_thread_ID_is_current_thread_((hndl_id).ID) 500 501 /** 502 * Wait until specified thread is ended and free thread handle on success. 503 * @param hndl_id_ handle with ID to watch 504 * @return nonzero on success, zero otherwise 505 */ 506 #define MHD_thread_handle_ID_join_thread_(hndl_id) \ 507 MHD_join_thread_(MHD_thread_handle_ID_get_native_handle_(hndl_id)) 508 509 #if defined(MHD_USE_POSIX_THREADS) 510 # define MHD_THRD_RTRN_TYPE_ void* 511 # define MHD_THRD_CALL_SPEC_ 512 #elif defined(MHD_USE_W32_THREADS) 513 # define MHD_THRD_RTRN_TYPE_ unsigned 514 # define MHD_THRD_CALL_SPEC_ __stdcall 515 #endif 516 517 /** 518 * Signature of main function for a thread. 519 * 520 * @param cls closure argument for the function 521 * @return termination code from the thread 522 */ 523 typedef MHD_THRD_RTRN_TYPE_ 524 (MHD_THRD_CALL_SPEC_ *MHD_THREAD_START_ROUTINE_)(void *cls); 525 526 527 /** 528 * Create a thread and set the attributes according to our options. 529 * 530 * If thread is created, thread handle must be freed by MHD_join_thread_(). 531 * 532 * @param handle_id handle to initialise 533 * @param stack_size size of stack for new thread, 0 for default 534 * @param start_routine main function of thread 535 * @param arg argument for start_routine 536 * @return non-zero on success; zero otherwise (with errno set) 537 */ 538 int 539 MHD_create_thread_ (MHD_thread_handle_ID_ *handle_id, 540 size_t stack_size, 541 MHD_THREAD_START_ROUTINE_ start_routine, 542 void *arg); 543 544 #ifndef MHD_USE_THREAD_NAME_ 545 #define MHD_create_named_thread_(t,n,s,r,a) MHD_create_thread_ ((t),(s),(r),(a)) 546 #else /* MHD_USE_THREAD_NAME_ */ 547 /** 548 * Create a named thread and set the attributes according to our options. 549 * 550 * @param handle_id handle to initialise 551 * @param thread_name name for new thread 552 * @param stack_size size of stack for new thread, 0 for default 553 * @param start_routine main function of thread 554 * @param arg argument for start_routine 555 * @return non-zero on success; zero otherwise 556 */ 557 int 558 MHD_create_named_thread_ (MHD_thread_handle_ID_ *handle_id, 559 const char *thread_name, 560 size_t stack_size, 561 MHD_THREAD_START_ROUTINE_ start_routine, 562 void *arg); 563 564 #endif /* MHD_USE_THREAD_NAME_ */ 565 566 #endif /* ! MHD_THREADS_H */