tarantool how to handle lua errors outside the fiber - tarantool

I am wondering, how can i set callback on fiber error throw.
example:
local fiber = require("fiber")
local status = 0 --in my case stored in db
local function time_consuming()
lua_error
status = 0
end
function external_api_function()
if status == 1 then return "already running" end
status = 1
fiber.create(time_consuming)
return "started"
end
so i want to set status to 0 if time_consuming function falls. Is where any way to catch it?
I think about checking fiber:status on next api visit. Or create a watchdog for fiber, if i want status to be valid. This will work for me, but seems like not the best solution.

There are multiple ways to achieve your goal.
Within your example both fibers (main and time_consuming task) share the same scope - status variable. If you change the value of the status variable in the child fiber, parent fiber will see an update. You can check this using simple snippet:
status = 1
fiber.create(function() status = 0 end)
print(status) -- 0
Now, to catch an exception, use pcall function. It accepts a function as a first argument, calls it and returns status as a first value following one or more function results. There is also xpcall function if you want to analyse the error being caught. It takes error handler as a second argument.
With pcall you may change your time_consuming function like this:
local function time_consuming()
local ok = pcall(function() lua_error end)
if not ok then
status = 0
end
end
and status will successfully updated if lua_error fails.
But usually I consider this as a bad practice. If more fibers share the same state, it may become difficult to maintain due to uncertainty of the order of fiber execution. So for more reliable solution you may want to use interfiber communication primitives aka channels. You will be able to explicitly ask child fiber to tell you its execution status whether it succeed or not using channel:put() and channel:get() function. See documentation for examples.

Related

How does WaitForDebugEvent[Ex] report a timeout?

