/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.internal.cache.ha; import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutput; import java.io.IOException; import java.util.Arrays; import org.apache.geode.DataSerializable; import org.apache.geode.DataSerializer; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.cache.EventID; /** * Class identifying a Thread uniquely across the distributed system. It is composed of two fields * 1) A byte array uniquely identifying the distributed system 2) A long value unqiuely identifying * the thread in the distributed system * * The application thread while operating on the Region gets an EventID object ( contained in * EntryEventImpl) This EventID object contains a ThreadLocal field which uniquely identifies the * thread by storing the Object of this class. * * @see EventID * * */ public class ThreadIdentifier implements DataSerializable { private static final long serialVersionUID = 3366884860834823186L; private byte[] membershipID; private long threadID; public static final long MAX_THREAD_PER_CLIENT = 1000000L; public static final int MAX_BUCKET_PER_PR = 1000; public static final long WAN_BITS_MASK = 0xFFFFFFFF00000000L; /** * Generates thread ids for parallel wan usage. */ public enum WanType { RESERVED, // original thread id incl putAll (or old format) PRIMARY, // parallel new wan SECONDARY, // parallel new wan PARALLEL; // parallel old wan /** * Generates a new thread id for usage in a parallel wan context. * * @param threadId the original thread id * @param offset the thread offset * @param gatewayIndex the index of the gateway * @return the new thread id */ public long generateWanId(long threadId, long offset, int gatewayIndex) { assert this != RESERVED; return Bits.WAN_TYPE.shift(ordinal()) | Bits.WAN.shift(offset) | Bits.GATEWAY_ID.shift(gatewayIndex) | threadId; } /** * Returns true if the supplied value is a wan thread identifier. * * @param tid the thread * @return true if the thread id is one of the wan types */ public static boolean matches(long tid) { return Bits.WAN_TYPE.extract(tid) > 0; } } /** * Provides type-safe bitwise access to the threadID when dealing with generated values for wan id * generation. */ public enum Bits { THREAD_ID(0, 32), // bits 0-31 thread id (including fake putAll bits) WAN(32, 16), // bits 32-47 wan thread index (or bucket for new wan) WAN_TYPE(48, 8), // bits 48-55 thread id type GATEWAY_ID(56, 7), // bits 56-62 gateway id (bit 63 would make the thread id negative) RESERVED(63, 1); // bit 63 unused /** the beginning bit position */ private final int position; /** the field width */ private final int width; private Bits(int position, int width) { this.position = position; this.width = width; } /** * Returns the field bitmask. * * @return the mask */ public long mask() { return (1L << width) - 1; } /** * Returns the value shifted into the field position. * * @param val the value to shift * @return the shifted value */ public long shift(long val) { assert val <= mask() : "Input value " + val + " is too large for " + this + " which has a maximum of " + mask(); return val << position; } /** * Extracts the field bits from the value. * * @param val the value * @return the field */ public long extract(long val) { return (val >> position) & mask(); } } public ThreadIdentifier() {} public ThreadIdentifier(final byte[] mid, long threadId) { this.membershipID = mid; this.threadID = threadId; } @Override public boolean equals(Object obj) { if ((obj == null) || !(obj instanceof ThreadIdentifier)) { return false; } return (this.threadID == ((ThreadIdentifier) obj).threadID && Arrays.equals(this.membershipID, ((ThreadIdentifier) obj).membershipID)); } // TODO: Asif : Check this implementation @Override public int hashCode() { int result = 17; final int mult = 37; if (this.membershipID != null && this.membershipID.length > 0) { for (int i = 0; i < this.membershipID.length; i++) { result = mult * result + this.membershipID[i]; } } result = mult * result + (int) this.threadID; result = mult * result + (int) (this.threadID >>> 32); return result; } public byte[] getMembershipID() { return membershipID; } public long getThreadID() { return threadID; } public static String toDisplayString(long tid) { StringBuilder sb = new StringBuilder(); long lower = Bits.THREAD_ID.extract(tid); if (lower != tid) { sb.append("0x"); sb.append(Long.toHexString(tid >> Bits.THREAD_ID.width)); sb.append("|"); } sb.append(lower); return sb.toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("ThreadId["); sb.append(toDisplayString(threadID)); sb.append("]"); return sb.toString(); } public String expensiveToString() { Object mbr; try { mbr = InternalDistributedMember .readEssentialData(new DataInputStream(new ByteArrayInputStream(membershipID))); } catch (Exception e) { mbr = membershipID; // punt and use the bytes } return "ThreadId[" + mbr + "; thread " + toDisplayString(threadID) + "]"; } /** * convert fake thread id into real thread id * * @param tid thread id * @return real thread id */ public static long getRealThreadID(long tid) { return Bits.THREAD_ID.extract(tid) % MAX_THREAD_PER_CLIENT; } /** * convert fake thread id into real thread id including WAN id * * @param tid thread id * @return real thread id */ public static long getRealThreadIDIncludingWan(long tid) { return getRealThreadID(tid) | (tid & WAN_BITS_MASK); } /** * check if current thread id is a fake thread id for putAll * * @param tid thread id * @return whether the thread id is fake */ public static boolean isPutAllFakeThreadID(long tid) { return Bits.THREAD_ID.extract(tid) / MAX_THREAD_PER_CLIENT > 0; } /** * check if current thread id is generated by ParallelWAN * * @param tid thread id * @return whether the thread id is generated by ParallelGatewaySender */ public static boolean isParallelWANThreadID(long tid) { return WanType.matches(tid) ? true : tid / MAX_THREAD_PER_CLIENT > (MAX_BUCKET_PER_PR + 2); } /** * Checks if the input thread id is a WAN_TYPE thread id * * @param tid * @return whether the input thread id is a WAN_TYPE thread id */ public static boolean isWanTypeThreadID(long tid) { return WanType.matches(tid); } /** * create a fake id for an operation on the given bucket * * @return the fake id */ public static long createFakeThreadIDForBulkOp(int bucketNumber, long originatingThreadId) { return (MAX_THREAD_PER_CLIENT * (bucketNumber + 1) + originatingThreadId); } /** * create a fake id for an operation on the given bucket * * @return the fake id */ public static long createFakeThreadIDForParallelGSPrimaryBucket(int bucketId, long originatingThreadId, int gatewayIndex) { return WanType.PRIMARY.generateWanId(originatingThreadId, bucketId, gatewayIndex); } /** * create a fake id for an operation on the given bucket * * @return the fake id */ public static long createFakeThreadIDForParallelGSSecondaryBucket(int bucketId, long originatingThreadId, int gatewayIndex) { return WanType.SECONDARY.generateWanId(originatingThreadId, bucketId, gatewayIndex); } /** * create a fake id for an operation on the given bucket * * @return the fake id */ public static long createFakeThreadIDForParallelGateway(int index, long originatingThreadId, int gatewayIndex) { return WanType.PARALLEL.generateWanId(originatingThreadId, index, gatewayIndex); } /** * checks to see if the membership id of this identifier is the same as in the argument * * @param other * @return whether the two IDs are from the same member */ public boolean isSameMember(ThreadIdentifier other) { return Arrays.equals(this.membershipID, other.membershipID); } public void fromData(DataInput in) throws IOException, ClassNotFoundException { membershipID = DataSerializer.readByteArray(in); threadID = in.readLong(); } public void toData(DataOutput out) throws IOException { DataSerializer.writeByteArray(membershipID, out); out.writeLong(threadID); } }