noderb 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (361) hide show
  1. data/ext/noderb_extension/libuv/AUTHORS +11 -1
  2. data/ext/noderb_extension/libuv/LICENSE +0 -8
  3. data/ext/noderb_extension/libuv/Makefile +5 -5
  4. data/ext/noderb_extension/libuv/README +11 -6
  5. data/ext/noderb_extension/libuv/common.gypi +158 -7
  6. data/ext/noderb_extension/libuv/config-unix.mk +34 -26
  7. data/ext/noderb_extension/libuv/gyp_uv +22 -4
  8. data/ext/noderb_extension/libuv/include/{eio.h → uv-private/eio.h} +2 -2
  9. data/ext/noderb_extension/libuv/include/{uv-unix.h → uv-private/uv-unix.h} +40 -0
  10. data/ext/noderb_extension/libuv/include/{uv-win.h → uv-private/uv-win.h} +96 -14
  11. data/ext/noderb_extension/libuv/include/uv.h +508 -83
  12. data/ext/noderb_extension/libuv/src/unix/cares.c +185 -0
  13. data/ext/noderb_extension/libuv/src/unix/core.c +828 -0
  14. data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_darwin.h +4 -0
  15. data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_linux.h +5 -15
  16. data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.c +23 -2
  17. data/ext/noderb_extension/libuv/src/unix/error.c +102 -0
  18. data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_linux.h +26 -15
  19. data/ext/noderb_extension/libuv/src/unix/fs.c +562 -0
  20. data/ext/noderb_extension/libuv/src/unix/internal.h +75 -0
  21. data/ext/noderb_extension/libuv/src/unix/pipe.c +282 -0
  22. data/ext/noderb_extension/libuv/src/unix/process.c +287 -0
  23. data/ext/noderb_extension/libuv/src/unix/stream.c +727 -0
  24. data/ext/noderb_extension/libuv/src/unix/tcp.c +226 -0
  25. data/ext/noderb_extension/libuv/src/unix/udp.c +524 -0
  26. data/ext/noderb_extension/libuv/src/{uv-eio.c → unix/uv-eio.c} +48 -27
  27. data/ext/noderb_extension/libuv/src/{uv-eio.h → unix/uv-eio.h} +1 -1
  28. data/ext/noderb_extension/libuv/src/uv-common.c +24 -14
  29. data/ext/noderb_extension/libuv/src/uv-common.h +12 -7
  30. data/ext/noderb_extension/libuv/src/win/async.c +14 -16
  31. data/ext/noderb_extension/libuv/src/win/cares.c +64 -79
  32. data/ext/noderb_extension/libuv/src/win/core.c +105 -53
  33. data/ext/noderb_extension/libuv/src/win/error.c +23 -13
  34. data/ext/noderb_extension/libuv/src/win/fs.c +807 -0
  35. data/ext/noderb_extension/libuv/src/win/getaddrinfo.c +61 -41
  36. data/ext/noderb_extension/libuv/src/win/handle.c +56 -30
  37. data/ext/noderb_extension/libuv/src/win/internal.h +134 -95
  38. data/ext/noderb_extension/libuv/src/win/loop-watcher.c +21 -18
  39. data/ext/noderb_extension/libuv/src/win/pipe.c +313 -158
  40. data/ext/noderb_extension/libuv/src/win/process.c +117 -131
  41. data/ext/noderb_extension/libuv/src/win/req.c +55 -31
  42. data/ext/noderb_extension/libuv/src/win/stdio.c +5 -5
  43. data/ext/noderb_extension/libuv/src/win/stream.c +19 -14
  44. data/ext/noderb_extension/libuv/src/win/tcp.c +278 -336
  45. data/ext/noderb_extension/libuv/src/win/threadpool.c +73 -0
  46. data/ext/noderb_extension/libuv/src/win/timer.c +44 -37
  47. data/ext/noderb_extension/libuv/src/win/udp.c +592 -0
  48. data/ext/noderb_extension/libuv/src/win/util.c +20 -6
  49. data/ext/noderb_extension/libuv/src/win/winapi.c +23 -6
  50. data/ext/noderb_extension/libuv/src/win/winapi.h +4206 -0
  51. data/ext/noderb_extension/libuv/src/win/winsock.c +270 -0
  52. data/ext/noderb_extension/libuv/src/win/winsock.h +134 -0
  53. data/ext/noderb_extension/libuv/test/benchmark-ares.c +10 -6
  54. data/ext/noderb_extension/libuv/test/benchmark-getaddrinfo.c +10 -7
  55. data/ext/noderb_extension/libuv/test/benchmark-list.h +21 -0
  56. data/ext/noderb_extension/libuv/test/benchmark-ping-pongs.c +11 -7
  57. data/ext/noderb_extension/libuv/test/benchmark-pound.c +147 -58
  58. data/ext/noderb_extension/libuv/test/benchmark-pump.c +29 -23
  59. data/ext/noderb_extension/libuv/test/benchmark-spawn.c +13 -10
  60. data/ext/noderb_extension/libuv/test/benchmark-udp-packet-storm.c +250 -0
  61. data/ext/noderb_extension/libuv/test/dns-server.c +11 -6
  62. data/ext/noderb_extension/libuv/test/echo-server.c +30 -22
  63. data/ext/noderb_extension/libuv/test/test-async.c +3 -3
  64. data/ext/noderb_extension/libuv/test/test-callback-stack.c +6 -6
  65. data/ext/noderb_extension/libuv/test/test-connection-fail.c +6 -5
  66. data/ext/noderb_extension/libuv/test/test-delayed-accept.c +13 -13
  67. data/ext/noderb_extension/libuv/test/test-fs.c +715 -0
  68. data/ext/noderb_extension/libuv/test/test-getaddrinfo.c +11 -8
  69. data/ext/noderb_extension/libuv/test/test-gethostbyname.c +8 -9
  70. data/ext/noderb_extension/libuv/test/test-getsockname.c +142 -16
  71. data/ext/noderb_extension/libuv/test/test-idle.c +4 -3
  72. data/ext/noderb_extension/libuv/test/test-list.h +29 -2
  73. data/ext/noderb_extension/libuv/test/test-loop-handles.c +9 -8
  74. data/ext/noderb_extension/libuv/test/test-ping-pong.c +9 -9
  75. data/ext/noderb_extension/libuv/test/test-pipe-bind-error.c +18 -14
  76. data/ext/noderb_extension/libuv/test/test-ref.c +17 -16
  77. data/ext/noderb_extension/libuv/test/test-shutdown-eof.c +5 -5
  78. data/ext/noderb_extension/libuv/test/test-spawn.c +17 -17
  79. data/ext/noderb_extension/libuv/test/test-tcp-bind-error.c +24 -18
  80. data/ext/noderb_extension/libuv/test/test-tcp-bind6-error.c +19 -14
  81. data/ext/noderb_extension/libuv/test/test-tcp-writealot.c +6 -5
  82. data/ext/noderb_extension/libuv/test/test-threadpool.c +59 -0
  83. data/ext/noderb_extension/libuv/test/test-timer-again.c +15 -12
  84. data/ext/noderb_extension/libuv/test/test-timer.c +8 -8
  85. data/ext/noderb_extension/libuv/test/test-udp-dgram-too-big.c +88 -0
  86. data/ext/noderb_extension/libuv/test/test-udp-ipv6.c +158 -0
  87. data/ext/noderb_extension/libuv/test/test-udp-send-and-recv.c +210 -0
  88. data/ext/noderb_extension/libuv/{all.gyp → uv.gyp} +75 -77
  89. data/ext/noderb_extension/libuv/vcbuild.bat +93 -0
  90. data/ext/noderb_extension/noderb.c +13 -0
  91. data/ext/noderb_extension/noderb_common.h +2 -1
  92. data/ext/noderb_extension/noderb_defer.c +54 -0
  93. data/ext/noderb_extension/noderb_defer.h +15 -0
  94. data/ext/noderb_extension/noderb_dns.c +1 -1
  95. data/ext/noderb_extension/noderb_fs.c +277 -0
  96. data/ext/noderb_extension/noderb_fs.h +27 -0
  97. data/ext/noderb_extension/noderb_process.c +5 -5
  98. data/ext/noderb_extension/noderb_tcp.c +10 -6
  99. data/ext/noderb_extension/noderb_timers.c +1 -1
  100. data/ext/noderb_extension/noderb_tools.c +42 -8
  101. data/ext/noderb_extension/noderb_tools.h +3 -1
  102. data/lib/noderb/defer.rb +25 -0
  103. data/lib/noderb/file.rb +119 -0
  104. data/lib/noderb/fs.rb +72 -0
  105. data/lib/noderb/version.rb +1 -1
  106. data/lib/noderb.rb +8 -0
  107. metadata +102 -266
  108. data/ext/noderb_extension/libuv/BSDmakefile +0 -2
  109. data/ext/noderb_extension/libuv/create-msvs-files.bat +0 -21
  110. data/ext/noderb_extension/libuv/deps/pthread-win32/ANNOUNCE +0 -482
  111. data/ext/noderb_extension/libuv/deps/pthread-win32/BUGS +0 -141
  112. data/ext/noderb_extension/libuv/deps/pthread-win32/Bmakefile +0 -268
  113. data/ext/noderb_extension/libuv/deps/pthread-win32/CONTRIBUTORS +0 -140
  114. data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING +0 -150
  115. data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING.LIB +0 -504
  116. data/ext/noderb_extension/libuv/deps/pthread-win32/ChangeLog +0 -5194
  117. data/ext/noderb_extension/libuv/deps/pthread-win32/FAQ +0 -451
  118. data/ext/noderb_extension/libuv/deps/pthread-win32/GNUmakefile +0 -593
  119. data/ext/noderb_extension/libuv/deps/pthread-win32/MAINTAINERS +0 -4
  120. data/ext/noderb_extension/libuv/deps/pthread-win32/Makefile +0 -516
  121. data/ext/noderb_extension/libuv/deps/pthread-win32/NEWS +0 -1245
  122. data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile +0 -24
  123. data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile.tests +0 -260
  124. data/ext/noderb_extension/libuv/deps/pthread-win32/PROGRESS +0 -4
  125. data/ext/noderb_extension/libuv/deps/pthread-win32/README +0 -601
  126. data/ext/noderb_extension/libuv/deps/pthread-win32/README.Borland +0 -57
  127. data/ext/noderb_extension/libuv/deps/pthread-win32/README.CV +0 -3036
  128. data/ext/noderb_extension/libuv/deps/pthread-win32/README.NONPORTABLE +0 -783
  129. data/ext/noderb_extension/libuv/deps/pthread-win32/README.Watcom +0 -62
  130. data/ext/noderb_extension/libuv/deps/pthread-win32/README.WinCE +0 -6
  131. data/ext/noderb_extension/libuv/deps/pthread-win32/TODO +0 -7
  132. data/ext/noderb_extension/libuv/deps/pthread-win32/WinCE-PORT +0 -222
  133. data/ext/noderb_extension/libuv/deps/pthread-win32/attr.c +0 -53
  134. data/ext/noderb_extension/libuv/deps/pthread-win32/autostatic.c +0 -69
  135. data/ext/noderb_extension/libuv/deps/pthread-win32/barrier.c +0 -47
  136. data/ext/noderb_extension/libuv/deps/pthread-win32/build/all.gyp +0 -207
  137. data/ext/noderb_extension/libuv/deps/pthread-win32/builddmc.bat +0 -9
  138. data/ext/noderb_extension/libuv/deps/pthread-win32/cancel.c +0 -44
  139. data/ext/noderb_extension/libuv/deps/pthread-win32/cleanup.c +0 -148
  140. data/ext/noderb_extension/libuv/deps/pthread-win32/condvar.c +0 -50
  141. data/ext/noderb_extension/libuv/deps/pthread-win32/config.h +0 -153
  142. data/ext/noderb_extension/libuv/deps/pthread-win32/context.h +0 -74
  143. data/ext/noderb_extension/libuv/deps/pthread-win32/create.c +0 -308
  144. data/ext/noderb_extension/libuv/deps/pthread-win32/dll.c +0 -92
  145. data/ext/noderb_extension/libuv/deps/pthread-win32/errno.c +0 -94
  146. data/ext/noderb_extension/libuv/deps/pthread-win32/exit.c +0 -44
  147. data/ext/noderb_extension/libuv/deps/pthread-win32/fork.c +0 -39
  148. data/ext/noderb_extension/libuv/deps/pthread-win32/global.c +0 -107
  149. data/ext/noderb_extension/libuv/deps/pthread-win32/implement.h +0 -944
  150. data/ext/noderb_extension/libuv/deps/pthread-win32/misc.c +0 -50
  151. data/ext/noderb_extension/libuv/deps/pthread-win32/mutex.c +0 -62
  152. data/ext/noderb_extension/libuv/deps/pthread-win32/need_errno.h +0 -145
  153. data/ext/noderb_extension/libuv/deps/pthread-win32/nonportable.c +0 -47
  154. data/ext/noderb_extension/libuv/deps/pthread-win32/private.c +0 -54
  155. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.c +0 -66
  156. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsp +0 -142
  157. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsw +0 -29
  158. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.h +0 -1368
  159. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_destroy.c +0 -79
  160. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getdetachstate.c +0 -86
  161. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getinheritsched.c +0 -51
  162. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedparam.c +0 -52
  163. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedpolicy.c +0 -61
  164. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getscope.c +0 -54
  165. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstackaddr.c +0 -97
  166. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstacksize.c +0 -100
  167. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_init.c +0 -117
  168. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setdetachstate.c +0 -91
  169. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setinheritsched.c +0 -57
  170. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedparam.c +0 -63
  171. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedpolicy.c +0 -55
  172. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setscope.c +0 -62
  173. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstackaddr.c +0 -97
  174. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstacksize.c +0 -110
  175. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_destroy.c +0 -103
  176. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_init.c +0 -69
  177. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_wait.c +0 -104
  178. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_destroy.c +0 -83
  179. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_getpshared.c +0 -95
  180. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_init.c +0 -85
  181. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_setpshared.c +0 -119
  182. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cancel.c +0 -189
  183. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_destroy.c +0 -253
  184. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_init.c +0 -167
  185. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_signal.c +0 -231
  186. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_wait.c +0 -567
  187. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_destroy.c +0 -86
  188. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_getpshared.c +0 -97
  189. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_init.c +0 -87
  190. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_setpshared.c +0 -117
  191. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_delay_np.c +0 -172
  192. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_detach.c +0 -136
  193. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_equal.c +0 -76
  194. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_exit.c +0 -106
  195. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getconcurrency.c +0 -45
  196. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getschedparam.c +0 -75
  197. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getspecific.c +0 -87
  198. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getunique_np.c +0 -47
  199. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getw32threadhandle_np.c +0 -65
  200. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_join.c +0 -157
  201. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_create.c +0 -108
  202. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_delete.c +0 -125
  203. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_kill.c +0 -105
  204. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_consistent.c +0 -187
  205. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_destroy.c +0 -148
  206. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_init.c +0 -130
  207. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_lock.c +0 -269
  208. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_timedlock.c +0 -324
  209. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_trylock.c +0 -154
  210. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_unlock.c +0 -175
  211. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_destroy.c +0 -83
  212. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getkind_np.c +0 -44
  213. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getpshared.c +0 -95
  214. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getrobust.c +0 -113
  215. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_gettype.c +0 -56
  216. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_init.c +0 -86
  217. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setkind_np.c +0 -44
  218. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setpshared.c +0 -119
  219. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setrobust.c +0 -119
  220. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_settype.c +0 -143
  221. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_num_processors_np.c +0 -56
  222. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_once.c +0 -79
  223. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_destroy.c +0 -143
  224. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_init.c +0 -109
  225. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_rdlock.c +0 -102
  226. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedrdlock.c +0 -109
  227. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedwrlock.c +0 -139
  228. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_tryrdlock.c +0 -102
  229. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_trywrlock.c +0 -122
  230. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_unlock.c +0 -93
  231. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_wrlock.c +0 -133
  232. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_destroy.c +0 -84
  233. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_getpshared.c +0 -97
  234. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_init.c +0 -83
  235. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_setpshared.c +0 -120
  236. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_self.c +0 -141
  237. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcancelstate.c +0 -125
  238. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcanceltype.c +0 -126
  239. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setconcurrency.c +0 -53
  240. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setschedparam.c +0 -123
  241. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setspecific.c +0 -167
  242. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_destroy.c +0 -111
  243. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_init.c +0 -123
  244. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_lock.c +0 -80
  245. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_trylock.c +0 -77
  246. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_unlock.c +0 -71
  247. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_testcancel.c +0 -103
  248. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_timechange_handler_np.c +0 -108
  249. data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_win32_attach_detach_np.c +0 -258
  250. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_MCS_lock.c +0 -278
  251. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_callUserDestroyRoutines.c +0 -232
  252. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_calloc.c +0 -56
  253. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_cond_check_need_init.c +0 -78
  254. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_getprocessors.c +0 -91
  255. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_is_attr.c +0 -47
  256. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_mutex_check_need_init.c +0 -92
  257. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_new.c +0 -94
  258. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processInitialize.c +0 -92
  259. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processTerminate.c +0 -105
  260. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_relmillisecs.c +0 -132
  261. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_reuse.c +0 -151
  262. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_cancelwrwait.c +0 -50
  263. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_check_need_init.c +0 -77
  264. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_semwait.c +0 -135
  265. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_spinlock_check_need_init.c +0 -78
  266. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadDestroy.c +0 -79
  267. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadStart.c +0 -357
  268. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_throw.c +0 -189
  269. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_timespec.c +0 -83
  270. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocCreate.c +0 -118
  271. data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocDestroy.c +0 -114
  272. data/ext/noderb_extension/libuv/deps/pthread-win32/rwlock.c +0 -51
  273. data/ext/noderb_extension/libuv/deps/pthread-win32/sched.c +0 -53
  274. data/ext/noderb_extension/libuv/deps/pthread-win32/sched.h +0 -183
  275. data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_max.c +0 -134
  276. data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_min.c +0 -135
  277. data/ext/noderb_extension/libuv/deps/pthread-win32/sched_getscheduler.c +0 -71
  278. data/ext/noderb_extension/libuv/deps/pthread-win32/sched_setscheduler.c +0 -83
  279. data/ext/noderb_extension/libuv/deps/pthread-win32/sched_yield.c +0 -71
  280. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_close.c +0 -58
  281. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_destroy.c +0 -144
  282. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_getvalue.c +0 -110
  283. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_init.c +0 -169
  284. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_open.c +0 -58
  285. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post.c +0 -128
  286. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post_multiple.c +0 -142
  287. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_timedwait.c +0 -238
  288. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_trywait.c +0 -117
  289. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_unlink.c +0 -58
  290. data/ext/noderb_extension/libuv/deps/pthread-win32/sem_wait.c +0 -187
  291. data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.c +0 -69
  292. data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.h +0 -169
  293. data/ext/noderb_extension/libuv/deps/pthread-win32/signal.c +0 -179
  294. data/ext/noderb_extension/libuv/deps/pthread-win32/spin.c +0 -46
  295. data/ext/noderb_extension/libuv/deps/pthread-win32/sync.c +0 -43
  296. data/ext/noderb_extension/libuv/deps/pthread-win32/tsd.c +0 -44
  297. data/ext/noderb_extension/libuv/deps/pthread-win32/version.rc +0 -388
  298. data/ext/noderb_extension/libuv/deps/pthread-win32/w32_CancelableWait.c +0 -161
  299. data/ext/noderb_extension/libuv/doc/iocp-links.html +0 -574
  300. data/ext/noderb_extension/libuv/src/uv-unix.c +0 -2421
  301. data/ext/noderb_extension/libuv/src/win/ntdll.h +0 -130
  302. /data/ext/noderb_extension/libuv/include/{ev.h → uv-private/ev.h} +0 -0
  303. /data/ext/noderb_extension/libuv/include/{ngx-queue.h → uv-private/ngx-queue.h} +0 -0
  304. /data/ext/noderb_extension/libuv/include/{tree.h → uv-private/tree.h} +0 -0
  305. /data/ext/noderb_extension/libuv/src/{uv-cygwin.c → unix/cygwin.c} +0 -0
  306. /data/ext/noderb_extension/libuv/src/{uv-darwin.c → unix/darwin.c} +0 -0
  307. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/Changes +0 -0
  308. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/LICENSE +0 -0
  309. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/Makefile.am +0 -0
  310. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/aclocal.m4 +0 -0
  311. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/autogen.sh +0 -0
  312. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config.h.in +0 -0
  313. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_cygwin.h +0 -0
  314. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_freebsd.h +0 -0
  315. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_sunos.h +0 -0
  316. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/configure.ac +0 -0
  317. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/demo.c +0 -0
  318. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/ecb.h +0 -0
  319. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.3 +0 -0
  320. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.pod +0 -0
  321. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/libeio.m4 +0 -0
  322. /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/xthread.h +0 -0
  323. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Changes +0 -0
  324. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/LICENSE +0 -0
  325. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Makefile.am +0 -0
  326. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Makefile.in +0 -0
  327. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/README +0 -0
  328. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/aclocal.m4 +0 -0
  329. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/autogen.sh +0 -0
  330. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.guess +0 -0
  331. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.h.in +0 -0
  332. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.sub +0 -0
  333. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_cygwin.h +0 -0
  334. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_darwin.h +0 -0
  335. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_freebsd.h +0 -0
  336. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_sunos.h +0 -0
  337. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/configure +0 -0
  338. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/configure.ac +0 -0
  339. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/depcomp +0 -0
  340. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev++.h +0 -0
  341. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.3 +0 -0
  342. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.c +0 -0
  343. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.pod +0 -0
  344. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_epoll.c +0 -0
  345. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_kqueue.c +0 -0
  346. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_poll.c +0 -0
  347. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_port.c +0 -0
  348. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_select.c +0 -0
  349. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_vars.h +0 -0
  350. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_win32.c +0 -0
  351. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_wrap.h +0 -0
  352. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/event.c +0 -0
  353. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/event.h +0 -0
  354. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/install-sh +0 -0
  355. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/libev.m4 +0 -0
  356. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ltmain.sh +0 -0
  357. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/missing +0 -0
  358. /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/mkinstalldirs +0 -0
  359. /data/ext/noderb_extension/libuv/src/{uv-freebsd.c → unix/freebsd.c} +0 -0
  360. /data/ext/noderb_extension/libuv/src/{uv-linux.c → unix/linux.c} +0 -0
  361. /data/ext/noderb_extension/libuv/src/{uv-sunos.c → unix/sunos.c} +0 -0
