Showing posts with label Safety. Show all posts
Showing posts with label Safety. Show all posts

Thursday, February 27, 2025

Let Them Burn

A recent article in MIT Technology Review (probably paywalled) is about dealing with electric vehicle battery fires.

https://www.technologyreview.com/2025/02/24/1111551/ev-lithium-ion-battery-fire-first-responders-firefighters/

It's based on the research by an EV battery pack designer who is also a volunteer firefighter, and who now consults with fire departments on this issue. His conclusion: let them burn, while trying to isolate them from surrounding vehicles and structures. Isolating can mean anything from covering them with a fire blanket, to (as one case study illustrated) moving the EV to a vacant lot with a forklift while it is burning. Wow.

Fires need three things to continue to burn: fuel, oxygen, and heat. Typical firefighting techniques involve interrupting one or more of these constituents. But lithium battery packs provide all three all by themselves, as part of a "thermal runaway" chemical reaction.

Traditional vehicle fires are typically centered around the easily accessible engine compartment, and can usually be put out in minutes with hundreds of gallons of water. EV fires are centered around the huge battery pack often underneath the vehicle, and - if they can be put out at all - may take hours and thousands of gallons of water, and may later spontaneously reignite.

The article has many worrisome case studies, including one where an EV owner accidentally drove his car off a pier in Florida. When the battery pack became saturated with electrically conductive salt water, it shorted and ignited... and continued to burn under thirty feet of water. Wow again. EV batteries igniting when saturated with salt water from flooding in coastal areas due to hurricanes is apparently a growing phenomena.

As a typical homeowner with lots of lithium battery packs - some quite large, for power tools - I've gotten concerned enough about this that I don't leave the packs on chargers when no one is at home (not even phones, laptops, or tablets). And I have a small chest of drawers inside the house just inside the door from the garage in which I store my expensive charged lithium battery packs (which don't like the cold either, but that's more of a longevity issue). I do keep rechargeable gear in both automobiles and on both motorcycles (jumper battery packs, tire inflators), and I worry about that.

Mrs. Overclock recently bought some small fire blankets, one of which is now out in the garage next to the wall mounted fire extinguisher.

Update: another recent article on the same topic from the same source, the gist being preventing EV battery fires is a lot more practical than extinguishing them.

Thursday, March 07, 2024

AI in the Battlefield

The name, "Tactical Intelligence Targeting Access Node" (TITAN), is pretty clever. Peter Thiel's Denver-Based Palantir Technologies, a software-driven data analytics company in the defense and intelligence domain, just won a US$178M contract to build an AI-driven mobile battlefield sensor fusion platform. From Palantir's home page: "AI-Powered Operations, For Every Decision". In this context, TITAN consumes a huge amount of data from remote sensors and tells soldiers what to destroy.

Cool. And absolutely necessary. Soldiers on the battlefield are inundated with information, more than humans can assimilate in the time they have. And even if we didn't build it, our peer adversaries surely will (or more likely, are).

This is the kind of neural network-based AI that's going to mistake a commercial airliner for an enemy bomber and recommend that it be shot down, even if its own cyber-finger isn't on the trigger. Because time is short, and if no other information is forthcoming, someone will pull that trigger.

In the inevitable following Congressional investigation, military officers, company executives, and AI scientists and engineers will be forced to admit that they have no idea why the AI made that mistake, and in fact they can't know, because no one can. When you have an AI with over a trillion - not an exaggeration - variables in its learning model, no one can understand how Deep Learning really works.

Seriously, this is a real problem in the AI field right now. AIs do things their own developers did not anticipate, and cannot explain.

Accidental commercial airliner shoot downs are so common they have their own Wikipedia page. And it's just a matter of time before the cyber-finger is on the trigger, because it can respond so more quickly than its overwhelmed human operators.

The worst thing that could happen is for TITAN be an unqualified success. Someone will get the idea that maybe such a system should have its cyber-finger on the red button for strategic ICBMs.

Update (2025-03-03)

The Associated Press recently had an interesting article on the use of U.S. large language models by the Israeli Defense Force.

Saturday, January 13, 2024

Military EMSO Versus Commercial Aircraft

Jeff Wise wrote this interesting article about how commercial aircraft are getting all crossways - figuratively and literally - as nation states and other actors are jamming and spoofing GPS/GNSS and using other ElectroMagnetic Spectrum Operations (the broader term that has replaced Electronic Warfare) generally targeted at military activity. Like a lot of embedded systems, the boxes inside commercial aircraft were never designed with malware and malicious signals in mind.

