How to interrupt fun(a,b) in a for loop after 10 seconds?

How to interrupt fun(a,b) in a for loop after 10 seconds?
If terminated before execution, fun x,y,z=false

for i=1:k
x,y,z=fun(a,b)
??? 10 sek > x,y,z=false,false,false
end

Thank you very much for your help, GPT doesn’t know but the matter is urgent!

It sepends on the nature of fun. Some code does not really have points at which it is able to exit. But if it occurs in a loop, you can check the elapsed time in the start of each loop.

But in the general case, without changes inside fun, I think you best best is along the lines of

my_task = @async fun(a, b)
sleep(timeout_duration)
my_result = if istaskfinnished(my_task)
    #code to retrieve result from task here
else
    # some `yieldto` code if you do not want the computation to finnish in the background
    (false, false, false)
end
x, y, z = my_result

This is very rough pseudo-code, but should give an idea. But beweare: what you are asking is hard, and asynchronous programming if significantly harder to do than sequential programming.

2 Likes

GPT dont know ! :wink:

:100: hanks, some aboout

istaskfinnished

?

julia> sleep(5)
  0.335358 seconds (4.62 k allocations: 325.352 KiB, 15.94% compilation time)
  0.125368 seconds (38.31 k allocations: 2.736 MiB, 99.26% compilation time)
  2.054542 seconds (433.44 k allocations: 29.480 MiB, 0.71% gc time, 97.21% compilation time)

julia> my_result = if istaskfinnished(my_task)
           #code to retrieve result from task here
       else
           # some `yieldto` code if you do not want the computation to finnish in the background
           (false, false, false)
       end
ERROR: UndefVarError: `istaskfinnished` not defined
Stacktrace:
 [1] top-level scope
   @ REPL[75]:1

julia> x, y, z = my_result
ERROR: UndefVarError: `my_result` not defined
Stacktrace:
 [1] top-level scope
   @ REPL[76]:1
 

The function in question is “istaskdone” - see Tasks · The Julia Language
This is why it’s good to not use LLMs and get used to the documentation…

3 Likes

I changed the function to eliminate any “Nothing”. Unfortunately, something clashes. Can this be done without parallelism?

julia> function limitowany_czas(url, tagg, limit)
       my_task = @sync get_links_by_tagg(url, tagg)
       sleep(limit)
       my_result = if istaskdone(my_task)

           #code to retrieve result from task here
       else
           # some `yieldto` code if you do not want the computation to finnish in the background
           (false, false, false)
       end
       x, y, z = my_result
       end
limitowany_czas (generic function with 1 method)

julia> limit(url, tagg, 200)
  0.002283 seconds (111 allocations: 7.078 KiB)
  0.000924 seconds (12 allocations: 608 bytes)
  0.054315 seconds (40.23 k allocations: 2.606 MiB)
 42.964573 seconds (174.80 k allocations: 86.045 MiB, 0.12% gc time)
ERROR: MethodError: no method matching iterate(::Nothing)

Closest candidates are:
  iterate(::BitSet)
   @ Base bitset.jl:326
  iterate(::BitSet, ::Any)
   @ Base bitset.jl:326
  iterate(::Base.AsyncGenerator, ::Base.AsyncGeneratorState)
   @ Base asyncmap.jl:362
  ...

Stacktrace:
 [1] indexed_iterate(I::Nothing, i::Int64)
   @ Base .\tuple.jl:95
 [2] limit(url::String, tagg::String, limit::Int64)
   @ Main .\REPL[88]:11
 [3] top-level scope
   @ REPL[94]:1

Not works. Works only then limit i shorter then fwork time .

Can this be done without parallelism? f.e. using

 julia> begin
            i = 0
            cb(timer) = (global i += 1; println(i))
            t = Timer(cb, 2, interval=0.2)
            wait(t)
            sleep(0.5)
            close(t)
        end 

or somthink similar ?

The async approach isn’t using parallelism, it’s using concurrency. I’m being picky because generally when we say parallelism we mean we’re actively doing things in multiple threads of execution, whereas here we’re not - one thread of execution is waiting for a specific amount of time for the other to complete.
In general, something is going to have to wait an amount of time to generate an interrupt of some kind whilst something else is doing work, so this is a kind of naturally concurrent task.

In any case, it’s clear that the if istaskdone() branch just needs to retrieve a value correctly (which it obviously doesn’t do in the version of the code you tested - that branch is empty!).
I’m not completely familiar with Julia’s preferred approach for inter-task communication, but it seems like:

