Giter Club home page Giter Club logo

Comments (11)

nikanar avatar nikanar commented on June 3, 2024 2

If that's right, then when running under PyInstaller, the children should look for the PPID of their parent process (the PyInterpreter) to see if that is 1 because aw-qt died. But then, I also have no idea if PPIDs work the same under Windows either.

from aw-qt.

nikanar avatar nikanar commented on June 3, 2024 1

Once aw-qt dies, the other aw-* become orphan processes, and their PPID becomes 1 (for the init process, rather than having the value of their parent's PID (which in our case is also their PGID). Since there is no formal tie between a child process and its parent), it is normal, if undesirable, that children processes stay alive after the parent crashes. Children that would rather die than stay as orphans typically poll their parents for aliveness, for instance checking whether their own PPID ever becomes 1 (and then shutting down).

This can be done in this way in Python.
# inspired from http://www.programcreek.com/python/example/4464/os.getppid
while True: # watcher loop, run() or heatbeat_loop()
    if os.getppid() == 1:
        sys.exit()

This kills the watchers when aw-qt is kill -9-ed.

Then I've been looking for the proper way to terminate the watchers, and while aw-qt.Module.stop looked nice, I couldn't access it, so I elected to just break out of the main loops. If there's a better thing to do, please let me know as I didn't find one.

PR will come in as soon as I figure a proper git way out of my local mess (I've been adding --user to all Makefiles here, and --upgrade --force-reinstall to aw-watcher-window or it wouldn't use newer code (maybe due to the make clean issue ?)).

Some other solution using prctl

Another solution involves prctl : prctl(PR_SET_PDEATHSIG, SIGTERM);, but that seems easier in C than in Python.

python-prctl is a thing, if someone wants to go that way.

And this track was yet another option but led nowhere

Otherwise, per section 10.6.4 in this document, supposedly all orphaned process groups receive a SIGHUP, and could take that hint to die graciously (but it doesn't seem to apply here, maybe because reasons ; this question is super relevant but gets complicated). That would take to install a SIGHUP handler. That would have simply entailed adding signal.signal(signal.SIGHUP, signal.SIGTERM) inside heartbeat_loop in aw-watcher-window/main.py.


Note that even if all I did works fine, aw-server still stays alive when aw-qt crashes, which I think is still an issue. (But that seemed like an entirely different beast, to be tamed another day.)

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

Yeah, I tried using a process group for this but I'm not that familiar with that part of POSIX.

Do you think you could look into that @johan-bjareholt?

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

It seems that kill -9 doesn't kill the whole process group. You'd have to do kill -9 -$PGID. (See here: https://stackoverflow.com/a/15139734/965332)

A solution to prevent double-running processes would be to run a process check upon start and see if any aw-* processes (that aw-qt wants to manage) are already running.

Also, from Wikipedia:

The distribution of signals to process groups forms the basis of job control employed by shell programs. The tty device driver incorporates a notion of a foreground process group, to which it sends signals generated by keyboard interrupts, notably SIGINT ("interrupt", Control+C), SIGTSTP ("terminal stop", Control+Z), and SIGQUIT ("quit", Control+).

SIGKILL is notably missing from that list.

from aw-qt.

johan-bjareholt avatar johan-bjareholt commented on June 3, 2024

A solution to prevent double-running processes would be to run a process check upon start and see if any aw-* processes (that aw-qt wants to manage) are already running.

Here's a proposal for this

ActivityWatch/aw-client#21

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

Closing this since it only happens in the case of kill -9, which we can't do anything about.

from aw-qt.

johan-bjareholt avatar johan-bjareholt commented on June 3, 2024

No, it does not only happen for kill -9, i only used kill when i tried to reproduce. Another way to reproduce on my machine is to simply press "open log folder" so it crashes normally, but in that case all modules continue running (aw-server, aw-watcher-afk and aw-watcher-window).

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

Oh, fair enough.

I think this depends on how Python crashes, if Python crashes with an internal error (possible that PyQt triggers this) instead of a normal Exception-caused crash then I'm not sure we can do much about it. If it crashes "normally" then I think the atexit should work (which I added here).

from aw-qt.

johan-bjareholt avatar johan-bjareholt commented on June 3, 2024

@nikanar Awesome! Much better solution than my suggestion.

Out of the 3 solutions you found the one you selected was clearly most clean.

Will also work nicely even if the watchers are not run with aw-qt.

Will check the PRs and merge :)

Thanks for the help!

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

Amazing work, learned a lot from reading those links.

But, as I mentioned in comments on the PRs, there might be an issue with the taken approach when run using the PyInstaller binaries. I'm confident we can find a way around that however.

from aw-qt.

ErikBjare avatar ErikBjare commented on June 3, 2024

Continued discussions should probably go here.

So, @nikanar did a nice fix with the PPID detection but I don't think that'll work with PyInstaller due to it using a bootloader process that in turn starts the application process.

Details here: https://pythonhosted.org/PyInstaller/advanced-topics.html#the-bootstrap-process-in-detail

The process tree would look something like this when run using the PyInstaller (iirc):

  • aw-qt (bootloader)
    • aw-qt
      • aw-server (bootloader)
        • aw-server
      • aw-watcher-afk (bootloader)
        • aw-watcher-afk
      • ...

As specified in the PyInstaller docs, the bootloader will exit when its child process exits.

There has been talk about moving the child processes into Python-managed processes or threads (this would get rid of all the secondary bootloader and is likely to save some memory), but this has not been investigated.

from aw-qt.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.