{"id":19765790,"url":"https://github.com/openhft/chronicle-threads","last_synced_at":"2026-01-29T01:12:56.159Z","repository":{"id":27773369,"uuid":"31261853","full_name":"OpenHFT/Chronicle-Threads","owner":"OpenHFT","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-12T08:32:36.000Z","size":1950,"stargazers_count":168,"open_issues_count":3,"forks_count":59,"subscribers_count":30,"default_branch":"ea","last_synced_at":"2024-04-12T15:32:46.242Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://chronicle.software","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenHFT.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.adoc","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2015-02-24T13:43:50.000Z","updated_at":"2024-04-15T11:37:44.367Z","dependencies_parsed_at":"2023-10-16T14:17:30.020Z","dependency_job_id":"2803d9aa-e8b2-4af2-920c-c258124d42f4","html_url":"https://github.com/OpenHFT/Chronicle-Threads","commit_stats":null,"previous_names":[],"tags_count":263,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenHFT%2FChronicle-Threads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenHFT%2FChronicle-Threads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenHFT%2FChronicle-Threads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenHFT%2FChronicle-Threads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenHFT","download_url":"https://codeload.github.com/OpenHFT/Chronicle-Threads/tar.gz/refs/heads/ea","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247730069,"owners_count":20986404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T04:19:27.033Z","updated_at":"2026-01-29T01:12:56.153Z","avatar_url":"https://github.com/OpenHFT.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"= Chronicle Threads\nChronicle Software\n:css-signature: demo\n:toc: macro\n:toclevels: 2\n:icons: font\n\nimage:https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-threads/badge.svg[caption=\"\",link=https://maven-badges.herokuapp.com/maven-central/net.openhft/chronicle-threads]\nimage:https://javadoc.io/badge2/net.openhft/chronicle-threads/javadoc.svg[link=\"https://www.javadoc.io/doc/net.openhft/chronicle-threads/latest/index.html\"]\nimage:https://img.shields.io/github/license/OpenHFT/Chronicle-Threads[GitHub]\nimage:https://img.shields.io/badge/release%20notes-subscribe-brightgreen[link=\"https://chronicle.software/release-notes/\"]\nimage:https://sonarcloud.io/api/project_badges/measure?project=OpenHFT_Chronicle-Threads\u0026metric=alert_status[link=\"https://sonarcloud.io/dashboard?id=OpenHFT_Chronicle-Threads\"]\n\nimage::docs/images/Thread Affinity_line.png[width=20%]\n\ntoc::[]\n\n== This Library\n\nThis library provides high performance event loop implementations and utility functions to help with threading and concurrency.\n\nConcurrency is _hard_, and event loops provide an abstraction to make dealing with concurrency easier.\nContext switching between threads is expensive and best practice in a low latency system is to keep your latency-sensitive operations executing on a small number of \"fast\" threads and the event loop abstraction fits well with this approach.\n\nFor best latency these fast threads will be busy-spinning i.e. consuming a whole core, and the core running the fast thread is isolated from the OS and other applications.\n\nThis library contains a number of event loop implementations and additional features, all implemented with a view to minimising latency and avoiding garbage in the fast path.\n\n* \u003c\u003cEvent Handlers and Event Loops\u003e\u003e\n* \u003c\u003cPausers\u003e\u003e\n* \u003c\u003cHandler priority\u003e\u003e\n* \u003c\u003cPerformance Monitoring\u003e\u003e\n\n== Event Handlers and Event Loops\n\nAn event loop can have multiple event handlers installed on it and the event loop will repeatedly execute these event handlers.\nEach event handler is guaranteed to be called on only one thread.\nEvent handlers are expected to perform a chunk of work quickly and return without blocking, although the \u003c\u003cEvent Loops,EventGroup\u003e\u003e does provide a mechanism to support blocking event handlers - see\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/HandlerPriority.java[`HandlerPriority`].\n\nSee\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/EventLoop.java[`EventLoop`]\nand\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/EventHandler.java[`EventHandler`].\n\nFigure 1 illustrates a diagram of an event loop that serves several event handlers sequentially.\n\n[#img-eventloop]\n.Event Loop and Event Handlers\nimage::docs/images/EventLoop.png[1000,600]\n\n=== Event Handlers\n\nIn this section we explore common event handler use cases\n\n==== Implement event handler\n\nAn event handler can be created by implementing the `EventHandler` interface and overriding its `action()` method so that it executes the required work.\nThe `action()` method returns a boolean value signifying whether some work was done by the `action()` - an example is an event handler that tries to poll from a Chronicle Queue - the return value of the `action()` should indicate if the read succeeded and a message was processed.\n\nNOTE: The event loop considers this return value by using a heuristic: if the `action()` did some work, it is likely to do some more work next time, and so we should call it again as soon as we can.\nIf it did not do some work, it is less likely to do work next time, so it may appropriate to yield or pause before calling again - see \u003c\u003cPausers\u003e\u003e.\n\nAs a rule of thumb, an action handler should do a limited amount of work then return.\nIf it knows for sure that there is remaining work to be done at the point of return then it should return `true`.\n\n[source,java]\n----\npublic final class ExampleEventHandler implements EventHandler {\n    @Override\n    public boolean action() throws InvalidEventHandlerException {\n        // do work\n        return didWork;\n    }\n}\n----\n\n==== Adding to event loop\n\ncall the `addHandler` method of the event loop, see also \u003c\u003cStart event loop\u003e\u003e\n\n[source,java]\n----\nel.addHandler(eh0);\n----\n\n==== Removing an event handler from an eventLoop\n\nWhen an event handler wants to remove itself from the event loop, its `action()` method should throw `InvalidEventHandlerException`.\nThe `InvalidEventHandlerException.reusable()` method returns a reusable, pre-created, `InvalidEventHandlerException` that is unmodifiable and contains no stack trace.\nThe below event handler uninstalls itself after being called 30 times.\n\n[source,java]\n----\npublic final class UninstallingEventHandler implements EventHandler {\n    private int actionCount = 0;\n\n    @Override\n    public boolean action() throws InvalidEventHandlerException {\n        if (++actionCount \u003e 30)\n            throw InvalidEventHandlerException.reusable();\n        // do work\n        return didWork;\n    }\n}\n----\n\n=== Event Loops\n\nChronicle Threads contains a number of event loop implementations.\nThese are aggregated together in the link:src/main/java/net/openhft/chronicle/threads/EventGroup.java[`EventGroup`], and the general recommendation is to make use of this, although the other implementations of `EventLoop` can of course by used, or the user can implement their own.\nThe `EventGroup` also automatically enables \u003c\u003cPerformance Monitoring\u003e\u003e..\n\n==== Creating event loop\n\nevent group is created by calling the using the\nlink:src/main/java/net/openhft/chronicle/threads/EventGroupBuilder.java[`EventGroupBuilder`].\nBasic example shown below:\n\n[source,java]\n----\nEventLoop eg = EventGroupBuilder.builder()\n                .withPauser(Pauser.busy())\n                .withName(\"my-eg\")\n                .build()\n----\n\n==== Start event loop\n\nthe `EventLoop.start()` method starts the event loop.\nEvent handlers can be added before and after starting the event loop but will not be executed until `start()` has been called.\n\n[source,java]\n----\nel.start();\n----\n\n==== Stop event loop\n\nCalling the `stop()` method will stop the event loop executing handlers and blocks until all handlers have finished executing.\nCalling `close()` on an event loop first calls stop and will then call close on all event handlers.\nOnce an event loop has been stopped it is not expected that it can be restarted.\n\nFor more details on lifecycle please see javadoc of\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/EventLoop.java[`EventLoop`]\nand\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/EventHandler.java[`EventHandler`].\n\n=== Handler priority\n\nEvent handlers have a\nlink:https://github.com/OpenHFT/Chronicle-Core/blob/ea/src/main/java/net/openhft/chronicle/core/threads/HandlerPriority.java[`HandlerPriority`]\n(the default is MEDIUM) and when an `EventHandler` is installed on an `EventGroup`, the `HandlerPriority`\ndetermines which of its child event loops the `EventHandler` is installed on.\nThe second use of `HandlerPriority` is to enable each (child) event loop to determine how often each\n`EventHandler` is called e.g. `HandlerPriority.HIGH` handlers are executed more than `HandlerPriority.MEDIUM` handlers.\n\n== Pausers\n\nChronicle Threads provides a number of implementations of the\nlink:src/main/java/net/openhft/chronicle/threads/Pauser.java[`Pauser`]\nand it is straightforward for the user to implement their own if need be.\nThe `Pauser` allows the developer to choose an appropriate trade-off between latency vs CPU consumption for when an `EventLoop` is running events which exhibit \"bursty\" behaviour.\n\nThe recommended way to use `Pauser` - and this is how Chronicle Thread's event loop implementations use it:\n\n[source,java]\n----\n    while (running) {\n        // pollForWork returns true if work was done\n        if (pollForWork())\n            pauser.reset();\n        else\n            pauser.pause();\n    }\n----\n\nThe `Pauser` implementation can choose to yield, pause (with back off if required).\n\n=== Back off pausers\n\nIn the context of the heuristic in \u003c\u003cImplement event handler\u003e\u003e above - if an\n`EventHandler` does no work, then it may well not need to do any work for a while, as events often occur in bursts in the real world.\nIn this case it makes sense for the `Pauser` to keep track of how many times it has been called, and progressively implement longer pauses every time its `pause()` is called.\nThis behaviour allows a back off pauser to strike a reasonable balance between handling bursts of events quickly, but backing off and reducing CPU consumption in case of gap in incoming events.\n\nA good example of a back off `Pauser` is the `LongPauser` which will busy-loop for `minBusy` events (allowing the event loop to respond quickly if a new event arrives immediately), then will yield for `minCount` times before it sleeping for `minTime` increasing up to `maxTime`.\n\n=== TimingPauser\n\n`TimingPauser` interface extends the `Pauser` interface and behaves the same, but if the\n\n[source,java]\n----\nvoid pause(long timeout, TimeUnit timeUnit) throws TimeoutException;\n----\n\nmethod is called, the `TimingPauser` will keep track of accumulated pause times and throw a `TimeoutException` if the specified timeout is exceeded.\n`LongPauser`, `TimedBusy` and `TimingPauser` are of type `TimingPauser`.\n\n=== PauserMode\n\n`PauserMode` contains factory methods for `Pauser` implementations.\nBecause `Pauser` is not an enum, and implementations are not Marshallable, `PauserMode` can be used in yaml config files.\n\nThere are `PauserMode` for all `Pauser` factory methods.\n\n.Available PauserModes\n[cols=\"1,7,6,6,1,1\"]\n|===\n| *Mode* | *Description* | *Benefits* | *Downside* | *Can be monitored* | *Requires CPU isolation*\n| `busy` | Busy-loops | Minimises jitter | Uses max CPU, no monitoring support |  | \u0026#9989;\n| `timedBusy` | Same as `busy` but also implements `TimingPauser` | Minimises jitter | Uses max CPU, no monitoring support | | \u0026#9989;\n| `yielding` | Very briefly busy-loops then yields | Low jitter, stateless and thus can be shared | Uses high CPU | \u0026#9989; |\n| `balanced` | Back off pauser - implemented with `LongPauser` | Good balance of busy waiting and back off | Uses less CPU, but more jitter | \u0026#9989; |\n| `milli` | Sleeps for one millisecond, no back off | Low CPU use | Up to 1 ms jitter | \u0026#9989; |\n| `sleepy` | Less aggressive version of `balanced` | Minimal CPU | High jitter | \u0026#9989; |\n|===\n\nThe `busy` pauser minimises jitter for best performance.\nHowever, it means that an entire core is consumed and care should be taken to ensure that there are enough cores for each busy thread.\nIf not, the machine will perform worse.\n\nThe graph below illustrates how each different type of `PauserMode` backs off over time:\n\n.Pauser Mode Performance\nimage::docs/images/pauserModes.png[700,600]\n\n== Performance Monitoring\n\nEvent Loop Monitoring (AKA Loop Block Monitoring) is a very useful feature.\nThis is implemented by a handler which is installed on the `MonitorEventLoop`\nby the `EventGroup`.\nThe `ThreadMonitorHarness` handler monitors fast event loop threads to make sure event handler latency remains within acceptable bounds.\nThe `ThreadMonitorHarness` monitors latency by measuring the time the `action` method of the application event handlers takes to run.\nWhenever the method runs beyond an acceptable latency limit, `ThreadMonitorHarness` prints a stack trace for the event loop thread.\nThis output can be processed with tools to help identify hotspots in your program.\n\nEach monitored thread is wrapped by a `ThreadMonitor` created via the `ThreadMonitors` helper.\nThe monitor loop runs these handlers at a fixed interval and compares the time since the thread last started work with the configured limit.\nWhen the limit is exceeded a stack trace of the target thread is logged, allowing blocked threads to be detected without halting the system.\n\nSet the monitor event interval with system property `MONITOR_INTERVAL_MS`.\n\nDisable the monitor by setting the system property:\n\n[source,java]\n----\ndisableLoopBlockMonitor=true\n----\n\nYou can use any stack trace information to improve the design for efficiency.\n\n=== Recommendations:\n\n* Impose an interval of Xms for every event loop, and gradually decrease as blockages are found and fixed.\n* Consider adding `Jvm.safepoint` calls to help identify hotspots in the code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenhft%2Fchronicle-threads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenhft%2Fchronicle-threads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenhft%2Fchronicle-threads/lists"}