function limitowany_czas(url, tagg, limit)
       my_task = @async get_links_by_tagg(url, tagg)
       sleep(limit)
       my_result = if istaskdone(my_task)
           fetch(my_task)
       else
           # some `yieldto` code if you do not want the computation to finnish in the background
           (false, false, false)
       end
       x, y, z = my_result
       end

might work?

3 Likes

Yes, thanks, but it has a significant drawback. It always waits for 200 seconds, which significantly extends the working time because many iterations last 1 - 2 seconds. 200 seconds is an absolute limit for difficult cases. You need a mechanism: done, move on… close t ?

Another appreach is to start two async processed that put results into a channel. One puts the default return into the channel after X seconds, the other puts the actual result in once computed. Then, you simply take! your result from the channel, and then close it. That should return after X seconds in any case.

Probably dmart to ensure that Threads.nthreads() is greater than one to make sure that Julia actually has multiple threads at its disposal. If not, the single available thread might not have a point at which it naturally yirlds to other tasks (see yield, or yieldto, or something like it)

1 Like

I have 8!

julia> Threads.nthreads()
8

Please help

GPT do know.

1 Like

Okay, so again, if you read the documentation I linked you to, you can see that there’s a timedwait function that waits for a particular test to be true, or a max time, and returns a value to let you know which happened.

Using that:

function limitowany_czas(url, tagg, limit)
       my_task = @async get_links_by_tagg(url, tagg)
       my_result = if timedwait(istaskdone(my_task), limit) == :ok
           fetch(my_task)
       else
           # some `yieldto` code if you do not want the computation to finnish in the background
           (false, false, false)
       end
       x, y, z = my_result
       end

Again, I’ve not tested this, but this is fairly easy to construct just by reading the documentation.

1 Like

This also works:

❯ julia --startup-file=no --threads=auto
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.10.2 (2024-03-01)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> function run_f_with_timeout(f, timeout=10, default_value=missing)
        Threads.nthreads() > 1  ||  @warn "Julia only has access to $(Threads.nthreads()) threads. This is likely to work better with more than one. To change this, start julia with e.g. `julia --threads=auto`"
        result_channel = Channel(1)
        @async begin
                sleep(timeout)
                put!(result_channel, default_value)
        end
        @async begin
                f_result = f()
                put!(result_channel, f_result)
        end

        # This call will block until the channel has content
        return_value = take!(result_channel)
        close(result_channel)  # Might be able to make the long-running task error before finnishing. Not sure though.
        return return_value
       end
run_f_with_timeout (generic function with 3 methods)

julia> run_f_with_timeout() do
           sleep(1)
           return pi
       end
π = 3.1415926535897...

julia> run_f_with_timeout() do
           sleep(11)
           return pi
       end
missing

Note that this does only may interrupt the loop - I am not sure. Again, it is often hard to reason about parallel/concurrent programs.

1 Like

No, I think your code is always correct, yes? (I was thinking of using a channel - but for this particular case, I think it’s easier for the person reading it to conceptualise the “wait” as a non-thread).

1 Like

3 hours GPT sends wrong answers to simple questions :wink:

I recently started using GPT myself for programming. My experience is that it is extremely helpful to have an idea of where you are going, and provide outlines of the functionality you want. You cannot really offload the creative aspects of the problem-solving and expect good results, unless it is a very common problem.

The quality of the output usually matches the quality of the input.

See also the chatbot on JuliaHub, which provided a decent answer given the following prompt:

Can you write a function that takes another 0-argument function as its input, and returns the return value from that function? Also, I want the outer function to return a default value after 10 seconds, so that the function always returns some value after 10 seconds

Output:

using Base.Threads
import Base.Threads.@spawn

function run_with_timeout(f::Function, default_value, timeout::Int)
    result = Ref{Any}()
    t = @spawn begin
        result[] = f()
    end
    
    sleep(timeout)
    
    if isready(t)
        return fetch(result)
    else
        return default_value
    end
end

# Example usage
function my_function()
    sleep(5)  # Simulating some computation
    return "Result after 5 seconds"
end

result = run_with_timeout(my_function, "Default Value", 10)
println(result)

This code errors because you have to use istaskdone instead of isready`. It also constrains the timeout to Int for no reason, and does not stop the long-running task.