|
process
Design and code for a non-blocking programming language, with all async i/o, might benefit from using a lightweight process model loosely modeled on a process model in an operating system (OS). In other words, if you intend to represent concurrent lightweight threads in a single process, you might want to pretend each thread is a separate process, especially if a process address space is disjoint with respect to any other process in the same runtime. In one OS heavyweight process, you can do this two different ways: 1) by keeping each lightweight process disjoint (non-overlapping) in memory use, or 2) by making shared state immutable (perhaps via copy-on-write) so sharing cannot be empirically seen by two lightweight processes altering state. purpose
«
This page aims to explore an idea of adding a process model similar to Unix (or Linux) to a Lathe programming language prototype. But presence of a programming language runtime engine is not assumed. (One might want to use the same lightweight process model to model any concurrent async engine in one process, even those written in C or C++.) The approach shown here amounts to considering various system calls in turn, supposing they are part of a cy::Process lightweight process api and, then, trying to define what that means, whether it's a good idea, and what consequences are entailed. So this is system call tryouts for a cy::Process api, plus anything else that comes to mind. (This page is a stub for content still in progress.) |
menu
Here's a menu of pages under qi:
native threads
«
Native threads are nearly ignored on this page. All process api described here is assumed used only in a single thread. You can have multiple threads, and they can talk to each other, but all api shown here assumes only one thread will call the api shown—at least for the state involved. Other threads will use disjoint mutable state, so only immutable state can be accessed by more than one thread using api here. Why the restriction? (You're not entitled to an answer; but one appears anyway.) I plan to use this api in at least one single-threaded application. In one sense, this is actually a substitute for native threads. I get benefits I miss from using native threads without paying their costs. (Lightweight processes and native threads, both, seems a masochistic goal to pursue.) Later I can make a multi-threaded version of this any time I need. But I'm not going to waste unpaid time telling you how to do it. due diligence
«
I aim to minimize obligations as a goal. The highest priority is to avoid making anyone responsible for reading docs located somewhere else. Local docs have strong precedence. borrowing api « Rather than invent new api from scratch, when you see old api you want to imitate, just try to follow the old api instead (as long as it does not subvert your intended semantics). This approach tends to minimize your work (to define new api) and a reader's work (to grasp new api). You can say, "This api is just like that one, except for details XYZ." This saves a lot of time. But this might accidentally give folks a mistaken idea docs elsewhere take precedence over local invention, as if there's an obligation to read all docs in the whole world first, before proceeding. explicit definitions « An api is only defined to do whatever you see in text right next to an api description. For example, any api on this page is defined on this page only. (If a method just looks like api you see elsewhere—say, a Linux api—the api here is defined only in terms of what you see here, and not in any docs you find elsewhere.) In other words, we inherit no definitions from the world at large as a default global env. No precedence is established by convention unless ratified here. Standards found elsewhere only apply locally when we say so. Any detail in a standard not yet incorporated is just a vague hint you might pursue that detail later. But hints carry no force—no obligation to act. semantic diffs « When you pick an api like an existing standard, but with small differences, take care to note the differences. This reduces odds a reader will follow a false trail by accident. Be considerate.
globals
Little to no global state should appear in LWP (lightweight process) api because two lightweight processes should not be forced to share mutable state in an OS heavyweight process. But forcing them to share immutable state is okay. As direct consequence, we should pass state into any method you normally expect to have global scope in an OS heavyweight process. At least one parameter—perhaps the this parameter in an object base api—should scope the mutable runtime state involved: a representation of "the runtime." So if we imitate Linux system calls, every such call needs an extra parameter that means "this process" to scope the runtime for a lightweight process. In fact, it makes sense to define a cy::Process class to represent what a lightweight process knows. Methods imitating system calls should be member functions of cy::Process, to scope process state. sharing
«
Immutable state shared by every lightweight cy::Process instance belongs in another object shared by every process instance. |