-
-
Notifications
You must be signed in to change notification settings - Fork 706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Split DList Node into DNode and PayNode. #2457
Conversation
| //Converts a DNode into a PayNode | ||
| private static inout(PayNode)* asPayNode(inout(DNode)* p) @trusted pure nothrow | ||
| { | ||
| return cast(typeof(return))p; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hackish but I guess we have to leave with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only issue with this approach, is safety. I marked it as trusted, but truth be told it really just isn't safe.
That said, currently everything is GC allocated, but if and when we move to allocators, the entire container will probably seize to be safe.
Might as well not make any eager false promises?
|
Overall - nice. Anybody else? |
|
I made an indentation change due to a private block. Suggest viewing with |
|
I'm having second thoughts about this: The issue is that it introduces an unsafe cast. If somebody where to call "front" (to assign) on an empty DList in a release build, then that's clobbering someone else's memory => unsafe. Furthermore, (currently), DList uses the GC, so it is possible to use a DList in a safe context. This pull would break this. Supposedly, if and when we migrate to an allocator DList, then all of DList will become unsafe anyways. But until then, it's a gratuitous breaking change (IMO). With all that said, I believe having a base non-template structure is a move in the right direction. But for the purposes of safety, I still think that the sentinel node should be allocated as a paynode, and with a payload. @DmitryOlshansky : What are your thoughts on this? Honestly (IMO), a little extra allocation for safety is acceptable. If you are creating a list, it implies you will want a fair amount of items. I don't think 1 extra item is the end of the world? The alternatives is either making the list Also, @andralex , I think your input would be relevant? |
| @@ -3,6 +3,83 @@ module std.container.dlist; | |||
| import std.exception, std.range, std.traits; | |||
| public import std.container.util; | |||
|
|
|||
| /+ | |||
| A DList Node without payload. Used to alocate the sentinel node (henceforth "sentinode"). | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ultra-nitpick: "allocate" instead of "alocate".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
I really like this PR. It reduces overhead for the sentinel node, but more importantly it cuts down template bloat, potentially by a lot if you have many As for calling |
|
@monarchdodra I share your reservations about braking The main gain is like @quickfur puts it in saving on template bloat. To that end there is a lot of unlocked potental - hoisting out functionality that takes |
|
I like the idea of As for |
|
Whoa, that was a totally random unrelated autotester failure. Some kind of race condition in |
|
Concept looks yummy. Figure out the auto-tester thing and it is LGTM |
|
I don't think the autotester failure has anything to do with this PR. It's probably an unrelated random breakage. But we shouldn't merge until we have a satisfactory solution for the |
What is the problem with replacing it with |
|
Good idea. Maybe we should just do that, then we can merge. |
|
You can even do |
|
OK, here is a status of the pull: I've reverted to having the sentinel allocated as a normal node with payload. This is the behavior we had before, so it's not any worst than we used to have it. Rather, let's just assume that this pull is now "only" about phasing out non-template code out of template code. Let's start with this step. Is that OK? My rationale for this is:
I guess, or just |
|
@monarchdodra Insertion/Deletion of nodes is exactly the same code for any kind of node. Neither of operation touches payload nor needs to know there is one. The only place where payload s needed is accessors like .front |
Yes, let's go with that. |
Well, yes and no. You still have to allocate the node, or potentially destroy it. That asside, it could work yeah. I had taken the approach to do it via a free function "connect", but doing it via a base struct could probably be cleaner. I'll give it a try. |
Easy to abstract away by taking a |
|
In other words - a one simple DListBase datastructure that speaks only in DNode and does all of List operations should be good enough basis for typed payload version. |
Yeah, that's what I had in mind. I'll do it in this pull. |
Wow, this is actually going incredibly well. I'm real happy about how it's going. One issue I'm running into though is the new code is non-template, which means it is pre-compiled, so all asserts in the base class are stripped away in the distributed build. Even if they are placed in contracts. I'm tempted to just say "fuck it" and purposefully add more of the issue in Phobos. It'll put more pressure on DMD to do something about the issue... |
|
ping |
...maybe. The changes of adding a |
|
Sounds good, thanks! |
|
|
||
| private this(BaseNode* first, BaseNode* last) | ||
| { | ||
| assert(!!first == !!last, "Dlist.Range.this: Invalid arguments"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for writing !!first instead of first !is null?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Legacy I guess :) . That said, it can be simplified to a single !, since we just want to compare binary states. Which do you prefer:
assert(!first == !last, "Dlist.Range.this: Invalid arguments");
or
assert(first is null == last is null, "Dlist.Range.this: Invalid arguments");There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer the latter, since it makes the intent clear. (Assuming, of course, that the compiler is smart enough that this produces identical machine code.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it then.
|
Overall, looks good. |
| @@ -820,3 +900,12 @@ unittest //13425 | |||
| r = list.linearRemove(r.take(1)); | |||
| assert(r.empty); // fails | |||
| } | |||
|
|
|||
| @safe unittest //check safety | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really sure what this tries to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? It's a safe unittest. It makes sure the code inside that unittest is useable in a safe context. It might be smarter to simply add the tag to existing unittests that actually do things though...
|
Fixed |
|
LGTM. |
|
LGTM, though I'll wait for Dmitry before merging :) |
Split DList Node into DNode and PayNode.
|
Thanks. I'm a bit busy, so I don't know how soon I can get to doing the "base DList" thing though. I'll do it eventually, just not in an immediate future. |
|
This pull request introduced a regression: |
|
This pull request introduced a regression: |
This splits the "Nodes" in DList into two types of node:
Doing this helps avoid allocating a T for no reason in the sentinel node. It should also remove some template bloat, as a few functions actually had no dependency on the type carried by the payload.
@DmitryOlshansky : What do you think?