# =========== Header =========== # File: Forks in NewtonScript # Project: (Newton Bowels) # Written by: Paul Guyot (pguyot@kallisys.net) # # Created on: 07/14/2000 # Internal version: 5 # # Copyright: © 2000 by Paul Guyot. # All rights reserved worldwide. # =========== # =========== Change History =========== # 07/19/2000 v5 [PG] Duh, misread Harri's report. I'm really tired. # 07/19/2000 v4 [PG] Fixed a typo. (Thanks to Chris for notifying it) # 07/18/2000 v3 [PG] Added support for eMate 300, US MP130 and D MP2100 # 07/16/2000 v2 [PG] Changes after Victor Rehorst precious remarks # 07/14/2000 v1 [PG] Creation of the file # =========== Abstract ======== This note explains how to use the Forks functions in NewtonScript. Those functions are undocumented. They don't allow to do a fork in the standard meaning of the term, but allow to build cooperative threads. Platforms ========= At least US MP120/2.0, US MP130, US MP2x00, eMate 300 and D MP2100. Probably any other 2.x platform, but you should check that the method I found to retrieve the fork functions works properly. (see later). Introduction ============ NewtonOS uses preemptive threads, called tasks. This means that the kernel is able to run several processes at a time, and that switching between processes is done without any intervention from the processes. But you cannot use threads from NewtonScript, because NewtonScript is not re-entrant, i.e. because running twice the code in NewtonScript may lead to errors especially with the GarbageCollect function. In fact, this is not totally true. Indeed, you probably already seen that you can do things while a progress dialog is open, although this dialog calls NewtonScript code (so does the activate package dialog). There is a mechanism in NewtonScript used for that which allows to use cooperative threads, i.e. processes that execute at the same time and which yields to the other. This document explains how to use them and discusses the underground principle of it. There is a sample project coming with this document, "Fork". FForkScript and FYieldToFork ============================ The operating system has two global functions with NewtonScript interface: Ref FForkScript( RefArg inRcvr, RefArg inFunc, RefArg inParamsArray ); Ref FYieldToFork( RefArg inRcvr ); Those are defined in the Jumptable. There is no global function object to call them, hence you need to setup your own function object. [There may be some day a note explaining JumpTable, Function Objects and Vectors] The vectors are: US MP120 US MP130 US MP2x00 eMate 300 D MP2100 FForkScript 0x01B54A54 0x1B54A7C 0x01ACE768 0x1ACE74C 0x01ACE754 FYieldToFork 0x01B55A9C 0x1B55AC4 0x01ACF7B0 0x1ACF794 0x01ACF79C Defining FForkScript & FYieldToFork: (the best method I found is to look into DoProgress) if not (GlobalFnExists('ForkScript) and GlobalFnExists('YieldToFork)) then begin local thePatchedFn := GetGlobalFn('DoProgress); UnDefGlobalFn('DoProgress); // I need the original DoProgress. This is for the case it has been // patched local theOriginalFn := GetGlobalFn('DoProgress); DefGlobalFn( EnsureInternal('ForkScript), theOriginalFn.literals[9]); // On the US MP120/2.0 & MP130, the function is the third literal. // On the US MP2x00, D MP2100 and eMate 300 // it is the second literal. // I guess (and hope) it only depends on the ROM version. local theROMVersion := Gestalt(kGestalt_SystemInfo).ROMVersion; local theYieldToForkFn := nil; if (theROMVersion = 0x20002) or (theROMVersion = 0x20003) then // US MP2x00 and eMate, D MP2100 theYieldToForkFn := theOriginalFn.literals[0].SetStatus.literals[2]; if (theROMVersion = 0x20000) or (theROMVersion = 0x20001) then // MP120/2.0 & MP130 theYieldToForkFn := theOriginalFn.literals[0].SetStatus.literals[3]; // I optimize the code if not compiled for debugging. if kDebugOn then if (theYieldToForkFn) then DefGlobalFn( EnsureInternal('YieldToFork), theYieldToForkFn ) else GetRoot():Notify(3, "Forks Methods", "Forks Methods are not supported for your ROM" ) else DefGlobalFn( EnsureInternal('YieldToFork), theYieldToForkFn ); if (theOriginalFn <> thePatchedFn) then DefGlobalFn('DoProgress, thePatchedFn); end; How to call them? ----------------- FForkScript takes two arguments. The first argument is the function to fork, the second argument the parameters to send to it in an array. You won't have the control back until the function ends (but the EventLoop will resume, therefore the Newton will be responsive). In the called function, you should call FYieldToFork to resume the EventLoop and therefore allow the user to interact with the Newton. The Sample Project ================== The sample project shows how to use those functions in the case you want to create asynchronous tasks using forks. The example is really basic and indeed based on the idea of traffic lights, but using radio buttons instead. The main layout offers a button to open a new window. Each window is added into an array to tell the threads to quit. Each thread window, in the ViewSetupFormScript, calls AddDeferredSend with itself as the first argument and 'start as the second. This has the advantage that at the next idle event in the event loop, this method will be called. Start forks the following way: func() begin // I setup a new fork, so that NewtonScript will // be able to react to events. ForkScript ( func (view) repeat begin view:Animate(); YieldToFork(); end until (view.quit), [self] ); // I don't have the control back until the function returned. // The end. I close myself :close(); end It forks to a new function which will loop forever calling Animate method and YieldToFork. It's important to understand that ForkScript won't return before the function stops. This is done by setting the quit slot of the view to true. So does the closebox' ButtonClickScript method and the main layout's viewQuitScript method. What's happening in the bowels? [Assumption] ============================================ To understand what's happening, you need to recall the event structure of the NewtonOS. There is a unique event loop. Each time an event happens, it is processed thus maybe involving NewtonScript. (example of events that don't are card related messages). When there is no event, NewtonScript main thread gives time to other threads created by fork methods. Those can quit or give time by using YieldToFork. NewtonScript also processes deferred messages. [There may be some day a note explaining the event loop] Other methods for asynchronous processing with NewtonScript =========================================================== There is another way to use asynchronous processing: it is based on deferred messages. These are sent when the event loop goes to idle. It has advantages over the fork methods, but also inconvenients. The main argument in favor of fork is that forking is much easier to implement. Besides, deferred messages introduce costs to transmit variables: functions must be splitted. Credits & History ================= The hint concerning FForkScript came from a discussion I had with Walter Smith and an engineer from Apple. Later, I found the FForkScript function when analyzing the TFlashStore class (it was in the stack trace because a filing was in progress). I thought it could be useful for the new nHTTPd version that supports multiple connections. I guess that NewtonScript native functions (those with a funcPtr) all start with an F and then comes the name of the function in NewtonScript. But ForkScript isn't defined. I then searched where it was used. It is only used into DoProgress global function. Victor Rehorst made precious remarks, so I clarified some parts and defined some terms. Special thanks to testers who helped me to add new entries in the table: Input for the US MP130 was provided by Harri Hohteri (the first test was made by Chris Ruprecht). Input for the eMate 300 was provided by Michael J. Huşmann. Input for the D MP2100 was provided by Heiko Cultus. ## ============================================================================ ## ## About the use of language: it is impossible to sharpen a pencil with a blunt ## ## ax. It is equally vain to try to do it with ten blunt axes instead. ## ## -- Edsger Dijkstra ## ## ============================================================================ ##