perf_get.c (19329B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2009, 2011 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file perf_get.c 24 * @brief benchmark simple GET operations (sequential access). 25 * Note that we run libcurl in the same process at the 26 * same time, so the execution time given is the combined 27 * time for both MHD and libcurl; it is quite possible 28 * that more time is spend with libcurl than with MHD, 29 * so the performance scores calculated with this code 30 * should NOT be used to compare with other HTTP servers 31 * (since MHD is actually better); only the relative 32 * scores between MHD versions are meaningful. 33 * Furthermore, this code ONLY tests MHD processing 34 * a single request at a time. This is again 35 * not universally meaningful (i.e. when comparing 36 * multithreaded vs. single-threaded or select/poll). 37 * @author Christian Grothoff 38 * @author Karlson2k (Evgeny Grin) 39 */ 40 41 #include "MHD_config.h" 42 #include "platform.h" 43 #include <curl/curl.h> 44 #include <microhttpd.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <time.h> 48 #include <errno.h> 49 #include "mhd_has_in_name.h" 50 51 #ifndef WINDOWS 52 #include <unistd.h> 53 #include <sys/socket.h> 54 #endif 55 56 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 57 #undef MHD_CPU_COUNT 58 #endif 59 #if ! defined(MHD_CPU_COUNT) 60 #define MHD_CPU_COUNT 2 61 #endif 62 63 /** 64 * How many rounds of operations do we do for each 65 * test? 66 */ 67 #if MHD_CPU_COUNT > 8 68 #ifndef _WIN32 69 #define ROUNDS (1 + (30000 / 12) / MHD_CPU_COUNT) 70 #else /* _WIN32 */ 71 #define ROUNDS (1 + (3000 / 12) / MHD_CPU_COUNT) 72 #endif /* _WIN32 */ 73 #else 74 #define ROUNDS 500 75 #endif 76 77 /** 78 * Do we use HTTP 1.1? 79 */ 80 static int oneone; 81 82 /** 83 * Response to return (re-used). 84 */ 85 static struct MHD_Response *response; 86 87 /** 88 * Time this round was started. 89 */ 90 static unsigned long long start_time; 91 92 93 /** 94 * Get the current timestamp 95 * 96 * @return current time in ms 97 */ 98 static unsigned long long 99 now (void) 100 { 101 struct timeval tv; 102 103 gettimeofday (&tv, NULL); 104 return (((unsigned long long) tv.tv_sec * 1000LL) 105 + ((unsigned long long) tv.tv_usec / 1000LL)); 106 } 107 108 109 /** 110 * Start the timer. 111 */ 112 static void 113 start_timer (void) 114 { 115 start_time = now (); 116 } 117 118 119 /** 120 * Stop the timer and report performance 121 * 122 * @param desc description of the threading mode we used 123 */ 124 static void 125 stop (const char *desc) 126 { 127 double rps = ((double) (ROUNDS * 1000)) / ((double) (now () - start_time)); 128 129 fprintf (stderr, 130 "Sequential GETs using %s: %f %s\n", 131 desc, 132 rps, 133 "requests/s"); 134 } 135 136 137 struct CBC 138 { 139 char *buf; 140 size_t pos; 141 size_t size; 142 }; 143 144 145 static size_t 146 copyBuffer (void *ptr, 147 size_t size, size_t nmemb, 148 void *ctx) 149 { 150 struct CBC *cbc = ctx; 151 152 if (cbc->pos + size * nmemb > cbc->size) 153 return 0; /* overflow */ 154 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 155 cbc->pos += size * nmemb; 156 return size * nmemb; 157 } 158 159 160 static enum MHD_Result 161 ahc_echo (void *cls, 162 struct MHD_Connection *connection, 163 const char *url, 164 const char *method, 165 const char *version, 166 const char *upload_data, size_t *upload_data_size, 167 void **req_cls) 168 { 169 static int ptr; 170 enum MHD_Result ret; 171 (void) cls; 172 (void) url; (void) version; /* Unused. Silent compiler warning. */ 173 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 174 175 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 176 return MHD_NO; /* unexpected method */ 177 if (&ptr != *req_cls) 178 { 179 *req_cls = &ptr; 180 return MHD_YES; 181 } 182 *req_cls = NULL; 183 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 184 if (ret == MHD_NO) 185 abort (); 186 return ret; 187 } 188 189 190 static unsigned int 191 testInternalGet (uint16_t port, uint32_t poll_flag) 192 { 193 struct MHD_Daemon *d; 194 CURL *c; 195 char buf[2048]; 196 struct CBC cbc; 197 CURLcode errornum; 198 unsigned int i; 199 char url[64]; 200 201 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 202 port = 0; 203 204 cbc.buf = buf; 205 cbc.size = 2048; 206 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 207 | (enum MHD_FLAG) poll_flag, 208 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 209 if (d == NULL) 210 return 1; 211 if (0 == port) 212 { 213 const union MHD_DaemonInfo *dinfo; 214 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 215 if ((NULL == dinfo) || (0 == dinfo->port) ) 216 { 217 MHD_stop_daemon (d); return 32; 218 } 219 port = dinfo->port; 220 } 221 snprintf (url, 222 sizeof (url), 223 "http://127.0.0.1:%u/hello_world", 224 (unsigned int) port); 225 start_timer (); 226 for (i = 0; i < ROUNDS; i++) 227 { 228 cbc.pos = 0; 229 c = curl_easy_init (); 230 curl_easy_setopt (c, CURLOPT_URL, url); 231 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 232 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 233 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 234 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 235 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 236 if (oneone) 237 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 238 else 239 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 240 /* NOTE: use of CONNECTTIMEOUT without also 241 setting NOSIGNAL results in really weird 242 crashes on my system!*/ 243 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 244 if (CURLE_OK != (errornum = curl_easy_perform (c))) 245 { 246 fprintf (stderr, 247 "curl_easy_perform failed: `%s'\n", 248 curl_easy_strerror (errornum)); 249 curl_easy_cleanup (c); 250 MHD_stop_daemon (d); 251 return 2; 252 } 253 curl_easy_cleanup (c); 254 } 255 stop (poll_flag == MHD_USE_AUTO ? "internal thread with 'auto'" : 256 poll_flag == MHD_USE_POLL ? "internal thread with poll()" : 257 poll_flag == MHD_USE_EPOLL ? "internal thread with epoll" : 258 "internal thread with select()"); 259 MHD_stop_daemon (d); 260 if (cbc.pos != strlen ("/hello_world")) 261 return 4; 262 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 263 return 8; 264 return 0; 265 } 266 267 268 static unsigned int 269 testMultithreadedGet (uint16_t port, uint32_t poll_flag) 270 { 271 struct MHD_Daemon *d; 272 CURL *c; 273 char buf[2048]; 274 struct CBC cbc; 275 CURLcode errornum; 276 unsigned int i; 277 char url[64]; 278 279 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 280 port = 0; 281 282 cbc.buf = buf; 283 cbc.size = 2048; 284 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 285 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 286 | (enum MHD_FLAG) poll_flag, 287 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 288 if (d == NULL) 289 return 16; 290 if (0 == port) 291 { 292 const union MHD_DaemonInfo *dinfo; 293 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 294 if ((NULL == dinfo) || (0 == dinfo->port) ) 295 { 296 MHD_stop_daemon (d); return 32; 297 } 298 port = dinfo->port; 299 } 300 snprintf (url, 301 sizeof (url), 302 "http://127.0.0.1:%u/hello_world", 303 (unsigned int) port); 304 start_timer (); 305 for (i = 0; i < ROUNDS; i++) 306 { 307 cbc.pos = 0; 308 c = curl_easy_init (); 309 curl_easy_setopt (c, CURLOPT_URL, url); 310 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 311 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 312 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 313 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 314 if (oneone) 315 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 316 else 317 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 318 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 319 /* NOTE: use of CONNECTTIMEOUT without also 320 setting NOSIGNAL results in really weird 321 crashes on my system! */ 322 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 323 if (CURLE_OK != (errornum = curl_easy_perform (c))) 324 { 325 fprintf (stderr, 326 "curl_easy_perform failed: `%s'\n", 327 curl_easy_strerror (errornum)); 328 curl_easy_cleanup (c); 329 MHD_stop_daemon (d); 330 return 32; 331 } 332 curl_easy_cleanup (c); 333 } 334 stop ((poll_flag & MHD_USE_AUTO) ? 335 "internal thread with 'auto' and thread per connection" : 336 (poll_flag & MHD_USE_POLL) ? 337 "internal thread with poll() and thread per connection" : 338 (poll_flag & MHD_USE_EPOLL) ? 339 "internal thread with epoll and thread per connection" : 340 "internal thread with select() and thread per connection"); 341 MHD_stop_daemon (d); 342 if (cbc.pos != strlen ("/hello_world")) 343 return 64; 344 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 345 return 128; 346 return 0; 347 } 348 349 350 static unsigned int 351 testMultithreadedPoolGet (uint16_t port, uint32_t poll_flag) 352 { 353 struct MHD_Daemon *d; 354 CURL *c; 355 char buf[2048]; 356 struct CBC cbc; 357 CURLcode errornum; 358 unsigned int i; 359 char url[64]; 360 361 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 362 port = 0; 363 364 cbc.buf = buf; 365 cbc.size = 2048; 366 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 367 | (enum MHD_FLAG) poll_flag, 368 port, NULL, NULL, &ahc_echo, NULL, 369 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 370 MHD_OPTION_END); 371 if (d == NULL) 372 return 16; 373 if (0 == port) 374 { 375 const union MHD_DaemonInfo *dinfo; 376 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 377 if ((NULL == dinfo) || (0 == dinfo->port) ) 378 { 379 MHD_stop_daemon (d); return 32; 380 } 381 port = dinfo->port; 382 } 383 snprintf (url, 384 sizeof (url), 385 "http://127.0.0.1:%u/hello_world", 386 (unsigned int) port); 387 start_timer (); 388 for (i = 0; i < ROUNDS; i++) 389 { 390 cbc.pos = 0; 391 c = curl_easy_init (); 392 curl_easy_setopt (c, CURLOPT_URL, url); 393 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 394 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 395 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 396 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 397 if (oneone) 398 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 399 else 400 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 401 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 402 /* NOTE: use of CONNECTTIMEOUT without also 403 setting NOSIGNAL results in really weird 404 crashes on my system!*/ 405 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 406 if (CURLE_OK != (errornum = curl_easy_perform (c))) 407 { 408 fprintf (stderr, 409 "curl_easy_perform failed: `%s'\n", 410 curl_easy_strerror (errornum)); 411 curl_easy_cleanup (c); 412 MHD_stop_daemon (d); 413 return 32; 414 } 415 curl_easy_cleanup (c); 416 } 417 stop (0 != (poll_flag & MHD_USE_AUTO) ? "internal thread pool with 'auto'" : 418 0 != (poll_flag & MHD_USE_POLL) ? "internal thread pool with poll()" : 419 0 != (poll_flag & MHD_USE_EPOLL) ? "internal thread pool with epoll" : 420 "internal thread pool with select()"); 421 MHD_stop_daemon (d); 422 if (cbc.pos != strlen ("/hello_world")) 423 return 64; 424 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 425 return 128; 426 return 0; 427 } 428 429 430 static unsigned int 431 testExternalGet (uint16_t port) 432 { 433 struct MHD_Daemon *d; 434 CURL *c; 435 char buf[2048]; 436 struct CBC cbc; 437 CURLM *multi; 438 CURLMcode mret; 439 fd_set rs; 440 fd_set ws; 441 fd_set es; 442 MHD_socket maxsock; 443 #ifdef MHD_WINSOCK_SOCKETS 444 int maxposixs; /* Max socket number unused on W32 */ 445 #else /* MHD_POSIX_SOCKETS */ 446 #define maxposixs maxsock 447 #endif /* MHD_POSIX_SOCKETS */ 448 int running; 449 struct CURLMsg *msg; 450 time_t start; 451 struct timeval tv; 452 unsigned int i; 453 char url[64]; 454 455 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 456 port = 0; 457 458 multi = NULL; 459 cbc.buf = buf; 460 cbc.size = 2048; 461 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 462 port, NULL, NULL, 463 &ahc_echo, NULL, 464 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 465 MHD_OPTION_END); 466 if (NULL == d) 467 return 256; 468 if (0 == port) 469 { 470 const union MHD_DaemonInfo *dinfo; 471 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 472 if ((NULL == dinfo) || (0 == dinfo->port) ) 473 { 474 MHD_stop_daemon (d); return 32; 475 } 476 port = dinfo->port; 477 } 478 snprintf (url, 479 sizeof (url), 480 "http://127.0.0.1:%u/hello_world", 481 (unsigned int) port); 482 start_timer (); 483 multi = curl_multi_init (); 484 if (multi == NULL) 485 { 486 MHD_stop_daemon (d); 487 return 512; 488 } 489 for (i = 0; i < ROUNDS; i++) 490 { 491 cbc.pos = 0; 492 c = curl_easy_init (); 493 curl_easy_setopt (c, CURLOPT_URL, url); 494 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 495 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 496 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 497 if (oneone) 498 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 499 else 500 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 501 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 502 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 503 /* NOTE: use of CONNECTTIMEOUT without also 504 setting NOSIGNAL results in really weird 505 crashes on my system! */ 506 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 507 mret = curl_multi_add_handle (multi, c); 508 if (mret != CURLM_OK) 509 { 510 curl_multi_cleanup (multi); 511 curl_easy_cleanup (c); 512 MHD_stop_daemon (d); 513 return 1024; 514 } 515 start = time (NULL); 516 while ((time (NULL) - start < 5) && (c != NULL)) 517 { 518 maxsock = MHD_INVALID_SOCKET; 519 maxposixs = -1; 520 FD_ZERO (&rs); 521 FD_ZERO (&ws); 522 FD_ZERO (&es); 523 curl_multi_perform (multi, &running); 524 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 525 if (mret != CURLM_OK) 526 { 527 curl_multi_remove_handle (multi, c); 528 curl_multi_cleanup (multi); 529 curl_easy_cleanup (c); 530 MHD_stop_daemon (d); 531 return 2048; 532 } 533 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 534 { 535 curl_multi_remove_handle (multi, c); 536 curl_multi_cleanup (multi); 537 curl_easy_cleanup (c); 538 MHD_stop_daemon (d); 539 return 4096; 540 } 541 tv.tv_sec = 0; 542 tv.tv_usec = 1000; 543 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 544 { 545 #ifdef MHD_POSIX_SOCKETS 546 if (EINTR != errno) 547 { 548 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 549 (int) errno, __LINE__); 550 fflush (stderr); 551 exit (99); 552 } 553 #else 554 if ((WSAEINVAL != WSAGetLastError ()) || 555 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 556 { 557 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 558 (int) WSAGetLastError (), __LINE__); 559 fflush (stderr); 560 exit (99); 561 } 562 Sleep (1); 563 #endif 564 } 565 curl_multi_perform (multi, &running); 566 if (0 == running) 567 { 568 int pending; 569 int curl_fine = 0; 570 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 571 { 572 if (msg->msg == CURLMSG_DONE) 573 { 574 if (msg->data.result == CURLE_OK) 575 curl_fine = 1; 576 else 577 { 578 fprintf (stderr, 579 "%s failed at %s:%d: `%s'\n", 580 "curl_multi_perform", 581 __FILE__, 582 __LINE__, curl_easy_strerror (msg->data.result)); 583 abort (); 584 } 585 } 586 } 587 if (! curl_fine) 588 { 589 fprintf (stderr, "libcurl haven't returned OK code\n"); 590 abort (); 591 } 592 curl_multi_remove_handle (multi, c); 593 curl_easy_cleanup (c); 594 c = NULL; 595 break; 596 } 597 /* two possibilities here; as select sets are 598 tiny, this makes virtually no difference 599 in actual runtime right now, even though the 600 number of select calls is virtually cut in half 601 (and 'select' is the most expensive of our system 602 calls according to 'strace') */ 603 if (0) 604 MHD_run (d); 605 else 606 MHD_run_from_select (d, &rs, &ws, &es); 607 } 608 if (NULL != c) 609 { 610 curl_multi_remove_handle (multi, c); 611 curl_easy_cleanup (c); 612 fprintf (stderr, "Timeout!?\n"); 613 } 614 } 615 stop ("external select"); 616 if (multi != NULL) 617 { 618 curl_multi_cleanup (multi); 619 } 620 MHD_stop_daemon (d); 621 if (cbc.pos != strlen ("/hello_world")) 622 return 8192; 623 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 624 return 16384; 625 return 0; 626 } 627 628 629 int 630 main (int argc, char *const *argv) 631 { 632 unsigned int errorCount = 0; 633 uint16_t port = 1130; 634 (void) argc; /* Unused. Silent compiler warning. */ 635 636 if ((NULL == argv) || (0 == argv[0])) 637 return 99; 638 oneone = has_in_name (argv[0], "11"); 639 if (oneone) 640 port += 15; 641 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 642 return 2; 643 response = MHD_create_response_from_buffer_copy (strlen ("/hello_world"), 644 "/hello_world"); 645 errorCount += testExternalGet (port++); 646 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 647 { 648 errorCount += testInternalGet (port++, MHD_USE_AUTO); 649 errorCount += testMultithreadedGet (port++, MHD_USE_AUTO); 650 errorCount += testMultithreadedPoolGet (port++, MHD_USE_AUTO); 651 errorCount += testInternalGet (port++, 0); 652 errorCount += testMultithreadedGet (port++, 0); 653 errorCount += testMultithreadedPoolGet (port++, 0); 654 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 655 { 656 errorCount += testInternalGet (port++, MHD_USE_POLL); 657 errorCount += testMultithreadedGet (port++, MHD_USE_POLL); 658 errorCount += testMultithreadedPoolGet (port++, MHD_USE_POLL); 659 } 660 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 661 { 662 errorCount += testInternalGet (port++, MHD_USE_EPOLL); 663 errorCount += testMultithreadedPoolGet (port++, MHD_USE_EPOLL); 664 } 665 } 666 MHD_destroy_response (response); 667 if (errorCount != 0) 668 fprintf (stderr, "Error (code: %u)\n", errorCount); 669 curl_global_cleanup (); 670 return errorCount != 0; /* 0 == pass */ 671 }