From bd4f66c8f1f6ad4a2f228a957f293bc157e13d9c Mon Sep 17 00:00:00 2001 From: Aaron Lehmann Date: Mon, 24 Jul 2017 09:52:31 -0700 Subject: [PATCH] cluster: Avoid recursive RLock GetTasks can call GetService and GetNode with the read lock held. These methods try to aquire the read side of the same lock. According to the sync package documentation, this is not safe: > If a goroutine holds a RWMutex for reading, it must not expect this or > any other goroutine to be able to also take the read lock until the > first read lock is released. In particular, this prohibits recursive > read locking. This is to ensure that the lock eventually becomes > available; a blocked Lock call excludes new readers from acquiring the > lock. Fix GetTasks to use the lower-level getService and getNode methods instead. Also, use lockedManagerAction to simplify GetTasks. Signed-off-by: Aaron Lehmann --- daemon/cluster/tasks.go | 79 +++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 43 deletions(-) diff --git a/daemon/cluster/tasks.go b/daemon/cluster/tasks.go index f0d6621dc5..26706a2fa5 100644 --- a/daemon/cluster/tasks.go +++ b/daemon/cluster/tasks.go @@ -11,57 +11,50 @@ import ( // GetTasks returns a list of tasks matching the filter options. func (c *Cluster) GetTasks(options apitypes.TaskListOptions) ([]types.Task, error) { - c.mu.RLock() - defer c.mu.RUnlock() + var r *swarmapi.ListTasksResponse - state := c.currentNodeState() - if !state.IsActiveManager() { - return nil, c.errNoManager(state) - } - - filterTransform := func(filter filters.Args) error { - if filter.Include("service") { - serviceFilters := filter.Get("service") - for _, serviceFilter := range serviceFilters { - service, err := c.GetService(serviceFilter, false) - if err != nil { - return err + if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { + filterTransform := func(filter filters.Args) error { + if filter.Include("service") { + serviceFilters := filter.Get("service") + for _, serviceFilter := range serviceFilters { + service, err := getService(ctx, state.controlClient, serviceFilter, false) + if err != nil { + return err + } + filter.Del("service", serviceFilter) + filter.Add("service", service.ID) } - filter.Del("service", serviceFilter) - filter.Add("service", service.ID) } - } - if filter.Include("node") { - nodeFilters := filter.Get("node") - for _, nodeFilter := range nodeFilters { - node, err := c.GetNode(nodeFilter) - if err != nil { - return err + if filter.Include("node") { + nodeFilters := filter.Get("node") + for _, nodeFilter := range nodeFilters { + node, err := getNode(ctx, state.controlClient, nodeFilter) + if err != nil { + return err + } + filter.Del("node", nodeFilter) + filter.Add("node", node.ID) } - filter.Del("node", nodeFilter) - filter.Add("node", node.ID) } + if !filter.Include("runtime") { + // default to only showing container tasks + filter.Add("runtime", "container") + filter.Add("runtime", "") + } + return nil } - if !filter.Include("runtime") { - // default to only showing container tasks - filter.Add("runtime", "container") - filter.Add("runtime", "") + + filters, err := newListTasksFilters(options.Filters, filterTransform) + if err != nil { + return err } - return nil - } - filters, err := newListTasksFilters(options.Filters, filterTransform) - if err != nil { - return nil, err - } - - ctx, cancel := c.getRequestContext() - defer cancel() - - r, err := state.controlClient.ListTasks( - ctx, - &swarmapi.ListTasksRequest{Filters: filters}) - if err != nil { + r, err = state.controlClient.ListTasks( + ctx, + &swarmapi.ListTasksRequest{Filters: filters}) + return err + }); err != nil { return nil, err }