Skip to content
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

Should it be necessary to clear all before unloading a pkg? #1160

Closed
cbm755 opened this issue Jun 29, 2022 · 15 comments · Fixed by #1174
Closed

Should it be necessary to clear all before unloading a pkg? #1160

cbm755 opened this issue Jun 29, 2022 · 15 comments · Fixed by #1174
Milestone

Comments

@cbm755
Copy link
Collaborator

cbm755 commented Jun 29, 2022

I'm playing with:

podman exec oc octave --eval "pkg load symbolic; sympref diagnose; syms x; pkg unload symbolic; pkg list"
....

Your kit looks good for running the Symbolic package.  Happy hacking!

Symbolic pkg v2.9.0+: Python communication link active, SymPy v1.5.1.
Package Name  | Version | Installation directory
--------------+---------+-----------------------
    symbolic  |  2.9.0+ | /usr/share/octave/packages/symbolic-2.9.0+
warning: onCleanup: error caught while executing cleanup function:
'python_ipc_popen2_reset' undefined near line 78, column 30
error: ignoring const execution_exception& while preparing to exit

I cannot reproduce locally. I can fix it with pkg load symbolic; sympref diagnose; syms x; clear all; pkg unload symbolic; That is adding clear all. Which makes sense. What is supposed to happen?


Observed on: gnuoctave/octave:7.1.0 container
Cannot reproduce: locally Fedora 35, Octave 6.4.0 from rpm.

@alexvong243f
Copy link
Collaborator

Do you mean docker exec oc... ?

@alexvong243f
Copy link
Collaborator

I can reproduce this error locally. In fact, the minimal reproducible example for me is octave --no-gui --eval "pkg load symbolic; sym('x'); pkg unload symbolic" with error message

Symbolic pkg v2.9.0+: Python communication link active, SymPy v1.10.1.
warning: onCleanup: error caught while executing cleanup function:
'python_ipc_popen2_reset' undefined near line 78, column 30

For reference, octave --no-gui --version outputs

GNU Octave, version 7.1.0
Copyright (C) 1993-2022 The Octave Project Developers.
This is free software; see the source code for copying conditions.
There is ABSOLUTELY NO WARRANTY; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.

Octave was configured for "x86_64-pc-linux-gnu".

Additional information about Octave is available at https://www.octave.org.

Please contribute if you find this software useful.
For more information, visit https://www.octave.org/get-involved.html

Read https://www.octave.org/bugs.html to learn how to submit bug reports.

@alexvong243f
Copy link
Collaborator

Could the underlying issue be python_ipc_popen2_reset being a private function?

Running find octsympy/ -name '*reset*' returns

octsympy/inst/private/python_ipc_popen2_reset.m

@alexvong243f
Copy link
Collaborator

Running grep -r onCleanup returns

inst/private/python_ipc_popen2.m:    cleanup = onCleanup (@() python_ipc_popen2_reset (fin, fout, pid));

So the reason that clear all gets rid of the problem is because it clears all cleanup functions.

@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 1, 2022

Makes sense. I guess I wonder what is correct here? Like if you have objects created from a package and then you unload that package, it seems to me you are asking for trouble...

@alexvong243f
Copy link
Collaborator

Is it possible to customize what pkg unload do?

My intuition is that pkg unload symbolic should call and unregister cleanup functions created by the symbolic package before actually unloading (i.e. removing the variable bindings of the symbolic package from the global environment / symbol table).

More generally, it appears to me that sensible implementation of (pkg load, pkg unload) should satisfy the following properties:

  1. loading twice is the same as loading once (idempotency)
  2. unloading after loading should be equivalent to doing nothing (left inverse)
  3. unloading if not loaded should be an error

But of course, we may not be able to achieve this if it is not possible to customize what pkg unload do...

@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 2, 2022

I don't recall there being a 'onUnload' hook, but I don't know. Take a look through the official package docs and then post a thread on discourse?

The current onCleanup hook is supposed to stop us from losing the two file descriptors (of Popen2).

@alexvong243f
Copy link
Collaborator

Both the documentation

     'unload'
          Remove named packages from the path.  After unloading a
          package it is no longer possible to use the functions provided
          by the package.  Trying to unload a package that other loaded
          packages still depend on will result in an error; no packages
          will be unloaded in this case.  A package can be forcibly
          removed with the '-nodeps' flag, but be aware that the
          functionality of dependent packages will likely be affected.
          As when loading packages, reloading dependencies after having
          unloaded them with the '-nodeps' flag may not restore all
          functionality of the dependent packages as the required
          loading order may be incorrect.

and the source code https://hg.savannah.gnu.org/hgweb/octave/file/04120d65778a/scripts/pkg/private/unload_packages.m don't indicate such functionality, I am pretty confident it doesn't exist.

If all the current onCleanup hook does is to free 2 fds, we can re-write the hook as an anonymous function without referencing any symbolic-specific functions (using some tricks). This arrangement won't satisfy the properties in my last comment, but maybe this is good enough for us (?)

