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 main(): ng = 100 # N(tasks) to spawn gstarted = chan() # main <- g mainexit = chan() # main -> all g # a task that wants to live longer than main 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 for i in range(ng): go(leaktask) # make sure all tasks are started for i in range(ng): gstarted.recv() # now we can exit mainexit.close() sys.exit(0)
def _serve(n): # XXX net.socket.close does not interrupt sk.accept # XXX we workaround it with accept timeout and polling for ._down n._oslistener.settimeout(1E-3) # 1ms while 1: if ready(n._down): break try: osconn, _ = n._oslistener.accept() except net.timeout: continue except Exception as e: n._vnet_down(e) return # XXX wg.Add(1) def _(osconn): # XXX defer wg.Done() myaddr = addrstr4(*n._oslistener.getsockname()) peeraddr = addrstr4(*osconn.getpeername()) try: n._loaccept(osconn) except Exception as e: if errcause(e) is not ErrConnRefused: log.error("lonet %s: serve %s <- %s : %s" % (qq(n._network), myaddr, peeraddr, e)) go(_, osconn)
def accept(l): h = l._socket._host with errctx("accept %s %s" % (h.network(), l.addr())): while 1: _, _rx = select( l._down.recv, # 0 l._dialq.recv, # 1 ) if _ == 0: l._excDown() if _ == 1: req = _rx with h._sockmu: sk = h._allocFreeSocket() ack = chan() req._resp.send(Accept(sk.addr(), ack)) _, _rx = select( l._down.recv, # 0 ack.recv, # 1 ) if _ == 0: def purgesk(): err = ack.recv() if err is None: try: req._netsk.close() except: pass with h._sockmu: h._socketv[sk._port] = None go(purgesk) l._excDown() if _ == 1: err = _rx if err is not None: with h._sockmu: h._socketv[sk._port] = None continue c = conn(sk, req._from, req._netsk) with h._sockmu: sk.conn = c return c
def test_sema_wakeup_different_thread(): sema = sync.Sema() sema.acquire() l = [] done = chan() def _(): time.sleep(1*dt) l.append('a') sema.release() done.close() go(_) sema.acquire() l.append('b') done.recv() assert l == ['a', 'b']
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'