Jeff Wise, "Air Travel Is Not Ready For Electronic Warfare", New York Magazine, 2024-01-02

I belong to the Association of Old Crows, a professional society for EMSO folks, and I get their Journal of Electromagnetic Dominance. It's mostly about RF stuff far more low level than my area of expertise, being an embedded/real-time/telecom software/firmware guy, so I can't really appreciate most of it. But the volume of ads and articles in the journal makes it obvious this is a highly active area for both defense and offense.

Black Box AIs in Air Defense Systems

I've said many times - everyone is probably tired of hearing me say it - that I think the use of neural network AI - like used in LLMs/GPTs - in air defense systems for target identification is inevitable. And putting the AI in control of firing to reduce response time will also happen. Accidental shootdowns of commercial aircraft due to human error is common enough that it has its own Wikipedia page, so the AI will actually probably be more accurate than humans. But it's just a matter of time until a commercial aircraft is misidentified by an AI as an enemy target. And when there's the resulting U.S. Congressional investigation about the loss of innocent civilian lives, many are going to be surprised when the defense contractors say that not only does no one know why the aircraft was misidentified, no one can know. That's how these massive neural network algorithms work; they're so far mostly black boxes.

Model Collapse In Air Defense System AIs

We need an enormous volume of high quality content created and curated by human experts to correctly train LLM/GPT-type AIs. Because such data sets are labor intensive, and therefore expensive, to create and to assemble, there will be enormous pressure to train AIs with AI-produced data. This might even happen unknowingly (as has already in fact happened) if the provenance of the original content isn't well documented (or the people building the AI just don't care). (There will be strong incentives not to reveal that content is AI generated, because human-created content will be so much more highly valued.) Training AIs with AI-generated data leads to model collapse, a kind of feedback loop in which errors and hallucinations in the training data are reinforced.

This is likely to occur with the air defense AIs I described above.

And there will be no quick way to fix this. We will likely have eliminated all the career paths of those very same human experts by our use of those same LLMs for their entry level jobs. As the existing cohort of experts retire, die, move into management, or otherwise quit producing content, there will be no one to take their place. See also: "eating your own seed corn".

Thursday, January 11, 2024

The Disastrous Cultural Evolution of Boeing

The news is full of the most recent Boeing debacle involving the 737 MAX 9 airliner and its door plug that bailed out during flight to land in someone's back yard, leading to sudden cabin depressurization and an emergency landing.

A colleague of mine (Thanks, Jeff!) passed along this interesting and short-ish article on some of the recent history of Boeing, published in The Atlantic about the time of the 737 MAX 8 crashes in 2019 involving the aircraft's Maneuvering Characteristics Augmentation System (MCAS).

Jerry Useem, "The Long Forgotten Flight That Sent Boeing Off Course", The Atlantic, 2019-11-20

The gist:

In 1997, Seattle-based Boeing merges with the much smaller McDonnell Douglas (MCD) in a stock swap. Analysts at the time described it as MCD buying Boeing with the larger company's own money. Surprisingly, the finance-centric (read: MBAs) management of MCD takes over the upper management tiers of Boeing that was previously manned by former engineers. Then, in 2001, the new MCD-based upper management gets tired, apparently, of being questioned by the engineers about cost-cutting and safety concerns, so the entire upper management team moves to new digs in Chicago, 1500 miles away from where the aircraft are built.

WTF?

My favorite jobs over the past four decades plus change have been those in which software, firmware, and hardware product development were closely associated - both culturally and geographically - not just with each other, but also with testing, management, production, and customer support.

My interest in this isn't just from a general product development perspective.

I've had the privilege of having worked on several embedded systems products for the business aviation market, products that could use the term cloud computing in a literal sense. None of those products were flight safety critical. For you aviation geeks, our processes conformed to AS9100, a quality standard, and with DO-178C DAL D, a safety standard, and were tested under DO-160. I even did some hacking with ARINC 429 (an aviation packet bus) and ARINC 717 (an aviation synchronous bus used to log to the aircraft flight data recorder). I got to make the Asterisk open source PBX work with the cockpit two-wire headsets, and with the Inmarsat and Iridium satellite constellations. That job had me crawling around in the equipment bay of a two-engine business jet, and taking short test flights.