@@ -1,3036 +0,0 @@
1
- README.CV -- Condition Variables
2
- --------------------------------
3
-
4
- The original implementation of condition variables in
5
- pthreads-win32 was based on a discussion paper:
6
-
7
- "Strategies for Implementing POSIX Condition Variables
8
- on Win32": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
9
-
10
- The changes suggested below were made on Feb 6 2001. This
11
- file is included in the package for the benefit of anyone
12
- interested in understanding the pthreads-win32 implementation
13
- of condition variables and the (sometimes subtle) issues that
14
- it attempts to resolve.
15
-
16
- Thanks go to the individuals whose names appear throughout
17
- the following text.
18
-
19
- Ross Johnson
20
-
21
- --------------------------------------------------------------------
22
-
23
- fyi.. (more detailed problem description/demos + possible fix/patch)
24
-
25
- regards,
26
- alexander.
27
-
28
-
29
- Alexander Terekhov
30
- 31.01.2001 17:43
31
-
32
- To: ace-bugs@cs.wustl.edu
33
- cc:
34
- From: Alexander Terekhov/Germany/IBM@IBMDE
35
- Subject: Implementation of POSIX CVs: spur.wakeups/lost
36
- signals/deadlocks/unfairness
37
-
38
-
39
-
40
- ACE VERSION:
41
-
42
- 5.1.12 (pthread-win32 snapshot 2000-12-29)
43
-
44
- HOST MACHINE and OPERATING SYSTEM:
45
-
46
- IBM IntelliStation Z Pro, 2 x XEON 1GHz, Win2K
47
-
48
- TARGET MACHINE and OPERATING SYSTEM, if different from HOST:
49
- COMPILER NAME AND VERSION (AND PATCHLEVEL):
50
-
51
- Microsoft Visual C++ 6.0
52
-
53
- AREA/CLASS/EXAMPLE AFFECTED:
54
-
55
- Implementation of POSIX condition variables - OS.cpp/.h
56
-
57
- DOES THE PROBLEM AFFECT:
58
-
59
- EXECUTION? YES!
60
-
61
- SYNOPSIS:
62
-
63
- a) spurious wakeups (minor problem)
64
- b) lost signals
65
- c) broadcast deadlock
66
- d) unfairness (minor problem)
67
-
68
- DESCRIPTION:
69
-
70
- Please see attached copy of discussion thread
71
- from comp.programming.threads for more details on
72
- some reported problems. (i've also posted a "fyi"
73
- message to ace-users a week or two ago but
74
- unfortunately did not get any response so far).
75
-
76
- It seems that current implementation suffers from
77
- two essential problems:
78
-
79
- 1) cond.waiters_count does not accurately reflect
80
- number of waiters blocked on semaphore - w/o
81
- proper synchronisation that could result (in the
82
- time window when counter is not accurate)
83
- in spurious wakeups organised by subsequent
84
- _signals and _broadcasts.
85
-
86
- 2) Always having (with no e.g. copy_and_clear/..)
87
- the same queue in use (semaphore+counter)
88
- neither signal nor broadcast provide 'atomic'
89
- behaviour with respect to other threads/subsequent
90
- calls to signal/broadcast/wait.
91
-
92
- Each problem and combination of both could produce
93
- various nasty things:
94
-
95
- a) spurious wakeups (minor problem)
96
-
97
- it is possible that waiter(s) which was already
98
- unblocked even so is still counted as blocked
99
- waiter. signal and broadcast will release
100
- semaphore which will produce a spurious wakeup
101
- for a 'real' waiter coming later.
102
-
103
- b) lost signals
104
-
105
- signalling thread ends up consuming its own
106
- signal. please see demo/discussion below.
107
-
108
- c) broadcast deadlock
109
-
110
- last_waiter processing code does not correctly
111
- handle the case with multiple threads
112
- waiting for the end of broadcast.
113
- please see demo/discussion below.
114
-
115
- d) unfairness (minor problem)
116
-
117
- without SignalObjectAndWait some waiter(s)
118
- may end up consuming broadcasted signals
119
- multiple times (spurious wakeups) because waiter
120
- thread(s) can be preempted before they call
121
- semaphore wait (but after count++ and mtx.unlock).
122
-
123
- REPEAT BY:
124
-
125
- See below... run problem demos programs (tennis.cpp and
126
- tennisb.cpp) number of times concurrently (on multiprocessor)
127
- and in multiple sessions or just add a couple of "Sleep"s
128
- as described in the attached copy of discussion thread
129
- from comp.programming.threads
130
-
131
- SAMPLE FIX/WORKAROUND:
132
-
133
- See attached patch to pthread-win32.. well, I can not
134
- claim that it is completely bug free but at least my
135
- test and tests provided by pthreads-win32 seem to work.
136
- Perhaps that will help.
137
-
138
- regards,
139
- alexander.
140
-
141
-
142
- >> Forum: comp.programming.threads
143
- >> Thread: pthread_cond_* implementation questions
144
- .
145
- .
146
- .
147
- David Schwartz <davids@webmaster.com> wrote:
148
-
149
- > terekhov@my-deja.com wrote:
150
- >
151
- >> BTW, could you please also share your view on other perceived
152
- >> "problems" such as nested broadcast deadlock, spurious wakeups
153
- >> and (the latest one) lost signals??
154
- >
155
- >I'm not sure what you mean. The standard allows an implementation
156
- >to do almost whatever it likes. In fact, you could implement
157
- >pthread_cond_wait by releasing the mutex, sleeping a random
158
- >amount of time, and then reacquiring the mutex. Of course,
159
- >this would be a pretty poor implementation, but any code that
160
- >didn't work under that implementation wouldn't be strictly
161
- >compliant.
162
-
163
- The implementation you suggested is indeed correct
164
- one (yes, now I see it :). However it requires from
165
- signal/broadcast nothing more than to "{ return 0; }"
166
- That is not the case for pthread-win32 and ACE
167
- implementations. I do think that these implementations
168
- (basically the same implementation) have some serious
169
- problems with wait/signal/broadcast calls. I am looking
170
- for help to clarify whether these problems are real
171
- or not. I think that I can demonstrate what I mean
172
- using one or two small sample programs.
173
- .
174
- .
175
- .
176
- ==========
177
- tennis.cpp
178
- ==========
179
-
180
- #include "ace/Synch.h"
181
- #include "ace/Thread.h"
182
-
183
- enum GAME_STATE {
184
-
185
- START_GAME,
186
- PLAYER_A, // Player A playes the ball
187
- PLAYER_B, // Player B playes the ball
188
- GAME_OVER,
189
- ONE_PLAYER_GONE,
190
- BOTH_PLAYERS_GONE
191
-
192
- };
193
-
194
- enum GAME_STATE eGameState;
195
- ACE_Mutex* pmtxGameStateLock;
196
- ACE_Condition< ACE_Mutex >* pcndGameStateChange;
197
-
198
- void*
199
- playerA(
200
- void* pParm
201
- )
202
- {
203
-
204
- // For access to game state variable
205
- pmtxGameStateLock->acquire();
206
-
207
- // Play loop
208
- while ( eGameState < GAME_OVER ) {
209
-
210
- // Play the ball
211
- cout << endl << "PLAYER-A" << endl;
212
-
213
- // Now its PLAYER-B's turn
214
- eGameState = PLAYER_B;
215
-
216
- // Signal to PLAYER-B that now it is his turn
217
- pcndGameStateChange->signal();
218
-
219
- // Wait until PLAYER-B finishes playing the ball
220
- do {
221
-
222
- pcndGameStateChange->wait();
223
-
224
- if ( PLAYER_B == eGameState )
225
- cout << endl << "----PLAYER-A: SPURIOUS WAKEUP!!!" << endl;
226
-
227
- } while ( PLAYER_B == eGameState );
228
-
229
- }
230
-
231
- // PLAYER-A gone
232
- eGameState = (GAME_STATE)(eGameState+1);
233
- cout << endl << "PLAYER-A GONE" << endl;
234
-
235
- // No more access to state variable needed
236
- pmtxGameStateLock->release();
237
-
238
- // Signal PLAYER-A gone event
239
- pcndGameStateChange->broadcast();
240
-
241
- return 0;
242
-
243
- }
244
-
245
- void*
246
- playerB(
247
- void* pParm
248
- )
249
- {
250
-
251
- // For access to game state variable
252
- pmtxGameStateLock->acquire();
253
-
254
- // Play loop
255
- while ( eGameState < GAME_OVER ) {
256
-
257
- // Play the ball
258
- cout << endl << "PLAYER-B" << endl;
259
-
260
- // Now its PLAYER-A's turn
261
- eGameState = PLAYER_A;
262
-
263
- // Signal to PLAYER-A that now it is his turn
264
- pcndGameStateChange->signal();
265
-
266
- // Wait until PLAYER-A finishes playing the ball
267
- do {
268
-
269
- pcndGameStateChange->wait();
270
-
271
- if ( PLAYER_A == eGameState )
272
- cout << endl << "----PLAYER-B: SPURIOUS WAKEUP!!!" << endl;
273
-
274
- } while ( PLAYER_A == eGameState );
275
-
276
- }
277
-
278
- // PLAYER-B gone
279
- eGameState = (GAME_STATE)(eGameState+1);
280
- cout << endl << "PLAYER-B GONE" << endl;
281
-
282
- // No more access to state variable needed
283
- pmtxGameStateLock->release();
284
-
285
- // Signal PLAYER-B gone event
286
- pcndGameStateChange->broadcast();
287
-
288
- return 0;
289
-
290
- }
291
-
292
-
293
- int
294
- main (int, ACE_TCHAR *[])
295
- {
296
-
297
- pmtxGameStateLock = new ACE_Mutex();
298
- pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock
299
- );
300
-
301
- // Set initial state
302
- eGameState = START_GAME;
303
-
304
- // Create players
305
- ACE_Thread::spawn( playerA );
306
- ACE_Thread::spawn( playerB );
307
-
308
- // Give them 5 sec. to play
309
- Sleep( 5000 );//sleep( 5 );
310
-
311
- // Set game over state
312
- pmtxGameStateLock->acquire();
313
- eGameState = GAME_OVER;
314
-
315
- // Let them know
316
- pcndGameStateChange->broadcast();
317
-
318
- // Wait for players to stop
319
- do {
320
-
321
- pcndGameStateChange->wait();
322
-
323
- } while ( eGameState < BOTH_PLAYERS_GONE );
324
-
325
- // Cleanup
326
- cout << endl << "GAME OVER" << endl;
327
- pmtxGameStateLock->release();
328
- delete pcndGameStateChange;
329
- delete pmtxGameStateLock;
330
-
331
- return 0;
332
-
333
- }
334
-
335
- ===========
336
- tennisb.cpp
337
- ===========
338
- #include "ace/Synch.h"
339
- #include "ace/Thread.h"
340
-
341
- enum GAME_STATE {
342
-
343
- START_GAME,
344
- PLAYER_A, // Player A playes the ball
345
- PLAYER_B, // Player B playes the ball
346
- GAME_OVER,
347
- ONE_PLAYER_GONE,
348
- BOTH_PLAYERS_GONE
349
-
350
- };
351
-
352
- enum GAME_STATE eGameState;
353
- ACE_Mutex* pmtxGameStateLock;
354
- ACE_Condition< ACE_Mutex >* pcndGameStateChange;
355
-
356
- void*
357
- playerA(
358
- void* pParm
359
- )
360
- {
361
-
362
- // For access to game state variable
363
- pmtxGameStateLock->acquire();
364
-
365
- // Play loop
366
- while ( eGameState < GAME_OVER ) {
367
-
368
- // Play the ball
369
- cout << endl << "PLAYER-A" << endl;
370
-
371
- // Now its PLAYER-B's turn
372
- eGameState = PLAYER_B;
373
-
374
- // Signal to PLAYER-B that now it is his turn
375
- pcndGameStateChange->broadcast();
376
-
377
- // Wait until PLAYER-B finishes playing the ball
378
- do {
379
-
380
- pcndGameStateChange->wait();
381
-
382
- if ( PLAYER_B == eGameState )
383
- cout << endl << "----PLAYER-A: SPURIOUS WAKEUP!!!" << endl;
384
-
385
- } while ( PLAYER_B == eGameState );
386
-
387
- }
388
-
389
- // PLAYER-A gone
390
- eGameState = (GAME_STATE)(eGameState+1);
391
- cout << endl << "PLAYER-A GONE" << endl;
392
-
393
- // No more access to state variable needed
394
- pmtxGameStateLock->release();
395
-
396
- // Signal PLAYER-A gone event
397
- pcndGameStateChange->broadcast();
398
-
399
- return 0;
400
-
401
- }
402
-
403
- void*
404
- playerB(
405
- void* pParm
406
- )
407
- {
408
-
409
- // For access to game state variable
410
- pmtxGameStateLock->acquire();
411
-
412
- // Play loop
413
- while ( eGameState < GAME_OVER ) {
414
-
415
- // Play the ball
416
- cout << endl << "PLAYER-B" << endl;
417
-
418
- // Now its PLAYER-A's turn
419
- eGameState = PLAYER_A;
420
-
421
- // Signal to PLAYER-A that now it is his turn
422
- pcndGameStateChange->broadcast();
423
-
424
- // Wait until PLAYER-A finishes playing the ball
425
- do {
426
-
427
- pcndGameStateChange->wait();
428
-
429
- if ( PLAYER_A == eGameState )
430
- cout << endl << "----PLAYER-B: SPURIOUS WAKEUP!!!" << endl;
431
-
432
- } while ( PLAYER_A == eGameState );
433
-
434
- }
435
-
436
- // PLAYER-B gone
437
- eGameState = (GAME_STATE)(eGameState+1);
438
- cout << endl << "PLAYER-B GONE" << endl;
439
-
440
- // No more access to state variable needed
441
- pmtxGameStateLock->release();
442
-
443
- // Signal PLAYER-B gone event
444
- pcndGameStateChange->broadcast();
445
-
446
- return 0;
447
-
448
- }
449
-
450
-
451
- int
452
- main (int, ACE_TCHAR *[])
453
- {
454
-
455
- pmtxGameStateLock = new ACE_Mutex();
456
- pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock
457
- );
458
-
459
- // Set initial state
460
- eGameState = START_GAME;
461
-
462
- // Create players
463
- ACE_Thread::spawn( playerA );
464
- ACE_Thread::spawn( playerB );
465
-
466
- // Give them 5 sec. to play
467
- Sleep( 5000 );//sleep( 5 );
468
-
469
- // Make some noise
470
- pmtxGameStateLock->acquire();
471
- cout << endl << "---Noise ON..." << endl;
472
- pmtxGameStateLock->release();
473
- for ( int i = 0; i < 100000; i++ )
474
- pcndGameStateChange->broadcast();
475
- cout << endl << "---Noise OFF" << endl;
476
-
477
- // Set game over state
478
- pmtxGameStateLock->acquire();
479
- eGameState = GAME_OVER;
480
- cout << endl << "---Stopping the game..." << endl;
481
-
482
- // Let them know
483
- pcndGameStateChange->broadcast();
484
-
485
- // Wait for players to stop
486
- do {
487
-
488
- pcndGameStateChange->wait();
489
-
490
- } while ( eGameState < BOTH_PLAYERS_GONE );
491
-
492
- // Cleanup
493
- cout << endl << "GAME OVER" << endl;
494
- pmtxGameStateLock->release();
495
- delete pcndGameStateChange;
496
- delete pmtxGameStateLock;
497
-
498
- return 0;
499
-
500
- }
501
- .
502
- .
503
- .
504
- David Schwartz <davids@webmaster.com> wrote:
505
- >> > It's compliant
506
- >>
507
- >> That is really good.
508
- >
509
- >> Tomorrow (I have to go urgently now) I will try to
510
- >> demonstrate the lost-signal "problem" of current
511
- >> pthread-win32 and ACE-(variant w/o SingleObjectAndWait)
512
- >> implementations: players start suddenly drop their balls :-)
513
- >> (with no change in source code).
514
- >
515
- >Signals aren't lost, they're going to the main thread,
516
- >which isn't coded correctly to handle them. Try this:
517
- >
518
- > // Wait for players to stop
519
- > do {
520
- >
521
- > pthread_cond_wait( &cndGameStateChange,&mtxGameStateLock );
522
- >printf("Main thread stole a signal\n");
523
- >
524
- > } while ( eGameState < BOTH_PLAYERS_GONE );
525
- >
526
- >I bet everytime you thing a signal is lost, you'll see that printf.
527
- >The signal isn't lost, it was stolen by another thread.
528
-
529
- well, you can probably loose your bet.. it was indeed stolen
530
- by "another" thread but not the one you seem to think of.
531
-
532
- I think that what actually happens is the following:
533
-
534
- H:\SA\UXX\pt\PTHREADS\TESTS>tennis3.exe
535
-
536
- PLAYER-A
537
-
538
- PLAYER-B
539
-
540
- ----PLAYER-B: SPURIOUS WAKEUP!!!
541
-
542
- PLAYER-A GONE
543
-
544
- PLAYER-B GONE
545
-
546
- GAME OVER
547
-
548
- H:\SA\UXX\pt\PTHREADS\TESTS>
549
-
550
- here you can see that PLAYER-B after playing his first
551
- ball (which came via signal from PLAYER-A) just dropped
552
- it down. What happened is that his signal to player A
553
- was consumed as spurious wakeup by himself (player B).
554
-
555
- The implementation has a problem:
556
-
557
- ================
558
- waiting threads:
559
- ================
560
-
561
- { /** Critical Section
562
-
563
- inc cond.waiters_count
564
-
565
- }
566
-
567
- /*
568
- /* Atomic only if using Win32 SignalObjectAndWait
569
- /*
570
- cond.mtx.release
571
-
572
- /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,
573
- /*** GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE
574
- /*** ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!
575
-
576
- cond.sem.wait
577
-
578
- Player-A after playing game's initial ball went into
579
- wait (called _wait) but was pre-empted before reaching
580
- wait semaphore. He was counted as waiter but was not
581
- actually waiting/blocked yet.
582
-
583
- ===============
584
- signal threads:
585
- ===============
586
-
587
- { /** Critical Section
588
-
589
- waiters_count = cond.waiters_count
590
-
591
- }
592
-
593
- if ( waiters_count != 0 )
594
-
595
- sem.post 1
596
-
597
- endif
598
-
599
- Player-B after he received signal/ball from Player A
600
- called _signal. The _signal did see that there was
601
- one waiter blocked on the condition (Player-A) and
602
- released the semaphore.. (but it did not unblock
603
- Player-A because he was not actually blocked).
604
- Player-B thread continued its execution, called _wait,
605
- was counted as second waiter BUT was allowed to slip
606
- through opened semaphore gate (which was opened for
607
- Player-B) and received his own signal. Player B remained
608
- blocked followed by Player A. Deadlock happened which
609
- lasted until main thread came in and said game over.
610
-
611
- It seems to me that the implementation fails to
612
- correctly implement the following statement
613
- from specification:
614
-
615
- http://www.opengroup.org/
616
- onlinepubs/007908799/xsh/pthread_cond_wait.html
617
-
618
- "These functions atomically release mutex and cause
619
- the calling thread to block on the condition variable
620
- cond; atomically here means "atomically with respect
621
- to access by another thread to the mutex and then the
622
- condition variable". That is, if another thread is
623
- able to acquire the mutex after the about-to-block
624
- thread has released it, then a subsequent call to
625
- pthread_cond_signal() or pthread_cond_broadcast()
626
- in that thread behaves as if it were issued after
627
- the about-to-block thread has blocked."
628
-
629
- Question: Am I right?
630
-
631
- (I produced the program output above by simply
632
- adding ?Sleep( 1 )?:
633
-
634
- ================
635
- waiting threads:
636
- ================
637
-
638
- { /** Critical Section
639
-
640
- inc cond.waiters_count
641
-
642
- }
643
-
644
- /*
645
- /* Atomic only if using Win32 SignalObjectAndWait
646
- /*
647
- cond.mtx.release
648
-
649
- Sleep( 1 ); // Win32
650
-
651
- /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,
652
- /*** GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE
653
- /*** ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!
654
-
655
- cond.sem.wait
656
-
657
- to the source code of pthread-win32 implementation:
658
-
659
- http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/
660
- condvar.c?rev=1.36&content-type=text/
661
- x-cvsweb-markup&cvsroot=pthreads-win32
662
-
663
-
664
- /*
665
- * We keep the lock held just long enough to increment the count of
666
- * waiters by one (above).
667
- * Note that we can't keep it held across the
668
- * call to sem_wait since that will deadlock other calls
669
- * to pthread_cond_signal
670
- */
671
- cleanup_args.mutexPtr = mutex;
672
- cleanup_args.cv = cv;
673
- cleanup_args.resultPtr = &result;
674
-
675
- pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *)
676
- &cleanup_args);
677
-
678
- if ((result = pthread_mutex_unlock (mutex)) == 0)
679
- {((result
680
- Sleep( 1 ); // @AT
681
-
682
- /*
683
- * Wait to be awakened by
684
- * pthread_cond_signal, or
685
- * pthread_cond_broadcast, or
686
- * a timeout
687
- *
688
- * Note:
689
- * ptw32_sem_timedwait is a cancelation point,
690
- * hence providing the
691
- * mechanism for making pthread_cond_wait a cancelation
692
- * point. We use the cleanup mechanism to ensure we
693
- * re-lock the mutex and decrement the waiters count
694
- * if we are canceled.
695
- */
696
- if (ptw32_sem_timedwait (&(cv->sema), abstime) == -1) {
697
- result = errno;
698
- }
699
- }
700
-
701
- pthread_cleanup_pop (1); /* Always cleanup */
702
-
703
-
704
- BTW, on my system (2 CPUs) I can manage to get
705
- signals lost even without any source code modification
706
- if I run the tennis program many times in different
707
- shell sessions.
708
- .
709
- .
710
- .
711
- David Schwartz <davids@webmaster.com> wrote:
712
- >terekhov@my-deja.com wrote:
713
- >
714
- >> well, it might be that the program is in fact buggy.
715
- >> but you did not show me any bug.
716
- >
717
- >You're right. I was close but not dead on. I was correct, however,
718
- >that the code is buggy because it uses 'pthread_cond_signal' even
719
- >though not any thread waiting on the condition variable can do the
720
- >job. I was wrong in which thread could be waiting on the cv but
721
- >unable to do the job.
722
-
723
- Okay, lets change 'pthread_cond_signal' to 'pthread_cond_broadcast'
724
- but also add some noise from main() right before declaring the game
725
- to be over (I need it in order to demonstrate another problem of
726
- pthread-win32/ACE implementations - broadcast deadlock)...
727
- .
728
- .
729
- .
730
- It is my understanding of POSIX conditions,
731
- that on correct implementation added noise
732
- in form of unnecessary broadcasts from main,
733
- should not break the tennis program. The
734
- only 'side effect' of added noise on correct
735
- implementation would be 'spurious wakeups' of
736
- players (in fact they are not spurious,
737
- players just see them as spurious) unblocked,
738
- not by another player but by main before
739
- another player had a chance to acquire the
740
- mutex and change the game state variable:
741
- .
742
- .
743
- .
744
-
745
- PLAYER-B
746
-
747
- PLAYER-A
748
-
749
- ---Noise ON...
750
-
751
- PLAYER-B
752
-
753
- PLAYER-A
754
-
755
- .
756
- .
757
- .
758
-
759
- PLAYER-B
760
-
761
- PLAYER-A
762
-
763
- ----PLAYER-A: SPURIOUS WAKEUP!!!
764
-
765
- PLAYER-B
766
-
767
- PLAYER-A
768
-
769
- ---Noise OFF
770
-
771
- PLAYER-B
772
-
773
- ---Stopping the game...
774
-
775
- PLAYER-A GONE
776
-
777
- PLAYER-B GONE
778
-
779
- GAME OVER
780
-
781
- H:\SA\UXX\pt\PTHREADS\TESTS>
782
-
783
- On pthread-win32/ACE implementations the
784
- program could stall:
785
-
786
- .
787
- .
788
- .
789
-
790
- PLAYER-A
791
-
792
- PLAYER-B
793
-
794
- PLAYER-A
795
-
796
- PLAYER-B
797
-
798
- PLAYER-A
799
-
800
- PLAYER-B
801
-
802
- PLAYER-A
803
-
804
- PLAYER-B
805
-
806
- ---Noise ON...
807
-
808
- PLAYER-A
809
-
810
- ---Noise OFF
811
- ^C
812
- H:\SA\UXX\pt\PTHREADS\TESTS>
813
-
814
-
815
- The implementation has problems:
816
-
817
- ================
818
- waiting threads:
819
- ================
820
-
821
- { /** Critical Section
822
-
823
- inc cond.waiters_count
824
-
825
- }
826
-
827
- /*
828
- /* Atomic only if using Win32 SignalObjectAndWait
829
- /*
830
- cond.mtx.release
831
- cond.sem.wait
832
-
833
- /*** ^^-- WAITER CAN BE PREEMPTED AFTER BEING UNBLOCKED...
834
-
835
- { /** Critical Section
836
-
837
- dec cond.waiters_count
838
-
839
- /*** ^^- ...AND BEFORE DECREMENTING THE COUNT (1)
840
-
841
- last_waiter = ( cond.was_broadcast &&
842
- cond.waiters_count == 0 )
843
-
844
- if ( last_waiter )
845
-
846
- cond.was_broadcast = FALSE
847
-
848
- endif
849
-
850
- }
851
-
852
- if ( last_waiter )
853
-
854
- /*
855
- /* Atomic only if using Win32 SignalObjectAndWait
856
- /*
857
- cond.auto_reset_event_or_sem.post /* Event for Win32
858
- cond.mtx.acquire
859
-
860
- /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (2)
861
-
862
- /*** ^^-- NESTED BROADCASTS RESULT IN A DEADLOCK
863
-
864
-
865
- else
866
-
867
- cond.mtx.acquire
868
-
869
- /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (3)
870
-
871
- endif
872
-
873
-
874
- ==================
875
- broadcast threads:
876
- ==================
877
-
878
- { /** Critical Section
879
-
880
- waiters_count = cond.waiters_count
881
-
882
- if ( waiters_count != 0 )
883
-
884
- cond.was_broadcast = TRUE
885
-
886
- endif
887
-
888
- }
889
-
890
- if ( waiters_count != 0 )
891
-
892
- cond.sem.post waiters_count
893
-
894
- /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)
895
-
896
- cond.auto_reset_event_or_sem.wait /* Event for Win32
897
-
898
- /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY
899
- HAPPEN TO GO INTO WAIT WHILE PREVIOUS
900
- BROADCAST IS STILL IN PROGRESS/WAITING
901
-
902
- endif
903
-
904
- a) cond.waiters_count does not accurately reflect
905
- number of waiters blocked on semaphore - that could
906
- result (in the time window when counter is not accurate)
907
- in spurios wakeups organised by subsequent _signals
908
- and _broadcasts. From standard compliance point of view
909
- that is OK but that could be a real problem from
910
- performance/efficiency point of view.
911
-
912
- b) If subsequent broadcast happen to go into wait on
913
- cond.auto_reset_event_or_sem before previous
914
- broadcast was unblocked from cond.auto_reset_event_or_sem
915
- by its last waiter, one of two blocked threads will
916
- remain blocked because last_waiter processing code
917
- fails to unblock both threads.
918
-
919
- In the situation with tennisb.c the Player-B was put
920
- in a deadlock by noise (broadcast) coming from main
921
- thread. And since Player-B holds the game state
922
- mutex when it calls broadcast, the whole program
923
- stalled: Player-A was deadlocked on mutex and
924
- main thread after finishing with producing the noise
925
- was deadlocked on mutex too (needed to declare the
926
- game over)
927
-
928
- (I produced the program output above by simply
929
- adding ?Sleep( 1 )?:
930
-
931
- ==================
932
- broadcast threads:
933
- ==================
934
-
935
- { /** Critical Section
936
-
937
- waiters_count = cond.waiters_count
938
-
939
- if ( waiters_count != 0 )
940
-
941
- cond.was_broadcast = TRUE
942
-
943
- endif
944
-
945
- }
946
-
947
- if ( waiters_count != 0 )
948
-
949
- Sleep( 1 ); //Win32
950
-
951
- cond.sem.post waiters_count
952
-
953
- /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)
954
-
955
- cond.auto_reset_event_or_sem.wait /* Event for Win32
956
-
957
- /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY
958
- HAPPEN TO GO INTO WAIT WHILE PREVIOUS
959
- BROADCAST IS STILL IN PROGRESS/WAITING
960
-
961
- endif
962
-
963
- to the source code of pthread-win32 implementation:
964
-
965
- http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/
966
- condvar.c?rev=1.36&content-type=text/
967
- x-cvsweb-markup&cvsroot=pthreads-win32
968
-
969
- if (wereWaiters)
970
- {(wereWaiters)sroot=pthreads-win32eb.cgi/pthreads/Yem...m
971
- /*
972
- * Wake up all waiters
973
- */
974
-
975
- Sleep( 1 ); //@AT
976
-
977
- #ifdef NEED_SEM
978
-
979
- result = (ptw32_increase_semaphore( &cv->sema, cv->waiters )
980
- ? 0
981
- : EINVAL);
982
-
983
- #else /* NEED_SEM */
984
-
985
- result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )
986
- ? 0
987
- : EINVAL);
988
-
989
- #endif /* NEED_SEM */
990
-
991
- }
992
-
993
- (void) pthread_mutex_unlock(&(cv->waitersLock));
994
-
995
- if (wereWaiters && result == 0)
996
- {(wereWaiters
997
- /*
998
- * Wait for all the awakened threads to acquire their part of
999
- * the counting semaphore
1000
- */
1001
-
1002
- if (WaitForSingleObject (cv->waitersDone, INFINITE)
1003
- == WAIT_OBJECT_0)
1004
- {
1005
- result = 0;
1006
- }
1007
- else
1008
- {
1009
- result = EINVAL;
1010
- }
1011
-
1012
- }
1013
-
1014
- return (result);
1015
-
1016
- }
1017
-
1018
- BTW, on my system (2 CPUs) I can manage to get
1019
- the program stalled even without any source code
1020
- modification if I run the tennisb program many
1021
- times in different shell sessions.
1022
-
1023
- ===================
1024
- pthread-win32 patch
1025
- ===================
1026
- struct pthread_cond_t_ {
1027
- long nWaitersBlocked; /* Number of threads blocked
1028
- */
1029
- long nWaitersUnblocked; /* Number of threads unblocked
1030
- */
1031
- long nWaitersToUnblock; /* Number of threads to unblock
1032
- */
1033
- sem_t semBlockQueue; /* Queue up threads waiting for the
1034
- */
1035
- /* condition to become signalled
1036
- */
1037
- sem_t semBlockLock; /* Semaphore that guards access to
1038
- */
1039
- /* | waiters blocked count/block queue
1040
- */
1041
- /* +-> Mandatory Sync.LEVEL-1
1042
- */
1043
- pthread_mutex_t mtxUnblockLock; /* Mutex that guards access to
1044
- */
1045
- /* | waiters (to)unblock(ed) counts
1046
- */
1047
- /* +-> Optional* Sync.LEVEL-2
1048
- */
1049
- }; /* Opt*) for _timedwait and
1050
- cancellation*/
1051
-
1052
- int
1053
- pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)
1054
- int result = EAGAIN;
1055
- pthread_cond_t cv = NULL;
1056
-
1057
- if (cond == NULL)
1058
- {(cond
1059
- return EINVAL;
1060
- }
1061
-
1062
- if ((attr != NULL && *attr != NULL) &&
1063
- ((*attr)->pshared == PTHREAD_PROCESS_SHARED))
1064
- {
1065
- /*
1066
- * Creating condition variable that can be shared between
1067
- * processes.
1068
- */
1069
- result = ENOSYS;
1070
-
1071
- goto FAIL0;
1072
- }
1073
-
1074
- cv = (pthread_cond_t) calloc (1, sizeof (*cv));
1075
-
1076
- if (cv == NULL)
1077
- {(cv
1078
- result = ENOMEM;
1079
- goto FAIL0;
1080
- }
1081
-
1082
- cv->nWaitersBlocked = 0;
1083
- cv->nWaitersUnblocked = 0;
1084
- cv->nWaitersToUnblock = 0;
1085
-
1086
- if (sem_init (&(cv->semBlockLock), 0, 1) != 0)
1087
- {(sem_init
1088
- goto FAIL0;
1089
- }
1090
-
1091
- if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)
1092
- {(sem_init
1093
- goto FAIL1;
1094
- }
1095
-
1096
- if (pthread_mutex_init (&(cv->mtxUnblockLock), 0) != 0)
1097
- {(pthread_mutex_init
1098
- goto FAIL2;
1099
- }
1100
-
1101
-
1102
- result = 0;
1103
-
1104
- goto DONE;
1105
-
1106
- /*
1107
- * -------------
1108
- * Failed...
1109
- * -------------
1110
- */
1111
- FAIL2:
1112
- (void) sem_destroy (&(cv->semBlockQueue));
1113
-
1114
- FAIL1:
1115
- (void) sem_destroy (&(cv->semBlockLock));
1116
-
1117
- FAIL0:
1118
- DONE:
1119
- *cond = cv;
1120
-
1121
- return (result);
1122
-
1123
- } /* pthread_cond_init */
1124
-
1125
- int
1126
- pthread_cond_destroy (pthread_cond_t * cond)
1127
- {
1128
- int result = 0;
1129
- pthread_cond_t cv;
1130
-
1131
- /*
1132
- * Assuming any race condition here is harmless.
1133
- */
1134
- if (cond == NULL
1135
- || *cond == NULL)
1136
- {
1137
- return EINVAL;
1138
- }
1139
-
1140
- if (*cond != (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1141
- {(*cond
1142
- cv = *cond;
1143
-
1144
- /*
1145
- * Synchronize access to waiters blocked count (LEVEL-1)
1146
- */
1147
- if (sem_wait(&(cv->semBlockLock)) != 0)
1148
- {(sem_wait(&(cv->semBlockLock))
1149
- return errno;
1150
- }
1151
-
1152
- /*
1153
- * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)
1154
- */
1155
- if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1156
- {((result
1157
- (void) sem_post(&(cv->semBlockLock));
1158
- return result;
1159
- }
1160
-
1161
- /*
1162
- * Check whether cv is still busy (still has waiters blocked)
1163
- */
1164
- if (cv->nWaitersBlocked - cv->nWaitersUnblocked > 0)
1165
- {(cv->nWaitersBlocked
1166
- (void) sem_post(&(cv->semBlockLock));
1167
- (void) pthread_mutex_unlock(&(cv->mtxUnblockLock));
1168
- return EBUSY;
1169
- }
1170
-
1171
- /*
1172
- * Now it is safe to destroy
1173
- */
1174
- (void) sem_destroy (&(cv->semBlockLock));
1175
- (void) sem_destroy (&(cv->semBlockQueue));
1176
- (void) pthread_mutex_unlock (&(cv->mtxUnblockLock));
1177
- (void) pthread_mutex_destroy (&(cv->mtxUnblockLock));
1178
-
1179
- free(cv);
1180
- *cond = NULL;
1181
- }
1182
- else
1183
- {
1184
- /*
1185
- * See notes in ptw32_cond_check_need_init() above also.
1186
- */
1187
- EnterCriticalSection(&ptw32_cond_test_init_lock);
1188
-
1189
- /*
1190
- * Check again.
1191
- */
1192
- if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1193
- {(*cond
1194
- /*
1195
- * This is all we need to do to destroy a statically
1196
- * initialised cond that has not yet been used (initialised).
1197
- * If we get to here, another thread
1198
- * waiting to initialise this cond will get an EINVAL.
1199
- */
1200
- *cond = NULL;
1201
- }
1202
- else
1203
- {
1204
- /*
1205
- * The cv has been initialised while we were waiting
1206
- * so assume it's in use.
1207
- */
1208
- result = EBUSY;
1209
- }
1210
-
1211
- LeaveCriticalSection(&ptw32_cond_test_init_lock);
1212
- }
1213
-
1214
- return (result);
1215
- }
1216
-
1217
- /*
1218
- * Arguments for cond_wait_cleanup, since we can only pass a
1219
- * single void * to it.
1220
- */
1221
- typedef struct {
1222
- pthread_mutex_t * mutexPtr;
1223
- pthread_cond_t cv;
1224
- int * resultPtr;
1225
- } ptw32_cond_wait_cleanup_args_t;
1226
-
1227
- static void
1228
- ptw32_cond_wait_cleanup(void * args)
1229
- {
1230
- ptw32_cond_wait_cleanup_args_t * cleanup_args =
1231
- (ptw32_cond_wait_cleanup_args_t *) args;
1232
- pthread_cond_t cv = cleanup_args->cv;
1233
- int * resultPtr = cleanup_args->resultPtr;
1234
- int eLastSignal; /* enum: 1=yes 0=no -1=cancelled/timedout w/o signal(s)
1235
- */
1236
- int result;
1237
-
1238
- /*
1239
- * Whether we got here as a result of signal/broadcast or because of
1240
- * timeout on wait or thread cancellation we indicate that we are no
1241
- * longer waiting. The waiter is responsible for adjusting waiters
1242
- * (to)unblock(ed) counts (protected by unblock lock).
1243
- * Unblock lock/Sync.LEVEL-2 supports _timedwait and cancellation.
1244
- */
1245
- if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1246
- {((result
1247
- *resultPtr = result;
1248
- return;
1249
- }
1250
-
1251
- cv->nWaitersUnblocked++;
1252
-
1253
- eLastSignal = (cv->nWaitersToUnblock == 0) ?
1254
- -1 : (--cv->nWaitersToUnblock == 0);
1255
-
1256
- /*
1257
- * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1258
- */
1259
- if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)
1260
- {((result
1261
- *resultPtr = result;
1262
- return;
1263
- }
1264
-
1265
- /*
1266
- * If last signal...
1267
- */
1268
- if (eLastSignal == 1)
1269
- {(eLastSignal
1270
- /*
1271
- * ...it means that we have end of 'atomic' signal/broadcast
1272
- */
1273
- if (sem_post(&(cv->semBlockLock)) != 0)
1274
- {(sem_post(&(cv->semBlockLock))
1275
- *resultPtr = errno;
1276
- return;
1277
- }
1278
- }
1279
- /*
1280
- * If not last signal and not timed out/cancelled wait w/o signal...
1281
- */
1282
- else if (eLastSignal == 0)
1283
- {
1284
- /*
1285
- * ...it means that next waiter can go through semaphore
1286
- */
1287
- if (sem_post(&(cv->semBlockQueue)) != 0)
1288
- {(sem_post(&(cv->semBlockQueue))
1289
- *resultPtr = errno;
1290
- return;
1291
- }
1292
- }
1293
-
1294
- /*
1295
- * XSH: Upon successful return, the mutex has been locked and is owned
1296
- * by the calling thread
1297
- */
1298
- if ((result = pthread_mutex_lock(cleanup_args->mutexPtr)) != 0)
1299
- {((result
1300
- *resultPtr = result;
1301
- }
1302
-
1303
- } /* ptw32_cond_wait_cleanup */
1304
-
1305
- static int
1306
- ptw32_cond_timedwait (pthread_cond_t * cond,
1307
- pthread_mutex_t * mutex,
1308
- const struct timespec *abstime)
1309
- {
1310
- int result = 0;
1311
- pthread_cond_t cv;
1312
- ptw32_cond_wait_cleanup_args_t cleanup_args;
1313
-
1314
- if (cond == NULL || *cond == NULL)
1315
- {(cond
1316
- return EINVAL;
1317
- }
1318
-
1319
- /*
1320
- * We do a quick check to see if we need to do more work
1321
- * to initialise a static condition variable. We check
1322
- * again inside the guarded section of ptw32_cond_check_need_init()
1323
- * to avoid race conditions.
1324
- */
1325
- if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1326
- {(*cond
1327
- result = ptw32_cond_check_need_init(cond);
1328
- }
1329
-
1330
- if (result != 0 && result != EBUSY)
1331
- {(result
1332
- return result;
1333
- }
1334
-
1335
- cv = *cond;
1336
-
1337
- /*
1338
- * Synchronize access to waiters blocked count (LEVEL-1)
1339
- */
1340
- if (sem_wait(&(cv->semBlockLock)) != 0)
1341
- {(sem_wait(&(cv->semBlockLock))
1342
- return errno;
1343
- }
1344
-
1345
- cv->nWaitersBlocked++;
1346
-
1347
- /*
1348
- * Thats it. Counted means waiting, no more access needed
1349
- */
1350
- if (sem_post(&(cv->semBlockLock)) != 0)
1351
- {(sem_post(&(cv->semBlockLock))
1352
- return errno;
1353
- }
1354
-
1355
- /*
1356
- * Setup this waiter cleanup handler
1357
- */
1358
- cleanup_args.mutexPtr = mutex;
1359
- cleanup_args.cv = cv;
1360
- cleanup_args.resultPtr = &result;
1361
-
1362
- pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args);
1363
-
1364
- /*
1365
- * Now we can release 'mutex' and...
1366
- */
1367
- if ((result = pthread_mutex_unlock (mutex)) == 0)
1368
- {((result
1369
-
1370
- /*
1371
- * ...wait to be awakened by
1372
- * pthread_cond_signal, or
1373
- * pthread_cond_broadcast, or
1374
- * timeout, or
1375
- * thread cancellation
1376
- *
1377
- * Note:
1378
- *
1379
- * ptw32_sem_timedwait is a cancellation point,
1380
- * hence providing the mechanism for making
1381
- * pthread_cond_wait a cancellation point.
1382
- * We use the cleanup mechanism to ensure we
1383
- * re-lock the mutex and adjust (to)unblock(ed) waiters
1384
- * counts if we are cancelled, timed out or signalled.
1385
- */
1386
- if (ptw32_sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
1387
- {(ptw32_sem_timedwait
1388
- result = errno;
1389
- }
1390
- }
1391
-
1392
- /*
1393
- * Always cleanup
1394
- */
1395
- pthread_cleanup_pop (1);
1396
-
1397
-
1398
- /*
1399
- * "result" can be modified by the cleanup handler.
1400
- */
1401
- return (result);
1402
-
1403
- } /* ptw32_cond_timedwait */
1404
-
1405
-
1406
- static int
1407
- ptw32_cond_unblock (pthread_cond_t * cond,
1408
- int unblockAll)
1409
- {
1410
- int result;
1411
- pthread_cond_t cv;
1412
-
1413
- if (cond == NULL || *cond == NULL)
1414
- {(cond
1415
- return EINVAL;
1416
- }
1417
-
1418
- cv = *cond;
1419
-
1420
- /*
1421
- * No-op if the CV is static and hasn't been initialised yet.
1422
- * Assuming that any race condition is harmless.
1423
- */
1424
- if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)
1425
- {(cv
1426
- return 0;
1427
- }
1428
-
1429
- /*
1430
- * Synchronize access to waiters blocked count (LEVEL-1)
1431
- */
1432
- if (sem_wait(&(cv->semBlockLock)) != 0)
1433
- {(sem_wait(&(cv->semBlockLock))
1434
- return errno;
1435
- }
1436
-
1437
- /*
1438
- * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)
1439
- * This sync.level supports _timedwait and cancellation
1440
- */
1441
- if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)
1442
- {((result
1443
- return result;
1444
- }
1445
-
1446
- /*
1447
- * Adjust waiters blocked and unblocked counts (collect garbage)
1448
- */
1449
- if (cv->nWaitersUnblocked != 0)
1450
- {(cv->nWaitersUnblocked
1451
- cv->nWaitersBlocked -= cv->nWaitersUnblocked;
1452
- cv->nWaitersUnblocked = 0;
1453
- }
1454
-
1455
- /*
1456
- * If (after adjustment) there are still some waiters blocked counted...
1457
- */
1458
- if ( cv->nWaitersBlocked > 0)
1459
- {(
1460
- /*
1461
- * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked
1462
- * LEVEL-1 access is left disabled until last signal/unblock
1463
- completes
1464
- */
1465
- cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1;
1466
-
1467
- /*
1468
- * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1469
- * This sync.level supports _timedwait and cancellation
1470
- */
1471
- if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)
1472
- {((result
1473
- return result;
1474
- }
1475
-
1476
-
1477
- /*
1478
- * Now, with LEVEL-2 lock released let first waiter go through
1479
- semaphore
1480
- */
1481
- if (sem_post(&(cv->semBlockQueue)) != 0)
1482
- {(sem_post(&(cv->semBlockQueue))
1483
- return errno;
1484
- }
1485
- }
1486
- /*
1487
- * No waiter blocked - no more LEVEL-1 access to blocked count needed...
1488
- */
1489
- else if (sem_post(&(cv->semBlockLock)) != 0)
1490
- {
1491
- return errno;
1492
- }
1493
- /*
1494
- * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed
1495
- too
1496
- * This sync.level supports _timedwait and cancellation
1497
- */
1498
- else
1499
- {
1500
- result = pthread_mutex_unlock(&(cv->mtxUnblockLock));
1501
- }
1502
-
1503
- return(result);
1504
-
1505
- } /* ptw32_cond_unblock */
1506
-
1507
- int
1508
- pthread_cond_wait (pthread_cond_t * cond,
1509
- pthread_mutex_t * mutex)
1510
- {
1511
- /* The NULL abstime arg means INFINITE waiting. */
1512
- return(ptw32_cond_timedwait(cond, mutex, NULL));
1513
- } /* pthread_cond_wait */
1514
-
1515
-
1516
- int
1517
- pthread_cond_timedwait (pthread_cond_t * cond,
1518
- pthread_mutex_t * mutex,
1519
- const struct timespec *abstime)
1520
- {
1521
- if (abstime == NULL)
1522
- {(abstime
1523
- return EINVAL;
1524
- }
1525
-
1526
- return(ptw32_cond_timedwait(cond, mutex, abstime));
1527
- } /* pthread_cond_timedwait */
1528
-
1529
-
1530
- int
1531
- pthread_cond_signal (pthread_cond_t * cond)
1532
- {
1533
- /* The '0'(FALSE) unblockAll arg means unblock ONE waiter. */
1534
- return(ptw32_cond_unblock(cond, 0));
1535
- } /* pthread_cond_signal */
1536
-
1537
- int
1538
- pthread_cond_broadcast (pthread_cond_t * cond)
1539
- {
1540
- /* The '1'(TRUE) unblockAll arg means unblock ALL waiters. */
1541
- return(ptw32_cond_unblock(cond, 1));
1542
- } /* pthread_cond_broadcast */
1543
-
1544
-
1545
-
1546
-
1547
- TEREKHOV@de.ibm.com on 17.01.2001 01:00:57
1548
-
1549
- Please respond to TEREKHOV@de.ibm.com
1550
-
1551
- To: pthreads-win32@sourceware.cygnus.com
1552
- cc: schmidt@uci.edu
1553
- Subject: win32 conditions: sem+counter+event = broadcast_deadlock +
1554
- spur.wakeup/unfairness/incorrectness ??
1555
-
1556
-
1557
-
1558
-
1559
-
1560
-
1561
-
1562
- Hi,
1563
-
1564
- Problem 1: broadcast_deadlock
1565
-
1566
- It seems that current implementation does not provide "atomic"
1567
- broadcasts. That may lead to "nested" broadcasts... and it seems
1568
- that nested case is not handled correctly -> producing a broadcast
1569
- DEADLOCK as a result.
1570
-
1571
- Scenario:
1572
-
1573
- N (>1) waiting threads W1..N are blocked (in _wait) on condition's
1574
- semaphore.
1575
-
1576
- Thread B1 calls pthread_cond_broadcast, which results in "releasing" N
1577
- W threads via incrementing semaphore counter by N (stored in
1578
- cv->waiters) BUT cv->waiters counter does not change!! The caller
1579
- thread B1 remains blocked on cv->waitersDone event (auto-reset!!) BUT
1580
- condition is not protected from starting another broadcast (when called
1581
- on another thread) while still waiting for the "old" broadcast to
1582
- complete on thread B1.
1583
-
1584
- M (>=0, <N) W threads are fast enough to go thru their _wait call and
1585
- decrement cv->waiters counter.
1586
-
1587
- L (N-M) "late" waiter W threads are a) still blocked/not returned from
1588
- their semaphore wait call or b) were preempted after sem_wait but before
1589
- lock( &cv->waitersLock ) or c) are blocked on cv->waitersLock.
1590
-
1591
- cv->waiters is still > 0 (= L).
1592
-
1593
- Another thread B2 (or some W thread from M group) calls
1594
- pthread_cond_broadcast and gains access to counter... neither a) nor b)
1595
- prevent thread B2 in pthread_cond_broadcast from gaining access to
1596
- counter and starting another broadcast ( for c) - it depends on
1597
- cv->waitersLock scheduling rules: FIFO=OK, PRTY=PROBLEM,... )
1598
-
1599
- That call to pthread_cond_broadcast (on thread B2) will result in
1600
- incrementing semaphore by cv->waiters (=L) which is INCORRECT (all
1601
- W1..N were in fact already released by thread B1) and waiting on
1602
- _auto-reset_ event cv->waitersDone which is DEADLY WRONG (produces a
1603
- deadlock)...
1604
-
1605
- All late W1..L threads now have a chance to complete their _wait call.
1606
- Last W_L thread sets an auto-reselt event cv->waitersDone which will
1607
- release either B1 or B2 leaving one of B threads in a deadlock.
1608
-
1609
- Problem 2: spur.wakeup/unfairness/incorrectness
1610
-
1611
- It seems that:
1612
-
1613
- a) because of the same problem with counter which does not reflect the
1614
- actual number of NOT RELEASED waiters, the signal call may increment
1615
- a semaphore counter w/o having a waiter blocked on it. That will result
1616
- in (best case) spurious wake ups - performance degradation due to
1617
- unnecessary context switches and predicate re-checks and (in worth case)
1618
- unfairness/incorrectness problem - see b)
1619
-
1620
- b) neither signal nor broadcast prevent other threads - "new waiters"
1621
- (and in the case of signal, the caller thread as well) from going into
1622
- _wait and overtaking "old" waiters (already released but still not returned
1623
- from sem_wait on condition's semaphore). Win semaphore just [API DOC]:
1624
- "Maintains a count between zero and some maximum value, limiting the number
1625
- of threads that are simultaneously accessing a shared resource." Calling
1626
- ReleaseSemaphore does not imply (at least not documented) that on return
1627
- from ReleaseSemaphore all waiters will in fact become released (returned
1628
- from their Wait... call) and/or that new waiters calling Wait... afterwards
1629
- will become less importance. It is NOT documented to be an atomic release
1630
- of
1631
- waiters... And even if it would be there is still a problem with a thread
1632
- being preempted after Wait on semaphore and before Wait on cv->waitersLock
1633
- and scheduling rules for cv->waitersLock itself
1634
- (??WaitForMultipleObjects??)
1635
- That may result in unfairness/incorrectness problem as described
1636
- for SetEvent impl. in "Strategies for Implementing POSIX Condition
1637
- Variables
1638
- on Win32": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
1639
-
1640
- Unfairness -- The semantics of the POSIX pthread_cond_broadcast function is
1641
- to wake up all threads currently blocked in wait calls on the condition
1642
- variable. The awakened threads then compete for the external_mutex. To
1643
- ensure
1644
- fairness, all of these threads should be released from their
1645
- pthread_cond_wait calls and allowed to recheck their condition expressions
1646
- before other threads can successfully complete a wait on the condition
1647
- variable.
1648
-
1649
- Unfortunately, the SetEvent implementation above does not guarantee that
1650
- all
1651
- threads sleeping on the condition variable when cond_broadcast is called
1652
- will
1653
- acquire the external_mutex and check their condition expressions. Although
1654
- the Pthreads specification does not mandate this degree of fairness, the
1655
- lack of fairness can cause starvation.
1656
-
1657
- To illustrate the unfairness problem, imagine there are 2 threads, C1 and
1658
- C2,
1659
- that are blocked in pthread_cond_wait on condition variable not_empty_ that
1660
- is guarding a thread-safe message queue. Another thread, P1 then places two
1661
- messages onto the queue and calls pthread_cond_broadcast. If C1 returns
1662
- from
1663
- pthread_cond_wait, dequeues and processes the message, and immediately
1664
- waits
1665
- again then it and only it may end up acquiring both messages. Thus, C2 will
1666
- never get a chance to dequeue a message and run.
1667
-
1668
- The following illustrates the sequence of events:
1669
-
1670
- 1. Thread C1 attempts to dequeue and waits on CV non_empty_
1671
- 2. Thread C2 attempts to dequeue and waits on CV non_empty_
1672
- 3. Thread P1 enqueues 2 messages and broadcasts to CV not_empty_
1673
- 4. Thread P1 exits
1674
- 5. Thread C1 wakes up from CV not_empty_, dequeues a message and runs
1675
- 6. Thread C1 waits again on CV not_empty_, immediately dequeues the 2nd
1676
- message and runs
1677
- 7. Thread C1 exits
1678
- 8. Thread C2 is the only thread left and blocks forever since
1679
- not_empty_ will never be signaled
1680
-
1681
- Depending on the algorithm being implemented, this lack of fairness may
1682
- yield
1683
- concurrent programs that have subtle bugs. Of course, application
1684
- developers
1685
- should not rely on the fairness semantics of pthread_cond_broadcast.
1686
- However,
1687
- there are many cases where fair implementations of condition variables can
1688
- simplify application code.
1689
-
1690
- Incorrectness -- A variation on the unfairness problem described above
1691
- occurs
1692
- when a third consumer thread, C3, is allowed to slip through even though it
1693
- was not waiting on condition variable not_empty_ when a broadcast occurred.
1694
-
1695
- To illustrate this, we will use the same scenario as above: 2 threads, C1
1696
- and
1697
- C2, are blocked dequeuing messages from the message queue. Another thread,
1698
- P1
1699
- then places two messages onto the queue and calls pthread_cond_broadcast.
1700
- C1
1701
- returns from pthread_cond_wait, dequeues and processes the message. At this
1702
- time, C3 acquires the external_mutex, calls pthread_cond_wait and waits on
1703
- the events in WaitForMultipleObjects. Since C2 has not had a chance to run
1704
- yet, the BROADCAST event is still signaled. C3 then returns from
1705
- WaitForMultipleObjects, and dequeues and processes the message in the
1706
- queue.
1707
- Thus, C2 will never get a chance to dequeue a message and run.
1708
-
1709
- The following illustrates the sequence of events:
1710
-
1711
- 1. Thread C1 attempts to dequeue and waits on CV non_empty_
1712
- 2. Thread C2 attempts to dequeue and waits on CV non_empty_
1713
- 3. Thread P1 enqueues 2 messages and broadcasts to CV not_empty_
1714
- 4. Thread P1 exits
1715
- 5. Thread C1 wakes up from CV not_empty_, dequeues a message and runs
1716
- 6. Thread C1 exits
1717
- 7. Thread C3 waits on CV not_empty_, immediately dequeues the 2nd
1718
- message and runs
1719
- 8. Thread C3 exits
1720
- 9. Thread C2 is the only thread left and blocks forever since
1721
- not_empty_ will never be signaled
1722
-
1723
- In the above case, a thread that was not waiting on the condition variable
1724
- when a broadcast occurred was allowed to proceed. This leads to incorrect
1725
- semantics for a condition variable.
1726
-
1727
-
1728
- COMMENTS???
1729
-
1730
- regards,
1731
- alexander.
1732
-
1733
- -----------------------------------------------------------------------------
1734
-
1735
- Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_*
1736
- implementation questions
1737
- Date: Wed, 21 Feb 2001 11:54:47 +0100
1738
- From: TEREKHOV@de.ibm.com
1739
- To: lthomas@arbitrade.com
1740
- CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
1741
- Nanbor Wang <nanbor@cs.wustl.edu>
1742
-
1743
- Hi Louis,
1744
-
1745
- generation number 8..
1746
-
1747
- had some time to revisit timeouts/spurious wakeup problem..
1748
- found some bugs (in 7.b/c/d) and something to improve
1749
- (7a - using IPC semaphores but it should speedup Win32
1750
- version as well).
1751
-
1752
- regards,
1753
- alexander.
1754
-
1755
- ---------- Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
1756
- given:
1757
- semBlockLock - bin.semaphore
1758
- semBlockQueue - semaphore
1759
- mtxExternal - mutex or CS
1760
- mtxUnblockLock - mutex or CS
1761
- nWaitersGone - int
1762
- nWaitersBlocked - int
1763
- nWaitersToUnblock - int
1764
-
1765
- wait( timeout ) {
1766
-
1767
- [auto: register int result ] // error checking omitted
1768
- [auto: register int nSignalsWasLeft ]
1769
- [auto: register int nWaitersWasGone ]
1770
-
1771
- sem_wait( semBlockLock );
1772
- nWaitersBlocked++;
1773
- sem_post( semBlockLock );
1774
-
1775
- unlock( mtxExternal );
1776
- bTimedOut = sem_wait( semBlockQueue,timeout );
1777
-
1778
- lock( mtxUnblockLock );
1779
- if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
1780
- if ( bTimeout ) { // timeout (or canceled)
1781
- if ( 0 != nWaitersBlocked ) {
1782
- nWaitersBlocked--;
1783
- }
1784
- else {
1785
- nWaitersGone++; // count spurious wakeups
1786
- }
1787
- }
1788
- if ( 0 == --nWaitersToUnblock ) {
1789
- if ( 0 != nWaitersBlocked ) {
1790
- sem_post( semBlockLock ); // open the gate
1791
- nSignalsWasLeft = 0; // do not open the gate below
1792
- again
1793
- }
1794
- else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
1795
- nWaitersGone = 0;
1796
- }
1797
- }
1798
- }
1799
- else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
1800
- semaphore :-)
1801
- sem_wait( semBlockLock );
1802
- nWaitersBlocked -= nWaitersGone; // something is going on here -
1803
- test of timeouts? :-)
1804
- sem_post( semBlockLock );
1805
- nWaitersGone = 0;
1806
- }
1807
- unlock( mtxUnblockLock );
1808
-
1809
- if ( 1 == nSignalsWasLeft ) {
1810
- if ( 0 != nWaitersWasGone ) {
1811
- // sem_adjust( -nWaitersWasGone );
1812
- while ( nWaitersWasGone-- ) {
1813
- sem_wait( semBlockLock ); // better now than spurious
1814
- later
1815
- }
1816
- }
1817
- sem_post( semBlockLock ); // open the gate
1818
- }
1819
-
1820
- lock( mtxExternal );
1821
-
1822
- return ( bTimedOut ) ? ETIMEOUT : 0;
1823
- }
1824
-
1825
- signal(bAll) {
1826
-
1827
- [auto: register int result ]
1828
- [auto: register int nSignalsToIssue]
1829
-
1830
- lock( mtxUnblockLock );
1831
-
1832
- if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
1833
- if ( 0 == nWaitersBlocked ) { // NO-OP
1834
- return unlock( mtxUnblockLock );
1835
- }
1836
- if (bAll) {
1837
- nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
1838
- nWaitersBlocked = 0;
1839
- }
1840
- else {
1841
- nSignalsToIssue = 1;
1842
- nWaitersToUnblock++;
1843
- nWaitersBlocked--;
1844
- }
1845
- }
1846
- else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
1847
- sem_wait( semBlockLock ); // close the gate
1848
- if ( 0 != nWaitersGone ) {
1849
- nWaitersBlocked -= nWaitersGone;
1850
- nWaitersGone = 0;
1851
- }
1852
- if (bAll) {
1853
- nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
1854
- nWaitersBlocked = 0;
1855
- }
1856
- else {
1857
- nSignalsToIssue = nWaitersToUnblock = 1;
1858
- nWaitersBlocked--;
1859
- }
1860
- }
1861
- else { // NO-OP
1862
- return unlock( mtxUnblockLock );
1863
- }
1864
-
1865
- unlock( mtxUnblockLock );
1866
- sem_post( semBlockQueue,nSignalsToIssue );
1867
- return result;
1868
- }
1869
-
1870
- ---------- Algorithm 8b / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE
1871
- ------
1872
- given:
1873
- semBlockLock - bin.semaphore
1874
- semBlockQueue - bin.semaphore
1875
- mtxExternal - mutex or CS
1876
- mtxUnblockLock - mutex or CS
1877
- nWaitersGone - int
1878
- nWaitersBlocked - int
1879
- nWaitersToUnblock - int
1880
-
1881
- wait( timeout ) {
1882
-
1883
- [auto: register int result ] // error checking omitted
1884
- [auto: register int nWaitersWasGone ]
1885
- [auto: register int nSignalsWasLeft ]
1886
-
1887
- sem_wait( semBlockLock );
1888
- nWaitersBlocked++;
1889
- sem_post( semBlockLock );
1890
-
1891
- unlock( mtxExternal );
1892
- bTimedOut = sem_wait( semBlockQueue,timeout );
1893
-
1894
- lock( mtxUnblockLock );
1895
- if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
1896
- if ( bTimeout ) { // timeout (or canceled)
1897
- if ( 0 != nWaitersBlocked ) {
1898
- nWaitersBlocked--;
1899
- nSignalsWasLeft = 0; // do not unblock next waiter
1900
- below (already unblocked)
1901
- }
1902
- else {
1903
- nWaitersGone = 1; // spurious wakeup pending!!
1904
- }
1905
- }
1906
- if ( 0 == --nWaitersToUnblock &&
1907
- if ( 0 != nWaitersBlocked ) {
1908
- sem_post( semBlockLock ); // open the gate
1909
- nSignalsWasLeft = 0; // do not open the gate below
1910
- again
1911
- }
1912
- else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
1913
- nWaitersGone = 0;
1914
- }
1915
- }
1916
- }
1917
- else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
1918
- semaphore :-)
1919
- sem_wait( semBlockLock );
1920
- nWaitersBlocked -= nWaitersGone; // something is going on here -
1921
- test of timeouts? :-)
1922
- sem_post( semBlockLock );
1923
- nWaitersGone = 0;
1924
- }
1925
- unlock( mtxUnblockLock );
1926
-
1927
- if ( 1 == nSignalsWasLeft ) {
1928
- if ( 0 != nWaitersWasGone ) {
1929
- // sem_adjust( -1 );
1930
- sem_wait( semBlockQueue ); // better now than spurious
1931
- later
1932
- }
1933
- sem_post( semBlockLock ); // open the gate
1934
- }
1935
- else if ( 0 != nSignalsWasLeft ) {
1936
- sem_post( semBlockQueue ); // unblock next waiter
1937
- }
1938
-
1939
- lock( mtxExternal );
1940
-
1941
- return ( bTimedOut ) ? ETIMEOUT : 0;
1942
- }
1943
-
1944
- signal(bAll) {
1945
-
1946
- [auto: register int result ]
1947
-
1948
- lock( mtxUnblockLock );
1949
-
1950
- if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
1951
- if ( 0 == nWaitersBlocked ) { // NO-OP
1952
- return unlock( mtxUnblockLock );
1953
- }
1954
- if (bAll) {
1955
- nWaitersToUnblock += nWaitersBlocked;
1956
- nWaitersBlocked = 0;
1957
- }
1958
- else {
1959
- nWaitersToUnblock++;
1960
- nWaitersBlocked--;
1961
- }
1962
- unlock( mtxUnblockLock );
1963
- }
1964
- else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
1965
- sem_wait( semBlockLock ); // close the gate
1966
- if ( 0 != nWaitersGone ) {
1967
- nWaitersBlocked -= nWaitersGone;
1968
- nWaitersGone = 0;
1969
- }
1970
- if (bAll) {
1971
- nWaitersToUnblock = nWaitersBlocked;
1972
- nWaitersBlocked = 0;
1973
- }
1974
- else {
1975
- nWaitersToUnblock = 1;
1976
- nWaitersBlocked--;
1977
- }
1978
- unlock( mtxUnblockLock );
1979
- sem_post( semBlockQueue );
1980
- }
1981
- else { // NO-OP
1982
- unlock( mtxUnblockLock );
1983
- }
1984
-
1985
- return result;
1986
- }
1987
-
1988
- ---------- Algorithm 8c / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE
1989
- ---------
1990
- given:
1991
- hevBlockLock - auto-reset event
1992
- hevBlockQueue - auto-reset event
1993
- mtxExternal - mutex or CS
1994
- mtxUnblockLock - mutex or CS
1995
- nWaitersGone - int
1996
- nWaitersBlocked - int
1997
- nWaitersToUnblock - int
1998
-
1999
- wait( timeout ) {
2000
-
2001
- [auto: register int result ] // error checking omitted
2002
- [auto: register int nSignalsWasLeft ]
2003
- [auto: register int nWaitersWasGone ]
2004
-
2005
- wait( hevBlockLock,INFINITE );
2006
- nWaitersBlocked++;
2007
- set_event( hevBlockLock );
2008
-
2009
- unlock( mtxExternal );
2010
- bTimedOut = wait( hevBlockQueue,timeout );
2011
-
2012
- lock( mtxUnblockLock );
2013
- if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2014
- if ( bTimeout ) { // timeout (or canceled)
2015
- if ( 0 != nWaitersBlocked ) {
2016
- nWaitersBlocked--;
2017
- nSignalsWasLeft = 0; // do not unblock next waiter
2018
- below (already unblocked)
2019
- }
2020
- else {
2021
- nWaitersGone = 1; // spurious wakeup pending!!
2022
- }
2023
- }
2024
- if ( 0 == --nWaitersToUnblock )
2025
- if ( 0 != nWaitersBlocked ) {
2026
- set_event( hevBlockLock ); // open the gate
2027
- nSignalsWasLeft = 0; // do not open the gate below
2028
- again
2029
- }
2030
- else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
2031
- nWaitersGone = 0;
2032
- }
2033
- }
2034
- }
2035
- else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2036
- event :-)
2037
- wait( hevBlockLock,INFINITE );
2038
- nWaitersBlocked -= nWaitersGone; // something is going on here -
2039
- test of timeouts? :-)
2040
- set_event( hevBlockLock );
2041
- nWaitersGone = 0;
2042
- }
2043
- unlock( mtxUnblockLock );
2044
-
2045
- if ( 1 == nSignalsWasLeft ) {
2046
- if ( 0 != nWaitersWasGone ) {
2047
- reset_event( hevBlockQueue ); // better now than spurious
2048
- later
2049
- }
2050
- set_event( hevBlockLock ); // open the gate
2051
- }
2052
- else if ( 0 != nSignalsWasLeft ) {
2053
- set_event( hevBlockQueue ); // unblock next waiter
2054
- }
2055
-
2056
- lock( mtxExternal );
2057
-
2058
- return ( bTimedOut ) ? ETIMEOUT : 0;
2059
- }
2060
-
2061
- signal(bAll) {
2062
-
2063
- [auto: register int result ]
2064
-
2065
- lock( mtxUnblockLock );
2066
-
2067
- if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2068
- if ( 0 == nWaitersBlocked ) { // NO-OP
2069
- return unlock( mtxUnblockLock );
2070
- }
2071
- if (bAll) {
2072
- nWaitersToUnblock += nWaitersBlocked;
2073
- nWaitersBlocked = 0;
2074
- }
2075
- else {
2076
- nWaitersToUnblock++;
2077
- nWaitersBlocked--;
2078
- }
2079
- unlock( mtxUnblockLock );
2080
- }
2081
- else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2082
- wait( hevBlockLock,INFINITE ); // close the gate
2083
- if ( 0 != nWaitersGone ) {
2084
- nWaitersBlocked -= nWaitersGone;
2085
- nWaitersGone = 0;
2086
- }
2087
- if (bAll) {
2088
- nWaitersToUnblock = nWaitersBlocked;
2089
- nWaitersBlocked = 0;
2090
- }
2091
- else {
2092
- nWaitersToUnblock = 1;
2093
- nWaitersBlocked--;
2094
- }
2095
- unlock( mtxUnblockLock );
2096
- set_event( hevBlockQueue );
2097
- }
2098
- else { // NO-OP
2099
- unlock( mtxUnblockLock );
2100
- }
2101
-
2102
- return result;
2103
- }
2104
-
2105
- ---------- Algorithm 8d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
2106
- given:
2107
- hevBlockLock - auto-reset event
2108
- hevBlockQueueS - auto-reset event // for signals
2109
- hevBlockQueueB - manual-reset even // for broadcasts
2110
- mtxExternal - mutex or CS
2111
- mtxUnblockLock - mutex or CS
2112
- eBroadcast - int // 0: no broadcast, 1: broadcast, 2:
2113
- broadcast after signal(s)
2114
- nWaitersGone - int
2115
- nWaitersBlocked - int
2116
- nWaitersToUnblock - int
2117
-
2118
- wait( timeout ) {
2119
-
2120
- [auto: register int result ] // error checking omitted
2121
- [auto: register int eWasBroadcast ]
2122
- [auto: register int nSignalsWasLeft ]
2123
- [auto: register int nWaitersWasGone ]
2124
-
2125
- wait( hevBlockLock,INFINITE );
2126
- nWaitersBlocked++;
2127
- set_event( hevBlockLock );
2128
-
2129
- unlock( mtxExternal );
2130
- bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );
2131
-
2132
- lock( mtxUnblockLock );
2133
- if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2134
- if ( bTimeout ) { // timeout (or canceled)
2135
- if ( 0 != nWaitersBlocked ) {
2136
- nWaitersBlocked--;
2137
- nSignalsWasLeft = 0; // do not unblock next waiter
2138
- below (already unblocked)
2139
- }
2140
- else if ( 1 != eBroadcast ) {
2141
- nWaitersGone = 1;
2142
- }
2143
- }
2144
- if ( 0 == --nWaitersToUnblock ) {
2145
- if ( 0 != nWaitersBlocked ) {
2146
- set_event( hevBlockLock ); // open the gate
2147
- nSignalsWasLeft = 0; // do not open the gate below
2148
- again
2149
- }
2150
- else {
2151
- if ( 0 != (eWasBroadcast = eBroadcast) ) {
2152
- eBroadcast = 0;
2153
- }
2154
- if ( 0 != (nWaitersWasGone = nWaitersGone ) {
2155
- nWaitersGone = 0;
2156
- }
2157
- }
2158
- }
2159
- else if ( 0 != eBroadcast ) {
2160
- nSignalsWasLeft = 0; // do not unblock next waiter
2161
- below (already unblocked)
2162
- }
2163
- }
2164
- else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2165
- event :-)
2166
- wait( hevBlockLock,INFINITE );
2167
- nWaitersBlocked -= nWaitersGone; // something is going on here -
2168
- test of timeouts? :-)
2169
- set_event( hevBlockLock );
2170
- nWaitersGone = 0;
2171
- }
2172
- unlock( mtxUnblockLock );
2173
-
2174
- if ( 1 == nSignalsWasLeft ) {
2175
- if ( 0 != eWasBroadcast ) {
2176
- reset_event( hevBlockQueueB );
2177
- }
2178
- if ( 0 != nWaitersWasGone ) {
2179
- reset_event( hevBlockQueueS ); // better now than spurious
2180
- later
2181
- }
2182
- set_event( hevBlockLock ); // open the gate
2183
- }
2184
- else if ( 0 != nSignalsWasLeft ) {
2185
- set_event( hevBlockQueueS ); // unblock next waiter
2186
- }
2187
-
2188
- lock( mtxExternal );
2189
-
2190
- return ( bTimedOut ) ? ETIMEOUT : 0;
2191
- }
2192
-
2193
- signal(bAll) {
2194
-
2195
- [auto: register int result ]
2196
- [auto: register HANDLE hevBlockQueue ]
2197
-
2198
- lock( mtxUnblockLock );
2199
-
2200
- if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2201
- if ( 0 == nWaitersBlocked ) { // NO-OP
2202
- return unlock( mtxUnblockLock );
2203
- }
2204
- if (bAll) {
2205
- nWaitersToUnblock += nWaitersBlocked;
2206
- nWaitersBlocked = 0;
2207
- eBroadcast = 2;
2208
- hevBlockQueue = hevBlockQueueB;
2209
- }
2210
- else {
2211
- nWaitersToUnblock++;
2212
- nWaitersBlocked--;
2213
- return unlock( mtxUnblockLock );
2214
- }
2215
- }
2216
- else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2217
- wait( hevBlockLock,INFINITE ); // close the gate
2218
- if ( 0 != nWaitersGone ) {
2219
- nWaitersBlocked -= nWaitersGone;
2220
- nWaitersGone = 0;
2221
- }
2222
- if (bAll) {
2223
- nWaitersToUnblock = nWaitersBlocked;
2224
- nWaitersBlocked = 0;
2225
- eBroadcast = 1;
2226
- hevBlockQueue = hevBlockQueueB;
2227
- }
2228
- else {
2229
- nWaitersToUnblock = 1;
2230
- nWaitersBlocked--;
2231
- hevBlockQueue = hevBlockQueueS;
2232
- }
2233
- }
2234
- else { // NO-OP
2235
- return unlock( mtxUnblockLock );
2236
- }
2237
-
2238
- unlock( mtxUnblockLock );
2239
- set_event( hevBlockQueue );
2240
- return result;
2241
- }
2242
- ---------------------- Forwarded by Alexander Terekhov/Germany/IBM on
2243
- 02/21/2001 09:13 AM ---------------------------
2244
-
2245
- Alexander Terekhov
2246
- 02/20/2001 04:33 PM
2247
-
2248
- To: Louis Thomas <lthomas@arbitrade.com>
2249
- cc:
2250
-
2251
- From: Alexander Terekhov/Germany/IBM@IBMDE
2252
- Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2253
- n questions
2254
- Importance: Normal
2255
-
2256
- >Sorry, gotta take a break and work on something else for a while.
2257
- >Real work
2258
- >calls, unfortunately. I'll get back to you in two or three days.
2259
-
2260
- ok. no problem. here is some more stuff for pauses you might have
2261
- in between :)
2262
-
2263
- ---------- Algorithm 7d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------
2264
- given:
2265
- hevBlockLock - auto-reset event
2266
- hevBlockQueueS - auto-reset event // for signals
2267
- hevBlockQueueB - manual-reset even // for broadcasts
2268
- mtxExternal - mutex or CS
2269
- mtxUnblockLock - mutex or CS
2270
- bBroadcast - int
2271
- nWaitersGone - int
2272
- nWaitersBlocked - int
2273
- nWaitersToUnblock - int
2274
-
2275
- wait( timeout ) {
2276
-
2277
- [auto: register int result ] // error checking omitted
2278
- [auto: register int bWasBroadcast ]
2279
- [auto: register int nSignalsWasLeft ]
2280
-
2281
- wait( hevBlockLock,INFINITE );
2282
- nWaitersBlocked++;
2283
- set_event( hevBlockLock );
2284
-
2285
- unlock( mtxExternal );
2286
- bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );
2287
-
2288
- lock( mtxUnblockLock );
2289
- if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {
2290
- if ( bTimeout ) { // timeout (or canceled)
2291
- if ( 0 != nWaitersBlocked ) {
2292
- nWaitersBlocked--;
2293
- nSignalsWasLeft = 0; // do not unblock next waiter
2294
- below (already unblocked)
2295
- }
2296
- else if ( !bBroadcast ) {
2297
- wait( hevBlockQueueS,INFINITE ); // better now than spurious
2298
- later
2299
- }
2300
- }
2301
- if ( 0 == --nWaitersToUnblock ) {
2302
- if ( 0 != nWaitersBlocked ) {
2303
- if ( bBroadcast ) {
2304
- reset_event( hevBlockQueueB );
2305
- bBroadcast = false;
2306
- }
2307
- set_event( hevBlockLock ); // open the gate
2308
- nSignalsWasLeft = 0; // do not open the gate below
2309
- again
2310
- }
2311
- else if ( false != (bWasBroadcast = bBroadcast) ) {
2312
- bBroadcast = false;
2313
- }
2314
- }
2315
- else {
2316
- bWasBroadcast = bBroadcast;
2317
- }
2318
- }
2319
- else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious
2320
- event :-)
2321
- wait( hevBlockLock,INFINITE );
2322
- nWaitersBlocked -= nWaitersGone; // something is going on here -
2323
- test of timeouts? :-)
2324
- set_event( hevBlockLock );
2325
- nWaitersGone = 0;
2326
- }
2327
- unlock( mtxUnblockLock );
2328
-
2329
- if ( 1 == nSignalsWasLeft ) {
2330
- if ( bWasBroadcast ) {
2331
- reset_event( hevBlockQueueB );
2332
- }
2333
- set_event( hevBlockLock ); // open the gate
2334
- }
2335
- else if ( 0 != nSignalsWasLeft && !bWasBroadcast ) {
2336
- set_event( hevBlockQueueS ); // unblock next waiter
2337
- }
2338
-
2339
- lock( mtxExternal );
2340
-
2341
- return ( bTimedOut ) ? ETIMEOUT : 0;
2342
- }
2343
-
2344
- signal(bAll) {
2345
-
2346
- [auto: register int result ]
2347
- [auto: register HANDLE hevBlockQueue ]
2348
-
2349
- lock( mtxUnblockLock );
2350
-
2351
- if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
2352
- if ( 0 == nWaitersBlocked ) { // NO-OP
2353
- return unlock( mtxUnblockLock );
2354
- }
2355
- if (bAll) {
2356
- nWaitersToUnblock += nWaitersBlocked;
2357
- nWaitersBlocked = 0;
2358
- bBroadcast = true;
2359
- hevBlockQueue = hevBlockQueueB;
2360
- }
2361
- else {
2362
- nWaitersToUnblock++;
2363
- nWaitersBlocked--;
2364
- return unlock( mtxUnblockLock );
2365
- }
2366
- }
2367
- else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
2368
- wait( hevBlockLock,INFINITE ); // close the gate
2369
- if ( 0 != nWaitersGone ) {
2370
- nWaitersBlocked -= nWaitersGone;
2371
- nWaitersGone = 0;
2372
- }
2373
- if (bAll) {
2374
- nWaitersToUnblock = nWaitersBlocked;
2375
- nWaitersBlocked = 0;
2376
- bBroadcast = true;
2377
- hevBlockQueue = hevBlockQueueB;
2378
- }
2379
- else {
2380
- nWaitersToUnblock = 1;
2381
- nWaitersBlocked--;
2382
- hevBlockQueue = hevBlockQueueS;
2383
- }
2384
- }
2385
- else { // NO-OP
2386
- return unlock( mtxUnblockLock );
2387
- }
2388
-
2389
- unlock( mtxUnblockLock );
2390
- set_event( hevBlockQueue );
2391
- return result;
2392
- }
2393
-
2394
-
2395
- ----------------------------------------------------------------------------
2396
-
2397
- Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2398
- n questions
2399
- Date: Mon, 26 Feb 2001 22:20:12 -0600
2400
- From: Louis Thomas <lthomas@arbitrade.com>
2401
- To: "'TEREKHOV@de.ibm.com'" <TEREKHOV@de.ibm.com>
2402
- CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
2403
- Nanbor Wang
2404
- <nanbor@cs.wustl.edu>
2405
-
2406
- Sorry all. Busy week.
2407
-
2408
- > this insures the fairness
2409
- > which POSIX does not (e.g. two subsequent broadcasts - the gate does
2410
- insure
2411
- > that first wave waiters will start the race for the mutex before waiters
2412
- > from the second wave - Linux pthreads process/unblock both waves
2413
- > concurrently...)
2414
-
2415
- I'm not sure how we are any more fair about this than Linux. We certainly
2416
- don't guarantee that the threads released by the first broadcast will get
2417
- the external mutex before the threads of the second wave. In fact, it is
2418
- possible that those threads will never get the external mutex if there is
2419
- enough contention for it.
2420
-
2421
- > e.g. i was thinking about implementation with a pool of
2422
- > N semaphores/counters [...]
2423
-
2424
- I considered that too. The problem is as you mentioned in a). You really
2425
- need to assign threads to semaphores once you know how you want to wake them
2426
- up, not when they first begin waiting which is the only time you can assign
2427
- them.
2428
-
2429
- > well, i am not quite sure that i've fully understood your scenario,
2430
-
2431
- Hmm. Well, it think it's an important example, so I'll try again. First, we
2432
- have thread A which we KNOW is waiting on a condition. As soon as it becomes
2433
- unblocked for any reason, we will know because it will set a flag. Since the
2434
- flag is not set, we are 100% confident that thread A is waiting on the
2435
- condition. We have another thread, thread B, which has acquired the mutex
2436
- and is about to wait on the condition. Thus it is pretty clear that at any
2437
- point, either just A is waiting, or A and B are waiting. Now thread C comes
2438
- along. C is about to do a broadcast on the condition. A broadcast is
2439
- guaranteed to unblock all threads currently waiting on a condition, right?
2440
- Again, we said that either just A is waiting, or A and B are both waiting.
2441
- So, when C does its broadcast, depending upon whether B has started waiting
2442
- or not, thread C will unblock A or unblock A and B. Either way, C must
2443
- unblock A, right?
2444
-
2445
- Now, you said anything that happens is correct so long as a) "a signal is
2446
- not lost between unlocking the mutex and waiting on the condition" and b) "a
2447
- thread must not steal a signal it sent", correct? Requirement b) is easy to
2448
- satisfy: in this scenario, thread C will never wait on the condition, so it
2449
- won't steal any signals. Requirement a) is not hard either. The only way we
2450
- could fail to meet requirement a) in this scenario is if thread B was
2451
- started waiting but didn't wake up because a signal was lost. This will not
2452
- happen.
2453
-
2454
- Now, here is what happens. Assume thread C beats thread B. Thread C looks to
2455
- see how many threads are waiting on the condition. Thread C sees just one
2456
- thread, thread A, waiting. It does a broadcast waking up just one thread
2457
- because just one thread is waiting. Next, before A can become unblocked,
2458
- thread B begins waiting. Now there are two threads waiting, but only one
2459
- will be unblocked. Suppose B wins. B will become unblocked. A will not
2460
- become unblocked, because C only unblocked one thread (sema_post cond, 1).
2461
- So at the end, B finishes and A remains blocked.
2462
-
2463
- We have met both of your requirements, so by your rules, this is an
2464
- acceptable outcome. However, I think that the spec says this is an
2465
- unacceptable outcome! We know for certain that A was waiting and that C did
2466
- a broadcast, but A did not become unblocked! Yet, the spec says that a
2467
- broadcast wakes up all waiting threads. This did not happen. Do you agree
2468
- that this shows your rules are not strict enough?
2469
-
2470
- > and what about N2? :) this one does allow almost everything.
2471
-
2472
- Don't get me started about rule #2. I'll NEVER advocate an algorithm that
2473
- uses rule 2 as an excuse to suck!
2474
-
2475
- > but it is done (decrement)under mutex protection - this is not a subject
2476
- > of a race condition.
2477
-
2478
- You are correct. My mistake.
2479
-
2480
- > i would remove "_bTimedOut=false".. after all, it was a real timeout..
2481
-
2482
- I disagree. A thread that can't successfully retract its waiter status can't
2483
- really have timed out. If a thread can't return without executing extra code
2484
- to deal with the fact that someone tried to unblock it, I think it is a poor
2485
- idea to pretend we
2486
- didn't realize someone was trying to signal us. After all, a signal is more
2487
- important than a time out.
2488
-
2489
- > when nSignaled != 0, it is possible to update nWaiters (--) and do not
2490
- > touch nGone
2491
-
2492
- I realize this, but I was thinking that writing it the other ways saves
2493
- another if statement.
2494
-
2495
- > adjust only if nGone != 0 and save one cache memory write - probably much
2496
- slower than 'if'
2497
-
2498
- Hmm. You are probably right.
2499
-
2500
- > well, in a strange (e.g. timeout test) program you may (theoretically)
2501
- > have an overflow of nWaiters/nGone counters (with waiters repeatedly
2502
- timing
2503
- > out and no signals at all).
2504
-
2505
- Also true. Not only that, but you also have the possibility that one could
2506
- overflow the number of waiters as well! However, considering the limit you
2507
- have chosen for nWaitersGone, I suppose it is unlikely that anyone would be
2508
- able to get INT_MAX/2 threads waiting on a single condition. :)
2509
-
2510
- Analysis of 8a:
2511
-
2512
- It looks correct to me.
2513
-
2514
- What are IPC semaphores?
2515
-
2516
- In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2517
- // HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2518
- because nWaitersGone is never modified without holding mtxUnblockLock. You
2519
- are correct that there is a harmless race on nWaitersBlocked, which can
2520
- increase and make the condition become true just after we check it. If this
2521
- happens, we interpret it as the wait starting after the signal.
2522
-
2523
- I like your optimization of this. You could improve Alg. 6 as follows:
2524
- ---------- Algorithm 6b ----------
2525
- signal(bAll) {
2526
- _nSig=0
2527
- lock counters
2528
- // this is safe because nWaiting can only be decremented by a thread that
2529
- // owns counters and nGone can only be changed by a thread that owns
2530
- counters.
2531
- if (nWaiting>nGone) {
2532
- if (0==nSignaled) {
2533
- sema_wait gate // close gate if not already closed
2534
- }
2535
- if (nGone>0) {
2536
- nWaiting-=nGone
2537
- nGone=0
2538
- }
2539
- _nSig=bAll?nWaiting:1
2540
- nSignaled+=_nSig
2541
- nWaiting-=_nSig
2542
- }
2543
- unlock counters
2544
- if (0!=_nSig) {
2545
- sema_post queue, _nSig
2546
- }
2547
- }
2548
- ---------- ---------- ----------
2549
- I guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings
2550
- depending upon whether the gate is open or closed.
2551
-
2552
- In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
2553
- semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
2554
-
2555
- What have you gained by making the last thread to be signaled do the waits
2556
- for all the timed out threads, besides added complexity? It took me a long
2557
- time to figure out what your objective was with this, to realize you were
2558
- using nWaitersGone to mean two different things, and to verify that you
2559
- hadn't introduced any bug by doing this. Even now I'm not 100% sure.
2560
-
2561
- What has all this playing about with nWaitersGone really gained us besides a
2562
- lot of complexity (it is much harder to verify that this solution is
2563
- correct), execution overhead (we now have a lot more if statements to
2564
- evaluate), and space overhead (more space for the extra code, and another
2565
- integer in our data)? We did manage to save a lock/unlock pair in an
2566
- uncommon case (when a time out occurs) at the above mentioned expenses in
2567
- the common cases.
2568
-
2569
- As for 8b, c, and d, they look ok though I haven't studied them thoroughly.
2570
- What would you use them for?
2571
-
2572
- Later,
2573
- -Louis! :)
2574
-
2575
- -----------------------------------------------------------------------------
2576
-
2577
- Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2578
- n questions
2579
- Date: Tue, 27 Feb 2001 15:51:28 +0100
2580
- From: TEREKHOV@de.ibm.com
2581
- To: Louis Thomas <lthomas@arbitrade.com>
2582
- CC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,
2583
- Nanbor Wang <nanbor@cs.wustl.edu>
2584
-
2585
- Hi Louis,
2586
-
2587
- >> that first wave waiters will start the race for the mutex before waiters
2588
- >> from the second wave - Linux pthreads process/unblock both waves
2589
- >> concurrently...)
2590
- >
2591
- >I'm not sure how we are any more fair about this than Linux. We certainly
2592
- >don't guarantee that the threads released by the first broadcast will get
2593
- >the external mutex before the threads of the second wave. In fact, it is
2594
- >possible that those threads will never get the external mutex if there is
2595
- >enough contention for it.
2596
-
2597
- correct. but gate is nevertheless more fair than Linux because of the
2598
- barrier it establishes between two races (1st and 2nd wave waiters) for
2599
- the mutex which under 'normal' circumstances (e.g. all threads of equal
2600
- priorities,..) will 'probably' result in fair behaviour with respect to
2601
- mutex ownership.
2602
-
2603
- >> well, i am not quite sure that i've fully understood your scenario,
2604
- >
2605
- >Hmm. Well, it think it's an important example, so I'll try again. ...
2606
-
2607
- ok. now i seem to understand this example. well, now it seems to me
2608
- that the only meaningful rule is just:
2609
-
2610
- a) "a signal is not lost between unlocking the mutex and waiting on the
2611
- condition"
2612
-
2613
- and that the rule
2614
-
2615
- b) "a thread must not steal a signal it sent"
2616
-
2617
- is not needed at all because a thread which violates b) also violates a).
2618
-
2619
- i'll try to explain..
2620
-
2621
- i think that the most important thing is how POSIX defines waiter's
2622
- visibility:
2623
-
2624
- "if another thread is able to acquire the mutex after the about-to-block
2625
- thread
2626
- has released it, then a subsequent call to pthread_cond_signal() or
2627
- pthread_cond_broadcast() in that thread behaves as if it were issued after
2628
- the about-to-block thread has blocked. "
2629
-
2630
- my understanding is the following:
2631
-
2632
- 1) there is no guarantees whatsoever with respect to whether
2633
- signal/broadcast
2634
- will actually unblock any 'waiter' if it is done w/o acquiring the mutex
2635
- first
2636
- (note that a thread may release it before signal/broadcast - it does not
2637
- matter).
2638
-
2639
- 2) it is guaranteed that waiters become 'visible' - eligible for unblock as
2640
- soon
2641
- as signalling thread acquires the mutex (but not before!!)
2642
-
2643
- so..
2644
-
2645
- >So, when C does its broadcast, depending upon whether B has started
2646
- waiting
2647
- >or not, thread C will unblock A or unblock A and B. Either way, C must
2648
- >unblock A, right?
2649
-
2650
- right. but only if C did acquire the mutex prior to broadcast (it may
2651
- release it before broadcast as well).
2652
-
2653
- implementation will violate waiters visibility rule (signal will become
2654
- lost)
2655
- if C will not unblock A.
2656
-
2657
- >Now, here is what happens. Assume thread C beats thread B. Thread C looks
2658
- to
2659
- >see how many threads are waiting on the condition. Thread C sees just one
2660
- >thread, thread A, waiting. It does a broadcast waking up just one thread
2661
- >because just one thread is waiting. Next, before A can become unblocked,
2662
- >thread B begins waiting. Now there are two threads waiting, but only one
2663
- >will be unblocked. Suppose B wins. B will become unblocked. A will not
2664
- >become unblocked, because C only unblocked one thread (sema_post cond, 1).
2665
- >So at the end, B finishes and A remains blocked.
2666
-
2667
- thread C did acquire the mutex ("Thread C sees just one thread, thread A,
2668
- waiting"). beginning from that moment it is guaranteed that subsequent
2669
- broadcast will unblock A. Otherwise we will have a lost signal with respect
2670
- to A. I do think that it does not matter whether the signal was physically
2671
- (completely) lost or was just stolen by another thread (B) - in both cases
2672
- it was simply lost with respect to A.
2673
-
2674
- >..Do you agree that this shows your rules are not strict enough?
2675
-
2676
- probably the opposite.. :-) i think that it shows that the only meaningful
2677
- rule is
2678
-
2679
- a) "a signal is not lost between unlocking the mutex and waiting on the
2680
- condition"
2681
-
2682
- with clarification of waiters visibility as defined by POSIX above.
2683
-
2684
- >> i would remove "_bTimedOut=false".. after all, it was a real timeout..
2685
- >
2686
- >I disagree. A thread that can't successfully retract its waiter status
2687
- can't
2688
- >really have timed out. If a thread can't return without executing extra
2689
- code
2690
- >to deal with the fact that someone tried to unblock it, I think it is a
2691
- poor
2692
- >idea to pretend we
2693
- >didn't realize someone was trying to signal us. After all, a signal is
2694
- more
2695
- >important than a time out.
2696
-
2697
- a) POSIX does allow timed out thread to consume a signal (cancelled is
2698
- not).
2699
- b) ETIMEDOUT status just says that: "The time specified by abstime to
2700
- pthread_cond_timedwait() has passed."
2701
- c) it seem to me that hiding timeouts would violate "The
2702
- pthread_cond_timedwait()
2703
- function is the same as pthread_cond_wait() except that an error is
2704
- returned if
2705
- the absolute time specified by abstime passes (that is, system time equals
2706
- or
2707
- exceeds abstime) before the condition cond is signaled or broadcasted"
2708
- because
2709
- the abs. time did really pass before cond was signaled (waiter was
2710
- released via semaphore). however, if it really matters, i could imaging
2711
- that we
2712
- can save an abs. time of signal/broadcast and compare it with timeout after
2713
- unblock to find out whether it was a 'real' timeout or not. absent this
2714
- check
2715
- i do think that hiding timeouts would result in technical violation of
2716
- specification.. but i think that this check is not important and we can
2717
- simply
2718
- trust timeout error code provided by wait since we are not trying to make
2719
- 'hard' realtime implementation.
2720
-
2721
- >What are IPC semaphores?
2722
-
2723
- <sys/sem.h>
2724
- int semctl(int, int, int, ...);
2725
- int semget(key_t, int, int);
2726
- int semop(int, struct sembuf *, size_t);
2727
-
2728
- they support adjustment of semaphore counter (semvalue)
2729
- in one single call - imaging Win32 ReleaseSemaphore( hsem,-N )
2730
-
2731
- >In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2732
- >// HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2733
- >because nWaitersGone is never modified without holding mtxUnblockLock. You
2734
- >are correct that there is a harmless race on nWaitersBlocked, which can
2735
- >increase and make the condition become true just after we check it. If
2736
- this
2737
- >happens, we interpret it as the wait starting after the signal.
2738
-
2739
- well, the reason why i've asked on comp.programming.threads whether this
2740
- race
2741
- condition is harmless or not is that in order to be harmless it should not
2742
- violate the waiters visibility rule (see above). Fortunately, we increment
2743
- the counter under protection of external mutex.. so that any (signalling)
2744
- thread which will acquire the mutex next, should see the updated counter
2745
- (in signal) according to POSIX memory visibility rules and mutexes
2746
- (memory barriers). But i am not so sure how it actually works on
2747
- Win32/INTEL
2748
- which does not explicitly define any memory visibility rules :(
2749
-
2750
- >I like your optimization of this. You could improve Alg. 6 as follows:
2751
- >---------- Algorithm 6b ----------
2752
- >signal(bAll) {
2753
- > _nSig=0
2754
- > lock counters
2755
- > // this is safe because nWaiting can only be decremented by a thread
2756
- that
2757
- > // owns counters and nGone can only be changed by a thread that owns
2758
- >counters.
2759
- > if (nWaiting>nGone) {
2760
- > if (0==nSignaled) {
2761
- > sema_wait gate // close gate if not already closed
2762
- > }
2763
- > if (nGone>0) {
2764
- > nWaiting-=nGone
2765
- > nGone=0
2766
- > }
2767
- > _nSig=bAll?nWaiting:1
2768
- > nSignaled+=_nSig
2769
- > nWaiting-=_nSig
2770
- > }
2771
- > unlock counters
2772
- > if (0!=_nSig) {
2773
- > sema_post queue, _nSig
2774
- > }
2775
- >}
2776
- >---------- ---------- ----------
2777
- >I guess this wouldn't apply to Alg 8a because nWaitersGone changes
2778
- meanings
2779
- >depending upon whether the gate is open or closed.
2780
-
2781
- agree.
2782
-
2783
- >In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
2784
- >semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
2785
-
2786
- you are correct. my mistake.
2787
-
2788
- >What have you gained by making the last thread to be signaled do the waits
2789
- >for all the timed out threads, besides added complexity? It took me a long
2790
- >time to figure out what your objective was with this, to realize you were
2791
- >using nWaitersGone to mean two different things, and to verify that you
2792
- >hadn't introduced any bug by doing this. Even now I'm not 100% sure.
2793
- >
2794
- >What has all this playing about with nWaitersGone really gained us besides
2795
- a
2796
- >lot of complexity (it is much harder to verify that this solution is
2797
- >correct), execution overhead (we now have a lot more if statements to
2798
- >evaluate), and space overhead (more space for the extra code, and another
2799
- >integer in our data)? We did manage to save a lock/unlock pair in an
2800
- >uncommon case (when a time out occurs) at the above mentioned expenses in
2801
- >the common cases.
2802
-
2803
- well, please consider the following:
2804
-
2805
- 1) with multiple waiters unblocked (but some timed out) the trick with
2806
- counter
2807
- seem to ensure potentially higher level of concurrency by not delaying
2808
- most of unblocked waiters for semaphore cleanup - only the last one
2809
- will be delayed but all others would already contend/acquire/release
2810
- the external mutex - the critical section protected by mtxUnblockLock is
2811
- made smaller (increment + couple of IFs is faster than system/kernel call)
2812
- which i think is good in general. however, you are right, this is done
2813
- at expense of 'normal' waiters..
2814
-
2815
- 2) some semaphore APIs (e.g. POSIX IPC sems) do allow to adjust the
2816
- semaphore counter in one call => less system/kernel calls.. imagine:
2817
-
2818
- if ( 1 == nSignalsWasLeft ) {
2819
- if ( 0 != nWaitersWasGone ) {
2820
- ReleaseSemaphore( semBlockQueue,-nWaitersWasGone ); // better now
2821
- than spurious later
2822
- }
2823
- sem_post( semBlockLock ); // open the gate
2824
- }
2825
-
2826
- 3) even on win32 a single thread doing multiple cleanup calls (to wait)
2827
- will probably result in faster execution (because of processor caching)
2828
- than multiple threads each doing a single call to wait.
2829
-
2830
- >As for 8b, c, and d, they look ok though I haven't studied them
2831
- thoroughly.
2832
- >What would you use them for?
2833
-
2834
- 8b) for semaphores which do not allow to unblock multiple waiters
2835
- in a single call to post/release (e.g. POSIX realtime semaphores -
2836
- <semaphore.h>)
2837
-
2838
- 8c/8d) for WinCE prior to 3.0 (WinCE 3.0 does have semaphores)
2839
-
2840
- ok. so, which one is the 'final' algorithm(s) which we should use in
2841
- pthreads-win32??
2842
-
2843
- regards,
2844
- alexander.
2845
-
2846
- ----------------------------------------------------------------------------
2847
-
2848
- Louis Thomas <lthomas@arbitrade.com> on 02/27/2001 05:20:12 AM
2849
-
2850
- Please respond to Louis Thomas <lthomas@arbitrade.com>
2851
-
2852
- To: Alexander Terekhov/Germany/IBM@IBMDE
2853
- cc: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>, Nanbor Wang
2854
- <nanbor@cs.wustl.edu>
2855
- Subject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio
2856
- n questions
2857
-
2858
- Sorry all. Busy week.
2859
-
2860
- > this insures the fairness
2861
- > which POSIX does not (e.g. two subsequent broadcasts - the gate does
2862
- insure
2863
- > that first wave waiters will start the race for the mutex before waiters
2864
- > from the second wave - Linux pthreads process/unblock both waves
2865
- > concurrently...)
2866
-
2867
- I'm not sure how we are any more fair about this than Linux. We certainly
2868
- don't guarantee that the threads released by the first broadcast will get
2869
- the external mutex before the threads of the second wave. In fact, it is
2870
- possible that those threads will never get the external mutex if there is
2871
- enough contention for it.
2872
-
2873
- > e.g. i was thinking about implementation with a pool of
2874
- > N semaphores/counters [...]
2875
-
2876
- I considered that too. The problem is as you mentioned in a). You really
2877
- need to assign threads to semaphores once you know how you want to wake
2878
- them
2879
- up, not when they first begin waiting which is the only time you can assign
2880
- them.
2881
-
2882
- > well, i am not quite sure that i've fully understood your scenario,
2883
-
2884
- Hmm. Well, it think it's an important example, so I'll try again. First, we
2885
- have thread A which we KNOW is waiting on a condition. As soon as it
2886
- becomes
2887
- unblocked for any reason, we will know because it will set a flag. Since
2888
- the
2889
- flag is not set, we are 100% confident that thread A is waiting on the
2890
- condition. We have another thread, thread B, which has acquired the mutex
2891
- and is about to wait on the condition. Thus it is pretty clear that at any
2892
- point, either just A is waiting, or A and B are waiting. Now thread C comes
2893
- along. C is about to do a broadcast on the condition. A broadcast is
2894
- guaranteed to unblock all threads currently waiting on a condition, right?
2895
- Again, we said that either just A is waiting, or A and B are both waiting.
2896
- So, when C does its broadcast, depending upon whether B has started waiting
2897
- or not, thread C will unblock A or unblock A and B. Either way, C must
2898
- unblock A, right?
2899
-
2900
- Now, you said anything that happens is correct so long as a) "a signal is
2901
- not lost between unlocking the mutex and waiting on the condition" and b)
2902
- "a
2903
- thread must not steal a signal it sent", correct? Requirement b) is easy to
2904
- satisfy: in this scenario, thread C will never wait on the condition, so it
2905
- won't steal any signals. Requirement a) is not hard either. The only way
2906
- we
2907
- could fail to meet requirement a) in this scenario is if thread B was
2908
- started waiting but didn't wake up because a signal was lost. This will not
2909
- happen.
2910
-
2911
- Now, here is what happens. Assume thread C beats thread B. Thread C looks
2912
- to
2913
- see how many threads are waiting on the condition. Thread C sees just one
2914
- thread, thread A, waiting. It does a broadcast waking up just one thread
2915
- because just one thread is waiting. Next, before A can become unblocked,
2916
- thread B begins waiting. Now there are two threads waiting, but only one
2917
- will be unblocked. Suppose B wins. B will become unblocked. A will not
2918
- become unblocked, because C only unblocked one thread (sema_post cond, 1).
2919
- So at the end, B finishes and A remains blocked.
2920
-
2921
- We have met both of your requirements, so by your rules, this is an
2922
- acceptable outcome. However, I think that the spec says this is an
2923
- unacceptable outcome! We know for certain that A was waiting and that C did
2924
- a broadcast, but A did not become unblocked! Yet, the spec says that a
2925
- broadcast wakes up all waiting threads. This did not happen. Do you agree
2926
- that this shows your rules are not strict enough?
2927
-
2928
- > and what about N2? :) this one does allow almost everything.
2929
-
2930
- Don't get me started about rule #2. I'll NEVER advocate an algorithm that
2931
- uses rule 2 as an excuse to suck!
2932
-
2933
- > but it is done (decrement)under mutex protection - this is not a subject
2934
- > of a race condition.
2935
-
2936
- You are correct. My mistake.
2937
-
2938
- > i would remove "_bTimedOut=false".. after all, it was a real timeout..
2939
-
2940
- I disagree. A thread that can't successfully retract its waiter status
2941
- can't
2942
- really have timed out. If a thread can't return without executing extra
2943
- code
2944
- to deal with the fact that someone tried to unblock it, I think it is a
2945
- poor
2946
- idea to pretend we
2947
- didn't realize someone was trying to signal us. After all, a signal is more
2948
- important than a time out.
2949
-
2950
- > when nSignaled != 0, it is possible to update nWaiters (--) and do not
2951
- > touch nGone
2952
-
2953
- I realize this, but I was thinking that writing it the other ways saves
2954
- another if statement.
2955
-
2956
- > adjust only if nGone != 0 and save one cache memory write - probably much
2957
- slower than 'if'
2958
-
2959
- Hmm. You are probably right.
2960
-
2961
- > well, in a strange (e.g. timeout test) program you may (theoretically)
2962
- > have an overflow of nWaiters/nGone counters (with waiters repeatedly
2963
- timing
2964
- > out and no signals at all).
2965
-
2966
- Also true. Not only that, but you also have the possibility that one could
2967
- overflow the number of waiters as well! However, considering the limit you
2968
- have chosen for nWaitersGone, I suppose it is unlikely that anyone would be
2969
- able to get INT_MAX/2 threads waiting on a single condition. :)
2970
-
2971
- Analysis of 8a:
2972
-
2973
- It looks correct to me.
2974
-
2975
- What are IPC semaphores?
2976
-
2977
- In the line where you state, "else if ( nWaitersBlocked > nWaitersGone ) {
2978
- // HARMLESS RACE CONDITION!" there is no race condition for nWaitersGone
2979
- because nWaitersGone is never modified without holding mtxUnblockLock. You
2980
- are correct that there is a harmless race on nWaitersBlocked, which can
2981
- increase and make the condition become true just after we check it. If this
2982
- happens, we interpret it as the wait starting after the signal.
2983
-
2984
- I like your optimization of this. You could improve Alg. 6 as follows:
2985
- ---------- Algorithm 6b ----------
2986
- signal(bAll) {
2987
- _nSig=0
2988
- lock counters
2989
- // this is safe because nWaiting can only be decremented by a thread that
2990
- // owns counters and nGone can only be changed by a thread that owns
2991
- counters.
2992
- if (nWaiting>nGone) {
2993
- if (0==nSignaled) {
2994
- sema_wait gate // close gate if not already closed
2995
- }
2996
- if (nGone>0) {
2997
- nWaiting-=nGone
2998
- nGone=0
2999
- }
3000
- _nSig=bAll?nWaiting:1
3001
- nSignaled+=_nSig
3002
- nWaiting-=_nSig
3003
- }
3004
- unlock counters
3005
- if (0!=_nSig) {
3006
- sema_post queue, _nSig
3007
- }
3008
- }
3009
- ---------- ---------- ----------
3010
- I guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings
3011
- depending upon whether the gate is open or closed.
3012
-
3013
- In the loop "while ( nWaitersWasGone-- ) {" you do a sema_wait on
3014
- semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.
3015
-
3016
- What have you gained by making the last thread to be signaled do the waits
3017
- for all the timed out threads, besides added complexity? It took me a long
3018
- time to figure out what your objective was with this, to realize you were
3019
- using nWaitersGone to mean two different things, and to verify that you
3020
- hadn't introduced any bug by doing this. Even now I'm not 100% sure.
3021
-
3022
- What has all this playing about with nWaitersGone really gained us besides
3023
- a
3024
- lot of complexity (it is much harder to verify that this solution is
3025
- correct), execution overhead (we now have a lot more if statements to
3026
- evaluate), and space overhead (more space for the extra code, and another
3027
- integer in our data)? We did manage to save a lock/unlock pair in an
3028
- uncommon case (when a time out occurs) at the above mentioned expenses in
3029
- the common cases.
3030
-
3031
- As for 8b, c, and d, they look ok though I haven't studied them thoroughly.
3032
- What would you use them for?
3033
-
3034
- Later,
3035
- -Louis! :)
3036
-