// TestMutexSessionRelock ensures that acquiring the same lock with the same // session will not result in deadlock. func TestMutexSessionRelock(t *testing.T) { clus := NewClusterV3(t, &ClusterConfig{Size: 3}) defer clus.Terminate(t) cli := clus.RandClient() m := concurrency.NewMutex(cli, "test-mutex") if err := m.Lock(context.TODO()); err != nil { t.Fatal(err) } m2 := concurrency.NewMutex(cli, "test-mutex") if err := m2.Lock(context.TODO()); err != nil { t.Fatal(err) } }
func doSTM(ctx context.Context, client *v3.Client, requests <-chan stmApply) { defer wg.Done() var m *v3sync.Mutex if stmMutex { m = v3sync.NewMutex(client, "stmlock") } for applyf := range requests { st := time.Now() if m != nil { m.Lock(context.TODO()) } _, err := mkSTM(context.TODO(), client, applyf) if m != nil { m.Unlock(context.TODO()) } var errStr string if err != nil { errStr = err.Error() } results <- result{errStr: errStr, duration: time.Since(st), happened: time.Now()} bar.Increment() } }
func doSTM(client *v3.Client, requests <-chan stmApply, results chan<- report.Result) { defer wg.Done() var m *v3sync.Mutex if stmMutex { s, err := v3sync.NewSession(client) if err != nil { panic(err) } m = v3sync.NewMutex(s, "stmlock") } for applyf := range requests { st := time.Now() if m != nil { m.Lock(context.TODO()) } _, err := mkSTM(context.TODO(), client, applyf) if m != nil { m.Unlock(context.TODO()) } results <- report.Result{Err: err, Start: st, End: time.Now()} bar.Increment() } }
func testMutex(t *testing.T, waiters int, chooseClient func() *clientv3.Client) { // stream lock acquisitions lockedC := make(chan *concurrency.Mutex) for i := 0; i < waiters; i++ { go func() { m := concurrency.NewMutex(chooseClient(), "test-mutex") if err := m.Lock(context.TODO()); err != nil { t.Fatalf("could not wait on lock (%v)", err) } lockedC <- m }() } // unlock locked mutexes timerC := time.After(time.Duration(waiters) * time.Second) for i := 0; i < waiters; i++ { select { case <-timerC: t.Fatalf("timed out waiting for lock %d", i) case m := <-lockedC: // lock acquired with m select { case <-lockedC: t.Fatalf("lock %d followers did not wait", i) default: } if err := m.Unlock(); err != nil { t.Fatalf("could not release lock (%v)", err) } } } }
func runRacer(getClient getClientFunc, round int) { rcs := make([]roundClient, 15) ctx := context.Background() cnt := 0 for i := range rcs { rcs[i].c = getClient() var ( s *concurrency.Session err error ) for { s, err = concurrency.NewSession(rcs[i].c) if err == nil { break } } m := concurrency.NewMutex(s, "racers") rcs[i].acquire = func() error { return m.Lock(ctx) } rcs[i].validate = func() error { if cnt++; cnt != 1 { return fmt.Errorf("bad lock; count: %d", cnt) } return nil } rcs[i].release = func() error { if err := m.Unlock(ctx); err != nil { return err } cnt = 0 return nil } } doRounds(rcs, round) }
func lockUntilSignal(c *clientv3.Client, lockname string) error { s, err := concurrency.NewSession(c) if err != nil { return err } m := concurrency.NewMutex(s, lockname) ctx, cancel := context.WithCancel(context.TODO()) // unlock in case of ordinary shutdown donec := make(chan struct{}) sigc := make(chan os.Signal, 1) signal.Notify(sigc, os.Interrupt, os.Kill) go func() { <-sigc cancel() close(donec) }() s, serr := concurrency.NewSession(c) if serr != nil { return serr } if err := m.Lock(ctx); err != nil { return err } k, kerr := c.Get(ctx, m.Key()) if kerr != nil { return kerr } if len(k.Kvs) == 0 { return errors.New("lock lost on init") } display.Get(*k) select { case <-donec: return m.Unlock(context.TODO()) case <-s.Done(): } return errors.New("session expired") }
func (e *EtcdClient) LockPath(path string) (KVLocker, error) { e.lockPathsMU.Lock() if e.lockPaths[path] == nil { e.lockPaths[path] = &sync.Mutex{} } e.lockPathsMU.Unlock() log.Debugf("Locking path %s", path) // First we lock the local lock for this path e.lockPaths[path].Lock() e.sessionMU.RLock() mu := concurrency.NewMutex(e.session, path) e.sessionMU.RUnlock() // Then we lock the global lock err := mu.Lock(ctx.Background()) if err != nil { e.lockPaths[path].Unlock() return nil, fmt.Errorf("Error while locking path %s: %s", path, err) } log.Debugf("Locked path %s", path) return &EtcdLocker{mutex: mu, path: path, localLock: e.lockPaths[path]}, nil }
func runRacer(eps []string, round int) { rcs := make([]roundClient, 15) ctx := context.Background() cnt := 0 for i := range rcs { rcs[i].c = randClient(eps) m := concurrency.NewMutex(rcs[i].c, "racers") rcs[i].acquire = func() error { return m.Lock(ctx) } rcs[i].validate = func() error { if cnt++; cnt != 1 { return fmt.Errorf("bad lock; count: %d", cnt) } return nil } rcs[i].release = func() error { if err := m.Unlock(ctx); err != nil { return err } cnt = 0 return nil } } doRounds(rcs, round) }