Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Allow GC implementations to be swappable. #1581

Merged
merged 13 commits into from
Jun 28, 2016
Merged

Conversation

Jebbs
Copy link
Contributor

@Jebbs Jebbs commented Jun 1, 2016

This wraps up GC implementations in a proxy so that they can easily be picked and initialized at runtime before the user program starts according to the config options passed or embedded.

This also adds a secondary "GC implementation" to swap to based on gc_stub. This implementation allows memory to be managed in a manual style using 'new' and 'delete' instead of relying on the GC.

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 1, 2016

@MartinNowak
@LightBender

import gc.stats;
import core.stdc.stdlib;

private
{
__gshared GC _gc;
__gshared conservative.GC _conservativeGC;
__gshared malloc.GC _mallocGC;
Copy link
Member

Choose a reason for hiding this comment

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

Those instances should be in the correspond gc implementation modules.

@rainers
Copy link
Member

rainers commented Jun 1, 2016

The proxy struct is emulating a virtual function table. Has it been discussed to make it an interface that each GC variant implements? This would remove all the boilerplate with function literals.

That's actually what I considered the way to go when Martin converted the GC class to a struct.

@leandro-lucarella-sociomantic
Copy link
Contributor

Even when I think being able to specify completely different implementations, for the case of manual vs. conservative, I wonder if it doesn't make more sense to just make it a feature, not a full implementation. Is there any practical difference between conservative and manual other than trying a collection before getting more memory when the heap is exhausted?

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 2, 2016

@leandro-lucarella-sociomantic
Well, two big differences would be that no memory is reserved and freed memory is returned to the host instead of sticking around. There is also no collection that happens when the program exits, so this is a completely manual implementation.

@leandro-lucarella-sociomantic
Copy link
Contributor

Do you really expect to someone for use that implementation or is it there just an example of a minimal GC implementation?

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 2, 2016

A bit of both, really. It probably won't be common, but it is fully usable and there are cases when using a gc would be more than necessary.

Do you have anything against it being accessible?

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 3, 2016

Did some updates based on comments. The function I added to config.d to parse string arguments will probably need to be changed though. It works, but I don't know what is expected for this situation.

@MartinNowak
Copy link
Member

Do you really expect to someone for use that implementation or is it there just an example of a minimal GC implementation?

That a replacement for gc_stub and one of it's usecases is to run valgrind (if you have enough memory).

{
if(!isspace(str[start]))
break;
}
Copy link
Member

Choose a reason for hiding this comment

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

The options are already space separated, so this will never do anything useful. There were also 2 helpers skip!isspace and find!isspace to avoid the loop.

Copy link
Member

@MartinNowak MartinNowak Jun 3, 2016

Choose a reason for hiding this comment

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

Let's just use the whole str as-is, no further parsing.

@MartinNowak
Copy link
Member

Mostly looks good now.

Has it been discussed to make it an interface that each GC variant implements?

I agree, that's a better idea. Let's do that as next step.


void initialize()
{
if(config.gc != "manual")
Copy link
Member

Choose a reason for hiding this comment

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

I don't agree with Martin here (but not very strongly). I think it's better to make the decision which GC implementation to initialize in gc_init, because it's more obvious that only one of them is being selected.

Copy link
Member

Choose a reason for hiding this comment

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

Nope! One goal of the implementation is that it's possible to link in new GCs, previously unknown to the runtime. Therefor it must be done like that.
We'll later switch to C constructors or run gc. module constructors earlier to make this work.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, not sure how this will work. Right now, gc_init needs to know and even import each implementation.

If you register implementations from a C constructor, you can just pass the name. That would be helpful to list available GCs, too.

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 3, 2016

Has it been discussed to make it an interface that each GC variant implements?

I agree, that's a better idea. Let's do that as next step.

What is the correct way to allocate a class in druntime without a gc?

@rainers
Copy link
Member

rainers commented Jun 3, 2016

What is the correct way to allocate a class in druntime without a gc?

Allocate memory and blit the initializer over it:

void*p = malloc(__traits(classInstanceSize,GC));
GC gc = cast(GC)memcpy(p, typeid(GC).init.ptr, typeid(GC).init.length);

@andralex
Copy link
Member

andralex commented Jun 4, 2016

Nice. Is my understanding correct that this replaces the old GC hot swappability introduced by @complexmath ? He already had an interface based on pointers to functions.

@andralex
Copy link
Member

andralex commented Jun 4, 2016

And yes it would be nice if we took advantage of vtables instead of manual tables of pointers to functions.

@MartinNowak
Copy link
Member

Classes and an Interface would be already much cleaner, you want to do it as part of this PR @Jebbs?

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 5, 2016

Yeah, I like that a lot.

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 13, 2016

Might need some more formatting, but this is ready for more reviewing.

Some key things:

  1. I had some issue trying to think of a good way to free the GC instance. This is currently being done in the Dtor function. Any advice here is greatly appreciated.
  2. The Range structure is bigger than it was previously so that the manual and conservative implementations use the same structure. I'm not sure if this should be changed or if it slows down the current GC, I haven't benchmarked yet.

Other than that, it is more or less at the same spot the last commit was except now it uses an interface instead of the Proxy structure.

@rainers
Copy link
Member

rainers commented Jun 14, 2016

This looks much cleaner.
Unfortunately, there is a disconnect for renamed files which makes it hard to review changes in gc.d. I guess these have just been removed and readded while they should have been git-moved.

struct Config
{
{ bool initialized;
Copy link
Member

Choose a reason for hiding this comment

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

is initialized used anywhere? BTW: bad old dmd-style formatting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Whoops! Nope, this isn't used anywhere. I had intended to test something with this, but forgot to double check the diff before I pushed and it wasn't deleted.

@rainers
Copy link
Member

rainers commented Jun 14, 2016

I had some issue trying to think of a good way to free the GC instance. This is currently being done in the Dtor function. Any advice here is greatly appreciated.

That might indeed cause trouble when compiling with invariants, as they are also run when exiting Dtor. Maybe just make Dtor static and call it on every implementation. This would be symmetric to initialize.

@PetarKirov
Copy link
Member

PetarKirov commented Jun 26, 2016

We can even get away without a deprecation cycle, if we add:

@property IGC GC() { return get_current_gc(); }

@MartinNowak
Copy link
Member

Just a bit of background. The function pointer stuff was imported from some work Walter did to support DLLs. For backwards compatibility reasons, it wasn't possible to use something like interfaces. Presumably this is no longer a requirement, but someone should double check.

Yes, vtbls and interfaces should just work when loaded dynamically, no problem here.

What do you all think about placing the GC interface in core.memory (and replacing the GC struct with static methods) and leaving only extern(C) wrappers in gc.proxy?

Let's keep gc stuff in gc.* until we know more. We still want to support runtime GC selection which requires to preinitialize all linked GCs, using the module constructors of the gc.* package would be one option for that.

alias pbot this; // only consider pbot for relative ordering (opCmp)
}

const uint GCVERSION = 1; // increment every time we change interface
Copy link
Member

Choose a reason for hiding this comment

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

No need for that, you can use __VERSION__ to get the dmd frontend version, and we're releasing druntime w/ that as well.
Also it's our duty to keep this interface somewhat backwards compatible.

Copy link
Member

Choose a reason for hiding this comment

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

Seems like that already existed before, but isn't used anywhere.

@MartinNowak
Copy link
Member

Except from #1581 (comment), LGTM.

Also removes some unnecessary lines dealing with GC versioning.
pthis.gc_addRoot = &gc_addRoot;
pthis.gc_addRange = &gc_addRange;
__gshared GC currentGC; //used for making the GC calls
__gshared GC initialGC; //used to reset currentGC if gc_clrProxy was called
Copy link
Member

@MartinNowak MartinNowak Jun 27, 2016

Choose a reason for hiding this comment

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

We shouldn't need initialGC. You are only allowed to use a single GC during a program's lifetime.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What about in the case of gc_clrProxy?

Copy link
Member

Choose a reason for hiding this comment

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

You're right it's needed for the whacky Windows DLL support, I renamed and added comments.
Jebbs@8f99b22

@Jebbs
Copy link
Contributor Author

Jebbs commented Jun 27, 2016

I updated those bits to use rt.util.container.array.Array, but I kept the removeRoot and removeRange functions mostly unchanged in how they function.

- also avoid {} initialization syntax, it doesn't allow to add a
  constructor later on, use `auto r = Range(p)` instead
- only need to keep the initial GC to iterate
  roots of the broken DLL implementation
@MartinNowak
Copy link
Member

MartinNowak commented Jun 27, 2016

I made a PR for your branch with a few things I found, once that is merged, it will show up here.
Jebbs#2

a bunch of smaller improvements
@LightBender
Copy link
Contributor

LGTM

@MartinNowak
Copy link
Member

Auto-merge toggled on

@MartinNowak MartinNowak merged commit f72c9f5 into dlang:master Jun 28, 2016
@MartinNowak
Copy link
Member

MartinNowak commented Jun 28, 2016

I'll take care of the support for unknown GCs that get linked into a program. It requires quite a bit of low-level linker understanding. Support for unknown GCs linked into an executable. on Backlog | Trello

@MartinNowak
Copy link
Member

This is still lacking a changelog entry and updated documentation on dlang.org @Jebbs, could you please add those.

@Jebbs
Copy link
Contributor Author

Jebbs commented Sep 20, 2016

Yep, I'll take care of that tomorrow.

@andralex
Copy link
Member

@Jebbs thx!

@Jebbs
Copy link
Contributor Author

Jebbs commented Sep 20, 2016

Since it wasn't in a change log before this, should I add it to changelog.dd for pending 2.072 changes?

@MartinNowak
Copy link
Member

Since it wasn't in a change log before this, should I add it to changelog.dd for pending 2.072 changes?

Yes @Jebbs, it hasn't yet been released and will be part of 2.072.

Jebbs added a commit to Jebbs/druntime that referenced this pull request Sep 23, 2016
MartinNowak pushed a commit to Jebbs/druntime that referenced this pull request Oct 9, 2016
MartinNowak pushed a commit to Jebbs/druntime that referenced this pull request Oct 9, 2016
MartinNowak added a commit that referenced this pull request Oct 9, 2016
Updated changelog to reflect #1581
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants