Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
208a18d
Enhance requirements document formatting and structure
peter-lawrey Nov 21, 2025
822021b
Refactor ThreadLocal variable names for consistency and clarity
peter-lawrey Nov 21, 2025
d7b7686
Refactor test methods for consistency and improve exception handling
peter-lawrey Nov 21, 2025
e3d05f2
Update third-party-bom version to 3.27ea7
peter-lawrey Nov 21, 2025
93bedc7
Add documentation files for AI agent guidelines and project overview
peter-lawrey Nov 21, 2025
4542f1b
Add decision log and rename requirements document for Java Thread Aff…
peter-lawrey Nov 21, 2025
7124abb
Apply checkstyle adjustments
peter-lawrey Nov 12, 2025
0f6c7dc
Checkstyle issues addressed
peter-lawrey Nov 13, 2025
b267ab0
Update documentation
peter-lawrey Nov 16, 2025
8571e45
Checkpoint
peter-lawrey Nov 18, 2025
f1e6e09
Document and test native integration
peter-lawrey Nov 18, 2025
7889a95
Add security hardening and version tracking to native affinity library
peter-lawrey Nov 18, 2025
0be8351
Add shared architectural, operational, and security standards documen…
peter-lawrey Nov 20, 2025
e1dcd23
Code Analysis fixes
peter-lawrey Nov 21, 2025
7869296
Code Analysis fixes
peter-lawrey Nov 21, 2025
7a3ef41
Remove unnecessary blank lines in various classes for improved code r…
peter-lawrey Nov 24, 2025
728b839
Remove unnecessary blank lines in various classes for improved code r…
peter-lawrey Nov 24, 2025
dc84304
Remove unnecessary blank lines in various classes for improved code r…
peter-lawrey Nov 24, 2025
e4808e0
Remove unnecessary blank lines and improve documentation across multi…
peter-lawrey Nov 24, 2025
1c863fb
Remove JDK activation from quality profile in pom.xml
peter-lawrey Nov 24, 2025
3989267
Update copyright license in package-info.java to Apache-2.0
peter-lawrey Nov 24, 2025
1baf231
Refactor Affinity class to improve OS detection logic and error handl…
peter-lawrey Nov 26, 2025
dd7f512
Add TODO comment to OSGiBundleTest to indicate pending fixes
peter-lawrey Nov 26, 2025
784289a
@Ignore old test
peter-lawrey Nov 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# Guidance for AI agents, bots, and humans contributing to Chronicle Software's OpenHFT projects.

LLM-based agents can accelerate development only if they respect our house rules. This file tells you:

* how to run and verify the build;
* what *not* to comment;
* when to open pull requests.

## Language & character-set policy

| Requirement | Rationale |
|--------------|-----------|
| **British English** spelling (`organisation`, `licence`, *not* `organization`, `license`) except technical US spellings like `synchronized` | Keeps wording consistent with Chronicle's London HQ and existing docs. See the [University of Oxford style guide](https://www.ox.ac.uk/public-affairs/style-guide) for reference. |
| **ISO-8859-1** (code-points 0-255). Avoid smart quotes, non-breaking spaces and accented characters. | ISO-8859-1 survives every toolchain Chronicle uses. |
| If a symbol is not available in ISO-8859-1, use a textual form such as `>=`, `:alpha:`, `:yes:`. This is the preferred approach and Unicode must not be inserted. | Extended or '8-bit ASCII' variants are *not* portable and are therefore disallowed. |
| Tools to check ASCII compliance include `iconv -f ascii -t ascii` and IDE settings that flag non-ASCII characters. | These help catch stray Unicode characters before code review. |

## Javadoc guidelines

**Goal:** Every Javadoc block should add information you cannot glean from the method signature alone. Anything else is
noise and slows readers down.

| Do | Don't |
|----|-------|
| State *behavioural contracts*, edge-cases, thread-safety guarantees, units, performance characteristics and checked exceptions. | Restate the obvious ("Gets the value", "Sets the name"). |
| Keep the first sentence short; it becomes the summary line in aggregated docs. | Duplicate parameter names/ types unless more explanation is needed. |
| Prefer `@param` for *constraints* and `@throws` for *conditions*, following Oracle's style guide. | Pad comments to reach a line-length target. |
| Remove or rewrite autogenerated Javadoc for trivial getters/setters. | Leave stale comments that now contradict the code. |

The principle that Javadoc should only explain what is *not* manifest from the
signature is well-established in the wider Java community.

Inline comments should also avoid noise. The following example shows the
difference:

```java
// BAD: adds no value
int count; // the count

// GOOD: explains a subtlety
// count of messages pending flush
int count;
```

## Build & test commands

Agents must verify that the project still compiles and all unit tests pass before opening a PR:

```bash
# From repo root
mvn -q verify
```

