Re: Fork and synchronous execution of child processes.

2010-09-04 Thread Alexander Burger
Hi Henrik,

> I have no idea either, very weird. I also tested to simply pass (1 2 3
> 4) and using your updatePresentFeed function, however I put (traceAll)
> at the top of updatePresentFeeds, this is what I get:
> 
>  task : -1000 (0 Pid NIL Cycles 0 Feeds Feeds (cond ((nand Pid (kill

'traceAll' doesn't help here very much, as it traces only Lisp-level
functions which are not called here (except 'task' itself). You need to
trace explicitly functions you are interested in, or set breakpoints.

Anyway, does the posted code work on itself, if you just 'load' it into
a ./dbg environment? That's how I tested it.


> Can you think of anything at all that might be interfering with the
> task execution, for instance running two servers/tasks listening on
> different ports at the same time in this very same parent?

This should not matter.

You could manually insert a breakpoint into the code to have it
stop when the task is executed:

  Cycles 0
  Feeds Feeds
  (! cond
 ((nand Pid (kill Pid 0))
(if (pop 'Feeds)

This will cause it to stop just before the 'cond'. Then you can single
step into the expression(s) to see what happens.

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: Fork and synchronous execution of child processes.

2010-09-04 Thread Henrik Sarvell
I have no idea either, very weird. I also tested to simply pass (1 2 3
4) and using your updatePresentFeed function, however I put (traceAll)
at the top of updatePresentFeeds, this is what I get:

 task : -1000 (0 Pid NIL Cycles 0 Feeds Feeds (cond ((nand Pid (kill
Pid 0)) (if (pop 'Feeds) (setq Pid (or (fork) (updatePresentFeed @))
Cycles 30) (task -1000))) ((=3D0 (dec 'Cycles)) (and (kill Pid) (off
Pid)
 task =3D (-1000 0 (job '((Pid) (Cycles . 0) (Feeds 1 2 3 4)) (cond
((nand Pid (kill Pid 0)) (if (pop 'Feeds) (setq Pid (or (fork)
(updatePresentFeed @)) Cycles 30) (task -1000))) ((=3D0 (dec 'Cycles))
(and (kill Pid) (off Pid))

I suspect that the above will not help much.

Can you think of anything at all that might be interfering with the
task execution, for instance running two servers/tasks listening on
different ports at the same time in this very same parent?



On Sat, Sep 4, 2010 at 5:26 PM, Alexander Burger  wrot=
e:
> Hi Henrik,
>
>> me. How exactly is (fork) working? The reference example:
>>
>> (unless (fork) (do 5 (println 'OK) (wait 1000)) (bye))
>>
>> seems to imply that the current expression, ie. (unless ... ) is being
>> rerun in the child (becoming the contents of *Fork) whilst execution
>> continues to the (bye) in the parent. The (fork) call will return NIL
>> in the child so the (do ... ) expression is being run there. It also
>> seems to imply that the (bye) expression waits for the child to finish
>> before shutting down?
>
> This description is not correct in some points.
>
> The PicoLisp 'fork' maps to the underlying fork() system call. When
> fork() is called, an new process is created, and both processes (the
> parent and the child) continue to execute from that point. At that
> moment, both processes are identical, the only difference is that in the
> parent process fork() returns the child's process ID, while in the child
> 0 is returned.
>
> The PicoLisp (fork) function returns a number (the child's PID) in the
> parent process, and NIL in the child process. So in the above example
>
> =A0 (unless (fork)
>
> is executed in the current process (which will be the "parent"
> thereafter), and (fork) returns the PID of the child. This is non-NIL,
> so the body of the 'unless' call is skipped by the parent, and it will
> continue execution with the next expression (not shown here).
>
> In the child process, (fork) returns NIL, and the body of the 'unless'
> call is executed. It consists of the two expressions
>
> =A0 (do 5 (println 'OK) (wait 1000))
> =A0 (bye)
>
> This is the code the child process sees. It prints five times 'OK' and
> then exits (terminating the child process).
>
> Note that the (bye) is probably important, because otherwise the child
> would continue with the next (not shown) expression which was already
> executed by the parent 5 seconds before.
>
> Thus, (unless (fork) (do-something) (bye)) is a typical pattern to have
> (do-something) done in a child process. In that regard, the formulation
> "(unless ..) is being rerun in the child" is a bit misleading, as the
> 'unless' is begins execution only in the parent, but after its condition
> (the (fork)) is evaluated, we suddenly have two processes which execute
> different branches in the code (the next statement in the parent, and
> the body of 'unless' in the child). That's the reason for the name of
> that system call, "fork".
>
> Then the formulation "(becoming the contents of *Fork)" is not correct.
> The global variable '*Fork' is not changed in that process, it may be
> set under program control _before_ the call to (fork) to contain
> expressions which should be executed in all child processes. Perhaps you
> meant '*Pid'? This is changed to a new value in the child process.
>
> Also, "whilst execution continues to the (bye) in the parent" is not
> right, as the parent never sees the (bye). It is only there for the
> child to terminate after it did its job.
>
>
>> Anyway, having a wait call like that as is the case in my code could
>> result in various race conditions, according to Alex he had to make
>> (wait) "smart" in order to avoid common cases, I'm sure he can
>> elaborate further on the details... In any case it works in my case
>
> Yes, though I would not say that there are race conditions.
>
> 'wait' is actually the PicoLisp "Event Handler". It can be called
> explicitly, and is called implicitly by other functions (see the
> reference of '*Run').
>
> This event handler takes care of several things, listening on file
> descriptors and watching timeout values. This includes all expressions
> in '*Run' (typically installed and de-installed by 'task'), but also
> internal events resulting from interprocess communication: The parent
> relays 'tell' messages from a child to all other children, accepts
> 'boss' commands (via 'hear'), detects when child processes terminate to
> clean up their resources, and is needed to synchronize the database
> between all child processes.
>
> So typically 'w

Re: Porting PicoLisp to Plan9

2010-09-04 Thread Vijay Mathew
Plan9 has a GCC port on which pico compiles with a few changes. (The
changes are required because the GCC port itself is not complete!).
The binary runs with no problems.

About the lack of shared libraries on Plan9: Plan9 don't need shared
libraries because components on Plan9 can interact using just four
system calls - open, read, write and close. As an example consider a
graphics library that could be shared by various applications:

/* Pseudo C */
handle =3D  LoadLibrary ("graphics.so");
CallFn (handle, "drawRect", 0, 0, 200, 200);
Close (handle);

The equivalent plan9 code will be something like this:

handle =3D open ("/sys/graphics/cntl");
cmd =3D "drawRect 0 0 200 200";
write (handle, cmd, strlen (cmd));
close (handle);

The interesting thing is that /sys/graphics/cntl/ could be on a remote
file system. This makes distributed computing easy and shared
libraries redundant.

Thanks,

-- Vijay

On Fri, Sep 3, 2010 at 1:17 PM, Mateusz Jan Przybylski
 wrote:
> Hi Tomas,
>
>
> On Thursday 02 September 2010 19:32:40 you wrote:
>> > makes up my daily work environment ;)
>>
>> cool. =A0I looked at it briefly but couldn't find my way around. =A0Do y=
ou
>> really use that acme editor or how it's called? =A0Is it possible to run
>> it as console only with vi (or emacs;-)?
>
> 
>
> To clear it up, i use the `Plan 9 from User Space' (i.e., the tools porte=
d to
> POSIX).
> Mostly because of Acme -- for very quick edit/[compile/]run/open-file-wit=
h-
> error cycle, and for the Rc shell -- practical for scripting.
>
> http://swtch.com/plan9port/man/man1/install.html
>
> Acme requires GUI (it could be implemented with exact same semantics in t=
ext
> mode, I guess, but it isn't). However, it uses little bandwidth, and so i=
t's
> usual and natural to use it over network in both native Plan 9 and ported=
 X11
> version. ssh -Y -C gets the job done even on slow links.
>
> It took me a few (3?) months to learn to use Acme efficiently.
>
> Acme takes a three-buttoned mouse, preferably with a scroll. It's pretty
> important to have all three physical buttons, since a lot is done via cho=
rding
> two buttons.
>
> Acme understands and uses a certain text processing language, based on se=
d's.
> It's executed via the `Edit THE_PROGRAM'.
> For example, I indent selected portion of text with:
> Edit s,^, =A0 =A0 =A0 ,g
>
> To find current selection address(es), I do
> Edit =3D
> etc.etc.
>
> You ought to have plumber up and running before you start Acme
> http://swtch.com/plan9port/man/man4/plumber.htm
> to get some extra functionality, mostly related to opening files you indi=
cate
> with rightclick.
> You want the plumber started on the same machine the Acme is running (one=
 of
> POSIX limitations)
>
> My plumbing rules (the file is $HOME/lib/plumbing) are as follows:
>
> #file / line in PHP format
> type is text
> data matches '(.+) on line ([0-9]+)'
> arg isfile $1
> data set $file
> attr add addr=3D$2
> plumb to edit
>
> include /usr/local/plan9/plumb/initial.plumbing
>
>
>
>>
>> > As a curiosity, there are no shared libraries in P9 -- only what we ca=
ll
>> > `static linking'. It's awesome in the longer run <3
>>
>> What's so awesome about it?
>
> No DLL hell.
> Programs (and thus processes) and libraries are very small.
> Greater percentage of code & data fits in CPU cache. Less indirect & more
> direct data access & code calls/jumps. etc. etc.
> http://harmful.cat-v.org/software/dynamic-linking/
>
> and it avoids the worst bane of DLL so far:
> http://harmful.cat-v.org/software/dynamic-linking/versioned-symbols
>
> 
>
> tl;dr:
> Plan 9 from User Space provides tools that may appeal to a long-time POSI=
X
> user ;-)
>
>
> Cheers,
> --
> Mateusz Jan Przybylski
>
>
> ``One can't proceed from the informal to the formal by formal means.''
> --
> UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=3dunsubscribe
>
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: Fork and synchronous execution of child processes.

2010-09-04 Thread Alexander Burger
Hi Henrik,

> me. How exactly is (fork) working? The reference example:
> 
> (unless (fork) (do 5 (println 'OK) (wait 1000)) (bye))
> 
> seems to imply that the current expression, ie. (unless ... ) is being
> rerun in the child (becoming the contents of *Fork) whilst execution
> continues to the (bye) in the parent. The (fork) call will return NIL
> in the child so the (do ... ) expression is being run there. It also
> seems to imply that the (bye) expression waits for the child to finish
> before shutting down?

This description is not correct in some points.

The PicoLisp 'fork' maps to the underlying fork() system call. When
fork() is called, an new process is created, and both processes (the
parent and the child) continue to execute from that point. At that
moment, both processes are identical, the only difference is that in the
parent process fork() returns the child's process ID, while in the child
0 is returned.

The PicoLisp (fork) function returns a number (the child's PID) in the
parent process, and NIL in the child process. So in the above example

   (unless (fork)

is executed in the current process (which will be the "parent"
thereafter), and (fork) returns the PID of the child. This is non-NIL,
so the body of the 'unless' call is skipped by the parent, and it will
continue execution with the next expression (not shown here).

In the child process, (fork) returns NIL, and the body of the 'unless'
call is executed. It consists of the two expressions

   (do 5 (println 'OK) (wait 1000))
   (bye)

This is the code the child process sees. It prints five times 'OK' and
then exits (terminating the child process).

Note that the (bye) is probably important, because otherwise the child
would continue with the next (not shown) expression which was already
executed by the parent 5 seconds before.

Thus, (unless (fork) (do-something) (bye)) is a typical pattern to have
(do-something) done in a child process. In that regard, the formulation
"(unless ..) is being rerun in the child" is a bit misleading, as the
'unless' is begins execution only in the parent, but after its condition
(the (fork)) is evaluated, we suddenly have two processes which execute
different branches in the code (the next statement in the parent, and
the body of 'unless' in the child). That's the reason for the name of
that system call, "fork".

Then the formulation "(becoming the contents of *Fork)" is not correct.
The global variable '*Fork' is not changed in that process, it may be
set under program control _before_ the call to (fork) to contain
expressions which should be executed in all child processes. Perhaps you
meant '*Pid'? This is changed to a new value in the child process.

Also, "whilst execution continues to the (bye) in the parent" is not
right, as the parent never sees the (bye). It is only there for the
child to terminate after it did its job.


> Anyway, having a wait call like that as is the case in my code could
> result in various race conditions, according to Alex he had to make
> (wait) "smart" in order to avoid common cases, I'm sure he can
> elaborate further on the details... In any case it works in my case

Yes, though I would not say that there are race conditions.

'wait' is actually the PicoLisp "Event Handler". It can be called
explicitly, and is called implicitly by other functions (see the
reference of '*Run').

This event handler takes care of several things, listening on file
descriptors and watching timeout values. This includes all expressions
in '*Run' (typically installed and de-installed by 'task'), but also
internal events resulting from interprocess communication: The parent
relays 'tell' messages from a child to all other children, accepts
'boss' commands (via 'hear'), detects when child processes terminate to
clean up their resources, and is needed to synchronize the database
between all child processes.

So typically 'wait' processes a list of events. Now you may well call
another 'wait' in a task, and this in fact happens frequently during
database synchronization between child processes, or when GUI functions
execute. However, 'wait' must ensure that while processing a list of
events, a recursively called 'wait' does not re-execute the same event a
second time. For that, the inner 'wait' takes care not to look at the
events in the outer 'wait', and this may result that outer events will
never get executed if the inner 'wait' is run in a loop in some task.

Anyway, this may get rather confusing, and isn't the way 'task's are
intended. I mean, instead that a task establishes its own timing loop
using nested 'wait's, one central capability of tasks _is_ being a
timer. That's why I recommended not to write such a loop in the task,
but execute the checking for that child process periodically with.


> but in order to avoid using (wait) Alex offered me the following
> version:
> 
> (de updatePresentFeeds (Feeds)
>(task -1000 0
>   Pid NIL
>   Cycles 0
>   Feeds Feeds
>   (

Fork and synchronous execution of child processes.

2010-09-04 Thread Henrik Sarvell
First some background to give a real world use case: During usage of
VizReader.com I soon realized that one in every few hundred feeds
would break the script that is responsible for importing their
articles. Due to the fact that this could be because of any kind of
reason and might not even be the same reason in each case, plus the
fact that I don't miss the lack of content at all (for some reason
these problems almost always arise with some obscure programming blog
by some clever programmer who coded his own blog app) I'm not going to
try and fix it.

Instead, what if it would be possible to initiate a child process that
in turn calls the parent process which is then responsible for
spawning subsequent processes which then imports the articles of each
feed? The problem is that this can not happen in an async. fashion, we
need to start the next import child after the first is finished or
30sec. If we hit the timeout we simply kill and start the next one.
Letting things happen in async. fashion would swamp the RAM with
hundreds of picolisp instances (trust me I know, I did the mistake at
first), apart from the fact that the database is locked during each
import so all these processes would have to wait anyway.

The shell command responsible for initiating the process looks like
this: ./p lib/http.l -'client "localhost" 8080 "import_feeds.l"' -bye

And in import_feeds.l there is among other things a call to
initiateUpdatePresentFeeds:

(de initiateUpdatePresentFeeds ()
=A0=A0 (let Feeds (collect 'fid '+Feed)
=A0=A0=A0=A0=A0 (boss 'updatePresentFeeds (lit Feeds

After some discussions with Alex on IRC he pointed me to line 60 of
misc/stress.l which gave me enough pointers to come up with the
following:

(de updatePresentFeeds (Feeds)
=A0=A0 (for Feed Feeds
=A0=A0=A0=A0=A0 (let Cycles 30
=A0=A0=A0=A0=A0=A0=A0=A0 (if (fork)
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (let Pid @
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (while (and (n0 Cycles) (kill Pi=
d 0))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (dec 'Cycles)
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (wait 1000))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (kill Pid))
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 (updatePresentFeed Feed)

The above works but this is where things are starting to get fussy for
me. How exactly is (fork) working? The reference example:

(unless (fork) (do 5 (println 'OK) (wait 1000)) (bye))

seems to imply that the current expression, ie. (unless ... ) is being
rerun in the child (becoming the contents of *Fork) whilst execution
continues to the (bye) in the parent. The (fork) call will return NIL
in the child so the (do ... ) expression is being run there. It also
seems to imply that the (bye) expression waits for the child to finish
before shutting down?

Anyway, having a wait call like that as is the case in my code could
result in various race conditions, according to Alex he had to make
(wait) "smart" in order to avoid common cases, I'm sure he can
elaborate further on the details... In any case it works in my case
but in order to avoid using (wait) Alex offered me the following
version:

(de updatePresentFeeds (Feeds)
   (task -1000 0
  Pid NIL
  Cycles 0
  Feeds Feeds
  (cond
 ((nand Pid (kill Pid 0))
(if (pop 'Feeds)
   (setq
  Pid (or (fork) (updatePresentFeed @))
  Cycles 30 )
   (task -1000) ) )
 ((=3D0 (dec 'Cycles))
(and (kill Pid) (off Pid)) ) ) ) )

The main thing here is to initiate a task that is being run every
second, when all feeds have been imported we terminate through the
(task -1000) call. The first condition will be run initially (Pid is
NIL) and when there is no child process: (kill Pid 0) is NIL, ie.
after the last condition have been run 30 times or when the update
present feed function has concluded (it ends with a bye call).

The problem: It won't run at all, not even once and I have no idea
why, it all looks good to me.

Cheers,
Henrik Sarvell
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe