Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Lua ➜ Writing coroutines (threads)

Writing coroutines (threads)

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Sun 28 Nov 2004 07:48 PM (UTC)

Amended on Sun 28 Nov 2004 07:49 PM (UTC) by Nick Gammon

Message
Lua has a powerful support for something called coroutines, which is a form of threading.

Many times people have asked for a way to make a script that does something, waits for a while, and then keeps going. Now it is possible! Read on ...

As a demonstration, I will make three functions in a script file. The three functions are:


  1. The coroutine itself (the thread) - this will execute over 10 seconds, printing a number that increments, and yields execution back to MUSHclient in between each one.

  2. A trigger script that (when fired) starts the thread running

  3. A "tickler" timer - this runs every second, and gives execution back to the thread


There are other ways you could do this, for example rather than executing the thread every second, you might do it in response to a trigger or alias, or some other event.

The important thing is that the coroutine (thread) keeps its internal status while it is running. However it has to explicitly "yield" when it is ready to pause. This is called "co-operative multitasking".

First function, the thread itself, which is pretty simple:


function backgrounder ()

  -- loop doing something, yield execution every time through
  for i = 1, 10 do
    print ("background thread - step", i)
    coroutine.yield ()
  end -- for

end -- backgrounder 



What we expect to see here is the step number being displayed each time through.

Next part is a "tickler" that will resume this task every second:


function tickle_it ()

  -- if background task exists, and is suspended, resume it
  if background_task ~= nil and
     coroutine.status (background_task) == "suspended" then
    coroutine.resume (background_task)
  else
    EnableTimer ("tickler", false)	-- don't need timer now
  end -- if
end -- tickle_it 



This is called by a timer that keeps firing. The timer is named "tickler", and this function will disable the timer when it isn't needed any more. This would happen when our thread terminates, so its status will go from "suspended" to "dead".

Finally, the trigger to kick the whole process off:


function mytrigger ()

-- see if backgrounder running

  if background_task == nil or
     coroutine.status (background_task) == "dead" then

     -- not running, create it
     background_task = coroutine.create (backgrounder)

     -- make timer to keep it going
     AddTimer ("tickler", 0, 0, 1, "",
               timer_flag.Enabled + timer_flag.Replace,
               "tickle_it")
  end  -- if

end -- mytrigger



In the example above I check to see if the background task has a "dead" status, or is nil. If it is nil then we have never created it, if it has a dead status then we had one previously but it terminated.

In either case, we create a new task (with coroutine.create) passing as an argument the function we wish to run (backgrounder). This is stored in the global vairable background_task.

Then we make a timer to call the "tickle_it" routine every second.

Now if you put all three functions into your script file, and then make a trigger to start things going, or just type into the command window:


/mytrigger ()


Then you will see the following lines displayed over a 10-second interval:


background thread - step 1
background thread - step 2
background thread - step 3
background thread - step 4
background thread - step 5
background thread - step 6
background thread - step 7
background thread - step 8
background thread - step 9
background thread - step 10


Incredible, but true! Of course, while this is happening you can do other things (eg. type "look"), because MUSHclient isn't "hanging" in the script.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #1 on Sun 28 Nov 2004 07:55 PM (UTC)
Message
As an alternative to the timer, you could use, say, a trigger that gives control back to the suspended routine on each line of input, so you could write a script that gradually evaluates incoming text lines.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #2 on Sun 28 Nov 2004 08:02 PM (UTC)
Message
According to the Lua manual, you can pass arguments to the coroutine.resume function (after the coroutine itself) which are then passed as returned values to the coroutine.yield.

What this means, is that when you resume a suspended function you can "talk to it", eg. tell it why is has been resumed. In the example of handling consecutive lines of input, then you could pass down as an argument, the line that you have just received.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


11,828 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.