## Commit-message & PR etiquette

1. **Subject line <= 72 chars**, imperative mood: Fix roll-cycle offset in `ExcerptAppender`.
2. Reference the JIRA/GitHub issue if it exists.
3. In *body*: *root cause -> fix -> measurable impact* (latency, allocation, etc.). Use ASCII bullet points.
4. **Run `mvn verify`** again after rebasing.

### When to open a PR

* Open a pull request once your branch builds and tests pass with `mvn -q clean verify`.
* Link the PR to the relevant issue or decision record.
* Keep PRs focused: avoid bundling unrelated refactoring with new features.
* Re-run the build after addressing review comments to ensure nothing broke.

## What to ask the reviewers

* *Is this AsciiDoc documentation precise enough for a clean-room re-implementation?*
* Does the Javadoc explain the code's *why* and *how* that a junior developer would not be expected to work out?
* Are the documentation, tests and code updated together so the change is clear?
* Does the commit point back to the relevant requirement or decision tag?
* Would an example or small diagram help future maintainers?

### Security checklist (review **after every change**)

**Run a security review on *every* PR**: Walk through the diff looking for input validation, authentication, authorisation, encoding/escaping, overflow, resource exhaustion and timing-attack issues.

**Never commit secrets or credentials**: tokens, passwords, private keys, TLS materials, internal hostnames, Use environment variables, HashiCorp Vault, AWS/GCP Secret Manager, etc.

**Document security trade-offs**: Chronicle prioritises low-latency systems; sometimes we relax safety checks for specific reasons. Future maintainers must find these hot-spots quickly, In Javadoc and `.adoc` files call out *why* e.g. "Unchecked cast for performance - assumes trusted input".

## Project requirements

See the [Decision Log](src/main/adoc/decision-log.adoc) for the latest project decisions.
See the [Project Requirements](src/main/adoc/project-requirements.adoc) for details on project requirements.

## Elevating the Workflow with Real-Time Documentation

Building upon our existing Iterative Workflow, the newest recommendation is to emphasise *real-time updates* to documentation.
Ensure the relevant `.adoc` files are updated when features, requirements, implementation details, or tests change.
This tight loop informs the AI accurately and creates immediate clarity for all team members.

### Benefits of Real-Time Documentation

* **Confidence in documentation**: Accurate docs prevent miscommunications that derail real-world outcomes.
* **Reduced drift**: Real-time updates keep requirements, tests and code aligned.
* **Faster feedback**: AI can quickly highlight inconsistencies when everything is in sync.
* **Better quality**: Frequent checks align the implementation with the specified behaviour.
* **Smoother onboarding**: Up-to-date AsciiDoc clarifies the system for new developers.
* **Incremental changes**: AIDE flags newly updated files so you can keep the documentation synchronised.

### Best Practices

* **Maintain Sync**: Keep documentation (AsciiDoc), tests, and code synchronised in version control. Changes in one area should prompt reviews and potential updates in the others.
* **Doc-First for New Work**: For *new* features or requirements, aim to update documentation first, then use AI to help produce or refine corresponding code and tests. For refactoring or initial bootstrapping, updates might flow from code/tests back to documentation, which should then be reviewed and finalised.
* **Small Commits**: Each commit should ideally relate to a single requirement or coherent change, making reviews easier for humans and AI analysis tools.
- **Team Buy-In**: Encourage everyone to review AI outputs critically and contribute to maintaining the synchronicity of all artefacts.

## AI Agent Guidelines

When using AI agents to assist with development, please adhere to the following guidelines:

* **Respect the Language & Character-set Policy**: Ensure all AI-generated content follows the British English and ISO-8859-1 guidelines outlined above.
Focus on Clarity: AI-generated documentation should be clear and concise and add value beyond what is already present in the code or existing documentation.
* **Avoid Redundancy**: Do not generate content that duplicates existing documentation or code comments unless it provides additional context or clarification.
* **Review AI Outputs**: Always review AI-generated content for accuracy, relevance, and adherence to the project's documentation standards before committing it to the repository.

## Company-Wide Tagging

This section records **company-wide** decisions that apply to *all* Chronicle projects. All identifiers use the <Scope>-<Tag>-xxx prefix. The `xxx` are unique across in the same Scope even if the tags are different. Component-specific decisions live in their xxx-decision-log.adoc files.

### Tag Taxonomy (Nine-Box Framework)

To improve traceability, we adopt the Nine-Box taxonomy for requirement and decision identifiers. These tags are used in addition to the existing ALL prefix, which remains reserved for global decisions across every project.

.Adopt a Nine-Box Requirement Taxonomy

