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