dispatch_apply.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. /*
  2. * Copyright (c) 2008-2011 Apple Inc. All rights reserved.
  3. *
  4. * @APPLE_APACHE_LICENSE_HEADER_START@
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. * @APPLE_APACHE_LICENSE_HEADER_END@
  19. */
  20. #include <dispatch/dispatch.h>
  21. #include <stdio.h>
  22. #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
  23. #include <unistd.h>
  24. #ifdef __ANDROID__
  25. #include <linux/sysctl.h>
  26. #else
  27. #if !defined(__linux__)
  28. #include <sys/sysctl.h>
  29. #endif
  30. #endif /* __ANDROID__ */
  31. #endif
  32. #include <stdlib.h>
  33. #include <assert.h>
  34. #ifdef __APPLE__
  35. #include <libkern/OSAtomic.h>
  36. #endif
  37. #include <sys/types.h>
  38. #include <bsdtests.h>
  39. #include "dispatch_test.h"
  40. static volatile int32_t busy_threads_started, busy_threads_finished;
  41. /*
  42. * Keep a thread busy, spinning on the CPU.
  43. */
  44. static volatile int all_done = 0;
  45. /* Fiddling with j in the middle and hitting this global will hopefully keep
  46. * the optimizer from cutting the whole thing out as dead code.
  47. */
  48. static volatile unsigned int busythread_useless;
  49. static void busythread(void *ignored)
  50. {
  51. (void)ignored;
  52. /* prevent i and j been optimized out */
  53. volatile uint64_t i = 0, j = 0;
  54. OSAtomicIncrement32(&busy_threads_started);
  55. while(!all_done)
  56. {
  57. if(i == 500000) { j -= busythread_useless; }
  58. j += i;
  59. i += 1;
  60. }
  61. (void)j;
  62. OSAtomicIncrement32(&busy_threads_finished);
  63. }
  64. /*
  65. * Test that dispatch_apply can make progress and finish, even if there are
  66. * so many other running and unblocked workqueue threads that the apply's
  67. * helper threads never get a chance to come up.
  68. *
  69. * <rdar://problem/10718199> dispatch_apply should not block waiting on other
  70. * threads while calling thread is available
  71. */
  72. static void test_apply_contended(dispatch_queue_t dq)
  73. {
  74. uint32_t activecpu;
  75. #if defined(__linux__) || defined(__OpenBSD__)
  76. activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN);
  77. #elif defined(_WIN32)
  78. SYSTEM_INFO si;
  79. GetSystemInfo(&si);
  80. activecpu = si.dwNumberOfProcessors;
  81. #else
  82. size_t s = sizeof(activecpu);
  83. sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0);
  84. #endif
  85. int tIndex, n_threads = (int)activecpu;
  86. dispatch_group_t grp = dispatch_group_create();
  87. for(tIndex = 0; tIndex < n_threads; tIndex++) {
  88. dispatch_group_async_f(grp, dq, NULL, busythread);
  89. }
  90. // Spin until all the threads have actually started
  91. while(busy_threads_started < n_threads) {
  92. usleep(1);
  93. }
  94. volatile __block int32_t count = 0;
  95. const int32_t final = 32;
  96. int32_t before = busy_threads_started;
  97. dispatch_apply(final, dq, ^(size_t i __attribute__((unused))) {
  98. OSAtomicIncrement32(&count);
  99. });
  100. int32_t after = busy_threads_finished;
  101. test_long("contended: threads started before apply", before, n_threads);
  102. test_long("contended: count", count, final);
  103. test_long("contended: threads finished before apply", after, 0);
  104. /* Release busy threads by setting all_done to 1 */
  105. all_done = 1;
  106. dispatch_group_wait(grp, DISPATCH_TIME_FOREVER);
  107. dispatch_release(grp);
  108. }
  109. int
  110. main(void)
  111. {
  112. dispatch_test_start("Dispatch Apply");
  113. volatile __block int32_t count = 0;
  114. const int32_t final = 32;
  115. dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
  116. test_ptr_notnull("dispatch_get_global_queue", queue);
  117. dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
  118. OSAtomicIncrement32(&count);
  119. });
  120. test_long("count", count, final);
  121. count = 0; // rdar://problem/9294578
  122. dispatch_apply(final, queue, ^(size_t i __attribute__((unused))) {
  123. dispatch_apply(final, queue, ^(size_t ii __attribute__((unused))) {
  124. dispatch_apply(final, queue, ^(size_t iii __attribute__((unused))) {
  125. OSAtomicIncrement32(&count);
  126. });
  127. });
  128. });
  129. test_long("nested count", count, final * final * final);
  130. test_apply_contended(queue);
  131. test_stop();
  132. return 0;
  133. }