|Tag | Scope | Typical examples |
|----|-------|------------------|
|FN |Functional user-visible behaviour | Message routing, business rules |
|NF-P |Non-functional - Performance | Latency budgets, throughput targets |
|NF-S |Non-functional - Security | Authentication method, TLS version |
|NF-O |Non-functional - Operability | Logging, monitoring, health checks |
|TEST |Test / QA obligations | Chaos scenarios, benchmarking rigs |
|DOC |Documentation obligations | Sequence diagrams, user guides |
|OPS |Operational / DevOps concerns | Helm values, deployment checklist |
|UX |Operator or end-user experience | CLI ergonomics, dashboard layouts |
|RISK |Compliance / risk controls | GDPR retention, audit trail |

`ALL-*` stays global, case-exact tags. Pick one primary tag if multiple apply.

### Decision Record Template

```asciidoc
=== [Identifier] Title of Decision

Date:: YYYY-MM-DD
Context::
* What is the issue that this decision addresses?
* What are the driving forces, constraints, and requirements?
Decision Statement :: What is the change that is being proposed or was decided?
Alternatives Considered::
* [Alternative 1 Name/Type]:
** *Description:* Brief description of the alternative.
** *Pros:* ...
** *Cons:* ...
* [Alternative 2 Name/Type]:
** *Description:* Brief description of the alternative.
** *Pros:* ...
** *Cons:* ...
Rationale for Decision::
* Why was the chosen decision selected?
* How does it address the context and outweigh the cons of alternatives?
Impact & Consequences::
* What are the positive and negative consequences of this decision?
* How does this decision affect the system, developers, users, or operations?
- What are the trade-offs made?
Notes/Links::
** (Optional: Links to relevant issues, discussions, documentation, proof-of-concepts)
```

## Asciidoc formatting guidelines

### List Indentation

Do not rely on indentation for list items in AsciiDoc documents. Use the following pattern instead:

```asciidoc
section:: Top Level Section
* first level
** nested level
```

### Emphasis and Bold Text

In AsciiDoc, an underscore `_` is _emphasis_; `*text*` is *bold*.

### Section Numbering

Use automatic section numbering for all `.adoc` files.

* Add `:sectnums:` to the document header.
* Do not prefix section titles with manual numbers to avoid duplication.

```asciidoc
= Document Title
Chronicle Software
:toc:
:sectnums:
:lang: en-GB
:source-highlighter: rouge

The document overview goes here.

== Section 1 Title
```
144 changes: 144 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Java-Thread-Affinity is a library that binds threads to specific CPU cores to improve performance, particularly on Linux systems. The library uses JNA (Java Native Access) to provide cross-platform thread affinity control with platform-specific implementations for Linux, Windows, macOS, and Solaris.

## Build Commands

```bash
# Build and run all tests
mvn clean verify

# Build without tests
mvn clean install -DskipTests

# Build only the affinity module
cd affinity && mvn clean verify

# Run a specific test class
mvn test -Dtest=AffinityLockTest

# Run a single test method
mvn test -Dtest=AffinityLockTest#testAcquireLock
```

Note: On Linux x86_64, the build will automatically compile native C code in `affinity/src/main/c/` during the `process-classes` phase. To skip native compilation, use `-DdontMake`.

## Module Structure

The project is a multi-module Maven build:

- **affinity**: Core library containing thread affinity APIs and platform-specific implementations
- **affinity-test**: Integration tests for the affinity module

## Architecture

### Platform Detection and Implementation Selection

The library uses a static initializer in `Affinity.java` that detects the OS at runtime and selects the appropriate `IAffinity` implementation:

- **Linux**: `LinuxJNAAffinity` (via JNA) - full affinity control, can get/set thread affinity, query CPU, get process/thread IDs
- **Windows**: `WindowsJNAAffinity` (via JNA) - thread affinity via kernel API, `getCpu()` returns -1
- **macOS**: `OSXJNAAffinity` (via JNA) - provides process/thread IDs only, no affinity modification
- **Solaris**: `SolarisJNAAffinity` (via JNA) - similar to macOS
- **Fallback**: `NullAffinity` - dummy implementation when JNA is unavailable

All implementations are in `affinity/src/main/java/net/openhft/affinity/impl/`.

### CPU Layout and Lock Management

The library builds a CPU topology model from `/proc/cpuinfo` (Linux) or assumes all CPUs are on one socket:

- `CpuLayout` interface represents CPU topology (cores, sockets, threads per core)
- `VanillaCpuLayout` parses `/proc/cpuinfo` to build the layout
- `NoCpuLayout` is used when CPU info is unavailable
- `LockInventory` tracks which threads hold locks on which CPUs

### AffinityLock Mechanism

`AffinityLock` is the main user-facing API that manages CPU reservations:

- Uses file-based locks in `java.io.tmpdir` (typically `/tmp/cpu-N.lock`) to coordinate between processes
- Supports try-with-resources pattern for automatic cleanup
- Provides strategies for CPU selection: `ANY`, `SAME_CORE`, `SAME_SOCKET`, `DIFFERENT_CORE`, `DIFFERENT_SOCKET`
- Can acquire locks by explicit CPU ID or string configuration ("last", "last-1", "any", "none", etc.)

The library distinguishes between:
- **BASE_AFFINITY**: CPUs available to the process on startup
- **RESERVED_AFFINITY**: CPUs reserved for thread affinity (isolated CPUs not in base affinity)

Use `-Daffinity.reserved={hex-mask}` to control which CPUs a process can reserve.

### Native Code

The `affinity/src/main/c/` directory contains:
- JNI implementations for higher-performance affinity operations (currently commented out in favour of JNA)
- Native clock implementation (`JNIClock.cpp`)
- Platform-specific code for macOS

## Key Classes

- `Affinity`: Static utility for low-level affinity operations (`getAffinity()`, `setAffinity()`, `getCpu()`, `getThreadId()`)
- `AffinityLock`: High-level API for acquiring CPU locks with automatic cleanup
- `AffinityThreadFactory`: Thread factory that automatically binds threads to CPUs based on affinity strategies
- `AffinityStrategies`: Enum of CPU selection strategies
- `CpuLayout`: Interface for CPU topology information

## Project Conventions (from AGENTS.md)

This project follows Chronicle Software standards:

- **Language**: British English spelling (organisation, licence, synchronised)
- **Character set**: ISO-8859-1 only, avoid Unicode/smart quotes
- **Javadoc**: Only document what isn't obvious from the signature - behavioural contracts, edge cases, thread safety, performance characteristics
- **Commit messages**: Subject <= 72 chars, imperative mood, reference issues, explain root cause -> fix -> impact
- **Testing**: Always run `mvn -q verify` before opening PRs
- **Documentation**: Keep .adoc files synchronised with code changes

## Common Development Patterns

### Acquiring CPU affinity in application code

```java
// Simple lock
try (AffinityLock lock = AffinityLock.acquireLock()) {
// work pinned to a CPU
}

// Lock a whole core (avoids hyperthreading sibling)
try (AffinityLock lock = AffinityLock.acquireCore()) {
// work
}

// Lock specific CPU
try (AffinityLock lock = AffinityLock.acquireLock(5)) {
// runs on CPU 5
}

// Lock with strategy relative to another lock
try (AffinityLock lock1 = AffinityLock.acquireLock()) {
try (AffinityLock lock2 = lock1.acquireLock(
AffinityStrategies.SAME_SOCKET,
AffinityStrategies.ANY)) {
// lock2 prefers same socket as lock1
}
}
```

### Using AffinityThreadFactory

```java
ExecutorService es = Executors.newFixedThreadPool(4,
new AffinityThreadFactory("worker",
AffinityStrategies.DIFFERENT_CORE,
AffinityStrategies.ANY));
```

## Testing Notes

Tests assume the system has isolated CPUs configured via kernel command line (`isolcpus=...`). Without isolated CPUs, some tests may not behave as expected since the library prioritises assigning threads to CPUs not in BASE_AFFINITY.

To check current lock state: `AffinityLock.dumpLocks()` prints CPU assignments.
3 changes: 3 additions & 0 deletions LICENSE.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
== Copyright 2016-2025 chronicle.software
:toc:
:lang: en-GB
:source-highlighter: rouge

Licensed under the *Apache License, Version 2.0* (the "License");
you may not use this file except in compliance with the License.
Expand Down
9 changes: 6 additions & 3 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
= Thread Affinity
:toc:
:lang: en-GB
:source-highlighter: rouge

image::docs/images/Thread-Affinity_line.png[width=20%]

Expand All @@ -10,9 +13,9 @@ image::https://maven-badges.herokuapp.com/maven-central/net.openhft/affinity/bad
image:https://javadoc.io/badge2/net.openhft/affinity/javadoc.svg[link="https://www.javadoc.io/doc/net.openhft/affinity/latest/index.html"]

== Overview
Lets you bind a thread to a given core, this can improve performance (this library works best on linux).
Lets you bind a thread to a given core; this can improve performance (this library works best on Linux).

OpenHFT Java Thread Affinity library
OpenHFT Java Thread Affinity library.

See https://github.com/OpenHFT/Java-Thread-Affinity/tree/master/affinity/src/test/java[affinity/src/test/java]
for working examples of how to use this library.
Expand Down Expand Up @@ -352,7 +355,7 @@ I have the cpuId in a configuration file, how can I set it using a string?

=== Answer: use one of the following

[source,java]
[source,java,opts=novalidate]
----
try (AffinityLock lock = AffinityLock.acquireLock("last")) {
assertEquals(PROCESSORS - 1, Affinity.getCpu());
Expand Down
Loading