(I took these photographs of our Bombardier Challenger 600 test aircraft at Centennial Airport (KAPA) near Denver Colorado.)

On Its Way

Interior Looking Aft

I even got to do some product integration at the Gulfstream Aerospace plant in Savannah Georgia, where I may have walked through Oprah Winfrey's new private jet on the assembly line.

It doesn't get much better than that.

Although our business aviation products were certified for DO-178C Design Assurance Level D - the least safety critical level for which the U.S. Federal Aviation Administration requires certification - we had to have our software certified by an FAA Designated Engineering Representative (DER), essentially an FAA licensed contracted inspector. That turned out to be no small thing. From what I've read, the processes for DAL A - flight safety critical - aviation products were like software development processes dialed up past eleven. The amount of scrutiny and testing that every single line of code receives makes you wonder how the MCAS debacle ever happened. Although it's interesting to note that, like many large aviation companies, Boeing had its own DERs on their payroll.

The cautionary tale of the disastrous cultural evolution of Boeing is a remarkable one, from both a safety and a product development point of view.

Friday, January 05, 2024

Right to Repair, Polish Train Hackers, and the NSA's Ghidra

Google "Polish train hackers" and you'll find dozens of articles in the tech press about this story. Here is the link to the one I read, which was translated from the original Polish. It's terrific. Compelling reading if you're interested in the misuse of Digital Rights Management or the Right To Repair movement. Or if you're into embedded systems development and troubleshooting. Or just if you're into stories of heroic efforts by engineers.



Polish embedded systems hackers use (get this) the U.S. National Security Agency's open source Ghidra tool, originally intended to reverse engineer binaries of computer viruses and other malware, to figure out why high-tech passenger trains, like in the video above, quit working after undergoing routine maintenance - following the train manufacturer's own two thousand page maintenance manual - by a third-party.

What did they discover in various versions of the train software/firmware?

  • Odometer checks that prevent a train from running after a million miles.
  • Year, month, and day checks that prevent a train from running after a certain date.
  • Geofencing checks (naturally the trains have GNSS receivers) that prevent a train from running if it is within the boundaries of a competitor's maintenance depot.

I've used Ghidra myself, and written about it in my blog. The tool includes not just a disassembler similar to objdump, but also a remarkable decompiler that can translate machine code using common C compiler idioms and patterns back into C code. Ghidra understands a wide variety of Instruction Set Architectures. Just recently I've been using it to study the binaries of my own code compiled for a RISC-V target.

DRM and Right To Repair is a big deal in the U.S., Manufacturers of agricultural equipment, like farm tractors costing six figures, have resorted to similar shenanigans to prevent even the farmers who own the equipment from repairing their own stuff. So much so that Right To Repair legislation is coming to the forefront of both state and federal legislators.

Friday, September 01, 2023

A Swiss Cheese of Errors

 In 2021, an F-35B fighter jet rolled off the front of the aircraft carrier HMS Queen Elizabeth during a failed take off. "Rolled" is the probably the right term, as British carriers do not use a catapult like U.S. carriers. The pilot ejected and landed on the flight deck with only minor injuries.

It was discovered later that a protective cover - part of the "red gear" because of its color - over the left engine intake had mistakenly been left in place. It was sucked into the compressor inlet of the single center-mounted jet engine, reducing power to where it was insufficient for take off.

The U.S. and its allies recovered the carcass of the F-35B. Which is good, because if they hadn't, somebody else would have.

As you would expect after totaling a bleeding edge US$80M aircraft, part of the enormously expensive and troubled U.S. F-35 program, there was a lengthy post mortem report written. I read a short (about forty pages) summary and analysis of this report this morning by Aerosurrance, a U.K. based aviation consultancy.

There is a concept in the study of organizational and complex systems failures - which is a hobby of mine that I've written about here before - called the Swiss cheese model. This is where "holes" in redundant layers of safety systems and checks (because no such system is perfect) just happen to line up at exactly the wrong time to produce a catastrophic failure.

