noderb 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README.md +25 -0
- data/ext/noderb_extension/extconf.rb +11 -0
- data/ext/noderb_extension/libuv/AUTHORS +11 -0
- data/ext/noderb_extension/libuv/LICENSE +48 -0
- data/ext/noderb_extension/libuv/Makefile +119 -0
- data/ext/noderb_extension/libuv/README +45 -0
- data/ext/noderb_extension/libuv/build/all.gyp +254 -0
- data/ext/noderb_extension/libuv/build/common.gypi +13 -0
- data/ext/noderb_extension/libuv/build/gyp_uv +43 -0
- data/ext/noderb_extension/libuv/config-mingw.mk +67 -0
- data/ext/noderb_extension/libuv/config-unix.mk +121 -0
- data/ext/noderb_extension/libuv/create-msvs-files.bat +14 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ANNOUNCE +482 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/BUGS +141 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/Bmakefile +268 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/CONTRIBUTORS +140 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING +150 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/COPYING.LIB +504 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ChangeLog +5194 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/FAQ +451 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/GNUmakefile +593 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/MAINTAINERS +4 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/Makefile +516 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/NEWS +1245 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile +24 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/Nmakefile.tests +260 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/PROGRESS +4 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README +601 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.Borland +57 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.CV +3036 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.NONPORTABLE +783 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.Watcom +62 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/README.WinCE +6 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/TODO +7 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/WinCE-PORT +222 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/attr.c +53 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/autostatic.c +69 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/barrier.c +47 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/build/all.gyp +207 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/builddmc.bat +9 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/cancel.c +44 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/cleanup.c +148 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/condvar.c +50 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/config.h +153 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/context.h +74 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/create.c +308 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/dll.c +92 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/errno.c +94 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/exit.c +44 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/fork.c +39 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/global.c +107 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/implement.h +944 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/misc.c +50 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/mutex.c +62 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/need_errno.h +145 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/nonportable.c +47 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/private.c +54 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.c +66 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsp +142 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.dsw +29 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread.h +1368 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_destroy.c +79 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getdetachstate.c +86 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getinheritsched.c +51 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedparam.c +52 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getschedpolicy.c +61 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getscope.c +54 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstackaddr.c +97 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_getstacksize.c +100 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_init.c +117 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setdetachstate.c +91 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setinheritsched.c +57 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedparam.c +63 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setschedpolicy.c +55 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setscope.c +62 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstackaddr.c +97 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_attr_setstacksize.c +110 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_destroy.c +103 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_init.c +69 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrier_wait.c +104 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_destroy.c +83 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_getpshared.c +95 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_init.c +85 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_barrierattr_setpshared.c +119 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cancel.c +189 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_destroy.c +253 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_init.c +167 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_signal.c +231 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_cond_wait.c +567 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_destroy.c +86 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_getpshared.c +97 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_init.c +87 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_condattr_setpshared.c +117 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_delay_np.c +172 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_detach.c +136 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_equal.c +76 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_exit.c +106 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getconcurrency.c +45 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getschedparam.c +75 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getspecific.c +87 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getunique_np.c +47 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_getw32threadhandle_np.c +65 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_join.c +157 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_create.c +108 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_key_delete.c +125 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_kill.c +105 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_consistent.c +187 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_destroy.c +148 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_init.c +130 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_lock.c +269 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_timedlock.c +324 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_trylock.c +154 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutex_unlock.c +175 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_destroy.c +83 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getkind_np.c +44 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getpshared.c +95 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_getrobust.c +113 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_gettype.c +56 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_init.c +86 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setkind_np.c +44 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setpshared.c +119 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_setrobust.c +119 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_mutexattr_settype.c +143 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_num_processors_np.c +56 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_once.c +79 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_destroy.c +143 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_init.c +109 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_rdlock.c +102 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedrdlock.c +109 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_timedwrlock.c +139 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_tryrdlock.c +102 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_trywrlock.c +122 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_unlock.c +93 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlock_wrlock.c +133 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_destroy.c +84 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_getpshared.c +97 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_init.c +83 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_rwlockattr_setpshared.c +120 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_self.c +141 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcancelstate.c +125 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setcanceltype.c +126 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setconcurrency.c +53 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setschedparam.c +123 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_setspecific.c +167 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_destroy.c +111 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_init.c +123 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_lock.c +80 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_trylock.c +77 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_spin_unlock.c +71 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_testcancel.c +103 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_timechange_handler_np.c +108 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/pthread_win32_attach_detach_np.c +258 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_MCS_lock.c +278 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_callUserDestroyRoutines.c +232 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_calloc.c +56 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_cond_check_need_init.c +78 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_getprocessors.c +91 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_is_attr.c +47 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_mutex_check_need_init.c +92 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_new.c +94 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processInitialize.c +92 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_processTerminate.c +105 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_relmillisecs.c +132 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_reuse.c +151 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_cancelwrwait.c +50 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_rwlock_check_need_init.c +77 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_semwait.c +135 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_spinlock_check_need_init.c +78 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadDestroy.c +79 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_threadStart.c +357 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_throw.c +189 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_timespec.c +83 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocCreate.c +118 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/ptw32_tkAssocDestroy.c +114 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/rwlock.c +51 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched.c +53 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched.h +183 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_max.c +134 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_get_priority_min.c +135 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_getscheduler.c +71 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_setscheduler.c +83 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sched_yield.c +71 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_close.c +58 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_destroy.c +144 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_getvalue.c +110 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_init.c +169 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_open.c +58 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post.c +128 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_post_multiple.c +142 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_timedwait.c +238 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_trywait.c +117 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_unlink.c +58 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sem_wait.c +187 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.c +69 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/semaphore.h +169 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/signal.c +179 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/spin.c +46 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/sync.c +43 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/tsd.c +44 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/version.rc +388 -0
- data/ext/noderb_extension/libuv/deps/pthread-win32/w32_CancelableWait.c +161 -0
- data/ext/noderb_extension/libuv/doc/desired-api.md +159 -0
- data/ext/noderb_extension/libuv/doc/iocp-links.html +574 -0
- data/ext/noderb_extension/libuv/include/ares.h +582 -0
- data/ext/noderb_extension/libuv/include/ares_version.h +24 -0
- data/ext/noderb_extension/libuv/include/eio.h +376 -0
- data/ext/noderb_extension/libuv/include/ev.h +835 -0
- data/ext/noderb_extension/libuv/include/ngx-queue.h +102 -0
- data/ext/noderb_extension/libuv/include/tree.h +762 -0
- data/ext/noderb_extension/libuv/include/uv-unix.h +138 -0
- data/ext/noderb_extension/libuv/include/uv-win.h +187 -0
- data/ext/noderb_extension/libuv/include/uv.h +635 -0
- data/ext/noderb_extension/libuv/src/ares/AUTHORS +37 -0
- data/ext/noderb_extension/libuv/src/ares/CHANGES +1198 -0
- data/ext/noderb_extension/libuv/src/ares/CMakeLists.txt +22 -0
- data/ext/noderb_extension/libuv/src/ares/NEWS +21 -0
- data/ext/noderb_extension/libuv/src/ares/README +60 -0
- data/ext/noderb_extension/libuv/src/ares/README.cares +13 -0
- data/ext/noderb_extension/libuv/src/ares/README.msvc +118 -0
- data/ext/noderb_extension/libuv/src/ares/README.node +21 -0
- data/ext/noderb_extension/libuv/src/ares/RELEASE-NOTES +25 -0
- data/ext/noderb_extension/libuv/src/ares/TODO +23 -0
- data/ext/noderb_extension/libuv/src/ares/ares__close_sockets.c +66 -0
- data/ext/noderb_extension/libuv/src/ares/ares__get_hostent.c +263 -0
- data/ext/noderb_extension/libuv/src/ares/ares__read_line.c +71 -0
- data/ext/noderb_extension/libuv/src/ares/ares__timeval.c +111 -0
- data/ext/noderb_extension/libuv/src/ares/ares_cancel.c +63 -0
- data/ext/noderb_extension/libuv/src/ares/ares_data.c +190 -0
- data/ext/noderb_extension/libuv/src/ares/ares_data.h +65 -0
- data/ext/noderb_extension/libuv/src/ares/ares_destroy.c +105 -0
- data/ext/noderb_extension/libuv/src/ares/ares_dns.h +90 -0
- data/ext/noderb_extension/libuv/src/ares/ares_expand_name.c +193 -0
- data/ext/noderb_extension/libuv/src/ares/ares_expand_string.c +75 -0
- data/ext/noderb_extension/libuv/src/ares/ares_fds.c +62 -0
- data/ext/noderb_extension/libuv/src/ares/ares_free_hostent.c +39 -0
- data/ext/noderb_extension/libuv/src/ares/ares_free_string.c +25 -0
- data/ext/noderb_extension/libuv/src/ares/ares_gethostbyaddr.c +292 -0
- data/ext/noderb_extension/libuv/src/ares/ares_gethostbyname.c +515 -0
- data/ext/noderb_extension/libuv/src/ares/ares_getnameinfo.c +426 -0
- data/ext/noderb_extension/libuv/src/ares/ares_getopt.c +122 -0
- data/ext/noderb_extension/libuv/src/ares/ares_getopt.h +53 -0
- data/ext/noderb_extension/libuv/src/ares/ares_getsock.c +72 -0
- data/ext/noderb_extension/libuv/src/ares/ares_init.c +1665 -0
- data/ext/noderb_extension/libuv/src/ares/ares_ipv6.h +78 -0
- data/ext/noderb_extension/libuv/src/ares/ares_library_init.c +132 -0
- data/ext/noderb_extension/libuv/src/ares/ares_library_init.h +39 -0
- data/ext/noderb_extension/libuv/src/ares/ares_llist.c +86 -0
- data/ext/noderb_extension/libuv/src/ares/ares_llist.h +42 -0
- data/ext/noderb_extension/libuv/src/ares/ares_mkquery.c +195 -0
- data/ext/noderb_extension/libuv/src/ares/ares_nowarn.c +59 -0
- data/ext/noderb_extension/libuv/src/ares/ares_nowarn.h +24 -0
- data/ext/noderb_extension/libuv/src/ares/ares_options.c +253 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_a_reply.c +260 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_aaaa_reply.c +256 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_mx_reply.c +170 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_ns_reply.c +182 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_ptr_reply.c +208 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_srv_reply.c +179 -0
- data/ext/noderb_extension/libuv/src/ares/ares_parse_txt_reply.c +201 -0
- data/ext/noderb_extension/libuv/src/ares/ares_private.h +351 -0
- data/ext/noderb_extension/libuv/src/ares/ares_process.c +1296 -0
- data/ext/noderb_extension/libuv/src/ares/ares_query.c +183 -0
- data/ext/noderb_extension/libuv/src/ares/ares_rules.h +144 -0
- data/ext/noderb_extension/libuv/src/ares/ares_search.c +322 -0
- data/ext/noderb_extension/libuv/src/ares/ares_send.c +134 -0
- data/ext/noderb_extension/libuv/src/ares/ares_setup.h +191 -0
- data/ext/noderb_extension/libuv/src/ares/ares_strcasecmp.c +66 -0
- data/ext/noderb_extension/libuv/src/ares/ares_strcasecmp.h +30 -0
- data/ext/noderb_extension/libuv/src/ares/ares_strdup.c +42 -0
- data/ext/noderb_extension/libuv/src/ares/ares_strdup.h +26 -0
- data/ext/noderb_extension/libuv/src/ares/ares_strerror.c +56 -0
- data/ext/noderb_extension/libuv/src/ares/ares_timeout.c +80 -0
- data/ext/noderb_extension/libuv/src/ares/ares_version.c +11 -0
- data/ext/noderb_extension/libuv/src/ares/ares_writev.c +79 -0
- data/ext/noderb_extension/libuv/src/ares/ares_writev.h +36 -0
- data/ext/noderb_extension/libuv/src/ares/bitncmp.c +59 -0
- data/ext/noderb_extension/libuv/src/ares/bitncmp.h +26 -0
- data/ext/noderb_extension/libuv/src/ares/config_cygwin/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_darwin/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_freebsd/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_linux/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_openbsd/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_sunos/ares_config.h +510 -0
- data/ext/noderb_extension/libuv/src/ares/config_win32/ares_config.h +369 -0
- data/ext/noderb_extension/libuv/src/ares/get_ver.awk +35 -0
- data/ext/noderb_extension/libuv/src/ares/inet_net_pton.c +450 -0
- data/ext/noderb_extension/libuv/src/ares/inet_net_pton.h +31 -0
- data/ext/noderb_extension/libuv/src/ares/inet_ntop.c +232 -0
- data/ext/noderb_extension/libuv/src/ares/inet_ntop.h +27 -0
- data/ext/noderb_extension/libuv/src/ares/nameser.h +193 -0
- data/ext/noderb_extension/libuv/src/ares/setup_once.h +488 -0
- data/ext/noderb_extension/libuv/src/ares/windows_port.c +22 -0
- data/ext/noderb_extension/libuv/src/eio/Changes +63 -0
- data/ext/noderb_extension/libuv/src/eio/LICENSE +36 -0
- data/ext/noderb_extension/libuv/src/eio/Makefile.am +15 -0
- data/ext/noderb_extension/libuv/src/eio/aclocal.m4 +8957 -0
- data/ext/noderb_extension/libuv/src/eio/autogen.sh +3 -0
- data/ext/noderb_extension/libuv/src/eio/config.h.in +86 -0
- data/ext/noderb_extension/libuv/src/eio/config_cygwin.h +77 -0
- data/ext/noderb_extension/libuv/src/eio/config_darwin.h +137 -0
- data/ext/noderb_extension/libuv/src/eio/config_freebsd.h +78 -0
- data/ext/noderb_extension/libuv/src/eio/config_linux.h +101 -0
- data/ext/noderb_extension/libuv/src/eio/config_sunos.h +81 -0
- data/ext/noderb_extension/libuv/src/eio/configure.ac +22 -0
- data/ext/noderb_extension/libuv/src/eio/demo.c +194 -0
- data/ext/noderb_extension/libuv/src/eio/ecb.h +370 -0
- data/ext/noderb_extension/libuv/src/eio/eio.3 +3428 -0
- data/ext/noderb_extension/libuv/src/eio/eio.c +2562 -0
- data/ext/noderb_extension/libuv/src/eio/eio.pod +969 -0
- data/ext/noderb_extension/libuv/src/eio/libeio.m4 +195 -0
- data/ext/noderb_extension/libuv/src/eio/xthread.h +164 -0
- data/ext/noderb_extension/libuv/src/ev/Changes +388 -0
- data/ext/noderb_extension/libuv/src/ev/LICENSE +36 -0
- data/ext/noderb_extension/libuv/src/ev/Makefile.am +18 -0
- data/ext/noderb_extension/libuv/src/ev/Makefile.in +771 -0
- data/ext/noderb_extension/libuv/src/ev/README +58 -0
- data/ext/noderb_extension/libuv/src/ev/aclocal.m4 +8957 -0
- data/ext/noderb_extension/libuv/src/ev/autogen.sh +6 -0
- data/ext/noderb_extension/libuv/src/ev/config.guess +1526 -0
- data/ext/noderb_extension/libuv/src/ev/config.h.in +125 -0
- data/ext/noderb_extension/libuv/src/ev/config.sub +1658 -0
- data/ext/noderb_extension/libuv/src/ev/config_cygwin.h +123 -0
- data/ext/noderb_extension/libuv/src/ev/config_darwin.h +122 -0
- data/ext/noderb_extension/libuv/src/ev/config_freebsd.h +120 -0
- data/ext/noderb_extension/libuv/src/ev/config_linux.h +130 -0
- data/ext/noderb_extension/libuv/src/ev/config_sunos.h +122 -0
- data/ext/noderb_extension/libuv/src/ev/configure +13037 -0
- data/ext/noderb_extension/libuv/src/ev/configure.ac +18 -0
- data/ext/noderb_extension/libuv/src/ev/depcomp +630 -0
- data/ext/noderb_extension/libuv/src/ev/ev++.h +816 -0
- data/ext/noderb_extension/libuv/src/ev/ev.3 +5311 -0
- data/ext/noderb_extension/libuv/src/ev/ev.c +3913 -0
- data/ext/noderb_extension/libuv/src/ev/ev.pod +5243 -0
- data/ext/noderb_extension/libuv/src/ev/ev_epoll.c +266 -0
- data/ext/noderb_extension/libuv/src/ev/ev_kqueue.c +198 -0
- data/ext/noderb_extension/libuv/src/ev/ev_poll.c +148 -0
- data/ext/noderb_extension/libuv/src/ev/ev_port.c +179 -0
- data/ext/noderb_extension/libuv/src/ev/ev_select.c +310 -0
- data/ext/noderb_extension/libuv/src/ev/ev_vars.h +203 -0
- data/ext/noderb_extension/libuv/src/ev/ev_win32.c +153 -0
- data/ext/noderb_extension/libuv/src/ev/ev_wrap.h +196 -0
- data/ext/noderb_extension/libuv/src/ev/event.c +402 -0
- data/ext/noderb_extension/libuv/src/ev/event.h +170 -0
- data/ext/noderb_extension/libuv/src/ev/install-sh +294 -0
- data/ext/noderb_extension/libuv/src/ev/libev.m4 +39 -0
- data/ext/noderb_extension/libuv/src/ev/ltmain.sh +8413 -0
- data/ext/noderb_extension/libuv/src/ev/missing +336 -0
- data/ext/noderb_extension/libuv/src/ev/mkinstalldirs +111 -0
- data/ext/noderb_extension/libuv/src/uv-common.c +172 -0
- data/ext/noderb_extension/libuv/src/uv-common.h +53 -0
- data/ext/noderb_extension/libuv/src/uv-cygwin.c +52 -0
- data/ext/noderb_extension/libuv/src/uv-darwin.c +64 -0
- data/ext/noderb_extension/libuv/src/uv-eio.c +113 -0
- data/ext/noderb_extension/libuv/src/uv-eio.h +13 -0
- data/ext/noderb_extension/libuv/src/uv-freebsd.c +65 -0
- data/ext/noderb_extension/libuv/src/uv-linux.c +51 -0
- data/ext/noderb_extension/libuv/src/uv-sunos.c +60 -0
- data/ext/noderb_extension/libuv/src/uv-unix.c +2408 -0
- data/ext/noderb_extension/libuv/src/win/async.c +129 -0
- data/ext/noderb_extension/libuv/src/win/cares.c +304 -0
- data/ext/noderb_extension/libuv/src/win/core.c +155 -0
- data/ext/noderb_extension/libuv/src/win/error.c +140 -0
- data/ext/noderb_extension/libuv/src/win/getaddrinfo.c +341 -0
- data/ext/noderb_extension/libuv/src/win/handle.c +176 -0
- data/ext/noderb_extension/libuv/src/win/internal.h +237 -0
- data/ext/noderb_extension/libuv/src/win/loop-watcher.c +128 -0
- data/ext/noderb_extension/libuv/src/win/pipe.c +828 -0
- data/ext/noderb_extension/libuv/src/win/process.c +936 -0
- data/ext/noderb_extension/libuv/src/win/req.c +141 -0
- data/ext/noderb_extension/libuv/src/win/stdio.c +75 -0
- data/ext/noderb_extension/libuv/src/win/stream.c +149 -0
- data/ext/noderb_extension/libuv/src/win/tcp.c +895 -0
- data/ext/noderb_extension/libuv/src/win/timer.c +269 -0
- data/ext/noderb_extension/libuv/src/win/util.c +82 -0
- data/ext/noderb_extension/libuv/test/benchmark-ares.c +117 -0
- data/ext/noderb_extension/libuv/test/benchmark-getaddrinfo.c +90 -0
- data/ext/noderb_extension/libuv/test/benchmark-list.h +77 -0
- data/ext/noderb_extension/libuv/test/benchmark-ping-pongs.c +210 -0
- data/ext/noderb_extension/libuv/test/benchmark-pound.c +237 -0
- data/ext/noderb_extension/libuv/test/benchmark-pump.c +459 -0
- data/ext/noderb_extension/libuv/test/benchmark-sizes.c +39 -0
- data/ext/noderb_extension/libuv/test/benchmark-spawn.c +154 -0
- data/ext/noderb_extension/libuv/test/dns-server.c +323 -0
- data/ext/noderb_extension/libuv/test/echo-server.c +299 -0
- data/ext/noderb_extension/libuv/test/run-benchmarks.c +64 -0
- data/ext/noderb_extension/libuv/test/run-tests.c +82 -0
- data/ext/noderb_extension/libuv/test/runner-unix.c +335 -0
- data/ext/noderb_extension/libuv/test/runner-unix.h +36 -0
- data/ext/noderb_extension/libuv/test/runner-win.c +343 -0
- data/ext/noderb_extension/libuv/test/runner-win.h +42 -0
- data/ext/noderb_extension/libuv/test/runner.c +311 -0
- data/ext/noderb_extension/libuv/test/runner.h +155 -0
- data/ext/noderb_extension/libuv/test/task.h +111 -0
- data/ext/noderb_extension/libuv/test/test-async.c +218 -0
- data/ext/noderb_extension/libuv/test/test-callback-stack.c +205 -0
- data/ext/noderb_extension/libuv/test/test-connection-fail.c +149 -0
- data/ext/noderb_extension/libuv/test/test-delayed-accept.c +198 -0
- data/ext/noderb_extension/libuv/test/test-fail-always.c +29 -0
- data/ext/noderb_extension/libuv/test/test-get-currentexe.c +53 -0
- data/ext/noderb_extension/libuv/test/test-getaddrinfo.c +110 -0
- data/ext/noderb_extension/libuv/test/test-gethostbyname.c +192 -0
- data/ext/noderb_extension/libuv/test/test-getsockname.c +196 -0
- data/ext/noderb_extension/libuv/test/test-hrtime.c +51 -0
- data/ext/noderb_extension/libuv/test/test-idle.c +83 -0
- data/ext/noderb_extension/libuv/test/test-list.h +165 -0
- data/ext/noderb_extension/libuv/test/test-loop-handles.c +361 -0
- data/ext/noderb_extension/libuv/test/test-pass-always.c +28 -0
- data/ext/noderb_extension/libuv/test/test-ping-pong.c +256 -0
- data/ext/noderb_extension/libuv/test/test-pipe-bind-error.c +148 -0
- data/ext/noderb_extension/libuv/test/test-ref.c +91 -0
- data/ext/noderb_extension/libuv/test/test-shutdown-eof.c +183 -0
- data/ext/noderb_extension/libuv/test/test-spawn.c +345 -0
- data/ext/noderb_extension/libuv/test/test-tcp-bind-error.c +204 -0
- data/ext/noderb_extension/libuv/test/test-tcp-bind6-error.c +164 -0
- data/ext/noderb_extension/libuv/test/test-tcp-writealot.c +198 -0
- data/ext/noderb_extension/libuv/test/test-timer-again.c +141 -0
- data/ext/noderb_extension/libuv/test/test-timer.c +134 -0
- data/ext/noderb_extension/noderb.c +340 -0
- data/ext/noderb_extension/noderb.h +2 -0
- data/lib/noderb/connection.rb +21 -0
- data/lib/noderb/process.rb +17 -0
- data/lib/noderb.rb +25 -0
- metadata +470 -0
@@ -0,0 +1,3036 @@
|
|
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
|
+
|