func (s *ManifoldSuite) TestConcurrentLockers(c *gc.C) { worker, err := s.manifold.Start(s.getResource) c.Assert(err, jc.ErrorIsNil) c.Check(worker, gc.NotNil) defer kill(worker) var locker charmdir.Locker err = s.manifold.Output(worker, &locker) c.Check(err, jc.ErrorIsNil) nlockers := 10 ch := make(chan struct{}, nlockers) for i := 0; i < nlockers; i++ { go func() { locker.SetAvailable(true) locker.SetAvailable(false) ch <- struct{}{} }() } for i := 0; i < nlockers; i++ { select { case <-ch: case <-time.After(coretesting.LongWait): c.Fatal("timed out waiting to confirm locker worker exit") } } }
// updateCharmDir sets charm directory availability for sharing among // concurrent workers according to local operation state. func updateCharmDir(opState operation.State, locker charmdir.Locker) { var changing bool // Determine if the charm content is changing. if opState.Kind == operation.Install || opState.Kind == operation.Upgrade { changing = true } else if opState.Kind == operation.RunHook && opState.Hook != nil && opState.Hook.Kind == hooks.UpgradeCharm { changing = true } available := opState.Started && !opState.Stopped && !changing logger.Tracef("charmdir: available=%v opState: started=%v stopped=%v changing=%v", available, opState.Started, opState.Stopped, changing) locker.SetAvailable(available) }
func (s *ManifoldSuite) TestConcurrentLockersConsumers(c *gc.C) { worker, err := s.manifold.Start(s.getResource) c.Assert(err, jc.ErrorIsNil) c.Check(worker, gc.NotNil) defer kill(worker) var consumer charmdir.Consumer err = s.manifold.Output(worker, &consumer) c.Check(err, jc.ErrorIsNil) var locker charmdir.Locker err = s.manifold.Output(worker, &locker) c.Check(err, jc.ErrorIsNil) nworkers := 20 before := make(chan struct{}) after := make(chan struct{}, nworkers) for i := 0; i < nworkers; i++ { var f func() if i%2 == 0 { f = func() { <-before err := consumer.Run(func() error { after <- struct{}{} return nil }) if err == charmdir.ErrNotAvailable { after <- struct{}{} } } } else { f = func() { <-before if i%3 == 0 { locker.SetAvailable(false) locker.SetAvailable(true) } after <- struct{}{} } } go f() } locker.SetAvailable(true) close(before) for i := 0; i < nworkers; i++ { select { case <-after: case <-time.After(coretesting.ShortWait): c.Fatal("timed out waiting to confirm consumer worker exit") } } }
func (s *ManifoldSuite) TestStartSuccess(c *gc.C) { worker, err := s.manifold.Start(s.getResource) c.Assert(err, jc.ErrorIsNil) c.Check(worker, gc.NotNil) defer kill(worker) var consumer charmdir.Consumer err = s.manifold.Output(worker, &consumer) c.Check(err, jc.ErrorIsNil) // charmdir is not available, so function will not run err = consumer.Run(func() error { return fmt.Errorf("nope") }) c.Check(err, gc.ErrorMatches, "charmdir not available") var locker charmdir.Locker err = s.manifold.Output(worker, &locker) c.Check(err, jc.ErrorIsNil) locker.SetAvailable(true) // charmdir is available, function will run err = consumer.Run(func() error { return fmt.Errorf("yep") }) c.Check(err, gc.ErrorMatches, "yep") state := "consumers have availability" var mu sync.Mutex beforeUnavail := make(chan struct{}) afterUnavail := make(chan struct{}) go func() { <-beforeUnavail locker.SetAvailable(false) mu.Lock() state = "no availability for you" mu.Unlock() close(afterUnavail) }() // charmdir available, Consumer.Run without error, confirm shared state. err = consumer.Run(func() error { mu.Lock() c.Check(state, gc.Equals, "consumers have availability") mu.Unlock() return nil }) c.Check(err, jc.ErrorIsNil) // Locker wants to make charmdir unavailable during Consumer.Run, has to // wait, confirm shared state. err = consumer.Run(func() error { close(beforeUnavail) select { case <-afterUnavail: c.Fatalf("Locker failed to keep charmdir locked during a Consumer.Run") case <-time.After(coretesting.ShortWait): } mu.Lock() c.Check(state, gc.Equals, "consumers have availability") mu.Unlock() return nil }) c.Check(err, jc.ErrorIsNil) // charmdir should now be unavailable, confirm shared state. select { case <-afterUnavail: mu.Lock() c.Check(state, gc.Equals, "no availability for you") mu.Unlock() err = consumer.Run(func() error { return fmt.Errorf("nope") }) c.Check(err, gc.ErrorMatches, "charmdir not available") case <-time.After(coretesting.ShortWait): c.Fatal("timed out waiting for locker to revoke availability") } }