Changing a variable in a worker
ayyess opened this issue · 4 comments
Is it possible to change a variable used by jobs in a worker? It seems that a possible way is to stop a worker and start it up again. The use case is a variable changes outside the worker and the worker needs to be able to read the new value. The code below shows that assignments don't stick between jobs
#!/bin/zsh
source ./async.zsh
async_init
set_val() {
val=$1
echo "set val: $val"
}
view_val() {
echo "view val: $val"
}
async_start_worker my_worker -n
async_register_callback my_worker completed_callback
# Create a callback function to process results
COMPLETED=0
completed_callback() {
COMPLETED=$(( COMPLETED + 1 ))
print $3
}
view_val
set_val 100
view_val
echo "======================"
async_job my_worker view_val
async_job my_worker set_val 200
async_job my_worker view_val
while (( COMPLETED < 3 )); do
print "Waiting..."
sleep 0.1
done
echo "======================"
COMPLETED=0
async_stop_worker my_worker -n
val=300
async_start_worker my_worker -n
async_register_callback my_worker completed_callback
async_job my_worker view_val
while (( COMPLETED < 1 )); do
print "Waiting..."
sleep 0.1
done
Displays:
view val:
set val: 100
view val: 100
======================
Waiting...
view val:
set val: 200
view val:
======================
Waiting...
view val: 300
The reason you're not seeing val
inside your worker is that each worker only has access to the global environment as it is when it was started, zpty
instances essentially create a copy of the current state, so updates are not propagated.
The way I recommend using async
is to have your job functions accept and return parameters (this still means there's no way to communicate to a running job, other than to kill it).
- Set function prints the newly set value
- Callback function sees that the set function has finished, and updates the variable on the global scope
- New value is passes to the view job through a parameter
I've updated your example slightly to illustrate this:
#!/usr/bin/env zsh
source ./async.zsh
async_init
set_val() {
val=$1
print $val
}
view_val() {
local val=$1
echo "view val: $val"
}
async_start_worker my_worker -n
async_register_callback my_worker completed_callback
# Create a callback function to process results
COMPLETED=0
completed_callback() {
case $1 in
set_val) val=$3;; # set val to the output of set_val
esac
COMPLETED=$(( COMPLETED + 1 ))
print $3
}
set_val 100
async_job my_worker view_val $val
async_job my_worker set_val 200
sleep 0.1 # waiting for set_val to return so that global var gets set
async_job my_worker view_val $val
while (( COMPLETED < 3 )); do
print "Waiting..."
sleep 0.1
done
Does this help with your problem? You should essentially synchronize your application through the callback function which is provided with information about which job has completed.
Is this still relevant to you @andjscott? I thought about this some more and think that it can indeed be a valid feature to change the environment inside the worker.
A change along these lines in async.zsh
could take care of it: fec46cf
I'm not yet married to the API, and need to think it through some more. There is definitely potential for hidden user error in the above commit.
With this change, you could modify the code example you provided like so:
--- change_var.zsh.orig 2016-07-12 00:59:19.000000000 +0300
+++ change_var.zsh 2016-07-12 00:59:47.000000000 +0300
@@ -5,7 +5,6 @@
set_val() {
val=$1
- echo "set val: $val"
}
view_val() {
@@ -30,10 +29,10 @@
echo "======================"
async_job my_worker view_val
-async_job my_worker set_val 200
+async_exec my_worker set_val 200
async_job my_worker view_val
-while (( COMPLETED < 3 )); do
+while (( COMPLETED < 2 )); do
print "Waiting..."
sleep 0.1
done
Of couse, the async_exec
is really flexible in this case, you could even just write async_exec my_worker "typeset val=200"
(no function).
Potentially we should limit this to only environment variables, and do some input validation based on that constraint. In this case the function signature should probably be something along these lines: async_set_env my_worker test=123 foo=bar
.
Yes that's very interesting. I'm using zsh-async for generating the PROMPT asynchronously. My workaround to updating a variable is to stop and start the worker.
I've given this some more thought and decided I will not implement it. Allowing this kind of behavior would provide an awkward API at best and with the release of v1.3.0
passing arguments to a function no longer has any limitations.
This means you should not rely on any kind of global state, parameters can, and should, be passed to the command/function during the async_job
call. The worker is a completely separated environment (kind of like web workers, for example) and should be treated as such. Relying on the state within a worker will only lead to headache, so it's best to think of it as not having any state.
Although, I might reconsider this, provided some good arguments in its favor.