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
Make it possible to create instances of Process::Status
.
#4563
base: master
Are you sure you want to change the base?
Conversation
1d9e25b
to
f701d43
Compare
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.
It seems to expose rather low-level details to Ruby.
Maybe it's enough to call rb_process_status_new()
from a C extension?
Although then the API should still make sense and avoid exposing too much implementation details.
BTW, I guess you need a way to set $?
. Is there already a way to do that?
> $? = nil
=> NameError ($? is a read-only variable)
process.c
Outdated
|
||
data->pid = NUM2PIDT(pid); | ||
data->status = NUM2INT(status); | ||
data->error = NUM2INT(error); |
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.
It should rb_obj_freeze(self);
here
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.
Even in the initialize
method? It makes sense to me for the _new
variant, but for _initialize
. Well,I don't have a strong opinion but this should probably just be treated like a struct?
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.
Yes, all Process::Status instances should be frozen, always.
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 don't agree with this. Instances returned by Process::Status.wait
are frozen. But no objects that I'm aware of are frozen after calling new
.
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 whole class (i.e., all current instances of that class that one can get) is immutable, there is no point to introduce a way to make it mutable, is it?
I guess the caller always knows |
Unfortunately I don't think it's enough information to implement front end interfaces. |
It turns out that the native I ended up I would say, the public C interface in Ruby is pretty hard to use correctly/seems patchy/messy. Could do with a good refactor. |
I think it's good enough and that seems the best abstraction for this, and avoids exposing any internal details of
Would need some specifics, but that's likely best discussed on its own. |
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.
There seems to be no need for this, Process::Status.wait
seems enough
If io_uring implements waitpid this will become necessary. |
It seems like EventMachine would like this interface. cc @sodabrew |
The previous MRI code used two hidden instance variables, so EventMachine was taking advantage of this to create an object of class Process::Status and manually set the pid and status:
Rubinius did nearly the same but with a visible variable, so the EventMachine code is:
The new Process::Status in Ruby 3 is using a C structure so @MSP-Greg attempted the approach to copying the structure definitions to create a Process::Status that is identical under the hood but separately defined in eventmachine/eventmachine#948. This bumps into a C++ compiler issue for older versions of g++, which surely with some workarounds we might solve, but is a continued effort to hack around MRI's lack of a clear interface for creating a Process::Status object that can respond to its usual methods. Given that Ruby 3.0 is already released, we may need to proceed with the identical struct approach but at the risk of breaking in weird ways if anybody changes |
Thinking again about this, I think it could be fine to add, with #4563 (comment). I'm still unsure why Process::Status even captures the errno, is anything using that? |
To clarify, for those who haven't looked at the EventMachine (EM) discussion, 'bumps into a C++ compiler issue for older versions of g++' happens with Ubuntu-18.04. CI in EM was only testing with 'latest' of Ubuntu, macOS, and Windows, and all passed. Someone pointed out that the code did not work with Ubuntu-18.04. Or, if a repo is using c++, testing against Ubuntu-18.04 is a very good idea. |
Yes it's used for propagating the error. |
If this is a big problem we can potentially backport it to 3.x - it's almost a bug if you have to copy the struct layout. @MSP-Greg one option to consider - do you call |
f701d43
to
7616d9a
Compare
7616d9a
to
5827368
Compare
Could you give more details? |
struct rb_process_status *data = RTYPEDDATA_DATA(instance); | ||
process_status_initialize(data, pid, status, error); | ||
|
||
rb_obj_freeze(instance); |
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.
This needs to move in rb_process_status_initialize
like @nobu did. No reason to keep the instance mutable once it has been initialize'd.
It's for Line 1465 in 5827368
Not sure what C extension would use that, but I guess best to keep it for compatibility. It's from #3998 |
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.
Looks good to me now, but all instances should be frozen once initialized.
I don't agree with this. I don't know why it should be frozen. Can you give me a reason why? |
It's part of the interface required by consumers of |
My POV is all classes ('s instances) which can be frozen should be frozen. |
I am fine if we follow a general model of immutability so that the user is not surprised that However, I am strongly against calling If we want to do this, it should be in Finally, I don't see any particular reason why |
For context, the addition of |
And #3671 is what made |
I tried this but it didn't work for me. The symbol was not found at runtime on MacOS. |
That's odd, because on 3.0 it's already there as a non-static function: https://github.com/ruby/ruby/blob/v3_0_0/process.c#L622-L634 |
MacOS differentiates external vs. non-external symbols, eventmachine/eventmachine#948 (comment) |
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.
Since it seems you don't want to keep Process::Status immutable (which is a regression and I can't agree with, nor nobu/ko1 AFAIK), how about having only rb_process_status_new()
and no Process::Status#initialize
, just like before?
Constructing a Process::Status
is fairly internal, I don't think there is a need to be able to do this in Ruby.
I have already written my thoughts about this problem, and have suggested a solution that avoids this by changing the fiber scheduler |
It's insufficient since errno is missing. This is why |
Then why not add it as a third array member to the return value: FWIW the errno is not exposed in the |
@ciconia at some point you need to choose an interface. Whether you return a tuple, or the actual object, seems relatively unimportant (except that it avoids extra allocations). While I appreciate your dedication to this topic, I'm not sure what we have to gain by discussing this particular detail of the interface since it's largely set in stone now.
Nope, and it never has been, but it's still a critical detail that needs to transit the scheduler boundary in order to be handled correctly by Please keep in mind that I did not build these interfaces, I did not design them, and I'm largely unable to change them. I look for the path of least resistance for fiber scheduler interface, and at the time, given the constraints I faced, that was the decision I made. Sometimes making a decision and rolling with it is more important than the actual specific design details, since they are all largely equivalent and or carry similar pros/cons which are themselves significantly outweighed by the benefit of having something that works. So while your criticism might be valid, I'm not sure how it helps. |
Regarding immutability, I've opened this discussion https://bugs.ruby-lang.org/issues/18035 - whatever decision we make, that we agree to apply consistently across all of stdlib, I will happily implement here. |
From what I can see the scheduler interface is still being actively changed, so why is that one OK but not the other?
I appreciate your point of view, but then again since fiber scheduling is still not "done" (as in, finalized API and functionality-I see you adding new stuff all the time-which is great!) I don't really see why we need to look at it as "set in stone". The present discussion (as well as others such as #4621) shows some of the design details are not "largely equivalent" and have consequences which might add complexity or require reworking significant amounts of code. I think the solution I proposed is not only valid but also much easier to implement (that is not "equivalent" as you maintain) even if it demands a backward-incompatible change to the fiber scheduler interface. I do wish there was some document describing the design of fiber scheduling in terms of API and of how it relates to the rest of the Ruby runtime, and which could be discussed and iterated on before committing to certain decisions. I'd really like to contribute from my own experience, but I feel like the design decisions are made by a single person - albeit a very capable one such as yourself - without any public discussion or public weighing of alternatives. Such a public design process (and it's not too late to start one!) might have helped avoid some of the effort we're seeing, now that we finally get into the details. |
Some parts are part of the public interface and other parts are experimental. The public interface is documented in
I don't think so. You will have to implement Feel free to review Lines 1352 to 1371 in 6072239
Almost all the discussions are happening on bugs.ruby-lang.org. https://bugs.ruby-lang.org/issues/17369 - please feel free to get involved in https://bugs.ruby-lang.org/issues/18020 and https://bugs.ruby-lang.org/issues/18035 |
I will add this clarification to
In this case, the blocking operation is |
You should add |
@nobu Thanks for the explanation! |
5827368
to
51f8541
Compare
In order to implement process wait in schedulers we need to be able to create
Process::Status
objects.