def test_rwmutex(): mu = sync.RWMutex() # Unlock without lock -> panic # RUnlock without lock -> panic with panics("sync: Unlock of unlocked RWMutex"): mu.Unlock() with panics("sync: RUnlock of unlocked RWMutex"): mu.RUnlock() # Lock vs Lock; was also tested in test_rwmutex_basic mu.Lock() l = [] done = chan() def _(): mu.Lock() l.append('b') mu.Unlock() done.close() go(_) time.sleep(1 * dt) l.append('a') mu.Unlock() done.recv() assert l == ['a', 'b']
def test_waitgroup(): wg = sync.WaitGroup() wg.add(2) ch = chan(3) def _(): wg.wait() ch.send('a') for i in range(3): go(_) wg.done() assert len(ch) == 0 time.sleep(0.1) assert len(ch) == 0 wg.done() for i in range(3): assert ch.recv() == 'a' wg.add(1) go(_) time.sleep(0.1) assert len(ch) == 0 wg.done() assert ch.recv() == 'a' with panics("sync: negative WaitGroup counter"): wg.done()
def _test_mutex(mu, lock, unlock): # verify that g2 mu.lock() blocks until g1 does mu.unlock() getattr(mu, lock)() l = [] done = chan() def _(): getattr(mu, lock)() l.append('b') getattr(mu, unlock)() done.close() go(_) time.sleep(1 * dt) l.append('a') getattr(mu, unlock)() done.recv() assert l == ['a', 'b'] # the same via with with mu: l = [] done = chan() def _(): with mu: l.append('d') done.close() go(_) time.sleep(1 * dt) l.append('c') done.recv() assert l == ['c', 'd']
def waitfor(cond): tstart = time.now() while 1: if cond(): return t = time.now() if (t - tstart) > 1*time.second: raise AssertionError("timeout waiting") time.sleep(1E-6) # NOTE sleep(0) consumes lot of CPU under gevent
def leaktask(): gstarted.send(1) mainexit.recv() # normally when main thread exits, the whole process is terminated. # however if go spawns a thread with daemon=0, we are left here to continue. # make sure it is not the case time.sleep(3) print("leaked goroutine: process did not terminate", file=sys.stderr) sys.stderr.flush() time.sleep(1) os._exit(1) # not sys.exit - that can be used only from main thread
def test_timer_stop_drain(): t = time.Timer(1 * dt) tx = time.Ticker(1 * dt) time.sleep(2 * dt) assert len(t.c) == 1 assert len(tx.c) == 1 assert t.stop() == False assert len(t.c) == 0 tx.stop() assert len(tx.c) == 0
def W(): # 1 writer mu.Lock() time.sleep(Nr2*dt) # give R2 readers more chance to call mu.RLock and run first locked.send('W') l.append('a') if not unlock_via_downgrade: locked.send('_WUnlock') mu.Unlock() else: locked.send('_WUnlockToRLock') mu.UnlockToRLock() time.sleep(Nr2*dt) locked.send('_WRUnlock') mu.RUnlock()
def test_notify_reinstall(): ch = chan(10, dtype=gos.Signal) def _(): signal.Stop(ch) defer(_) for i in range(N): signal.Stop(ch) signal.Notify(ch, syscall.SIGUSR1) time.sleep(0.1*time.second) assert len(ch) == 0 killme(syscall.SIGUSR1) time.sleep(0.1*time.second) assert len(ch) == 1
def main(): # build "all signals" list allsigv = [] for attr in dir(syscall): if attr.startswith("SIG") and "_" not in attr: sig = getattr(syscall, attr) if sig not in allsigv: # avoid e.g. SIGCHLD/SIGCLD dups allsigv.append(sig) allsigv.sort(key=lambda sig: sig.signo) allsigv.remove(syscall.SIGKILL) # SIGKILL/SIGSTOP cannot be caught allsigv.remove(syscall.SIGSTOP) allsigv.remove(syscall.SIGBUS) # AddressSanitizer crashes on SIGBUS/SIGFPE/SIGSEGV allsigv.remove(syscall.SIGFPE) allsigv.remove(syscall.SIGSEGV) # Notify() -> kill * -> should be notified ch = chan(10, dtype=gos.Signal) signal.Notify(ch) # all signals for sig in allsigv: emit("-> %d %s" % (sig.signo, sig)) killme(sig) xsig = ch.recv() emit("<- %d %s" % (xsig.signo, xsig)) if xsig != sig: raise AssertionError("got %s, expected %s" % (xsig, sig)) emit("ok (notify)") # Ignore() -> kill * -> should not be notified emit() signal.Ignore() # all signals assert len(ch) == 0 for sig in allsigv: emit("-> %d %s" % (sig.signo, sig)) killme(sig) assert len(ch) == 0 time.sleep(0.3) assert len(ch) == 0 emit("ok (ignore)") # Reset() -> kill * should be handled by OS by default emit() signal.Reset() # all signals emit("terminating ...") killme(syscall.SIGTERM) raise AssertionError("not terminated")
def _(): time.sleep(1 * dt) l.append('a') sema.release() done.close()
def test_rwmutex_lock_vs_rlock(unlock_via_downgrade): mu = sync.RWMutex() # Lock vs RLock l = [] # accessed as R R R ... R W R R R ... R Nr1 = 10 # Nreaders queued before W Nr2 = 15 # Nreaders queued after W mu.RLock() locked = chan(Nr1 + 1 * 3 + Nr2) # main <- R|W: mu locked rcont = chan() # main -> R: continue def R(): # readers mu.RLock() locked.send(('R', len(l))) rcont.recv() mu.RUnlock() for i in range(Nr1): go(R) # make sure all Nr1 readers entered mu.RLock for i in range(Nr1): assert locked.recv() == ('R', 0) # spawn W def W(): # 1 writer mu.Lock() time.sleep( Nr2 * dt) # give R2 readers more chance to call mu.RLock and run first locked.send('W') l.append('a') if not unlock_via_downgrade: locked.send('_WUnlock') mu.Unlock() else: locked.send('_WUnlockToRLock') mu.UnlockToRLock() time.sleep(Nr2 * dt) locked.send('_WRUnlock') mu.RUnlock() go(W) # spawn more readers to verify that Lock has priority over RLock time.sleep(1 * dt) # give W more chance to call mu.Lock first for i in range(Nr2): go(R) # release main rlock, make sure nor W nor more R are yet ready, and let all readers continue time.sleep((1 + 1) * dt) mu.RUnlock() time.sleep(1 * dt) for i in range(100): _, _rx = select( default, # 0 locked.recv, # 1 ) assert _ == 0 rcont.close() # W must get the lock first and all R2 readers only after it assert locked.recv() == 'W' if not unlock_via_downgrade: assert locked.recv() == '_WUnlock' else: assert locked.recv() == '_WUnlockToRLock' for i in range(Nr2): assert locked.recv() == ('R', 1) if unlock_via_downgrade: assert locked.recv() == '_WRUnlock'
def test_signal(): # Notify/Stop with wrong chan dtype -> panic _ = panics("pychan: channel type mismatch") with _: signal.Notify(chan(2), syscall.SIGUSR1) with _: signal.Stop (chan(2)) with _: signal.Notify(chan(2, dtype='C.int'), syscall.SIGUSR1) with _: signal.Stop (chan(2, dtype='C.int')) # Notify/Ignore/Reset with wrong signal type _ = raises(TypeError) with _: signal.Notify(chan(dtype=gos.Signal), None) with _: signal.Ignore(None) with _: signal.Reset(None) # subscribe ch1(USR1), ch12(USR1,USR2) and ch2(USR2) ch1 = chan(2, dtype=gos.Signal) ch12 = chan(2, dtype=gos.Signal) ch2 = chan(2, dtype=gos.Signal) signal.Notify(ch1, syscall.SIGUSR1) signal.Notify(ch12, syscall.SIGUSR1, syscall.SIGUSR2) signal.Notify(ch2, syscall.SIGUSR2) def _(): signal.Reset() defer(_) for i in range(N): # raise SIGUSR1 -> should be delivered to ch1 and ch12 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1 and len(ch12) == 1) sig1 = ch1.recv() sig12 = ch12.recv() assert sig1 == syscall.SIGUSR1 assert sig12 == syscall.SIGUSR1 # raise SIGUSR2 -> should be delivered to ch12 and ch2 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR2) waitfor(lambda: len(ch12) == 1 and len(ch2) == 1) sig12 = ch12.recv() sig2 = ch2.recv() assert sig12 == syscall.SIGUSR2 assert sig2 == syscall.SIGUSR2 # if SIGUSR2 will be eventually delivered to ch1 - it will break # in SIGUSR1 check on next iteration. # Stop(ch2) -> signals should not be delivered to ch2 anymore signal.Stop(ch2) for i in range(N): # USR1 -> ch1, ch12 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1 and len(ch12) == 1) sig1 = ch1.recv() sig12 = ch12.recv() assert sig1 == syscall.SIGUSR1 assert sig12 == syscall.SIGUSR1 # USR2 -> ch12, !ch2 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR2) waitfor(lambda: len(ch12) == 1) sig12 = ch12.recv() assert sig12 == syscall.SIGUSR2 # if SIGUSR2 will be eventually delivered to ch2 - it will break on # next iteration. # Ignore(USR1) -> ch1 should not be delivered to anymore, ch12 should be delivered only USR2 signal.Ignore(syscall.SIGUSR1) for i in range(N): # USR1 -> ø assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) time.sleep(1E-6) # USR2 -> ch12 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR2) waitfor(lambda: len(ch12) == 1) sig12 = ch12.recv() assert sig12 == syscall.SIGUSR2 # if SIGUSR1 or SIGUSR2 will be eventually delivered to ch1 or ch2 - it # will break on next iteration. # Notify after Ignore signal.Notify(ch1, syscall.SIGUSR1) for i in range(N): # USR1 -> ch1 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1) sig1 = ch1.recv() assert sig1 == syscall.SIGUSR1 # USR2 -> ch12 assert len(ch1) == 0 assert len(ch12) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR2) waitfor(lambda: len(ch12) == 1) sig12 = ch12.recv() assert sig12 == syscall.SIGUSR2
def test_stdlib_interop_KeyboardInterrupt(): # KeyboardInterrupt is raised by default with raises(KeyboardInterrupt): killme(syscall.SIGINT) time.sleep(1) ch1 = chan(2, dtype=gos.Signal) ch2 = chan(2, dtype=gos.Signal) def _(): signal.Reset(syscall.SIGINT) defer(_) # Notify disables raising KeyboardInterrupt by default on SIGINT signal.Notify(ch1, syscall.SIGINT) try: killme(syscall.SIGINT) waitfor(lambda: len(ch1) == 1) obj1 = ch1.recv() assert obj1 == syscall.SIGINT time.sleep(0.1) # just in case except KeyboardInterrupt: raise AssertionError("KeyboardInterrupt raised after signal.Notify +ch1") signal.Notify(ch2, syscall.SIGINT) try: killme(syscall.SIGINT) waitfor(lambda: len(ch1) == 1 and len(ch2) == 1) obj1 = ch1.recv() obj2 = ch2.recv() assert obj1 == syscall.SIGINT assert obj2 == syscall.SIGINT time.sleep(0.1) # just in case except KeyboardInterrupt: raise AssertionError("KeyboardInterrupt raised after signal.Notify +ch1 +ch2") # last Stop should reenable raising KeyboardInterrupt by default on SIGINT signal.Stop(ch1) try: killme(syscall.SIGINT) waitfor(lambda: len(ch2) == 1) obj2 = ch2.recv() assert obj2 == syscall.SIGINT time.sleep(0.1) # just in case assert len(ch1) == 0 except KeyboardInterrupt: raise AssertionError("KeyboardInterrupt raised after signal.Notify +ch1 +ch2 -ch1") signal.Stop(ch2) with raises(KeyboardInterrupt): killme(syscall.SIGINT) time.sleep(1) time.sleep(0.1) # just in case assert len(ch1) == 0 assert len(ch2) == 0 # Ignore disables raising KeyboardInterrupt by default on SIGINT signal.Ignore(syscall.SIGINT) try: killme(syscall.SIGINT) time.sleep(0.1) assert len(ch1) == 0 assert len(ch2) == 0 except KeyboardInterrupt: raise AssertionError("KeyboardInterrupt raised after signal.Ignore") # Reset restores original behaviour signal.Reset(syscall.SIGINT) with raises(KeyboardInterrupt): killme(syscall.SIGINT) time.sleep(1) time.sleep(0.1) # just in case assert len(ch1) == 0 assert len(ch2) == 0
def test_stdlib_interop(): import signal as pysig ch1 = chan(2, dtype=object) # NOTE not gos.Signal nor 'C.os::Signal' def _(signo, frame): ch1.send("USR1") pysig.signal(pysig.SIGUSR1, _) def _(): pysig.signal(pysig.SIGUSR1, pysig.SIG_IGN) defer(_) # verify that plain pysig delivery works for i in range(N): assert len(ch1) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1) obj1 = ch1.recv() assert obj1 == "USR1" # verify that combined pysig + golang.os.signal delivery works ch2 = chan(2, dtype=gos.Signal) signal.Notify(ch2, syscall.SIGUSR1) def _(): signal.Stop(ch2) defer(_) for i in range(N): assert len(ch1) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1 and len(ch2) == 1) obj1 = ch1.recv() sig2 = ch2.recv() assert obj1 == "USR1" assert sig2 == syscall.SIGUSR1 # Ignore stops delivery to both pysig and golang.os.signal signal.Ignore(syscall.SIGUSR1) for i in range(N): assert len(ch1) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) time.sleep(1E-6) time.sleep(0.1) # just in case assert len(ch1) == 0 assert len(ch2) == 0 # after Reset pysig delivery is restored even after Ignore signal.Reset(syscall.SIGUSR1) for i in range(N): assert len(ch1) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1) assert len(ch2) == 0 obj1 = ch1.recv() assert obj1 == "USR1" # Reset stops delivery to golang.os.signal and restores pysig delivery signal.Notify(ch2, syscall.SIGUSR1) signal.Reset(syscall.SIGUSR1) for i in range(N): assert len(ch1) == 0 assert len(ch2) == 0 killme(syscall.SIGUSR1) waitfor(lambda: len(ch1) == 1) assert len(ch2) == 0 obj1 = ch1.recv() assert obj1 == "USR1"
def tick(): # cpython 2.7 time.time uses max microsecond precision time.sleep(1 * time.microsecond)
def test_deadline(): t0 = time.now() d1 = t0 + 10 * dt d2 = t0 + 20 * dt d3 = t0 + 30 * dt ctx1, cancel1 = context.with_deadline(bg, d2) assert ctx1.done() != bg.done() assertCtx(ctx1, Z, deadline=d2) ctx11 = context.with_value(ctx1, kA, "b") assert ctx11.done() == ctx1.done() assert ctx11.value(kA) == "b" assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, Z, deadline=d2) ctx111, cancel111 = context.with_cancel(ctx11) assert ctx111.done() != ctx11.done assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, Z, deadline=d2) ctx1111, cancel1111 = context.with_deadline(ctx111, d3) # NOTE deadline > parent assert ctx1111.done() != ctx111.done() assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) # NOTE not d3 ctx12, cancel12 = context.with_deadline(ctx1, d1) assert ctx12.done() != ctx1.done() assertCtx(ctx1, {ctx11, ctx12}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) assertCtx(ctx12, Z, deadline=d1) ctxM, cancelM = context.merge(ctx1111, ctx12) assert ctxM.done() != ctx1111.done() assert ctxM.done() != ctx12.done() assert ctxM.value(kA) == "b" assertCtx(ctx1, {ctx11, ctx12}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, {ctxM}, deadline=d2) assertCtx(ctx12, {ctxM}, deadline=d1) assertCtx(ctxM, Z, deadline=d1) time.sleep(11 * dt) assertCtx(ctx1, {ctx11}, deadline=d2) assertCtx(ctx11, {ctx111}, deadline=d2) assertCtx(ctx111, {ctx1111}, deadline=d2) assertCtx(ctx1111, Z, deadline=d2) assertCtx(ctx12, Z, deadline=d1, err=D, done=Y) assertCtx(ctxM, Z, deadline=d1, err=D, done=Y) # explicit cancel first -> err=canceled instead of deadlineExceeded for i in range(2): cancel1() assertCtx(ctx1, Z, deadline=d2, err=C, done=Y) assertCtx(ctx11, Z, deadline=d2, err=C, done=Y) assertCtx(ctx111, Z, deadline=d2, err=C, done=Y) assertCtx(ctx1111, Z, deadline=d2, err=C, done=Y) assertCtx(ctx12, Z, deadline=d1, err=D, done=Y) assertCtx(ctxM, Z, deadline=d1, err=D, done=Y) # with_timeout ctx, cancel = context.with_timeout(bg, 10 * dt) assert ctx.done() != bg.done() d = ctx.deadline() assert abs(d - (time.now() + 10 * dt)) < 1 * dt assertCtx(ctx, Z, deadline=d) time.sleep(11 * dt) assertCtx(ctx, Z, deadline=d, err=D, done=Y)