Error using @sync/@async within macro, help is highly appreciated

Hi there,
I would like to write a macro which just waits until a next time, if I run it on my own in a repl, it works, but if I put it into a macro, it fails with

ERROR: syntax: invalid let syntax around task.jl:453

here the macro

using Dates
macro waituntil(nexttime_from_now, sleeptime_from_diff)
    quote
        nexttime = $nexttime_from_now()
        @sync @async begin
            diff = nexttime - Dates.now()
            while diff > Dates.Millisecond(0)
                sleep($sleeptime_from_diff(diff))
                diff = nexttime - Dates.now()
            end
        end
    end
end
julia> nexttime_from_now() = ceil(Dates.now(), Dates.Second(30))
nexttime_from_now (generic function with 1 method)

julia> sleeptime_from_diff(diff) = max(div(diff,2), Dates.Millisecond(2000))
sleeptime_from_diff (generic function with 1 method)

julia> @waituntil(nexttime_from_now, sleeptime_from_diff)
ERROR: syntax: invalid let syntax around task.jl:453
Stacktrace:
 [1] top-level scope
   @ REPL[25]:1

doing it without a macro works…

begin
    nexttime = nexttime_from_now()
    @sync @async begin
        diff = nexttime - Dates.now()
        while diff > Dates.Millisecond(0)
            sleep(sleeptime_from_diff(diff))
            diff = nexttime - Dates.now()
        end
    end
end

any help on how to get this into a macro is highly appreciated

running on Julia 1.8.1

EDIT: inspecting the macros it seems to be that @sync is generating invalid let syntax like let Main.:(var"##sync#48") = Base.Channel(Base.Inf) when used within a macro. Is this a bug?

If I understand things correctly - and I may not, because macro hygiene is difficult - var"##sync#48" comes from expansion of the @sync macro, and then the hygiene pass of the outer @waituntil turns it into Main.:(var"##sync#48"). Which it shouldn’t do imho, because it is clearly a local variable. So, maybe a bug? Let’s see what others say on the matter.

With this macro there shouldn’t be any unintentional side effects when the returned expression is escaped, and nexttime is explicitely hygiened(?)

 macro waituntil(nexttime_from_now, sleeptime_from_diff)
           nexttime = gensym("nexttime")
           esc(quote
               $nexttime = $nexttime_from_now()
               @sync @async begin
                   diff = $nexttime - Dates.now()
                   while diff > Dates.Millisecond(0)
                       sleep($sleeptime_from_diff(diff))
                       diff = $nexttime - Dates.now()
                   end
               end
           end)
       end

I honestly fail to see why this ought to be a macro though.

1 Like

thank you for the analysis. I agree, the macro does not make much sense here. The case is simplified – the original version indeed makes use of the macro part.