-
Notifications
You must be signed in to change notification settings - Fork 1
Core Functionality
This section of the wiki covers core concepts and functionality, placed in the needle-core
module
These are the foundations on which the entire needle
suite of libraries and modules are built upon.
Taking the time to familiarize yourself them will help you make the most out of this library while avoiding common operational mistakes.
Simply put, an AffinityDescriptor
contains core affinity specifications. It has two factory methods you can choose from
var descriptor = AffinityDescriptor.from("0-2,4");
var descriptor = AffinityDescriptor.from(23);
- The above two affinity descriptors are effectually identical:
"0-2,4"
implies cores 1,2,3 and 5 (the core count starts at 0) while23
, which is 10111 in binary, refers to an identical core specification (core #1 is the right-most bit). - Using either
AffinityDescriptor.from("")
orAffinityDescriptor.from(0)
will result in a 'no affinity' (empty) descriptor. This is useful for scenarios where affinity settings are irrelevant or undesired (for example, when running tests)
At the core of this library lies the PinnedThread
- a JVM Thread
subclass that adds affinity awareness.
Once the thread is started, affinity settings passed via the AffinityDescriptor
are applied.
AffinityDescriptor descriptor = AffinityDescriptor.from("0-1,4-5");
Runnable runnable = () -> System.out.printf("My affinity - '%s'%n", Pinned.current().affinity());
PinnedThread thread = new PinnedThread(runnable, descriptor);
thread.start();
thread.join();
For maximum flexibility, a PinnedThread
can be started with or without an initial affinity, as various constructors exist.
Unlike traditional affinity solutions, one of the key strengths of the PinnedThread
is its ability have its affinity settings altered externally (not from within the thread's execution scope), beyond its inception!
Runnable runnable = () -> {
try {
System.out.printf("My affinity - '%s'%n", Pinned.current().affinity());
Thread current = Thread.currentThread();
synchronized (current) { current.wait(); }
} catch (InterruptedException ix) {
} finally {
System.out.printf("My affinity - '%s'%n", Pinned.current().affinity());
}
};
PinnedThread thread = new PinnedThread(runnable);
thread.start();
Thread.sleep(1000L);
AffinityDescriptor descriptor = AffinityDescriptor.from(3L);
thread.affinity(descriptor);
thread.interrupt();
thread.join();
The only requirement is for the thread to be started before trying to access affinity related functionality. Failure in doing so will result in an error
Runnable runnable = () -> {};
PinnedThread thread = new PinnedThread(runnable);
AffinityDescriptor descriptor = AffinityDescriptor.from("1,3");
thread.affinity(descriptor); // a NeedleException is thrown
Subclassing the PinnedThread
class is also a viable option, if your design works that way. Note that this approach requires additional attention to affinity setting initialization.
class ExtensionThread extends PinnedThread {
ExtensionThread(AffinityDescriptor descriptor) {
super(descriptor);
}
@Override
public void run() {
super.run(); // Always call super.run() first in order to ensure affinity settings are properly applied
// Your code goes here
}
}
In scenarios where it's undesirable or impossible to replace Thread
implementations with PinnedThread
, you can apply affinity setting from within the run()
method in the following way:
// Put the affinity setting inside the Runnable
Runnable runnable = () -> {
var descriptor = AffinityDescriptor.from("0-1,3");
Needle.affinity(descriptor);
// Your code goes here
};
// Create a thread and start it
Thread thread = new Thread(runnable);
thread.start();