2

When it comes to algorithms for lock free / wait free data structures, Some algorithms will steal the 2 least significant bits from a pointer since they aren't used, and use them as status bits (like if a node is logically deleted or something). I figured that in java, I'd just use the AtomicStampedReference instead of bit stealing. However, I realized that the only way to solve the ABA problem in java is to use the AtomicStampedReference to keep track of whether a node was changed or not.

NOTE: If your'e not sure what the ABA problem is, wikipedia's gives a wonderful example explaining how badly things gets screwed up by it: https://en.wikipedia.org/wiki/ABA_problem

NOTE: The reason I say the ONLY way to solve the ABA problem is to use the AtomicStampedReference is based on this post: http://tutorials.jenkov.com/java-util-concurrent/atomicstampedreference.html#atomicstampedreference-and-the-a-b-a-problem

So, since I can't use the integer in the atomic stamped reference to keep track of things like logical deletion anymore, is there a way I can steal the unused bits in the reference itself? I tried to access the "unsafe" package to do this task by calling:

import sun.misc.Unsafe;

But when I do that, I get the following error from Eclipse:

Access restriction: The type Unsafe is not accessible due to restriction on required library C:\Program Files\Java\jre1.8.0_40\lib\rt.jar

Anyone have any ideas? If you're curious what I'm trying to do, I'm trying to implement a thread safe lock free hashmap in java as a school project. And I need to use the 2 LSB bits to differentiate between 3 different node types: a data node (00), a marked data node (01), or an array node (10)

EDIT: I should mention, I need the 2 status bits to be inside the atomic reference. The reason I need this is because I'm going to be doing compare and swap operations, and I need to Compare And Swap to fail if a data node (00) gets marked (01), or turned into an arrayNode(10). I originally used the integer in the AtomicStampedReference for this, but I can't do that anymore as the AtomicStampedReference should be reserved to prevent problems caused by ABA.

SomeGuy
  • 1,702
  • 1
  • 20
  • 19
  • If you need two bits, just use a [`BitSet`](http://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html). Also, Java does **not** have *pointers*. – Elliott Frisch Nov 18 '15 at 04:32
  • well, the problem is that I need the bits to be in the atomic reference. The reason for this is because I'm going to do a compare and set, and I want the compare and set to fail if a data node (00) gets marked (01) or is turned into an array node (10). – SomeGuy Nov 18 '15 at 04:38
  • I don't think you get to have what you want. Bit stealing makes the assumption that there's random meaningless scratch space, and that's not accurate in Java at either the JVM model or implementation levels. – chrylis -cautiouslyoptimistic- Nov 18 '15 at 04:50
  • Also, is there any reason why this isn't a duplicate of http://stackoverflow.com/questions/20537011/java-stealing-bits-from-references? – chrylis -cautiouslyoptimistic- Nov 18 '15 at 04:51
  • 2
    I don't understand why you can't use an `AtomicStampedReference` and just increment that every time a node is changed. If you need other data-structure-specific state information, such as whether a node is a data/array/marked node, that probably belongs in your data structure itself. You *could* steal a couple of bits (probably the MSB so increment still works) of your AtomicStampedReference, I suppose, but that seems unnecessarily convoluted. – type_outcast Nov 18 '15 at 04:53
  • I am planning on using the AtomicStampedReference to increment a counter every time a node changes to prevent ABA. The reason I'm storing the data/array/marked node in the reference itself is because I needed to have Compare and Set fail incase another thread just converted the data node into a marked data node or an array node. I didn't think of marking bits on the stamp, but I think that comment solves my problem as you ARE allowed to access bits in an integer... Thank you so very much! – SomeGuy Nov 18 '15 at 05:17

2 Answers2

2

AFAIK, there's no way to steal those 2 bits in Java.

However, when writing lock-free data structures in Java, the ABA problem is generally solved just by not reusing objects. Every time you change the atomic reference, you set it to a new object and just throw the old one away. The garbage collector guarantees that a new object will not be created at the same address as the old one until it's safe to do so and no ABA problems can result.

For things like node marking, you can have the atomic reference point to an intermediate wrapper class that just contains the reference to your node. Then CAS to a new wrapper with a different concrete type in order to change the mark. (e.g., DataNodeWrapper -> MarkedNodeWrapper) Again, every time you change the atomic reference, throw the old wrapper away so it won't cause you any ABA grief.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
-1

Okay, so it's impossible to steal bits from references in Java, but I ended up stealing bits from the atomic stamped reference. I stole the 2 MSB bits from the integer stamp to be used as status bits, and I used the remaining 30 bits in the integer as a counter. I can do this since java lets you do bit operations on integers:

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

I implemented and tested this in my java concurrent hashmap code. This solves the ABA problem, and still allows me to keep track of whether the reference is pointing to a node, marked node, or an arraynode.

Thanks you type_outcast for making the suggestion of stealing bits from the integer of the AtomicStampedReference!

EDIT I think I'll post the functions I wrote, to use the lower 30 bits of the integer as a counter, and the upper 2 bits as flags. The functions take in the integer of the AtomicStampedReference as the input, and the output depends on the function. I hope it helps anyone who might have a similar situation.

//get the incremented counter without messing up the mask bits
//if already at highest value (2^30 - 1, or 1073741823, reset to 0)
private int custInc(int rawStamp){
    int markedBits = 0xC0000000 & rawStamp;
    if (getUnmarkedStamp(rawStamp) == 1073741823)
        return (0 | markedBits);
    else 
        return ((rawStamp + 1) | markedBits);
}

//get the stamp value without the marked bits
private int getUnmarkedStamp(int rawStamp){
    int stampMask = 0x3FFFFFFF;
    return stampMask & rawStamp;
}


//call this to determine if the AtomicStampedReference is pointing to an array node
//10XXXXX... indicates arrayNode;
//01XXXXX... indicates marked data node
//00XXXXX... indicates a normal data node
private boolean isStampArrayNode(int rawStamp){
    int isArrayNodeMask = 0xC0000000;
    if ((isArrayNodeMask & rawStamp) == 0x80000000)
        return true;
    else 
        return false;               
}

//call this to determine if the AtomicStampedReference is pointing to an marked data node
//10XXXXX... indicates arrayNode;
//01XXXXX... indicates marked data node
//00XXXXX... indicates a normal data node
private boolean isStampMarkedDataNode(int rawStamp){
    int isArrayNodeMask = 0xC0000000;
    if ((isArrayNodeMask & rawStamp) == 0x40000000)
        return true;
    else 
        return false;               
}

//call this to get what the raw stamp value if you are to mark it as a marked data node
//01XXXXX... indicates marked data node.ensure that this is returned
private int getStampMarkedAsDataNode(int rawStamp){
    return (rawStamp | 0x40000000) & 0x7FFFFFFF;
}

//call this to get what the raw stamp value if you are to mark it as an array node
//10XXXXX... indicates arrayNode;
private int getStampMarkedAsArrayNode(int rawStamp){
    return (rawStamp | 0x80000000) & 0xBFFFFFFF;
}
SomeGuy
  • 1,702
  • 1
  • 20
  • 19