All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.finos.tracdap.common.netty.EventLoopResolver Maven / Gradle / Ivy

/*
 * Licensed to the Fintech Open Source Foundation (FINOS) under one or
 * more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * FINOS 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.finos.tracdap.common.netty;

import org.finos.tracdap.common.exception.ETracInternal;
import org.finos.tracdap.common.exception.EUnexpected;

import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.OrderedEventExecutor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Resolve the Netty event loop for the current thread.
 * 
* * Two resolution methods are supported, thread ID map and directly checking inEventLoop(). * The strict flag in the resolve method controls what happens if the event loop cannot be * resolved, strict = true will raise ETracInternal, strict = false will return null. This * class provides event loop resolution for EventLoopInterceptor and EventLoopScheduler, it * can also be called directly. *
* * Offload tracking can be supported by passing in an offload tracker and using the method * callingEventLoop(). The method currentEventLoop() will never resolve offloads. */ public class EventLoopResolver { // Java 19 introduces Thread.threadId() and deprecates Thread.getId() // The GET_THREAD_ID method is resolved to one or other depending on the Java version private static final Method GET_THREAD_ID = getThreadIdMethod(); private final Logger log = LoggerFactory.getLogger(getClass()); private final Map register; private final EventExecutor[] eventExecutors; private final EventLoopOffloadTracker offloadTracker; public EventLoopResolver(EventExecutorGroup eventLoopGroup) { this(eventLoopGroup, null); } public EventLoopResolver(EventExecutorGroup eventLoopGroup, EventLoopOffloadTracker offloadTracker) { this.register = new ConcurrentHashMap<>(); this.eventExecutors = null; this.offloadTracker = offloadTracker; eventLoopGroup.forEach(el -> el.submit(() -> registerEventLoop(el))); } public EventLoopResolver(EventExecutor[] eventExecutors, EventLoopOffloadTracker offloadTracker) { this.register = null; this.eventExecutors = eventExecutors; this.offloadTracker = offloadTracker; } private void registerEventLoop(EventExecutor executor) { if (executor instanceof OrderedEventExecutor) { var threadId = getThreadId(Thread.currentThread()); register.put(threadId, (OrderedEventExecutor) executor); } } private static long getThreadId(Thread thread) { // Invoke the method that was found for GET_THREAD_ID // Errors are not expected try { return (long) GET_THREAD_ID.invoke(thread); } catch (InvocationTargetException | IllegalAccessException e) { throw new EUnexpected(e); } } @SuppressWarnings("all") private static Method getThreadIdMethod() { // Try to find the best method for getting thread IDs // Reflective access to non-existent methods is a warning, so using SuppressWarnings on this method try { // Thread.threadId() should be available for Java 19+ and is the first choice try { return Thread.class.getMethod("threadId"); } catch (NoSuchMethodException e) { // no-op } // Thread.getId() is deprecated as of Java 19 and is the fallback choice // As of Java 21, Thread.getId() has not been removed try { return Thread.class.getMethod("getId"); } catch (NoSuchMethodException e) { // no-op } // Errors are not expected, at least one of these methods should be available throw new EUnexpected(); } catch (Exception e) { throw new EUnexpected(e); } } public OrderedEventExecutor currentEventLoop(boolean strict) { return resolveEventLoop(strict, false); } public OrderedEventExecutor callingEvnetLoop(boolean strict) { return resolveEventLoop(strict, true); } private OrderedEventExecutor resolveEventLoop(boolean strict, boolean includeOffloadMapping) { OrderedEventExecutor eventLoop; if (register != null) { eventLoop = resolveByRegister(includeOffloadMapping); } else if (eventExecutors != null) { eventLoop = (OrderedEventExecutor) resolveByDirectTest(includeOffloadMapping); } else { eventLoop = null; } if (eventLoop != null) return eventLoop; if (strict) { var message = "The current operation is running outside the registered event loop group"; log.error(message); throw new ETracInternal(message); } else return null; } private OrderedEventExecutor resolveByRegister(boolean includeOffloadMapping) { var currentThreadId = getThreadId(Thread.currentThread()); var currentEventLoop = register.get(currentThreadId); if (currentEventLoop != null) return currentEventLoop; if (includeOffloadMapping && offloadTracker != null) { var callingThread = offloadTracker.offloadCallingThread(); if (callingThread != null) { var callingEventLoop = register.get(getThreadId(callingThread)); if (callingEventLoop != null) return callingEventLoop;; } } return null; } private EventExecutor resolveByDirectTest(boolean includeOffloadMapping) { for (var eventExecutor : eventExecutors) { if (eventExecutor.inEventLoop()) return eventExecutor; } if (includeOffloadMapping && offloadTracker != null) { var callingThread = offloadTracker.offloadCallingThread(); if (callingThread != null) { for (var eventExecutor : eventExecutors) { if (eventExecutor.inEventLoop(callingThread)) return eventExecutor; } } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy