# Is the LinkedList Package Safe for Concurrent Access? Complete Analysis of Aperturerobotics/util

> Discover if the aperturerobotics/util linkedlist package is safe for concurrent access. This analysis reveals its robust sync RWMutex concurrency protection.

- Repository: [Aperture Robotics/util](https://github.com/aperturerobotics/util)
- Tags: deep-dive
- Published: 2026-02-25

---

**Yes, the `linkedlist` package in aperturerobotics/util is fully concurrency-safe, protecting all operations with an embedded `sync.RWMutex` that serializes access to internal pointers.**

The `linkedlist` package provides a generic doubly-linked list implementation in Go, designed for concurrent environments within the aperturerobotics/util repository. Understanding its thread-safety guarantees is essential for developers building multi-goroutine applications that require safe shared access to list structures.

## Concurrency Safety Mechanism

The `LinkedList[T]` type achieves concurrency safety through a straightforward locking strategy. Every public method acquires an exclusive lock before accessing or modifying the internal state, ensuring that only one goroutine can operate on the list at any given time.

### Mutex Protection Strategy

In [`linkedlist/linkedlist.go`](https://github.com/aperturerobotics/util/blob/main/linkedlist/linkedlist.go), the list structure embeds a `sync.RWMutex` named `mtx` alongside the mutable pointers `head` and `tail` ([lines 9‑13](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L9-L13)):

```go
type LinkedList[T any] struct {
    mtx  sync.RWMutex
    head *element[T]
    tail *element[T]
}

```

This design ensures that the mutex and the fields it protects are colocated, making it impossible to access `head` or `tail` without first acquiring `mtx`.

### Public API Locking Behavior

All public methods in [`linkedlist/linkedlist.go`](https://github.com/aperturerobotics/util/blob/main/linkedlist/linkedlist.go) follow the same pattern: acquire the lock at entry and release it via deferred unlock before returning. This applies to both mutating and read-only operations:

- **`Push`** ([lines 38‑41](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L38-L41)): Acquires `l.mtx.Lock()` to append elements to the tail.
- **`PushFront`** ([lines 46‑55](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L46-L55)): Acquires `l.mtx.Lock()` to prepend elements to the head.
- **`Peek`** ([lines 58‑66](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L58-L66)): Acquires `l.mtx.Lock()` to safely read the head value.
- **`IsEmpty`** ([lines 70‑74](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L70-L74)): Acquires `l.mtx.Lock()` to check list state.
- **`PeekTail`** ([lines 77‑86](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L77-L86)): Acquires `l.mtx.Lock()` to read the tail value.
- **`Pop`** ([lines 89‑105](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L89-L105)): Acquires `l.mtx.Lock()` to remove and return the head element.
- **`Reset`** ([lines 107‑112](https://github.com/aperturerobotics/util/blob/master/linkedlist/linkedlist.go#L107-L112)): Acquires `l.mtx.Lock()` to clear all elements.

Because every operation acquires an exclusive lock, concurrent goroutines cannot corrupt the list's internal pointers or observe partially updated state.

## Read-Write Lock Considerations

The implementation uses `sync.RWMutex` rather than a standard `sync.Mutex`, but **always acquires exclusive locks** (`Lock`) even for read-only operations like `Peek` or `IsEmpty`. The source code never calls `RLock()`.

This design choice trades potential read parallelism for implementation simplicity. While multiple concurrent readers could theoretically operate simultaneously using `RLock()`, the current implementation serializes all access. This does not compromise safety—it simply means the list does not optimize for high-concurrency read-heavy workloads.

## Concurrent Usage Example

The following example demonstrates safe concurrent access to a `LinkedList` from multiple goroutines without additional synchronization:

```go
package main

import (
	"fmt"
	"sync"

	"github.com/aperturerobotics/util/linkedlist"
)

func main() {
	// Create a shared list
	ll := linkedlist.NewLinkedList[int]()

	var wg sync.WaitGroup

	// Writer goroutine – pushes numbers 0‑9
	wg.Add(1)
	go func() {
		defer wg.Done()
		for i := 0; i < 10; i++ {
			ll.Push(i)
			fmt.Printf("pushed %d\n", i)
		}
	}()

	// Reader goroutine – pops values while they exist
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			v, ok := ll.Pop()
			if !ok {
				// List empty, give writers a chance
				continue
			}
			fmt.Printf("popped %d\n", v)
			if v == 9 { // stop after last expected value
				return
			}
		}
	}()

	wg.Wait()
}

```

Running this program with the race detector (`go run -race`) produces no warnings because every method internally acquires the necessary locks.

## Summary

- The `linkedlist` package in **aperturerobotics/util** is fully safe for concurrent access across multiple goroutines.
- **Mutex protection**: All operations acquire an exclusive lock on the embedded `sync.RWMutex` before accessing `head` or `tail` pointers.
- **Complete coverage**: Every public method—`Push`, `PushFront`, `Pop`, `Peek`, `PeekTail`, `IsEmpty`, and `Reset`—follows the lock-acquire pattern defined in [`linkedlist/linkedlist.go`](https://github.com/aperturerobotics/util/blob/main/linkedlist/linkedlist.go).
- **Trade-off**: The implementation uses exclusive locks even for reads, sacrificing read parallelism for simplicity without compromising safety.

## Frequently Asked Questions

### Is the linkedlist package safe for concurrent access?

Yes. The `linkedlist` package is designed for concurrent use and is safe to share across multiple goroutines without additional synchronization. Every public method acquires an exclusive lock on the internal `sync.RWMutex` before accessing the list's state.

### Does the linkedlist use read-write locks or exclusive locks?

The struct embeds a `sync.RWMutex`, but the implementation always uses exclusive locks (`Lock` and `Unlock`) even for read-only operations like `Peek` or `IsEmpty`. It never calls `RLock()`, meaning reads are serialized just like writes.

### Can I use LinkedList from multiple goroutines without additional synchronization?

Yes. You can safely call any combination of `Push`, `Pop`, `Peek`, and other methods from multiple concurrent goroutines. The internal mutex ensures that operations are atomic and that the list remains consistent under concurrent access.

### Where is the mutex defined in the source code?

The mutex is defined in [`linkedlist/linkedlist.go`](https://github.com/aperturerobotics/util/blob/main/linkedlist/linkedlist.go) at lines 9‑13, embedded directly in the `LinkedList[T]` struct alongside the `head` and `tail` pointers. This colocation ensures that all mutable state is protected by the same lock.