Easiest way to make lua script wait/pause/sleep/block for a few seconds?
Asked Answered
B

20

39

I cant figure out how to get lua to do any common timing tricks, such as

  • sleep - stop all action on thread

  • pause/wait - don't go on to the next command, but allow other code in the application to continue

  • block - don't go on to next command until the current one returns

And I've read that a

while os.clock()<time_point do 
--nothing
end

eats up CPU time.

Any suggestions? Is there an API call I'm missing?

UPDATE: I wrote this question a long time ago trying to get WOW Lua to replay actions on a schedule (i.e. stand, wait 1 sec, dance, wait 2 sec, sit. Without pauses, these happen almost all in the same quarter second.) As it turned out WOW had purposely disabled pretty much everything that allows doing action on a clock because it could break the game or enable bots. I figured to re-create a clock once it had been taken away, I'd have to do something crazy like create a work array (with an action and execution time) and then register an event handler on a bunch of common events, like mouse move, then in the even handler, process any action whose time had come. The event handler wouldn't actually happen every X milliseconds, but if it was happening every 2-100 ms, it would be close enough. Sadly I never tried it.

Boaten answered 23/6, 2009 at 18:35 Comment(0)
U
24

[I was going to post this as a comment on John Cromartie's post, but didn't realize you couldn't use formatting in a comment.]

I agree. Dropping it to a shell with os.execute() will definitely work but in general making shell calls is expensive. Wrapping some C code will be much quicker at run-time. In C/C++ on a Linux system, you could use:

static int lua_sleep(lua_State *L)
{
    int m = static_cast<int> (luaL_checknumber(L,1));
    usleep(m * 1000); 
    // usleep takes microseconds. This converts the parameter to milliseconds. 
    // Change this as necessary. 
    // Alternatively, use 'sleep()' to treat the parameter as whole seconds. 
    return 0;
}

Then, in main, do:

lua_pushcfunction(L, lua_sleep);
lua_setglobal(L, "sleep");

where "L" is your lua_State. Then, in your Lua script called from C/C++, you can use your function by calling:

sleep(1000) -- Sleeps for one second
Used answered 7/7, 2009 at 15:25 Comment(0)
H
19

If you happen to use LuaSocket in your project, or just have it installed and don't mind to use it, you can use the socket.sleep(time) function which sleeps for a given amount of time (in seconds).

This works both on Windows and Unix, and you do not have to compile additional modules.

I should add that the function supports fractional seconds as a parameter, i.e. socket.sleep(0.5) will sleep half a second. It uses Sleep() on Windows and nanosleep() elsewhere, so you may have issues with Windows accuracy when time gets too low.

Hackbut answered 31/10, 2011 at 8:32 Comment(0)
G
10

You can't do it in pure Lua without eating CPU, but there's a simple, non-portable way:

os.execute("sleep 1")

(it will block)

Obviously, this only works on operating systems for which "sleep 1" is a valid command, for instance Unix, but not Windows.

Gaddy answered 25/6, 2009 at 21:44 Comment(1)
Note that on some systems where this works (e.g. OS X) this will not cause os.clock() to advance, since CPU time is not consumed.Sewole
A
9

Sleep Function - Usage : sleep(1) -- sleeps for 1 second

local clock = os.clock
function sleep(n)  -- seconds
   local t0 = clock()
   while clock() - t0 <= n do
   end
end

Pause Function - Usage : pause() -- pause and waits for the Return key

function pause()
   io.stdin:read'*l'
end

hope, this is what you needed! :D - Joe DF

Atomizer answered 7/11, 2012 at 4:24 Comment(1)
The first one is just while os.clock()<time_point do --nothing end Which, as OP said, eats up CPU time.Miscegenation
E
8

for windows you can do this:

os.execute("CHOICE /n /d:y /c:yn /t:5")
Emanuel answered 23/1, 2012 at 17:13 Comment(0)
G
6

It doesn't get easier than this. Sleep might be implemented in your FLTK or whatever, but this covers all the best ways to do standard sort of system sleeps without special event interrupts. Behold:

-- we "pcall" (try/catch) the "ex", which had better include os.sleep
-- it may be a part of the standard library in future Lua versions (past 5.2)
local ok,ex = pcall(require,"ex")
if ok then
   -- print("Ex")
   -- we need a hack now too? ex.install(), you say? okay
   pcall(ex.install)
   -- let's try something else. why not?
   if ex.sleep and not os.sleep then os.sleep = ex.sleep end
end

if not os.sleep then
   -- we make os.sleep
   -- first by trying ffi, which is part of LuaJIT, which lets us write C code
   local ok,ffi = pcall(require,"ffi")
   if ok then
      -- print("FFI")
      -- we can use FFI
      -- let's just check one more time to make sure we still don't have os.sleep
      if not os.sleep then
         -- okay, here is our custom C sleep code:
         ffi.cdef[[
            void Sleep(int ms);
            int poll(struct pollfd *fds,unsigned long nfds,int timeout);
         ]]
         if ffi.os == "Windows" then
            os.sleep = function(sec)
               ffi.C.Sleep(sec*1000)
            end
         else
            os.sleep = function(sec)
               ffi.C.poll(nil,0,sec*1000)
            end
         end
      end
   else
      -- if we can't use FFI, we try LuaSocket, which is just called "socket"
      -- I'm 99.99999999% sure of that
      local ok,socket = pcall(require,"socket")
      -- ...but I'm not 100% sure of that
      if not ok then local ok,socket = pcall(require,"luasocket") end
      -- so if we're really using socket...
      if ok then
         -- print("Socket")
         -- we might as well confirm there still is no os.sleep
         if not os.sleep then
            -- our custom socket.select to os.sleep code:
            os.sleep = function(sec)
               socket.select(nil,nil,sec)
            end
         end
      else
         -- now we're going to test "alien"
         local ok,alien = pcall(require,"alien")
         if ok then
         -- print("Alien")
         -- beam me up...
            if not os.sleep then
               -- if we still don't have os.sleep, that is
               -- now, I don't know what the hell the following code does
               if alien.platform == "windows" then
                  kernel32 = alien.load("kernel32.dll")
                  local slep = kernel32.Sleep
                  slep:types{ret="void",abi="stdcall","uint"}
                  os.sleep = function(sec)
                     slep(sec*1000)
                  end
               else
                  local pol = alien.default.poll
                  pol:types('struct', 'unsigned long', 'int')
                  os.sleep = function(sec)
                     pol(nil,0,sec*1000)
                  end
               end
            end
         elseif package.config:match("^\\") then
            -- print("busywait")
            -- if the computer is politically opposed to NIXon, we do the busywait
            -- and shake it all about
            os.sleep = function(sec)
               local timr = os.time()
               repeat until os.time() > timr + sec
            end
         else
            -- print("NIX")
            -- or we get NIXed
            os.sleep = function(sec)
               os.execute("sleep " .. sec)
            end
         end
      end
   end
end
Grained answered 4/6, 2014 at 12:26 Comment(2)
I keep hating lua more when searching for a simple solution ...Hesitancy
There must be an easier way to do this... Oh well, in my case, I can always use @zslayton's answer.Wise
L
4

For the second request, pause/wait, where you stop processing in Lua and continue to run your application, you need coroutines. You end up with some C code like this following:

Lthread=lua_newthread(L);
luaL_loadfile(Lthread, file);
while ((status=lua_resume(Lthread, 0) == LUA_YIELD) {
  /* do some C code here */
}

and in Lua, you have the following:

function try_pause (func, param)
  local rc=func(param)
  while rc == false do
    coroutine.yield()
    rc=func(param)
  end
end

function is_data_ready (data)
  local rc=true
  -- check if data is ready, update rc to false if not ready
  return rc
end

try_pause(is_data_ready, data)
Leitman answered 25/6, 2011 at 14:58 Comment(0)
S
3

I would implement a simple function to wrap the host system's sleep function in C.

Spinode answered 23/6, 2009 at 18:46 Comment(0)
M
3

Pure Lua uses only what is in ANSI standard C. Luiz Figuereido's lposix module contains much of what you need to do more systemsy things.

Mcanally answered 25/6, 2009 at 21:38 Comment(0)
M
3
require 'alien'

if alien.platform == "windows" then
  kernel32 = alien.load("kernel32.dll")
  sleep = kernel32.Sleep
  sleep:types{ret="void",abi="stdcall","uint"}
else
  -- untested !!!
  libc = alien.default
  local usleep = libc.usleep
  usleep:types('int', 'uint')
  sleep = function(ms)
    while ms > 1000 do
      usleep(1000)
      ms = ms - 1000
    end
    usleep(1000 * ms)
  end
end 

print('hello')
sleep(500)  -- sleep 500 ms
print('world')
Mongol answered 13/12, 2012 at 9:38 Comment(0)
G
2

I agree with John on wrapping the sleep function. You could also use this wrapped sleep function to implement a pause function in lua (which would simply sleep then check to see if a certain condition has changed every so often). An alternative is to use hooks.

I'm not exactly sure what you mean with your third bulletpoint (don't commands usually complete before the next is executed?) but hooks may be able to help with this also.

See: Question: How can I end a Lua thread cleanly? for an example of using hooks.

Gerlac answered 23/6, 2009 at 21:9 Comment(0)
T
2

You can use:

os.execute("sleep 1") -- I think you can do every command of CMD using os.execute("command")

or you can use:

function wait(waitTime)
    timer = os.time()
    repeat until os.time() > timer + waitTime
end

wait(YourNumberHere)
Tushy answered 17/1, 2014 at 15:36 Comment(0)
C
1

You want  win.Sleep(milliseconds), methinks.

Yeah, you definitely don't want to do a busy-wait like you describe.

Coyle answered 23/6, 2009 at 18:39 Comment(0)
U
1

It's also easy to use Alien as a libc/msvcrt wrapper:

> luarocks install alien

Then from lua:

require 'alien'

if alien.platform == "windows" then
    -- untested!!
    libc = alien.load("msvcrt.dll")
else
    libc = alien.default
end 

usleep = libc.usleep
usleep:types('int', 'uint')

function sleep(ms)
    while ms > 1000 do
        usleep(1000)
        ms = ms - 1000
    end
    usleep(1000 * ms)
end

print('hello')
sleep(500)  -- sleep 500 ms
print('world')

Caveat lector: I haven't tried this on MSWindows; I don't even know if msvcrt has a usleep()

Underwriter answered 31/10, 2011 at 0:38 Comment(0)
C
1

I started with Lua but, then I found that I wanted to see the results instead of just the good old command line flash. So i just added the following line to my file and hey presto, the standard:

please press any key to continue...

os.execute("PAUSE")

My example file is only a print and then a pause statment so I am sure you don't need that posted here.

I am not sure of the CPU implications of a running a process for a full script. However stopping the code mid flow in debugging could be useful.

Chester answered 25/6, 2013 at 23:15 Comment(0)
B
1

I believe for windows you may use: os.execute("ping 1.1.1.1 /n 1 /w <time in milliseconds> >nul as a simple timer. (remove the "<>" when inserting the time in milliseconds) (there is a space between the rest of the code and >nul)

Bibbie answered 4/7, 2013 at 16:59 Comment(0)
J
1
cy = function()
    local T = os.time()
        coroutine.yield(coroutine.resume(coroutine.create(function()
    end)))
    return os.time()-T
end
sleep = function(time)
    if not time or time == 0 then 
        time = cy()
    end
    local t = 0
    repeat
        local T = os.time()
        coroutine.yield(coroutine.resume(coroutine.create(function() end)))
        t = t + (os.time()-T)
    until t >= time
end
Jackanapes answered 5/4, 2014 at 6:7 Comment(0)
L
1

You can do this:

function Sleep(seconds)
    local endTime = os.time() + seconds
    while os.time() < endTime do
    end
end
print("This is printed first!")
Sleep(5)
print("This is printed 5 seconds later!")
Limpid answered 25/2, 2020 at 17:0 Comment(0)
R
1

You could try this:

function wait(seconds)
    local start = os.time()
    repeat until os.time() > start + seconds
end
wait(5) print("cargo. Cargo what? Cargo, storage.") -- waits 5 seconds and then prints

It worked for me in Lua CLI.

Resistance answered 24/1, 2022 at 1:49 Comment(0)
I
0

This should work:

    os.execute("PAUSE")
Irremissible answered 7/3, 2014 at 15:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.