First some background to give a real world use case: During usage of 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

(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

(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

(de updatePresentFeeds (Feeds)
   (task -1000 0
      Pid NIL
      Cycles 0
      Feeds Feeds
         ((nand Pid (kill Pid 0))
            (if (pop 'Feeds)
                  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.

Henrik Sarvell

Reply via email to