lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On Jan 29, 2005, at 18:41, André Carregal wrote:

PS I struggle with co-routines.

Doesn't everyone? :o)

Well, here we go again, my first foolish attempt to make sense of this new Wonderland. I really do feel like Alice though. Or was it Dorothy? One way or another, we are not in Kansas anymore!

Anyhow, I tried to wrap this coroutine business with something more or less semantically equivalent to NSThread +detachNewThreadSelector:toTarget:withObject:

http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ ObjC_classic/Classes/NSThread.html

It goes something like this:

-- Assume some 'long' running object
local aConvolutedObject = ConvolutedObject.new()

-- The 'magic' incarnation
local aTask = LUTask.new( aConvolutedObject, aConvolutedObject.doIt() )

-- Run the task
aTask.run()

A LUTask provides a couple of public methods to access the standard coroutine functionalities:

aTask.status()
aTask.resume()
aTask.yield()

As well as accessor methods to what underlying LUObject and method the task is running:

-- returns the underlying object this task is wrapped around
aTask.object()

-- returns the underlying method that will be execute
aTask.method()

The 'magic' happens in the "run" method:

-- instance method to 'run' the routine by registering it with the class run loop
local run = function()
    if not ivars.routine then
	    routine()
	    self.class().register( self )
    end
end

First, the method check if it was run already or not. Then, if this is the first time, it register itself with the class run loop.

The private 'routine' method creates the 'coroutine' itself:

-- private method to wrap ivars.method in a coroutine
local routine = function()
    if not ivars.routine then
	    ivars.routine = coroutine.create( self.method() )
    end

    return ivars.routine
end

Then the task is passed on to LUTask.register():

-- class method to register a task with the run loop
local register = function( aTask )
    if aTask then
	    aTask.object().setTask( aTask )
	    tasks().add( aTask )
	    run()
    end
end

LUTask.register() simply adds the given task to its list and set it as the current task of the underlying object:

aTask.object().setTask( aTask )

This allows any LUObject to access their current 'task' when their coroutine method is invoked:

local doIt()
    -- do stuff

    this.task().yield()

    -- do some more stuff

    this.task().yield()

    -- be done with it
end

Finally, LUTask.register() kicks in the run loop itself:

         -- class method to 'run' through all the tasks
        local run = function()
                local someTasks = tasks()

                if someTasks then
                        local count = someTasks.size()

                        if count > 0 then
                                for index = 1, count do
local aTask = someTasks.get( index )
                                        local aStatus = aTask.status()

                                        if aStatus == "dead" then
aTask.object().setTask( nil ) someTasks.remove( index )
                                        else
                                                aTask.resume()
                                        end
                                end
                        end
                end
        end

The run loop goes through the list of tasks and check their status. If a task is 'dead' (ie finished), it removes it from its list. Otherwise, it invokes its resume() method.

The run loop runs only once through the list... this is somewhat on purpose... as I would like to avoid the run loop "busy waiting" on something to execute even when strictly nothing is going on at all... ideally I would like to have some sort of "event" driven system... but I have no idea how to do that yet... instead the run loop "rewind" itself by somehow (ab)using recursion... the "rewinding" is driven by the objects being executed... each time an object interacts with its task, the run loop gets automatically "rewinded":

-- instance method to yield the routine
local yield = function()
    if ivars.routine then

-- note the additional call to LUTask.run() at the end of the return statement

	    return coroutine.yield( self.routine() ), self.class().run()
    end

    return nil
end

Not sure if any of this will fly in practice as I even tested this concoction yet :))

See attachment for the current draft implementation.

Feedbacks and head slapping very much welcome!

Cheers

--
PA, Onnay Equitursay
http://alt.textdrive.com/

Attachment: LUObject.lua
Description: Binary data

Attachment: LUTask.lua
Description: Binary data

Attachment: LUList.lua
Description: Binary data