Skip to content

Conversation

@anuptalwalkar
Copy link
Contributor

access to the nodes map, duplicate code, and all the locking logic is getting difficult to work around. This refactor should help.

  1. container.go -> user APIs to deal with package dig (Provide, Resolve, Invoke, etc)

pros - container doesn't know about nodes map.

  1. package graph contains all the logic to insert object/constructors to the graph, read values from the graph and maintain locking throughout the usage.

pros- graph can manage its own lock

Copy link
Contributor

@HelloGrayson HelloGrayson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see where you're going with this, but can you help me understand why we think we need locking?

// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package graph
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this package internal/ - I'd like to keep the API surface as small as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 agreed. moving to internal.

)

// Graph contains all Graph for current graph
type Graph struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im still thinking through this PR, but one thing is for certain: we don't want this to be a public type that users can interact with. It should either be private in the same package, or at the very least an internal package.

We have known for a while that options are coming: things like named instances, or private non-shared instances. I'd like for us to start thinking and working through those APIs, because only then we will be able to tell if this refactor is a good or bad thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving to internal package to avoid exposing graph to public.
With options coming, this will save us lot of headache of dealing with locks, maps, and reflect :)

@anuptalwalkar
Copy link
Contributor Author

@breerly Locking is needed right now because we have APIs reading and iterating over the nodes map, Resolving of ProvideConstructor, and Invoke are iterating over the map and updating the map in the same call. nodes.value() call does the same recursively.

container.go Outdated
switch v.Type().Kind() {
case reflect.Ptr:
c.insertObjectToGraph(v, v.Type())
c.Graph.InsertObject(v, v.Type())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like we should strip type from the InsertObject, container should get it form the object itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. I'll update.

type Graph struct {
sync.RWMutex

nodes map[interface{}]graphNode
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why it is a map[interface{}]graphNode not a map[reflect.Type]graphNode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to change the map type in the same PR as it will get hard to read and change. Will follow up.

}
for i := 0; i < argc; i++ {
arg := ctype.In(i)
if arg.Kind() != reflect.Ptr && arg.Kind() != reflect.Interface {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, should the container care about it? Extending for slices and map will be hard then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are constructing values from the node, which container isn't aware of, so as of this PR graph is ideal place for building constructor arguments. I'll add/change it once slices and maps are introduced.

@coveralls
Copy link

coveralls commented May 2, 2017

Coverage Status

Changes Unknown when pulling 53fa0da on refactor into ** on master**.

n, ok := g.nodes[objType]
g.RUnlock()
if !ok {
return reflect.Zero(objType), fmt.Errorf("type %v is not registered", objType)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: just reflect.Value{}?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, why return values, not pointers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We set the pointer value of the provided object to the instance pointer in container. It makes sense since the function is defined to read value from the graph.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also,"reflect.Zero returns a Value representing the zero value for the specified type." seems more appropriate to return as a default than reflect.Value{}?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no point of having a reflect.Value of specific type if there is an error :)
My comment was about unnecessary copying of reflect.Value that is going all around the graph. After digging through the code it looks like you'll need to change the graphNode interface to avoid it. May be another time then.

}

// InsertConstructor adds the constructor to the Graph
func (g *Graph) InsertConstructor(constr interface{}) error {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: usually constructor is abbreviated with ctor, constr is to close to const :)


// When a new constructor is being inserted, detect any present cycles
func (g *Graph) detectCycles(n *funcNode) error {
l := []string{}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's not needed, recursiveDetectCycles will work with nil too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting..

@coveralls
Copy link

coveralls commented May 2, 2017

Coverage Status

Changes Unknown when pulling b218032 on refactor into ** on master**.

@anuptalwalkar anuptalwalkar merged commit 53306d6 into master May 2, 2017
@anuptalwalkar anuptalwalkar deleted the refactor branch May 2, 2017 20:31
hbdf pushed a commit to hbdf/dig that referenced this pull request Jul 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

5 participants