-
Notifications
You must be signed in to change notification settings - Fork 232
-
Notifications
You must be signed in to change notification settings - Fork 232
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
Invalid literal for int() #124
Comments
This is probably the case. I don't worry about such issues as long as they aren't going to corrupt files, as the errors are very transitory: when the user enters the next character and your Script Filter is run again, the PID will have been written to the file and the error will be gone.
It doesn't solve the problem. The damn library raises a The solution is not to immediately try to read the file after starting the background process: updating = False
if not wf.cached_data_fresh('documents', max_age=60 * 60 * 60):
cmd = ['/usr/bin/python', wf.workflowfile('quip-update.py')]
run_in_background('update', cmd)
updating = True
if updating or is_running('update'):
wf.add_item('Getting new posts from Pinboard',
valid=False,
icon=ICON_INFO) |
I've pulled some of the code out of I wasn't able to provoke the I think I prefer the exception to be honest. The code might be "bad", but it strikes me as better than There's no way around setting your own flag (which is of course much faster than going to the filesystem) unless you give the background task a significant fraction of a second to get up and running before calling Unless you can think of a way? The only thing that occurs to me is some sort of short-term memory in |
Hmm, why not reverse it. Perhaps the Workflow process that launches the subprocess should write the childs PID to the file. That way we can ensure it's written before Alfred restarts the process. That would eliminate the race condition we are facing and make |
It can't: the "runner" process performs a double fork (i.e. it's a real daemon process). Thus the calling code in your process only has access to the PID of the parent process (which exits immediately), not the actual child process that runs your command. I've tried implementing a cache that holds onto recently-launched job names for half a second. In my tests, this causes I'm unsure of how sound this logic is, however. I think that should be okay: the purpose of the background API is so your calling script can exit and return control to Alfred, not to run parallel jobs while your calling script waits on the results. On the next run, the pidfile will be used. |
Hmm, that could be an OK workaround but it doesn't seem very elegant. Regarding the double fork, couldn't we just make the first fork write the childs PID before it exits? That way the Just spitballing here, but that sounds more foolproof than the cache IMO. |
There are two forks, so no. The PID of the persistent background process is never known within the calling process. The only possibility I see would be for |
Hmm, what about this: def _fork_and_exit_parent(errmsg, wait_pid=False, write_pid=False):
try:
pid = os.fork()
if pid > 0:
if write_pid:
write_pid_file(pid)
if wait_pid:
os.waitpid(pid)
os._exit(0)
except OSError as err:
_log().critical('%s: (%d) %s', errmsg, err.errno, err.strerror)
raise err
# Do first fork.
_fork_and_exit_parent('fork #1 failed', wait_pid=True)
# Decouple from parent environment.
os.chdir(wf().workflowdir)
os.setsid()
# Do second fork.
_fork_and_exit_parent('fork #2 failed', write_pid=True) This would work like so:
Correct me if I'm wrong (and I might well be, sorry for wasting your time if I've got the wrong end of the stick), but wouldn't this work? We form a chain of processes as part of the fork, and use them terminating as a signal that the file has been written. This means that |
I'm going to release a version where From my tests, it only adds ~0.015s (about 10%) to the run time of It will time out after 1s waiting for the PID file, but that shouldn't really ever happen. This seems the cleanest solution, though I'd still recommend setting a variable rather than calling I've also made the PID file write atomic, so no more |
Let me try it your way. It seems a cleaner solution. |
This means that `is_running()` can be called immediately and will return the right result. Also write PID file atomically to prevent `is_running()` raising a `ValueError` if PID file is empty.
Oh yes. That works very well indeed. Unsurprisingly, it's faster than waiting for the PID file to appear on disk, and much, much cleaner. Added in v1.29. Thanks very much for the help. That's a big improvement, imo. |
My pleasure, thank you for this great library 👍 |
Hmm. It looks like this change may have re-triggered #111 (on High Sierra at least). I hope not: I really like this feature. |
Hmm. I do remember reading some warnings about doing work after forking,
specifically that it is quite dangerous to do much else that quit unless
you are very very careful. Not sure why I didn't think of this before.
If you've got some cycles to burn, it could be good to try and replace this
double fork situation with multiprocessing. The advantage would be you can
get the PID a bit cleaner (multiprocessing functions can return values),
you don't need to handle forking and you can get rid of the argument cache
situation you have right now (you can just pass in arguments to a process
like any other function).
Just a thought, if this issue is down to forking and shared memory
shenanigans it would solve it.
|
How do you mean that? A double-fork is standard for daemons to do on startup, AFAIK.
I don't think The typical use case is to launch a single background process to update a local cache from a web API or an application (via AppleScript). The Script Filter can work at lightning speed from the cache data while the background process slowly fetches the new data and then updates the cache. |
Hmm, perhaps I am imagining things, I cannot find a reference to what I was
talking about. It is strange though, the only thing we really changed was
making the first fork write a file, and two waitpids.
Regarding multiprocessing, yeah I understand but the module contains
powerful primitives for starting a Python subprocess and running a
specified function with some arguments, handling serialising arguments etc.
The multiprocessing queue, map etc implementations are all built on these.
I think you can even pass something similar to 'threading.Event' between
processes, which could be useful.
Anyhoo, sounds like a horrible bug to track down.
…On 4 Dec 2017 23:11, "Dean Jackson" ***@***.***> wrote:
it is quite dangerous to do much else that quit unless you are very very
careful
How do you mean that? A double-fork is standard for daemons to do on
startup, AFAIK.
replace this double fork situation with multiprocessing
I don't think multiprocessing can help here. The primary goal isn't
parallelism, it's to allow the calling Script Filter process to exit as
quickly as possible, so Alfred can run it again.
The typical use case is to launch a single background process to update a
local cache from a web API or an application (via AppleScript). The Script
Filter can work at lightning speed from the cache data while the background
process slowly fetches the new data and then updates the cache.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#124 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AA-sh23h6GYyZT5yoa4LT8XhA3hqcvFoks5s9HwLgaJpZM4QxY5j>
.
|
Yeah, but the issue is down to some funky interaction between Python and macOS. In any case, I've only had one report of it misbehaving so far. If there are more (and there will be if there's a real issue), I'll back out the
I know |
Looks like I totally jumped the gun here. The bug in the workflow was (fingers crossed) entirely down to a really stupid logic error I made. But nowhere near as bad as the High Sierra root login one, so I'll take solace in that. |
…anishe#124 This means that `is_running()` can be called immediately and will return the right result. Also write PID file atomically to prevent `is_running()` raising a `ValueError` if PID file is empty.
Firstly, thank you for this library. It's seriously impressive and so useful!
I ran into an exception when working with the
is_running
function:The code is taken from the getting started guide and isn't doing anything crazy:
I haven't dug into it, but it seems to me like it's a race condition where the file is created by the new process but the pid has not been written to it yet.
Using something like the
pidfile
library solves a lot of these issue, could you vendor it inside this library?The text was updated successfully, but these errors were encountered: