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 > (cond > ((nand Pid (kill Pid 0)) > (if (pop 'Feeds) > (setq > Pid (or (fork) (updatePresentFeed @)) > Cycles 30 ) > (task -1000) ) ) > ((=0 (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 Correct. This will start a task which runs once a second, and has three local variables Pid, Cycles and Feeds (a closure). > The problem: It won't run at all, not even once and I have no idea > why, it all looks good to me. Strange. I cannot see at the moment what might be the problem. To test it locally, I tried the following, using a stub function for 'updatePresentFeed': (de updatePresentFeed (F) (msg F '<feed> *Pid) (bye) ) (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) ) ) ((=0 (dec 'Cycles)) (and (kill Pid) (off Pid)) ) ) ) ) (updatePresentFeeds (1 2 3 4)) This seems to work, printing the numbers 1 .. 4 (the "feeds") and the PIDs of the individual child processes: 1<feed>28662 2<feed>28711 3<feed>28763 4<feed>28815 What exactly goes wrong? Cheers, - Alex -- UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe