I am unable to kill background tasks by the methods provided

What I’m trying to do:
Just learning about about background tasks to use in a project.

What I’ve tried and what’s not working:
I can start the background from the anvil.server module - the background task is remote and is started with the uplink and then access remote information using task.get_state()[‘processes’] . However if I try to kill the process (which is designed to run indefinitely using the returned task object I get an error message saying I have to do this in the server module (basically). However when I return the task object from the server module is not longer available.

I tried passing the task object back to the server module with something simple shown below.

I thought that the task ID might be a way back to the object from the server, but I see no documentation on accessing the task from the id. I really do not see any clear example of killing the task from server after started and the object return to the foreground code? I am sorry if I missed that information.

Thank you,
JM

Code Sample:

@anvil.server.callable
def stop_monitor(task):
  task.kill()

# this is a formatted code snippet.
# paste your code between ``` 

Clone link:
share a copy of your app

It seems that you’re running Uplink code as a background task? Uplink code doesn’t run on the Anvil Server. The in-Server “kill” signal can’t reach it.

However, you can code your Uplink program to disconnect and terminate itself, on command, i.e., on a specific, new anvil.server.call(). Write a function, in the Uplink program, to do that, and mark it as @anvil.server.callable.

I’m not sure what the guts of that function would look like, having never coded one myself. See Disconnecting. I’m not sure whether your Uplink program will stop running by itself after the disconnect.

I see … I put the calling code in the anvil.server module – and tried this for example. I realized quickly that task was not retained after I returned it back to foreground –

So in the foreground i have (this is just testing code to learn how this works) –

self.process = anvil.server.call('monitor_processes','start')

and trying self.process.kill() fails. This code calls the code below which lives in the anvil.server module.

def monitor_processes(process):
  task = None
  if process == "start":
    task = anvil.server.launch_background_task('chkpython')
    print("starting server task")
    return task
  elif process == "stop":
    print("trying to stop the process")
    task.kill()
    if task.get_termination_status() == "killed":
      print("Yes, the task was killed!")
      return "done"
  else:
    print("failed to find start or stop")

this code calls the code on remote server down the uplink… And that code is simply looking at python running processes to identify starts and stops of code of interest …
That code is below – please if it is not much work tell me if this is the wrong procedure to run the remote code…

@anvil.server.background_task
def chkpython():
    # add a list of process that should not run at the same time : next one python buildpost.py
    print (f"checking for an existing instance of this software ...")
    # show processes

    waitforprocessess = ["buildpost.py","truthmafia_wploginapi.py","startsingletranscript.py","masterproai_uplink_v1.py",
                         "startsinglefeedbuildpost","startbuildpost.py"]

   
    while True:
        try:
            results = None
            total_processes_found = []

            cit = subprocess.check_output(['ps', '-df'])
            kit = subprocess.check_call(['ps', '-df'])
            #print(str(cit))

            for process in waitforprocessess:

                print (f"the process {process}")
                # .?(python).*(masterproai_uplink_v1.py).*
                candidate = f"(.?(python).*({process}))"
                results = re.findall(candidate,str(cit))

                if results:
                    total_processes_found.append(results[0][2])

            if total_processes_found:
                print(f"Currently Running: {total_processes_found}, will wait till next time.")
                anvil.server.task_state['processes'] = total_processes_found
                #return True
            else:
                print(f"No running instances found ")
                #return False

            time.sleep(2)

        except Exception as e:
            print (f"exception trying to check for running process {str(e)}")
            return

When you say “remote server”, it isn’t clear to me which computer you mean. It’s often been used to mean either one of the following:

  1. The Anvil Server
  2. The computer running your Uplink program

…probably because both provide functions via @anvil.server.callable, which are called via anvil.server.call(). But for a specific case, to be debugged, it really helps to be a bit more precise. These locations are on two different computers, with different memory contents, and communicating only intermittently via Internet, when an anvil.server.call() from one to the other is executed.

It’s not clear to me which code is running where. But I suspect that kill only works within a single computer. That is, the program that invokes kill must be running on the same computer as the background task that it wants to stop.

I don’t use background tasks enough to be sure, though. Others here on the forum will almost certainly know.

remote code: is the ‘remote code’ connected via the uplink.

In my test situation the remote-method(on remote server connected by uplink) is decorated with @anvil.server.background_task.

So the Foreground Website Code calls the local anvil.server module code which is decorated with @anvil.server.callable.

The server module code calls the remote-code(connected via uplink) method which has the decorator @anvil.server.background_task

Workflow:
Foreground
self.process = anvil.server.call('monitor_processes','start')

middleware anvil.server module
@anvil.server.callable.
def monitor_processes(startorstop):

task = anvil.server.launch_background_task(‘chkpython’)
return task

Remote Code (living down the uplink pipe)
@anvil.server.background_task
def chkpython():
do a while loop and send data back to foreground (which works)

My primary question, please, is this: Is this the correct procedure and if so how does one Kill the remote background task?

Thank you, I am sorry I was not clear.

  • kill(): stops the task and makes its status "killed" rather than "failed". This can only be executed on the server side.

Emphasis mine. So it appears that .kill() can be applied to an Uplink-running background task.

Reference: Task object

Note that with the code

@anvil.server.callable
def monitor_processes(process):
  task = None
  if process == "start":
    task = anvil.server.launch_background_task('chkpython')
    print("starting server task")
    return task
  elif process == "stop":
    print("trying to stop the process")
    task.kill()
    if task.get_termination_status() == "killed":
      print("Yes, the task was killed!")
      return "done"
  else:
    print("failed to find start or stop")

if you call

    anvil.server.call('monitor_processes', 'stop')

then, in monitor_processes, its local variable task will be None at the time it tries to call task.kill(). Local variables are deleted at the end of the call, and even if they weren’t, any earlier value would be set to None at the function’s first line of code.

This is better off handled by a separate function:

@anvil.server.callable
def stop_monitor(task_id: str):
  task = anvil.server.get_background_task(task_id)
  task.kill()

which receives the identity of the intended task. Of course, in order for the Client (browser program) to have the task id in the first place, whatever created the background task would have to return it, e.g.,

@anvil.server.callable
def start_monitor() -> str:
  task = anvil.server.launch_background_task('chkpython')
  return task.get_id()

Uncertainty: I would expect .kill() might take a second or two. This may mean that .get_termination_status() might not be immediately valid; I’m not sure, I haven’t tried it.

1 Like

That is what I was looking for – I looked for documentation that showed how to access the background task with the ID – if it is there I missed it – I understand now… thank you Phil.

Look at the very top of Task object.