@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 2, 2022

I see what you mean. I just looked at ...reset and it also tries to shutdown the pipe, has verbose settings etc. As you say "using some tricks" it should be possible to have a pure anonymous function here (but implies more maintenance burden I suspect?)

Seems to me this is a least somewhat an upstream issue?

For reference: https://octave.sourceforge.io/octave/function/onCleanup.html

How urgent do you think this is? I think onCleanup was added by @mtmiller during the time since the last release. If we release 3.0.0 "as-is" then anyone using Symbolic and explicitly unloading Symbolic will tend to get this error Warning.

I just noticed its a warning not an error so I think this is "paper cut" rather than bug... (?)

@cbm755 cbm755 added this to the 3.1.0 milestone Jul 2, 2022
@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 2, 2022

So "needs decision" for question: "can we defer worrying about this?" If people are anything like me, they never "unload" packages in real life. And for the record, the MWE that users could reasonably expect is:

pkg load symbolic; x=sym('x'); x=42; pkg unload symbolic; clear all

That is, without that x=42, you still have sym variables around: unloading the package is asking for trouble and you get to keep both pieces. (This is unrelated to this bug as its not sym objects that have the onCleanup but rather a persistent variable inside a private ipc function).

@cbm755 cbm755 modified the milestones: 3.1.0, 3.0.0 Jul 2, 2022
@alexvong243f
Copy link
Collaborator

I can't recall the last time I use pkg unload. Having extra packages loaded have not caused issues for me in the past, so I don't find a need to unload them.

Regarding "using some tricks", what I mean is that we can define conditional as an anonymous function and for other things such as sequential execution / unordered execution as well. The end result is that we can do almost everything w/o using any statement. For instance, the following example cleanup function runs correctly w/o any warning:

    % see https://stackoverflow.com/questions/35115039
    if__ = @(varargin) varargin{3 - varargin{1}}();
    if_ = @(pred, if_clause, else_clause) if__(pred, if_clause, else_clause);
    % this will be called when we `clear cleanup`: avoids dangling file handles
    cleanup = onCleanup (@() ...
                          if_(randi(2) == 1, ...
                              @() fprintf('pid = %d, fin = %d\n', pid, fin), ...
                              @() fprintf('pid = %d, fout = %d\n', pid, fout)));

I don't think this will cause more maintenance burden because we don't have to change it once it's written. But we need comments to explain what's going on.

@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 3, 2022

wow! Shall we release as-is and see what happens?

If you take your approach, maybe we can move (well "move back") all the flush and wait logic to python_ipc_popen2.m, and have the onCleanup be a simpler "emergency close":

cleanup = onCleanup (@() [fclose(fin) fclose(fout)]);

(EXCEPT that will be an error if they are already closed)

@cbm755
Copy link
Collaborator Author

cbm755 commented Jul 3, 2022

Oh, how about? is_valid_file_id(fin) && fclose(fin) using short-circuiting instead of if?

@alexvong243f
Copy link
Collaborator

Yes, simplifying the cleanup function sounds like a good idea to me!

@alexvong243f
Copy link
Collaborator

alexvong243f commented Jul 4, 2022

Oh, how about? is_valid_file_id(fin) && fclose(fin) using short-circuiting instead of if?

I think is_valid_file_id is an Octave-only function. Well, we use Octave-only function popen2 in this part of code anyway, so it doesn't matter...

alexvong243f pushed a commit to alexvong243f/octsympy that referenced this issue Jul 4, 2022
This warning

--8<---------------cut here---------------start------------->8---
warning: onCleanup: error caught while executing cleanup function:
'python_ipc_popen2_reset' undefined near line 78, column 30
  "'python_ipc_popen2_reset' undefined"
--8<---------------cut here---------------end--------------->8---

is caused by "pkg unload symbolic".

We fix it by closing file descriptors and waitpid directly in the
cleanup function instead of using private/python_ipc_popen2_reset.

Fixes gnu-octave#1160.

* inst/private/python_ipc_popen2.m: Close file descriptors and waitpid
directly in the cleanup function.
* inst/private/python_ipc_popen2_reset.m: Remove file.
alexvong243f pushed a commit to alexvong243f/octsympy that referenced this issue Jul 4, 2022
This warning

--8<---------------cut here---------------start------------->8---
warning: onCleanup: error caught while executing cleanup function:
'python_ipc_popen2_reset' undefined near line 78, column 30
  "'python_ipc_popen2_reset' undefined"
--8<---------------cut here---------------end--------------->8---

is caused by "pkg unload symbolic".

We fix it by closing file descriptors and waitpid directly in the
cleanup function instead of using private/python_ipc_popen2_reset.

Fixes gnu-octave#1160.

* inst/private/python_ipc_popen2.m: Close file descriptors and waitpid
directly in the cleanup function.
* inst/private/python_ipc_popen2_reset.m: Remove file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants