Shared Resources · Software Development

Signalling Ownership

When creating complex software, a very important question is going to be asked about shared resources. Who owns that resource. Monolithic, multi threaded, multi process systems all need to deal with this issue, in one way, or another.

Some common examples: Memory, if a library allocates some memory, then who is its owner, and therefore responsible for its cleanup, the caller, or the library.

Databases, if more than one service shares a table in a database, who has the final say on schema changes, consumers or producers.

In Go, channels, who gets to close the channel, the creator of the channel, or the writer (this one might seem a given, whomever is writing to the channel should be in control of closing it, but when multiple goroutines are writing to a single channel, which of these is allowed to close it).

Again in Go, slices, if a slice is passed to a function that modifies the slice in some way, there is potential for the changes to either be seen by the caller, or completely different version of that slice is created that the caller cannot ever see the changes of.

There are a number of approaches that seek to solve some of these issues, garbage collected languages specifically work to say “nobody is using this piece of memory that was allocated for X anymore, I will free it” - in theory relieving the caller and the callee the discussion on who’s responsibility it was.

Rust’s famous borrow-checker within one of its compilers seeks to force callees and callers to have the discussion (on memory responsibilities) and will not move on until it’s resolved.

The functional programming paradigm has elements designed to avoid this issue, by ruling that no functions may have “side effects”, that is modification are only permitted by the owner. In effect this means that copies are passed around, and changes to those copies are communicated back to the callee, who makes the changes themselves.

Synchronisation primitives are specifically designed to allow multiple users to share contested resources. Whomever owns the lock protecting the resource may use the resource how they wish.

Operating systems manage sharing of the networking resource by creating a logical map, port numbers to applications, which is enforced such that traffic marked with a specified port number is only passed to the application that is registered to that port, and not allowing multiple applications to register for the same port concurrently.

Still with Operating Systems, file ownership is used to determine who can access those files.

This point is where I am going to focus for the rest of this blog article.

With resources being shared, the developer doesn’t necessarily have the tools available, or fitting, for the given usecase. Without compiler, or kernel, enforcement, the only things left to the developers are policy, coding conventions and documentation.

Operating systems have metadata that they use to enforce ownership, but this hard enforcement isn’t possible when creating software (even Rust’s borrow checker is only going to mandate shared memory issues, other issues require developer management).

Policy.

A team-wide policy on how to communicate shared resource ownership needs to be created. For example I personally have advocated that, if a slice is intended to be allowed to be modified by a callee, then a pointer to that slice should be provided, in all other cases a copy of that slice should be provided, and possibly returned.

Such a policy is then enforced during code reviews, and, if the policy is implemented on an existing codebase, when non-compliant code is discovered, code may be marked for change to become compliant.

Coding conventions.

This is very similar to policy, but I’m using this term to describe external policies. Policies that are created by people outside of the immediate team. Often these policies have automated tools that assist with enforcement, such as linters.

A popular convention might be related to the naming of variables to indicate responsibilities.

Documentation.

When a developer goes to use a library that creates or uses shared resources, clear documentation is required that explicitly informs users of their responsibilities.

However it’s almost impossible to ensure that the correct documentation exists, or even that a given developer will read it properly. It’s great when it exists and is used, but a folly to rely on it.

Summary.

Shared resource management is a massive issue in computer science and software development, with a multitude of soft and hard enforcement options. Those presented are but a small selection.

Published:
comments powered by Disqus