The WaitForDebugEvent and WaitForDebugEventEx APIs allow a caller to specify a timeout. The functions return when a debug event occurred, or the timeout expired, whichever happened first. While the documentation describes how debug events are reported, there's no information at all on the APIs' behaviors in case of a timeout.
Questions
How do the APIs report a timeout?
Does this protocol hold for a value of 0 for dwMilliseconds as well?
How do the APIs report a timeout?
The function returns FALSE and sets the last error to ERROR_SEM_TIMEOUT(0x79).
Does this protocol hold for a value of 0 for dwMilliseconds as well?
Yes.
Since it's not documented we'll go with the implementation (I'm on Win 10).
Both kernelbase!WaitForDebugEvent and kernelbase!WaitForDebugEventEx APIs call the same piece of code (simply with a different arg).
In the shared piece of code the second call is made on ntdll!DbgUiWaitStateChange which itself is a simple wrapper around ntdll!NtWaitForDebugEvent.
On function return the code checks if the returned status is either STATUS_ALERTED (0x101) or STATUS_USER_APC (0xc0). If it is, it keeps calling the previous function (ntdll!DbgUiWaitStateChange), otherwise it checks if the status is an error (more precisely if the status is signed).
If it's not signed it checks that the status is STATUS_TIMEOUT (0x102). If it is, the NT Status is converted to a win32 error - from STATUS_TIMEOUT to ERROR_SEM_TIMEOUT (0x79) - and the function is exited.
Inside nt!NtWaitForDebugEvent (kernel level) we can have a good overview of what is happening with the ReactOS source code.
After a call to nt!KeWaitForSingleObject on the debug object the code checks (source) what is the status of the call. If it is STATUS_TIMEOUT (which, if I'm not mistaken should happen in case of a 0 timeout), the function bails out and return the current status, which is, as explained above, converted to ERROR_SEM_TIMEOUT in userland.
Status = KeWaitForSingleObject(&DebugObject->EventsPresent,
Executive,
PreviousMode,
Alertable,
Timeout);
if (!NT_SUCCESS(Status) ||
(Status == STATUS_TIMEOUT) ||
(Status == STATUS_ALERTED) ||
(Status == STATUS_USER_APC))
{
/* Break out the wait */
break;

Why am I not allowed to break a Promise?

The following simple Promise is vowed and I am not allowed to break it.
my $my_promise = start {
loop {} # or sleep x;
'promise response'
}
say 'status : ', $my_promise.status; # status : Planned
$my_promise.break('promise broke'); # Access denied to keep/break this Promise; already vowed
# in block <unit> at xxx line xxx
Why is that?
Because the Promise is vowed, you cannot change it: only something that actually has the vow, can break the Promise. That is the intent of the vow functionality.
What are you trying to achieve by breaking the promise as you showed? Is it to stop the work being done inside of the start block? Breaking the Promise would not do that. And the vow mechanism was explicitly added to prevent you from thinking it can somehow stop the work inside a start block.
If you want work inside a start block to be interruptible, you will need to add some kind of semaphore that is regularly checked, for instance:
my int $running = 1;
my $my_promise = start {
while $running {
# do stuff
}
$running
}
# do other stuff
$running = 0;
await $my_promise;
Hope this made sense.
The reason why you cannot directly keep/break Promise from outside or stop it on Thread Pool are explained here in Jonathans comment.
Common misuse of Promises comes from timeout pattern.
await Promise.anyof(
start { sleep 4; say "finished"; },
Promise.in( 1 )
);
say "moving on...";
sleep;
This will print "finished". And when user realize that the next logical step for him is to try to kill obsolete Promise. While the only correct way to solve it is to make Promise aware that its work is no longer needed. For example through periodically checking some shared variable.
Things gets complicated if you have blocking code on Promise (for example database query) that runs for too long and you want to terminate it from main thread. That is not doable on Promises. All you can do is to ensure Promise will run in finite time (for example on MySQL by setting MAX_EXECUTION_TIME before running query). And then you have choice:
You can grind your teeth and patiently wait for Promise to finish. For example if you really must disconnect database in main thread.
Or you can move on immediately and allow "abandoned" Promise to finish on its own, without ever receiving its result. In this case you should control how many of those Promises can stack up in background by using Semaphore or running them on dedicated ThreadPoolScheduler.

Press any key in 5 seconds

To clarify, I am using ComputerCraft (emulator: http://gravlann.github.io/, language: Lua)
I know that to wait for a keypress
os.pullEvent("key")
and to wait 5 seconds I need to use this
sleep(5)
but I want to wait for a keypress and disable the event waiter after 5 seconds.
I'm not familiar with ComputerCraft API, but I guess, You could use parallel API for this. Basically, it allows executing two or more functions in parallel.
To be specific - parallel.waitForAny. Which returns after any of function finished, so, only the one being executed. In contrary, parallel.waitForAll waits for all functions being executed.
I'd use something like this:
local action_done = 0
local function wait_for_keypress()
local event, key_code = os.pullEvent("key")
--do something according to separate key codes? :}
end
local function wait_some_time()
sleep(5)
end
action_done = parallel.waitForAny(wait_for_keypress, wait_some_time)
--action done now contains the number of function which was finished first.
EDIT: if using only ComputerCraft API, You should change to this (using timer event):
local function wait_some_time()
os.startTimer(5)
end

Ruby - Redis based mutex with expiration implementation

I'm trying to implement a memory based, multi process shared mutex, which supports timeout, using Redis.
I need the mutex to be non-blocking, meaning that I just need to be able to know if I was able to fetch the mutex or not, and if not - simply continue with execution of fallback code.
something along these lines:
if lock('my_lock_key', timeout: 1.minute)
# Do some job
else
# exit
end
An un-expiring mutex could be implemented using redis's setnx mutex 1:
if redis.setnx('#{mutex}', '1')
# Do some job
redis.delete('#{mutex}')
else
# exit
end
But what if I need a mutex with a timeout mechanism (In order to avoid a situation where the ruby code fails before the redis.delete command, resulting the mutex being locked forever, for example, but not for this reason only).
Doing something like this obviously doesn't work:
redis.multi do
redis.setnx('#{mutex}', '1')
redis.expire('#{mutex}', key_timeout)
end
since I'm re-setting an expiration to the mutex EVEN if I wasn't able to set the mutex (setnx returns 0).
Naturally, I would've expected to have something like setnxex which atomically sets a key's value with an expiration time, but only if the key does not exist already. Unfortunately, Redis does not support this as far as I know.
I did however, find renamenx key otherkey, which lets you rename a key to some other key, only if the other key does not already exist.
I came up with something like this (for demonstration purposes, I wrote it down monolithically, and didn't break it down to methods):
result = redis.multi do
dummy_key = "mutex:dummy:#{Time.now.to_f}#{key}"
redis.setex dummy_key, key_timeout, 0
redis.renamenx dummy_key, key
end
if result.length > 1 && result.second == 1
# do some job
redis.delete key
else
# exit
end
Here, i'm setting an expiration for a dummy key, and try to rename it to the real key (in one transaction).
If the renamenx operation fails, then we weren't able to obtain the mutex, but no harm done: the dummy key will expire (it can be optionally deleted immediately by adding one line of code) and the real key's expiration time will remain intact.
If the renamenx operation succeeded, then we were able to obtain the mutex, and the mutex will get the desired expiration time.
Can anyone see any flaw with the above solution? Is there a more standard solution for this problem? I would really hate using an external gem in order to solve this problem...
If you're using Redis 2.6+, you can do this much more simply with the Lua scripting engine. The Redis documentation says:
A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.
Implementing it is trivial:
LUA_ACQUIRE = "return redis.call('setnx', KEYS[1], 1) == 1 and redis.call('expire', KEYS[1], KEYS[2]) and 1 or 0"
def lock(key, timeout = 3600)
if redis.eval(LUA_ACQUIRE, key, timeout) == 1
begin
yield
ensure
redis.del key
end
end
end
Usage:
lock("somejob") { do_exclusive_job }
Starting from redis 2.6.12 you can do: redis.set(key, 1, nx: true, ex: 3600) which is actually SET key 1 NX EX 3600.
I was inspired by the simplicity that of both Chris's and Mickey's solutions, and created gem - simple_redis_lock with this code(and some features and rspec):
def lock(key, timeout)
if #redis.set(key, Time.now, nx: true, px: timeout)
begin
yield
ensure
release key
end
end
end
I explored some other awesome alternatives:
mlanett/redis-lock
PatrickTulskie/redis-lock
leandromoreira/redlock-rb
dv/redis-semaphore
but they had too many features of blocking to acquire lock and didn't use this single SET KEY 1 NX EX 3600 atomic redis statement.

CheckAbort inside MathLink functions?

I just found that such MathLink functions as LinkWrite and LinkRead have something like its own internal CheckAbort that absorbs any aborts, and does not propagate them further.
This can be easily shown with LinkRead:
link = LinkLaunch[First[$CommandLine] <> " -mathlink"];
LinkRead[link];
LinkWrite[link, Unevaluated[Pause[10]]];
{LinkRead[link], Print["!!"]}
After evaluating the above code press Alt+. and you will get the following output:
During evaluation of In[6]:= !!
Out[9]= {ReturnPacket[$Aborted], Null}
As you see the abort was absorbed by LinkRead.
My problem is that it breaks my own flow control of evaluation based on CheckAbort.
Is there a way to intercept aborts absorbed by such functions as LinkRead and LinkWrite?
The way MathLink works, LinkRead blocks if there is nothing to read on the link. If you try to abort at this time, an abort message is passed via MathLink message channel to the other end of the link. If the program on the other end behaves nicely, it will drop whatever it was doing and send a return value (in many cases $Aborted). If you want to propagate the abort to your end of the link, so that you can catch it with CheckAbort, you will need to check the return value and generate another abort, for example:
If[LinkRead[link] == $Aborted, Abort[]]
This works if you know that the other end of the link returns $Aborted in case it is aborted.

Resources