noderb 0.0.4 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/noderb_extension/libuv/AUTHORS +11 -1
- data/ext/noderb_extension/libuv/LICENSE +0 -8
- data/ext/noderb_extension/libuv/Makefile +5 -5
- data/ext/noderb_extension/libuv/README +11 -6
- data/ext/noderb_extension/libuv/common.gypi +158 -7
- data/ext/noderb_extension/libuv/config-unix.mk +34 -26
- data/ext/noderb_extension/libuv/gyp_uv +22 -4
- data/ext/noderb_extension/libuv/include/{eio.h → uv-private/eio.h} +2 -2
- data/ext/noderb_extension/libuv/include/{uv-unix.h → uv-private/uv-unix.h} +40 -0
- data/ext/noderb_extension/libuv/include/{uv-win.h → uv-private/uv-win.h} +96 -14
- data/ext/noderb_extension/libuv/include/uv.h +508 -83
- data/ext/noderb_extension/libuv/src/unix/cares.c +185 -0
- data/ext/noderb_extension/libuv/src/unix/core.c +828 -0
- data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_darwin.h +4 -0
- data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_linux.h +5 -15
- data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.c +23 -2
- data/ext/noderb_extension/libuv/src/unix/error.c +102 -0
- data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_linux.h +26 -15
- data/ext/noderb_extension/libuv/src/unix/fs.c +562 -0
- data/ext/noderb_extension/libuv/src/unix/internal.h +75 -0
- data/ext/noderb_extension/libuv/src/unix/pipe.c +282 -0
- data/ext/noderb_extension/libuv/src/unix/process.c +287 -0
- data/ext/noderb_extension/libuv/src/unix/stream.c +727 -0
- data/ext/noderb_extension/libuv/src/unix/tcp.c +226 -0
- data/ext/noderb_extension/libuv/src/unix/udp.c +524 -0
- data/ext/noderb_extension/libuv/src/{uv-eio.c → unix/uv-eio.c} +48 -27
- data/ext/noderb_extension/libuv/src/{uv-eio.h → unix/uv-eio.h} +1 -1
- data/ext/noderb_extension/libuv/src/uv-common.c +24 -14
- data/ext/noderb_extension/libuv/src/uv-common.h +12 -7
- data/ext/noderb_extension/libuv/src/win/async.c +14 -16
- data/ext/noderb_extension/libuv/src/win/cares.c +64 -79
- data/ext/noderb_extension/libuv/src/win/core.c +105 -53
- data/ext/noderb_extension/libuv/src/win/error.c +23 -13
- data/ext/noderb_extension/libuv/src/win/fs.c +807 -0
- data/ext/noderb_extension/libuv/src/win/getaddrinfo.c +61 -41
- data/ext/noderb_extension/libuv/src/win/handle.c +56 -30
- data/ext/noderb_extension/libuv/src/win/internal.h +134 -95
- data/ext/noderb_extension/libuv/src/win/loop-watcher.c +21 -18
- data/ext/noderb_extension/libuv/src/win/pipe.c +313 -158
- data/ext/noderb_extension/libuv/src/win/process.c +117 -131
- data/ext/noderb_extension/libuv/src/win/req.c +55 -31
- data/ext/noderb_extension/libuv/src/win/stdio.c +5 -5
- data/ext/noderb_extension/libuv/src/win/stream.c +19 -14
- data/ext/noderb_extension/libuv/src/win/tcp.c +278 -336
- data/ext/noderb_extension/libuv/src/win/threadpool.c +73 -0
- data/ext/noderb_extension/libuv/src/win/timer.c +44 -37
- data/ext/noderb_extension/libuv/src/win/udp.c +592 -0
- data/ext/noderb_extension/libuv/src/win/util.c +20 -6
- data/ext/noderb_extension/libuv/src/win/winapi.c +23 -6
- data/ext/noderb_extension/libuv/src/win/winapi.h +4206 -0
- data/ext/noderb_extension/libuv/src/win/winsock.c +270 -0
- data/ext/noderb_extension/libuv/src/win/winsock.h +134 -0
- data/ext/noderb_extension/libuv/test/benchmark-ares.c +10 -6
- data/ext/noderb_extension/libuv/test/benchmark-getaddrinfo.c +10 -7
- data/ext/noderb_extension/libuv/test/benchmark-list.h +21 -0
- data/ext/noderb_extension/libuv/test/benchmark-ping-pongs.c +11 -7
- data/ext/noderb_extension/libuv/test/benchmark-pound.c +147 -58
- data/ext/noderb_extension/libuv/test/benchmark-pump.c +29 -23
- data/ext/noderb_extension/libuv/test/benchmark-spawn.c +13 -10
- data/ext/noderb_extension/libuv/test/benchmark-udp-packet-storm.c +250 -0
- data/ext/noderb_extension/libuv/test/dns-server.c +11 -6
- data/ext/noderb_extension/libuv/test/echo-server.c +30 -22
- data/ext/noderb_extension/libuv/test/test-async.c +3 -3
- data/ext/noderb_extension/libuv/test/test-callback-stack.c +6 -6
- data/ext/noderb_extension/libuv/test/test-connection-fail.c +6 -5
- data/ext/noderb_extension/libuv/test/test-delayed-accept.c +13 -13
- data/ext/noderb_extension/libuv/test/test-fs.c +715 -0
- data/ext/noderb_extension/libuv/test/test-getaddrinfo.c +11 -8
- data/ext/noderb_extension/libuv/test/test-gethostbyname.c +8 -9
- data/ext/noderb_extension/libuv/test/test-getsockname.c +142 -16
- data/ext/noderb_extension/libuv/test/test-idle.c +4 -3
- data/ext/noderb_extension/libuv/test/test-list.h +29 -2
- data/ext/noderb_extension/libuv/test/test-loop-handles.c +9 -8
- data/ext/noderb_extension/libuv/test/test-ping-pong.c +9 -9
- data/ext/noderb_extension/libuv/test/test-pipe-bind-error.c +18 -14
- data/ext/noderb_extension/libuv/test/test-ref.c +17 -16
- data/ext/noderb_extension/libuv/test/test-shutdown-eof.c +5 -5
- data/ext/noderb_extension/libuv/test/test-spawn.c +17 -17
- data/ext/noderb_extension/libuv/test/test-tcp-bind-error.c +24 -18
- data/ext/noderb_extension/libuv/test/test-tcp-bind6-error.c +19 -14
- data/ext/noderb_extension/libuv/test/test-tcp-writealot.c +6 -5
- data/ext/noderb_extension/libuv/test/test-threadpool.c +59 -0
- data/ext/noderb_extension/libuv/test/test-timer-again.c +15 -12
- data/ext/noderb_extension/libuv/test/test-timer.c +8 -8
- data/ext/noderb_extension/libuv/test/test-udp-dgram-too-big.c +88 -0
- data/ext/noderb_extension/libuv/test/test-udp-ipv6.c +158 -0
- data/ext/noderb_extension/libuv/test/test-udp-send-and-recv.c +210 -0
- data/ext/noderb_extension/libuv/{all.gyp → uv.gyp} +75 -77
- data/ext/noderb_extension/libuv/vcbuild.bat +93 -0
- data/ext/noderb_extension/noderb.c +13 -0
- data/ext/noderb_extension/noderb_common.h +2 -1
- data/ext/noderb_extension/noderb_defer.c +54 -0
- data/ext/noderb_extension/noderb_defer.h +15 -0
- data/ext/noderb_extension/noderb_dns.c +1 -1
- data/ext/noderb_extension/noderb_fs.c +277 -0
- data/ext/noderb_extension/noderb_fs.h +27 -0
- data/ext/noderb_extension/noderb_process.c +5 -5
- data/ext/noderb_extension/noderb_tcp.c +10 -6
- data/ext/noderb_extension/noderb_timers.c +1 -1
- data/ext/noderb_extension/noderb_tools.c +42 -8
- data/ext/noderb_extension/noderb_tools.h +3 -1
- data/lib/noderb/defer.rb +25 -0
- data/lib/noderb/file.rb +119 -0
- data/lib/noderb/fs.rb +72 -0
- data/lib/noderb/version.rb +1 -1
- data/lib/noderb.rb +8 -0
- metadata +102 -266
- data/ext/noderb_extension/libuv/BSDmakefile +0 -2
- data/ext/noderb_extension/libuv/create-msvs-files.bat +0 -21
- data/ext/noderb_extension/libuv/deps/pthread-win32/ANNOUNCE +0 -482
- data/ext/noderb_extension/libuv/deps/pthread-win32/BUGS +0 -141
- data/ext/noderb_extension/libuv/deps/pthread-win32/Bmakefile +0 -268
- data/ext/noderb_extension/libuv/deps/pthread-win32/CONTRIBUTORS +0 -140
- data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING +0 -150
- data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING.LIB +0 -504
- data/ext/noderb_extension/libuv/deps/pthread-win32/ChangeLog +0 -5194
- data/ext/noderb_extension/libuv/deps/pthread-win32/FAQ +0 -451
- data/ext/noderb_extension/libuv/deps/pthread-win32/GNUmakefile +0 -593
- data/ext/noderb_extension/libuv/deps/pthread-win32/MAINTAINERS +0 -4
- data/ext/noderb_extension/libuv/deps/pthread-win32/Makefile +0 -516
- data/ext/noderb_extension/libuv/deps/pthread-win32/NEWS +0 -1245
- data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile +0 -24
- data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile.tests +0 -260
- data/ext/noderb_extension/libuv/deps/pthread-win32/PROGRESS +0 -4
- data/ext/noderb_extension/libuv/deps/pthread-win32/README +0 -601
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.Borland +0 -57
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.CV +0 -3036
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.NONPORTABLE +0 -783
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.Watcom +0 -62
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.WinCE +0 -6
- data/ext/noderb_extension/libuv/deps/pthread-win32/TODO +0 -7
- data/ext/noderb_extension/libuv/deps/pthread-win32/WinCE-PORT +0 -222
- data/ext/noderb_extension/libuv/deps/pthread-win32/attr.c +0 -53
- data/ext/noderb_extension/libuv/deps/pthread-win32/autostatic.c +0 -69
- data/ext/noderb_extension/libuv/deps/pthread-win32/barrier.c +0 -47
- data/ext/noderb_extension/libuv/deps/pthread-win32/build/all.gyp +0 -207
- data/ext/noderb_extension/libuv/deps/pthread-win32/builddmc.bat +0 -9
- data/ext/noderb_extension/libuv/deps/pthread-win32/cancel.c +0 -44
- data/ext/noderb_extension/libuv/deps/pthread-win32/cleanup.c +0 -148
- data/ext/noderb_extension/libuv/deps/pthread-win32/condvar.c +0 -50
- data/ext/noderb_extension/libuv/deps/pthread-win32/config.h +0 -153
- data/ext/noderb_extension/libuv/deps/pthread-win32/context.h +0 -74
- data/ext/noderb_extension/libuv/deps/pthread-win32/create.c +0 -308
- data/ext/noderb_extension/libuv/deps/pthread-win32/dll.c +0 -92
- data/ext/noderb_extension/libuv/deps/pthread-win32/errno.c +0 -94
- data/ext/noderb_extension/libuv/deps/pthread-win32/exit.c +0 -44
- data/ext/noderb_extension/libuv/deps/pthread-win32/fork.c +0 -39
- data/ext/noderb_extension/libuv/deps/pthread-win32/global.c +0 -107
- data/ext/noderb_extension/libuv/deps/pthread-win32/implement.h +0 -944
- data/ext/noderb_extension/libuv/deps/pthread-win32/misc.c +0 -50
- data/ext/noderb_extension/libuv/deps/pthread-win32/mutex.c +0 -62
- data/ext/noderb_extension/libuv/deps/pthread-win32/need_errno.h +0 -145
- data/ext/noderb_extension/libuv/deps/pthread-win32/nonportable.c +0 -47
- data/ext/noderb_extension/libuv/deps/pthread-win32/private.c +0 -54
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.c +0 -66
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsp +0 -142
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsw +0 -29
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.h +0 -1368
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_destroy.c +0 -79
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getdetachstate.c +0 -86
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getinheritsched.c +0 -51
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedparam.c +0 -52
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedpolicy.c +0 -61
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getscope.c +0 -54
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstackaddr.c +0 -97
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstacksize.c +0 -100
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_init.c +0 -117
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setdetachstate.c +0 -91
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setinheritsched.c +0 -57
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedparam.c +0 -63
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedpolicy.c +0 -55
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setscope.c +0 -62
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstackaddr.c +0 -97
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstacksize.c +0 -110
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_destroy.c +0 -103
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_init.c +0 -69
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_wait.c +0 -104
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_destroy.c +0 -83
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_getpshared.c +0 -95
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_init.c +0 -85
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_setpshared.c +0 -119
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cancel.c +0 -189
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_destroy.c +0 -253
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_init.c +0 -167
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_signal.c +0 -231
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_wait.c +0 -567
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_destroy.c +0 -86
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_getpshared.c +0 -97
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_init.c +0 -87
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_setpshared.c +0 -117
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_delay_np.c +0 -172
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_detach.c +0 -136
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_equal.c +0 -76
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_exit.c +0 -106
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getconcurrency.c +0 -45
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getschedparam.c +0 -75
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getspecific.c +0 -87
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getunique_np.c +0 -47
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getw32threadhandle_np.c +0 -65
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_join.c +0 -157
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_create.c +0 -108
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_delete.c +0 -125
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_kill.c +0 -105
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_consistent.c +0 -187
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_destroy.c +0 -148
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_init.c +0 -130
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_lock.c +0 -269
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_timedlock.c +0 -324
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_trylock.c +0 -154
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_unlock.c +0 -175
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_destroy.c +0 -83
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getkind_np.c +0 -44
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getpshared.c +0 -95
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getrobust.c +0 -113
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_gettype.c +0 -56
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_init.c +0 -86
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setkind_np.c +0 -44
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setpshared.c +0 -119
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setrobust.c +0 -119
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_settype.c +0 -143
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_num_processors_np.c +0 -56
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_once.c +0 -79
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_destroy.c +0 -143
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_init.c +0 -109
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_rdlock.c +0 -102
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedrdlock.c +0 -109
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedwrlock.c +0 -139
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_tryrdlock.c +0 -102
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_trywrlock.c +0 -122
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_unlock.c +0 -93
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_wrlock.c +0 -133
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_destroy.c +0 -84
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_getpshared.c +0 -97
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_init.c +0 -83
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_setpshared.c +0 -120
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_self.c +0 -141
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcancelstate.c +0 -125
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcanceltype.c +0 -126
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setconcurrency.c +0 -53
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setschedparam.c +0 -123
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setspecific.c +0 -167
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_destroy.c +0 -111
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_init.c +0 -123
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_lock.c +0 -80
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_trylock.c +0 -77
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_unlock.c +0 -71
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_testcancel.c +0 -103
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_timechange_handler_np.c +0 -108
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_win32_attach_detach_np.c +0 -258
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_MCS_lock.c +0 -278
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_callUserDestroyRoutines.c +0 -232
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_calloc.c +0 -56
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_cond_check_need_init.c +0 -78
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_getprocessors.c +0 -91
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_is_attr.c +0 -47
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_mutex_check_need_init.c +0 -92
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_new.c +0 -94
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processInitialize.c +0 -92
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processTerminate.c +0 -105
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_relmillisecs.c +0 -132
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_reuse.c +0 -151
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_cancelwrwait.c +0 -50
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_check_need_init.c +0 -77
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_semwait.c +0 -135
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_spinlock_check_need_init.c +0 -78
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadDestroy.c +0 -79
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadStart.c +0 -357
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_throw.c +0 -189
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_timespec.c +0 -83
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocCreate.c +0 -118
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocDestroy.c +0 -114
- data/ext/noderb_extension/libuv/deps/pthread-win32/rwlock.c +0 -51
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched.c +0 -53
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched.h +0 -183
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_max.c +0 -134
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_min.c +0 -135
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_getscheduler.c +0 -71
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_setscheduler.c +0 -83
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_yield.c +0 -71
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_close.c +0 -58
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_destroy.c +0 -144
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_getvalue.c +0 -110
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_init.c +0 -169
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_open.c +0 -58
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post.c +0 -128
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post_multiple.c +0 -142
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_timedwait.c +0 -238
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_trywait.c +0 -117
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_unlink.c +0 -58
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_wait.c +0 -187
- data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.c +0 -69
- data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.h +0 -169
- data/ext/noderb_extension/libuv/deps/pthread-win32/signal.c +0 -179
- data/ext/noderb_extension/libuv/deps/pthread-win32/spin.c +0 -46
- data/ext/noderb_extension/libuv/deps/pthread-win32/sync.c +0 -43
- data/ext/noderb_extension/libuv/deps/pthread-win32/tsd.c +0 -44
- data/ext/noderb_extension/libuv/deps/pthread-win32/version.rc +0 -388
- data/ext/noderb_extension/libuv/deps/pthread-win32/w32_CancelableWait.c +0 -161
- data/ext/noderb_extension/libuv/doc/iocp-links.html +0 -574
- data/ext/noderb_extension/libuv/src/uv-unix.c +0 -2421
- data/ext/noderb_extension/libuv/src/win/ntdll.h +0 -130
- /data/ext/noderb_extension/libuv/include/{ev.h → uv-private/ev.h} +0 -0
- /data/ext/noderb_extension/libuv/include/{ngx-queue.h → uv-private/ngx-queue.h} +0 -0
- /data/ext/noderb_extension/libuv/include/{tree.h → uv-private/tree.h} +0 -0
- /data/ext/noderb_extension/libuv/src/{uv-cygwin.c → unix/cygwin.c} +0 -0
- /data/ext/noderb_extension/libuv/src/{uv-darwin.c → unix/darwin.c} +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/Changes +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/LICENSE +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/Makefile.am +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/aclocal.m4 +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/autogen.sh +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config.h.in +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_cygwin.h +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_freebsd.h +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/config_sunos.h +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/configure.ac +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/demo.c +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/ecb.h +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.3 +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/eio.pod +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/libeio.m4 +0 -0
- /data/ext/noderb_extension/libuv/src/{eio → unix/eio}/xthread.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Changes +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/LICENSE +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Makefile.am +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/Makefile.in +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/README +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/aclocal.m4 +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/autogen.sh +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.guess +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.h.in +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config.sub +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_cygwin.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_darwin.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_freebsd.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/config_sunos.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/configure +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/configure.ac +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/depcomp +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev++.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.3 +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev.pod +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_epoll.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_kqueue.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_poll.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_port.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_select.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_vars.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_win32.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ev_wrap.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/event.c +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/event.h +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/install-sh +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/libev.m4 +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/ltmain.sh +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/missing +0 -0
- /data/ext/noderb_extension/libuv/src/{ev → unix/ev}/mkinstalldirs +0 -0
- /data/ext/noderb_extension/libuv/src/{uv-freebsd.c → unix/freebsd.c} +0 -0
- /data/ext/noderb_extension/libuv/src/{uv-linux.c → unix/linux.c} +0 -0
- /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
|
-
|