lua-users home
lua-l archive

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


On Thursday 09 March 2006 1:16 pm, Vijay Aswadhati wrote:
> This may be the beginning of something new and exciting!!

i hope so! either because it's accepted, or because somebody does something 
much better after seeing this as a proof of concept.

> A few concrete examples would be of great help. I am particularly
> interested in how one would go about creating a singleton timer

this is precisely what i did as a testbed for the library.  here's the full C 
code:

#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>

#include "lua.h"
#include "lauxlib.h"

#include "helper.h"

typedef struct timer_udata {
	struct timeval tv;
	int ret;
} timer_udata;

static int timer_prepare (lua_State *L, void **udata) {
	lua_Number t = luaL_checknumber (L, 1);
	timer_udata *td = (timer_udata *)malloc (sizeof (timer_udata));
	if (!td)
		luaL_error (L, "can't alloc udata");
	
	td->tv.tv_sec = (int) t;
	td->tv.tv_usec = (t - td->tv.tv_sec) * 1000000;
	
	*udata = td;
	
	return 0;
}

static int timer_work (void *udata) {
	timer_udata *td = (timer_udata *)udata;
	fd_set fd_a, fd_b, fd_c;
	FD_ZERO (&fd_a);
	FD_ZERO (&fd_b);
	FD_ZERO (&fd_c);
	
	printf ("before select\n");
	td->ret = select (0, &fd_a, &fd_b, &fd_c, &td->tv);
	printf ("after select\n");
	
	return 0;
}

static int timer_finish (lua_State *L, void *udata) {
	timer_udata *td = (timer_udata *)udata;
	
	if (td->ret < 0)
		luaL_error (L, strerror (td->ret));
	
	free (td);
	
	return 0;
}

static task_ops timer_ops = {
	timer_prepare,
	timer_work,
	timer_finish
};

int luaopen_timer (lua_State *L);
int luaopen_timer (lua_State *L) {

	helper_init ();
	add_helperfunc (L, &timer_ops);
	return 1;
}


as you can see, timer_prepare() gets the Lua parameters (a single number), 
allocates a private structure and fills it with a timeval (with sec and usec 
fields).  timer_work() runs in the background, so it can't touch the Lua 
space; it gets the timeval struct and does a select() to wait some time. 
timer_finish() verifies the return value of the select(), and frees the 
udata.  if there was any final result (like on a read funcion), it would be 
pushed in the Lua stack.

note that since it defines just a single function, it's returned in the stack 
by luaopen_timer(), instead of building a package.

this is how i tested it from Lua:

require "helper"
timer = require "timer"

q1=helper.newqueue()
q2=helper.newqueue()
print ("queues:", q1, q2)

th=helper.newthread (q1, q2)
print ("thread:", th)

tsk=timer(10)
print ("task:", tsk)

q1:addtask (tsk)
t2=q2:wait()
print ("t2:", t2)

helper.finish (t2)
print ("end")

----------------------------

th is the helper thread.  it uses q1 and q2 as input and output queues, 
respectively.  as soon as it's created, it tries to get a task from q1; but 
since it's empty, it's blocked.

the timer() function doesn't do the 'work' immediately, it just creates and 
returns a 'task' (a lightuserdata).  it also calls the timer_prepare() 
function.

when the task is added to q1, the thread gets it and executes the timer_work() 
function.  the Lua code continues to run in the 'main' thread.

the q2:wait() function blocks until there's a task in q2.  when timer_work() 
ends, the task is pushed to q2 and the Lua code unblocks.  t2 gets the task, 
it should be equal to tsk (that's why i used lightuserdata).  the 
helper.finish() call gets any result (none in this example)

> instance using this package and how the main dispatch loop would
> look like in the "C" or "C++" world.

my idea is that the main dispatch loop would be Lua code, not C.

i haven't written one yet, but this would be the general idea:

wrap all functions that return a task, like this:

local _read = read
function read (file)
    return helper.finish (yield (_read (file)))
end

and in the dispatcher:

while true do
    local tsk_done = out_q:wait()
    local co = coros [tsk_done]
    local tsk_blk = coroutine.resume (co, tsk_done)
    coros [tsk_blk] = co
    in_q:addtask (tsk_blk)
end


> On a frivolous note, I would probably name this package as
> 'concurrency' instead of 'helper'.

i'm notoriously bad at picking names for my code... any explanation why you 
think 'concurrency' is better than 'helper' ?

-- 
Javier

Attachment: pgpH8QY7BGa9Y.pgp
Description: PGP signature