Re: French translation updated & Yet Another Scripting Idea.

Nicolas Noble <[email protected]> Tue, 08 Nov 2005 17:14:21 +0100
Newsgroups gmane.network.lftp.devel
Message-ID <[email protected]>
Alexander V. Lukyanov wrote:

> The time format is `ls' specific. I don't think it is available in locale
> information.
>  
>
Okay, my bad for not digging enough :)


>> The only thing I am a bit affraid of would be to make LUA's coroutine 
>> to work nicely within lftp's built-in thread system. I'd need to go 
>> back   
>
>
> That can be a problem indeed. Are LUA routines blocking?
>  
>
Actually, yes, but, any LUA call "shouldn't" last long, that is, should 
be considered atomic, somehow. Of course, you can't know if it's not 
gonna stall. Since LUA, like any other language, isn't computable, you 
can't know if you're not enter any endless loop. Okay, so, this returns 
immediately:

$ lua -e "function calc (a,b) return a+b end; x = calc(12,24); print(x)"
36

And this should loop forever without any "direct way" to stop it:
$ lua -e "while true do end"

(even though there are ways to break it).

Now, in the usual way Lua works, it should call any function that the 
host software binded. There, the called C functions are free to yeild 
the current LUA thread, that is, freeze the VM nicely, and return to the 
main software. Therefore, the Lua VM shouldn't stall in any normal 
usage. It should rather return "very quickly", or call the hosts's 
functions and 'block' there.


The way I roughly see it is the following:
1) consider that running a simple "runnable Lua class" wouldn't block 
(atomic calls, if we forget about endless loops yet - even though this 
might get solved by itself afterward, since it's somewhat possible to 
force it to yield every XX instructions for example, but yet I am not 
100% sure if this is safe, so maybe it's better to keep it for later). 
So that this runnable Lua class wouldn't directly set up any wake-up 
condition.
2) the Lua VM itself might call back some of the Lftp's functions, which 
in turn would probably want to yield and set up wake-up conditions. 
These conditions should be fowarded to the Lua class so that it gets 
woken up correctly and try to execute the Do method of the class which 
caused the yield. If the class still wants to sleep, repeat. Otherwise, 
resume the Lua VM with the correct return. Since code is usually better 
than english words, if you want a better idea about how the Lua 
threading system works, you can read a complete example here: 
http://paste.nobis-crew.org/51 and its results when run at: 
http://paste.nobis-crew.org/52


Actually, given your next paragraph, there's something I still didn't 
understand correctly: does a SMTask object has to set a wake-up 
condition for "I want to be woken-up as soon as this object is in state 
MOVED" ? Or would each SMTask object be woken-up as soon as any object 
goes in MOVED state ?


> An "runnable" class is a derivative of SMTask. It has to implement Do 
> method
> which is non-blocking, i.e. does not wait for external events itself. 
> Instead,
> it sets wake-up condition (poll on a file descriptor, a timeout) and 
> returns.
> It can return MOVED if it's internal state has changed so that other 
> objects
> can check for it. STALL means that internal state has not changed 
> significantly.
>  
>
Okay, so, if I understand correctly, the pseudo-code I describe above at 
2) would be (very very roughly - I haven't had time to dig your code in 
full yet) something like

class LuaJob : public SMTask {
public:
   int state;
   bool done;
   SMTask * subjob;

   LuaJob() : state(INITIAL), done(false), subjob(0) {}
   bool Done() { return done; }
   int Do() {
       switch(state) {
           case INITIAL:
               [... some lua init ...]
               m = MOVED;
               state = RUN_CODE;
           case RUN_CODE:
               if (lua_resume() == LUA_DONE) {
                   done = true;
                   state = ALL_DONE;
                   return m;
               } else {
                   state = WAIT_FOR_JOB;
               }
           case WAIT_FOR_JOB:
               if (!subjob->Done())
                   return m;
               state = RUN_CODE;
               delete subjob;  // didn't notice that in your code yet ; 
are the SMTask garbage-collected in the Scheduler ?
       }
   }
}

and any Lua binding would set up the caller's subjob to the actual 
SMTask object the engine is waiting at, using a wrapper:

int wrapper_for_some_binding(lua_State * L) {
   LuaJob * lj = findparent(L);
   lj->subjob = new the_luaSMTask_for_that_binding(L);
   return lua_yield(L, 0);
}


The "drawback" of this is that we have to create a lots of SMTask class, 
one for each binding. But it makes sense anyway: the binding has to be a 
task anyway since it'll most probably fire up some more atomic 
subcommands in order to process the Lua's request.

Well, I am in the right direction or completely wrong understanding how 
it should be handled ? :)

Cheers,

 -- Nicolas Noble