The point being, when K&R was written, threading was highly experimental, and it remained so for at least the next decade.
Especially when charged by the precise amount of time/space/resources used, ?? 60's/70's mainfram vs. pc at home vs. today's cloud computing ??
Kind of a pity, really; nice hardware, but terrible luck with processor families.
https://en.wikipedia.org/wiki/Hybrid_kernel
Original linux/unix, same things could be done without the use of Windows NT style threads (aka fork/exec/pipe). Unix user space threads allow for a reduction in OS resource usage.
Threads concept: Outside of mainframe in 60's, threads just extra stuff unix service (80's internet), 1 running program providing a service to multiple users 90's forward - service on an internet port allowing multiple user access to a service, where extra unused resources per user add up fast!
Atomic types needed to implement things like mutexes to serialize access to shared state have become a language feature, lately. But K&R was written before that happened.
The operations performed on those objects are Undefined Behavior as far as ISO Standards are concerned, but OSes have traditionally provided definitions, often by reference to Posix. So, a program using them is relying on definitions from both places, and usually several others besides.
You're also sort of wrong even with regards to C. While threading isn't really cross platform, threads also can't be implemented purely as a library, see the paper "Threads Cannot Be Implemented as Libraries". You need at least some support from the language in terms of controlling operation reordering.
K&R certainly could have at least shown how to simulate concurrency in C. I wish I would have known about continuation passing/event loops when I implemented a dumbed down TCP on Arduinos.
Don't think concurrency in C would have been included per unix philosopy of program does one thing well. Would have been program to feed data to named pipe, program to read data from named pipe.
Closest K&R C related example: Duff's Device & Simon Tatham's coroutines in C using macros. https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
On simple-enough machines, it suffices to control when interrupts are enabled. More complicated machines with multiple out-of-order cores and fancy cache systems are enormously more fiddly.
But I don't think you can go far without controlling instruction reordering done by the compiler (as well as the CPU), which is what GP was talking about.
With more than one core, you cannot do without one of atomics or custom assembly code sequences. And if the CPU reorders instructions all by itself, you need special instructions besides.
Atomics in the language mean you get to let the compiler worry about most things. But an atomic operation may take enormously longer than you hope. An atomic increment may take a hundred times as long as an ordinary increment.
You can implement practical SRSW queues without atomics. Preventing reordering in compiler and CPU is enough.
I believe you don't need atomics to achieve mutual exclusion, at least not for 2 processes (Peterson's algorithm).
> You always need control over your compiler
Yes, this was the other poster's point, you cannot implement threading purely as a library. Function calls like pthread_mutex_lock() must be synchronization points and the compiler must not reorder any instructions around them.
This just seems weird with C because C language was designed to map as closely to hardware instructions as possible (hence why quite a lot of things in C are 'undefined' / dependent on platform used.)
Most modern languages are designed so that hardware issues are not a programmer's thing, but a compiler implementor's thing. Allows for much more sophisticated hardware without burdening the non-systems programmer.
I don't necessarily think that more is required in a language. Why would you want in a language what can be easily provided by a library?
We didn't have commoditized SMP machines until late 90s, early 2000s. Similarly, we didn't have 'good' threads until the early 2000s. In fact if you were coding in the 90s and early 00s you would've heard that threads were "bad", which was true at the time. Unfortunately, today you can still find this reiterated on places such as stack overflow even though it has long been untrue. We also deal with the fact that a lot of software made in the 90s has never been upgraded or replaced to deal with the architectural considerations of those days versus now. Today you can get up to 416 vCPUs on Google Cloud.
It’s funny how more than a few programmers that I’ve talked to seemed to firmly associate threads (and processes) with the perceived need in multiple CPU cores to be able to run them. Preemptive multitasking on a single core simply did not look natural to them, to the point that some did not realize that it’s even possible. (One of them was also astonished to see, and to hear, a mechanical watch as he did not know such things existed.)
In general, threads and their specific API are a feature of the OS.
Closest thing to direct "in-language thread concept" would be proposed C language definition addtion in which source code could specify a case statment block(s) be assigned to run on different cpu core(s) than the running program.
The first edition of K&R was published in 1978.
Wikipedia says that OS/360 had a notion of threads as early as 1967, although the distinction between threads and processes wasn't as clearly defined at that time.
In 1978, threading and concurrency was still an area of academic research. There are some great papers from Dijkstra and Lamport from that era, but much of the research at that time considered each concurrent "thread" to be a separate program; that is, each process was conceptually single-threaded, and if you wanted to do multiple things you made multiple processes for them.
The idea of multiple mini-processes inside a single process came later and took a while to stabilize into its modern form. POSIX didn't standardize thread-related APIs until 1995 and Linux didn't get modern POSIX threads support until Linux 2.6 in 2003.
Threads were certainly known of when C and UNIX were designed, but they weren't that common for typical programming tasks. Real-time tasks, mostly, where you need to respond to something while busy. Networks and GUIs were a big push behind threading becoming standard in most languages and environments in some way. But if you have no network sockets or other sources of events, and no need to render graphical output simultaneously with processing, you probably don't need threads. That's the kind of computing environment the K&R Book was written for.
Mac OS X Archived documentation talks about it, presumably Microsoft documentation does as well, but AFAIK there is no official Linux documentation for it outside of compiler documentation.
.dll, .dylib, .so are files created by/used by the linker the compiler calls to create/use the OS specific .dll, .dylib, .so files.
.dll is Microsoft Windows library
.so is unix related static library.
.dylib is MacOS related library.
Although, K&R is pre-C 89 standard, Plauger is more along the lines of C-89/92 standard.
More recent "standard ansi C library" : https://en.wikibooks.org/wiki/C_Programming/Standard_librari...