This report was like that: maintenance crews were overworked, fatigued, and under staffed; procedures were insufficient or not followed; poor design of the red gear; no sharing of similar failures, four of which had occurred before in the U.S.; etc. (And after having previously read several long ProPublica articles about failures in the U.S. Navy that were in part due to similar issues with overwork and fatigue, I'm seeing a pattern.)

While reading this article, it occurred to me that we already have a term for Swiss cheese types of failures, and have had for eons, we just tend not to use it when it the result is tragic.

We call them "a comedy of errors".

Tuesday, March 19, 2019

Automatic Pilot

My 2016 Subaru WRX - a turbocharged all-wheel-drive sport sedan that Mrs. Overclock and I refer to as The Batmobile - has a Continuously Variable Transmission (CVT), with which the WRX simulates a six-speed in software, and the EyeSight driver assist feature which includes lane keeping, collision avoidance, and adaptive cruise control. I had the WRX only a few months before Mrs. Overclock and I attended MidAmeriCon II, the 74thWorld Science Fiction Convention, in Kansas City Missouri. Having driven my WRX from Denver to KC, we afterwards drove to Branson Missouri, which is near the Arkansas border.

2016 Subaru WRX Limited

I routinely used the intelligent cruise control on that trip, and it was a marvel: automatically slowing down and then speeding back up as traffic allowed. I did play with the lane keeping on a lightly populated stretch of the Kansas Turnpike. I typically don't use the lane keeping feature in "active" mode, preferring instead to merely have it warn me of a "lane departure". But on the Turnpike, active mode worked as advertised; it was spooky to feel the steering wheel being turned underneath my hands to keep the car in the lane.

Mrs. Overclock and I were on a four lane divided highway in Missouri - maybe US65 - that was not controlled access: it had roads - sometimes gravel - branching off at right angles into the woods on either side of the highway. I had both the collision avoidance and the adaptive cruise control engaged.

I was in the right hand lane when a local in a car right out of the Dukes of Hazzard came roaring past me in the left lane like his hair was on fire. Maybe he suddenly realized his turn was imminent. He cut in front of me and slammed on his brakes to make his turn.

I was paying attention, had my hands on the wheel, and saw all of this. I barely got my foot off the accelerator to apply the brakes when EyeSight simultaneously chopped the throttle, downshifted several times, and slammed on the brakes.

Mrs. Overclock and I were thrown into our seatbelts. My WRX slowed down, the local made his sharp right hand turn onto a gravel road, and we missed rear-ending him. After he turned, EyeSight let off the brakes, accelerated, upshifted, and resumed our prior cruising speed as if nothing had happened.

I'd like to think I could have done as well... but I'm just has glad I didn't have to prove that. It was a remarkable experience.

Never the less, I wouldn't trust EyeSight in my WRX - or Autopilot in your Tesla - to drive the car for me under all foreseeable conditions. EyeSight is good. But not perfect.

In daily driving I've had EyeSight in my WRX, and in Mrs. Overclock's 2016 Subaru Outback station wagon, turn itself off when visibility was very bad due to heavy rain or snow, the lane markings were so worn I couldn't even tell where the lane was, or temporary markings due to construction appeared unclear or ambiguous. Even doing something as mundane as driving through the car wash requires I disable the collision avoidance feature so that the computer doesn't freak out.

Most recently, EyeSight was useless as we crawled home in the Outback from Denver International Airport during the "bomb cyclone" - our flight from Chicago O'Hare (a Boeing 757 in which I am quite sure our pilots kept their hands firmly on the controls) was one of the very few arrivals not cancelled that day - because the ground blizzards reduced visibility to just a few feet beyond the hood of the car. Near the airport, where it was at its worst (it looked like a war zone) we had to resort to using GPS on a mobile phone to determine when we were approaching a major intersection, and what intersection it was, visibility was so poor. (The fact that GPS - possibly augmented by cell tower triangulation - worked at all is a remarkable technical achievement in signal processing.)  What we did may have been risky, but it kept us from being among the four thousand passengers that ended up sleeping at DIA that night because the arrival and departure boards were a sea of CANCELLED.

Untitled

Automation works great... until it doesn't. Driver assist automation is great for augmenting our abilities, but not replacing them.

Automation for self-driving vehicles is a frequent topic of discussion amongst my real-time/embedded colleagues. It may be that it only has to do better than the average human at driving in conditions ranging from ideal to adverse. But our experience driving home from DIA suggests to me that that bar may not actually be all that low.

It's also hard not to ponder the unintended side effects of automation - particularly automation that overrides the human at the controls - when reading about the Boeing 737 MAX 8 story. But that's a topic for another day, when the facts are in.

Update (2022-10-28)

Not that long ago, Mrs. Overclock and I were at a stoplight preparing to exit a parking lot of a furniture store not far from our home. The cross street was a busy multilane state route. Mrs. Overclock was driving her 2016 Subaru Outback station wagon with the Eyesight system.

Our light turned green and she started to pull out. Suddenly the Outback autonomously slammed on the brakes as another car blasted across and through the intersection, inches away from the front of our car, at highway speeds, its driver having clearly missed seeing the stoplight. We could see the eyes of the passengers in the car across from us, which was also pulling out, grow wide.

Subaru's driver assist system may have saved our lives. At the very least, it kept my spousal unit's car from being totaled.

Wednesday, November 07, 2018

Vamoose, Rustler

For the past forty years I've been keeping my eyes open for a new programming language to do the kinds of things I need to do: concurrent, parallel, asynchronous, real-time, close to bare metal, mobile, embedded, and lately, internet of things. Most of my paying gigs are still in C++ or C. But I've seen more than one ginormous C++ code base that was effectively undebuggable. And as productive as I am in C, my PDP-11 experience means I know it for what it is: a portable structured assembler language.

After a few false starts, I've finally arrived at Go and Rust.

Go - also known by the more search-friendly name Golang - is a language that compiles to machine code, unlike Python or Java. It is a product of Google. Go was invented in part by some former Bell Labs folks even older than I am that were among those responsible for the invention of UNIX and C.

Rust is also a language that compiles to machine code. It is product of Mozilla, the folks that brought you among other things the Firefox browser, and a host of other folks that have formed a Rust community. Rust has been recently growing in popularity, if one is to believe more than one survey of programming languages.

If you want to skip the rest of this article and just peruse some open source Go and Rust code that I believe are reasonably idiomatic - that is, that uses these languages in the way in which they are each intended - you can find my repositories on GitHub. They are both licensed under the LGPL 2.1.

Each repository has a README that tells you how to extract the software from GitHub, build it, run the unit tests, and run the functional test. Both projects have been built, unit tested, and run on both an x86_64 Ubuntu system and an ARMv7 Raspbian system.

I will not teach you Go or Rust in this article. It's not even a "Hello, World!" kind of tutorial. I will give you a taste of what the languages look like, tell you what I learned about each language, what I liked and did not like, and what I think they would be useful for, as I solved the same problem in each.

Disclaimer: I am neither a Go nor a Rust expert, as will quickly become obvious.

Approach

My close friends and long-time colleagues will confirm that I have my share of personality defects. One of them is that I can only really learn by doing. Only through the laying on of hands (as I like to say) am I able to internalize new knowledge. One of the ways I choose to do this in a new programming language is to implement a non-trivial piece of code. My non-trivial code of choice is the Generic Cell Rate Algorithm (GCRA).

The GCRA is a rate control algorithm that I first encountered around 1996 when I was writing firmware in C++ at Lucent Technologies for products based on Asynchronous Transfer Mode (ATM). Implementing the GCRA - a library that implements the algorithm, unit tests, some utilities that use the library, and which can be used as a functional test - captures a lot of the day-to-day nuts-and-bolts experience of using a language: its usability, its support for object-oriented design, its build environment, its run-time library, its debuggability, its testability, its documentation, and so forth. I have implemented the GCRA in C++ (which shipped in a product), Java, C, and now Go and Rust.

Each of the two repositories cited above implements the GCRA in a library (Rust: crate), in the form of several functional units (Go: package, Rust: module), that includes an interface (Rust: trait) called Throttle that defines the common API, with two derived classes (Go and Rust: struct) named Gcra and Contract; a Contract is composed of two Gcra objects, one to limit the sustained rate, the other to limit the peak rate of an event stream. Events are whatever you want to rate limit: bits, bytes, packets, what have you. Unit tests are implemented for all functional units. One unit test simulates an event stream through the implementations. Another unit test generates actual events in the form of bytes in data packets through a test harness with multiple concurrent tasks using each language's native concurrency mechanism. The library is used to implement two utilities (Go: command, Rust: executable) named fletch and shape that are in turn used to create a functional test.

Go

Here is a snapshot taken from the GCRA implementation in Go as it renders in the Eclipse IDE. It is just a code snippet to give you a feel for what different portions of the language looks like. You can click on it to see a larger version.

Screen Shot 2018-11-07 at 11.22.50 AM

The package keyword controls visibility; variables and functions inside a package are visible to functions inside the same package. Access is otherwise controlled simply by capitalizing the name of the variable or function, which makes them publicly accessible.

A class is created by defining a struct inside the package. (Several classes can be defined inside the same package). Methods for that class are established by defining a function with an explicit object pointer as a kind of argument list separate from the function argument list.

There is no inheritance, but you can define an interface, which defines method signatures that have no implementation. Interfaces are not associated explicitly with a class but are defined via duck typing ("If it walks like a duck and quacks like a duck, it must be a duck"): if a class implements all the required method signatures of an interface, it is automatically a subclass that interface. There is however a way to inherit the methods of another class via composition (but I didn't use that in this project).

Like Java and Python, Go is a garbage collected language. Variables may be allocated from either the stack or the heap; the syntax of the language is agnostic to this and the developer has no control over it. The compiler performs escape analysis at compile time: if the pointer can escape the scope of the code block in which it was allocated, the object is allocated on the heap and is later deallocated by the garbage collector; otherwise it is allocated on the stack and deallocated when it goes out of scope.

Go has arrays, but unlike C and C++ they aren't ambiguously used sometimes as a variable and sometimes as a pointer. Arrays have Go are first class variables with a fixed length. If you want to use a subset of an array, Go provides an operator to define an array slice: a starting position within an array and a count of the number of elements in the slice. For example: you allocate a buffer as a fixed length array

var buffer = make([] byte, int(burstsize))

but when, for example, you use a standard library function to read data into the buffer, you get back a slice 

buffer[0:length]

depending on how much of the array was used.

Go has explicit width integer types, something every embedded developer will appreciate. Unlike C or C++, there is no implicit conversion between integer (or floating point) types. When you do math with mixed types, you must explicitly cast the types to match.

datum[0] = byte(rand.Int31n(int32('~') - int32(' ') + 1) + int32(' '))

The Go compiler complains if you import a package that you don't use, or if you declare a variable that you don't use. While this is irritating at first, it really contributes to cleaner code.

Here is a code snippet that shows how the unit test harness I wrote in Go creates four different concurrent tasks, one to produce a data stream, one to shape it to conform to a traffic contract, one to police it using another traffic contract, and one to consume it.

Screen Shot 2018-11-07 at 11.36.38 AM
These concurrent tasks in Go aren't POSIX threads. They are goroutines, very low overhead coroutines, all of which run in the context of one or more POSIX threads. Go typically creates a POSIX thread for every logical core on the processor on which the Go program runs. Each thread can multiplex one or more goroutines. Because goroutines are much lighter weight than a POSIX thread, they are much more scalable; a single Go program can consist of dozens, hundreds, or even thousands of goroutines.  This allows you to exploit many processor cores by assigning a new goroutine to, for example, every individual incoming network packet, or to every one of thousands of concurrent incoming data streams.

I've implemented this kind of architecture myself in embedded projects, where each coroutine was one state machine among many, all managed by a single thread (a VxWorks task in my specific case). Each state machine handled one among tens of thousands of simultaneous data streams. But I wrote thousands of lines of C++ code to do that; in Go, I could have done it in just a few pages of code.

This is what I think is Go's real raison d'etre: allowing the developer to exploit large numbers of processor cores by making it easy, even trivial, to write highly parallel or pipelined algorithms.

The go keyword is used to spawn off a function into a goroutine. Above, the function is defined inline, as a kind of closure. I use a WaitGroup, a kind of counting semaphore, to block the main thread of control until every goroutine completes. Inside each goroutine body I use the defer keyword to register a function that will be automatically called when the function that is the goroutine goes out of scope (essentially exits its terminating curley bracket) for any reason. That function signals the WaitGroup. Then I call my own function that actually implements the goroutine intended action.

Although I don't show it here, Go has a built-in message passing mechanism called a channel or chan. My producer and shaper goroutines, and policer and consumer goroutines, communicate over channels. A channel has a type, defining what kind of object it queues, and a depth, defining how many of those objects can be queued before the sender blocks.

My shaper and policer goroutines communicate using UDP sockets. The standard Go library has a comprehensive set of packages that include sockets, encryption, JSON, and other useful stuff.

If you stick to the directory layout expected by the Go tool chain, building a library and applications is as simple as

go build github.com/coverclock/com-diag-vamoose/Vamoose/cmd/fletch
go build github.com/coverclock/com-diag-vamoose/Vamoose/cmd/shape

and running the unit tests is a matter of

go test github.com/coverclock/com-diag-vamoose/Vamoose/pkg/ticks
go test github.com/coverclock/com-diag-vamoose/Vamoose/pkg/fletcher
go test github.com/coverclock/com-diag-vamoose/Vamoose/pkg/throttle
go test github.com/coverclock/com-diag-vamoose/Vamoose/pkg/gcra
go test github.com/coverclock/com-diag-vamoose/Vamoose/pkg/contract

One of the downsides of Go is that it doesn't play well with valgrind. I'd like to think that with its garbage collector, checking for memory leaks isn't necessary. But call me paranoid. Running valgrind on a Go application is an invitation to be inundated with worrisome warning messages that probably have nothing to do with your own code.

Rust

Here is another snapshot taken from the GCRA implementation in Rust as it renders in the Eclipse IDE. It is also just a code snippet to give you a feel for what different portions of the language looks like. You can click on it to see a larger version.

Screen Shot 2018-11-07 at 11.21.45 AM

Visibility in Rust is defined by what is inside the same module defined in the mod code block. Unlike Go, access is controlled using a keyword pub.

A class in Rust is defined with as a struct. More than one class can be defined inside the same module. Methods for a class are defined in the impl code block, and applicable interfaces identified using the post-fix for keyword.

In Rust, an interface is defined as a trait. Like Go, Rust doesn't have inheritance, but a trait can implement actual code.

When I write code in C++, I of course use the new and delete operators to explicitly allocate and deallocate objects on the heap. I always have to come to grips with when it is appropriate to call delete. I go through a kind of static code analysis in my head: if I pass this pointer to this function, is it merely borrowing it (so that it is still the responsibility of the caller to deallocate it), or am I moving the object to the function (so that it is now responsible for either deallocating it, or passing that responsibility on to someone else).

The Rust compiler does this too, at compile time, through the actions of its borrow checker. Rust does not do garbage collection. Instead, memory is allocated when the developer defines a variable. Then the compiler tracks that memory reference at compile time, enforcing hard and fast rules as to whether you can pass that pointer to a function, and whether that pass is a borrow or a move. The memory is automatically deallocated when it goes out of the scope in which is was originally allocated, or in which it was moved into.

There are some exceptions to this. There are containers provided by the Rust standard library that are allocated on the heap and which implement reference counts to determine when they can be deallocated. And you can explicitly deallocate memory before it goes out of scope using the drop operator.

Along with this almost no-cost memory management scheme is a set of rules which are rigidly enforced at compile time: you can have either one and only one read/write (mutable or mut) reference (pointer) to an object, or you can have multiple read-only (immutable) references to an object; and no null references.

Rust implements arrays and array slices very similarly to Go. You allocate a fixed size array

let mut buffer = [0u8; 65536];

and an input function effectively returns a slice

buffer[0..length]

depending on how much data was read in.

Just like Go, Rust has explicit width integer types, and there is no implicit conversion between integer (or floating point) types. When you do math with mixed types, you must explicitly cast the types to match.

let byte: u8 = ((rand() % (maximum as raw::c_int)) + 1) as u8;

Like Go, the Rust compiler complains if you import a module that you don't use, or if you declare a variable that you don't use, or if you initialize a variable to a value when you declare it and then don't use that value, or even if you have extra parenthesis in an expression (that part really irritates me).

Here is a code snippet that shows how the unit test harness I wrote in Rust creates four different concurrent tasks, one to produce a data stream, one to shape it to conform to a traffic contract, one to police it using another traffic contract, and one to consume it, just like I did in Go.

Screen Shot 2018-11-07 at 11.37.41 AM

Rust implements concurrency using full POSIX threads. Above I spawn off a thread defined as a kind of closure, and each thread calls its producer, shaper, policer, or consumer implementation in the form of a function. The main routine uses a POSIX thread join to wait until the four threads complete.

In the spirit of the borrow checker described above, the Rust compiler prevents data races between threads by forcing the developer to protect shared resources using a synchronization mechanism. I use a Mutex, not shown here except for the use of its lock method; the unlock is performed automatically when the variable goes out of scope. The compiler also forces the developer to allocate shared data on the heap, also not shown here except for its use of the unwrap method which returns a pointer to the object from its heap container, an Arc (for Atomic reference counting) in this case. The use of a reference counted container allocated on the heap prevents the object from being deallocated when its original reference goes out of scope in the main thread (which can exit before the child thread), and instead is deallocated when its reference count goes to zero. Like the borrow checker, this is all enforced at compile time.

The Rust standard library also provides the channel as a message passing mechanism, and this implementation uses them in a very similar way to how I used them in Go. UDP sockets are also used similarly to that in the Go implementation, and are provided by the Rust standard library.

One thing that I couldn't find in the Rust standard library was a random number generator, which is needed by my unit test harness. But one of the things I really liked about Rust was how easy it was to interface my code to the standard C library and call its random number function. Go has a way to do this as well, but it's not nearly as straight forward (but then I didn't need to use it in Go).

Screen Shot 2018-11-09 at 10.13.18 AM

If you stick to the expected directory layout, building a Rust library and applications can be done by

cargo build

and running the unit tests is as simple as

cargo test

My Rust applications played just fine with valgrind.

Remarks

I found Go very intuitive to use. Perhaps that was because it was inspired by Communicating Sequential Processes (CSP), a formal language for describing concurrent programs developed by British computer scientist Tony Hoare in 1978, and I recall reading his original paper in graduate school decades ago (and I have his book on CSP around here somewhere).

But more to the point, Go is an excellent fit for the post-Moore's Law world in which processors aren't getting faster, but are providing a lot more execution cores; in which if you want more performance, you need to parallelize your code.

Mostly I think Go is easy to use because it was designed by some pragmatic and experienced software developers who wanted a language in which they could get some work done.

Rust has a well deserved reputation for having a steep and high learning curve. What I did in days in Go took me weeks in Rust. It reminds me of a comment a colleague of mine made decades ago about the Ada programming language: "If you can just get your program to compile, it frequently works the first time." I was sharing my Go and Rust experience with a more contemporary colleague - who has a Ph.D. in physics and had worked at Fermilab - who has also used both languages, and he darkly remarked "I may not be smart enough to use Rust." Which, as we all know, is code for "seems overly complicated". But we both loved Go.

On the other hand, I probably won't be writing any device drivers, hard real-time algorithms, or micro controller firmware - tasks that are absolutely in my wheelhouse - in Go. Its background garbage collector would make that problematic. But I sure would be tempted to do so in Rust. Rust eliminates entire classes of errors regarding memory leaks and data races by simply eliminating your ability to write code with those bugs. Along the way, it eliminates common - and legitimate - design patterns I've used for years with concurrent code. It's Rust's way or the highway. But maybe that's okay.

In addition to its learning curve, a big complaint I have about Rust is that it's under documented. Despite the books and web sites to which Rust aficionados will point you, many of its features are undocumented, and the examples either don't work (because of recent changes in the language) or are too simple to be useful. That makes the Rust learning experience painfully full of reverse engineering and trial and error.

(One of the tricks I learned with Rust was to code a call to some function(s) in the standard library and assign the result to something like an integer variable. The type mismatch error message from the compiler would include the fully qualified type name of the function return. That was often more useful than what little documentation existed. Important safety tip: the "suggestions" made by the compiler were typically not really what I needed to do.)

If you are tempted to use Rust, you have to decide on the economic trade off between climbing the Rust learning curve versus writing in C or C++ and just avoiding making the kinds of mistakes that Rust eliminates. Those of us that have been writing large systems in C or C++ for decades already know how to do that. But since we're all so old as to almost be dead, Rust might be just the thing for the men and women that replace folks like me.
Update 2018-11-15: one feature both Go and Rust have that put them way ahead of C and C++: they do array bounds checking, made possible by the lack of confusion between arrays and pointers. This is a huge win, not just from a reliability point of view, but security as well: no more buffer overflows. That is probably reason enough to use Rust over C or C++.
I don't see Go and Rust as competitors. I believe that every programming language can be considered a domain specific language, and this is true of Go and Rust. The design of every programming language makes a different compromise in its choice between performance, usability, applicability, and so forth. And every successful programming language survived because it found a niche for which it was unusually well suited. There is no programming language that can fulfill all needs for all people. That's why everything isn't written in Lisp or Smalltalk. But while I might well use Rust for the really low-level stuff, I am pretty sure I could happily write everything else in Go.