I had successfully got Python running as a server in z/OS started task. The next job I had was to be able to shut it down cleanly. This was much harder than I expected. The concepts apply to all servers – not just a Python Server.
My mission.
Now that I can run a Python server as a started task. How do I stop it? I want
- A thread waiting on the operating system to notify the task when a shutdown request or operator request arrives.
- or after 20 seconds of no activity (during prototyping)
Python has no capability to cancel a thread once it has started running. There is a thread.cancel() which will remove the work request from the “list of work to run” before it has been scheduled.
My first attempt failed.
- I created an operator task which goes to sleep, and is woken up when a request arrives
- and a timeout task. This sleeps for 20 seconds and returns.
My first attempt was to wait for either of these task to complete.
Case 1: The operator entered a command
- The operator task woke up, and signalled shutdown.
- The time out task carried on waiting till the end of the 20 seconds.
- The system then shutdown
Case 2: There was no shutdown command, the request timed out
- The time-out task woke up and signalled shutdown.
- The operator task carried on waiting. It never returned.
- I had to cancel it.
My second attempt was to fix the operator task
I changed the operator thread to pass a shutdown_request token. The task waits for either
- the operating system to signal a command was entered,
- or the “shutdown_request” was made.
The helped with case 2:
- The time out task woke up and signalled shutdown.
- The shutdown_request was posted (to the operator thread).
- The operator thread woke up, and ended.
- The server shut show cleanly – success!
My third attempt was to fix the time out task. This was slightly better.
I changed the time-out task to pass a shutdown_request token. I could not get the time-out task to wait on two events.
- When the operator task command is woken, it notifies the time_out task. through the shutdown_request token
- The time out task sleeps for 5 seconds then wakes up
- If the shutdown request has been made, then leave.
My third attempt can delay up to 5 seconds before shutting down, so the solution is better – but not perfect. Is there a better way? To make it shutdown faster you could decrease the internal sleep period. This means it wakes up more frequently and so uses more CPU while doing nothing constructive.
My fourth attempted worked, by using a timer
I used a timer event instead of waiting.
def callb(handle): print("CALLBACK",datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f'),flush=True) # tell the operator task to close down. zconsole.kill(handle) h = zconsole.init() t = threading.Timer(30,callb,[h]) print("SCHEDULE ",datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f'),flush=True) t.start() ... t.cancel() #to remove the request
Where
- 30 is the delay before starting, in seconds
- callb is the name of my function to be called when the timer “pops”
- [h] is the parameters passed in. You must specify a list of parameter [..]. Without it I got the message TypeError: callb() argument after * must be an iterable, not int.
The output was
SCHEDULE 2022-07-05 16:50:00.202267
CALLBACK 2022-07-05 16:50:30.208368
This worked!
This timer request can be cancelled by using the t.cancel() before it is scheduled. Once it has been scheduled, it only lasts for a few milliseconds.
Summary
if you want an application or server to be able to respond to different events you need.
- To be able to cancel a thread while it is executing (if available) – or be able to send it a “shutdown” request.
- It is better to schedule an event using a timer, than to have a thread waiting in a sleep. The scheduled event can be cancelled before it executes. A sleep cannot be cancelled.