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

org.eclipse.jetty.server.AbstractConnector Maven / Gradle / Ivy

There is a newer version: 1.0-rc5
Show newest version
// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================

package org.eclipse.jetty.server;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.atomic.AtomicLong;

import javax.servlet.ServletRequest;

import org.eclipse.jetty.http.HttpBuffers;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpSchemes;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.statistic.SampleStatistic;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.thread.ThreadPool;

/**
 * Abstract Connector implementation. This abstract implementation of the
 * Connector interface provides:
 * 
    *
  • AbstractLifeCycle implementation
  • *
  • Implementations for connector getters and setters
  • *
  • Buffer management
  • *
  • Socket configuration
  • *
  • Base acceptor thread
  • *
  • Optional reverse proxy headers checking
  • *
* * */ public abstract class AbstractConnector extends HttpBuffers implements Connector { private String _name; private Server _server; private ThreadPool _threadPool; private String _host; private int _port = 0; private String _integralScheme = HttpSchemes.HTTPS; private int _integralPort = 0; private String _confidentialScheme = HttpSchemes.HTTPS; private int _confidentialPort = 0; private int _acceptQueueSize = 0; private int _acceptors = 1; private int _acceptorPriorityOffset = 0; private boolean _useDNS; private boolean _forwarded; private String _hostHeader; private String _forwardedHostHeader = "X-Forwarded-Host"; private String _forwardedServerHeader = "X-Forwarded-Server"; private String _forwardedForHeader = "X-Forwarded-For"; private String _forwardedProtoHeader = "X-Forwarded-Proto"; private boolean _reuseAddress = true; protected int _maxIdleTime = 200000; protected int _lowResourceMaxIdleTime = -1; protected int _soLingerTime = -1; private transient Thread[] _acceptorThread; private final AtomicLong _statsStartedAt = new AtomicLong(-1L); /** connections to server */ private final CounterStatistic _connectionStats = new CounterStatistic(); /** requests per connection */ private final SampleStatistic _requestStats = new SampleStatistic(); /** duration of a connection */ private final SampleStatistic _connectionDurationStats = new SampleStatistic(); /* ------------------------------------------------------------ */ /** */ public AbstractConnector() { } /* ------------------------------------------------------------ */ public final Buffer newBuffer(int size) { // TODO remove once no overrides established return null; } /* ------------------------------------------------------------ */ @Override public Buffer newRequestBuffer(int size) { return new ByteArrayBuffer(size); } /* ------------------------------------------------------------ */ @Override public Buffer newRequestHeader(int size) { return new ByteArrayBuffer(size); } /* ------------------------------------------------------------ */ @Override public Buffer newResponseBuffer(int size) { return new ByteArrayBuffer(size); } /* ------------------------------------------------------------ */ @Override public Buffer newResponseHeader(int size) { return new ByteArrayBuffer(size); } /* ------------------------------------------------------------ */ @Override protected boolean isRequestHeader(Buffer buffer) { return true; } /* ------------------------------------------------------------ */ @Override protected boolean isResponseHeader(Buffer buffer) { return true; } /* ------------------------------------------------------------ */ /* */ public Server getServer() { return _server; } /* ------------------------------------------------------------ */ public void setServer(Server server) { _server = server; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.http.HttpListener#getHttpServer() */ public ThreadPool getThreadPool() { return _threadPool; } /* ------------------------------------------------------------ */ public void setThreadPool(ThreadPool pool) { _threadPool = pool; } /* ------------------------------------------------------------ */ /** */ public void setHost(String host) { _host = host; } /* ------------------------------------------------------------ */ /* */ public String getHost() { return _host; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.server.HttpListener#setPort(int) */ public void setPort(int port) { _port = port; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.server.HttpListener#getPort() */ public int getPort() { return _port; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime. */ public int getMaxIdleTime() { return _maxIdleTime; } /* ------------------------------------------------------------ */ /** * Set the maximum Idle time for a connection, which roughly translates to * the {@link Socket#setSoTimeout(int)} call, although with NIO * implementations other mechanisms may be used to implement the timeout. * The max idle time is applied: *
    *
  • When waiting for a new request to be received on a connection
  • *
  • When reading the headers and content of a request
  • *
  • When writing the headers and content of a response
  • *
* Jetty interprets this value as the maximum time between some progress * being made on the connection. So if a single byte is read or written, * then the timeout (if implemented by jetty) is reset. However, in many * instances, the reading/writing is delegated to the JVM, and the semantic * is more strictly enforced as the maximum time a single read/write * operation can take. Note, that as Jetty supports writes of memory mapped * file buffers, then a write may take many 10s of seconds for large content * written to a slow device. *

* Previously, Jetty supported separate idle timeouts and IO operation * timeouts, however the expense of changing the value of soTimeout was * significant, so these timeouts were merged. With the advent of NIO, it * may be possible to again differentiate these values (if there is demand). * * @param maxIdleTime * The maxIdleTime to set. */ public void setMaxIdleTime(int maxIdleTime) { _maxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime when resources are low. */ public int getLowResourcesMaxIdleTime() { return _lowResourceMaxIdleTime; } /* ------------------------------------------------------------ */ /** * @param maxIdleTime * The maxIdleTime to set when resources are low. */ public void setLowResourcesMaxIdleTime(int maxIdleTime) { _lowResourceMaxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime when resources are low. * @deprecated */ public final int getLowResourceMaxIdleTime() { return getLowResourcesMaxIdleTime(); } /* ------------------------------------------------------------ */ /** * @param maxIdleTime * The maxIdleTime to set when resources are low. * @deprecated */ public final void setLowResourceMaxIdleTime(int maxIdleTime) { setLowResourcesMaxIdleTime(maxIdleTime); } /* ------------------------------------------------------------ */ /** * @return Returns the soLingerTime. */ public int getSoLingerTime() { return _soLingerTime; } /* ------------------------------------------------------------ */ /** * @return Returns the acceptQueueSize. */ public int getAcceptQueueSize() { return _acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @param acceptQueueSize * The acceptQueueSize to set. */ public void setAcceptQueueSize(int acceptQueueSize) { _acceptQueueSize = acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @return Returns the number of acceptor threads. */ public int getAcceptors() { return _acceptors; } /* ------------------------------------------------------------ */ /** * @param acceptors * The number of acceptor threads to set. */ public void setAcceptors(int acceptors) { if (acceptors > 2*Runtime.getRuntime().availableProcessors()) Log.warn("Acceptors should be <=2*availableProcessors: "+this); _acceptors = acceptors; } /* ------------------------------------------------------------ */ /** * @param soLingerTime * The soLingerTime to set or -1 to disable. */ public void setSoLingerTime(int soLingerTime) { _soLingerTime = soLingerTime; } /* ------------------------------------------------------------ */ @Override protected void doStart() throws Exception { if (_server == null) throw new IllegalStateException("No server"); // open listener port open(); super.doStart(); if (_threadPool == null) _threadPool = _server.getThreadPool(); if (_threadPool != _server.getThreadPool() && (_threadPool instanceof LifeCycle)) ((LifeCycle)_threadPool).start(); // Start selector thread synchronized (this) { _acceptorThread = new Thread[getAcceptors()]; for (int i = 0; i < _acceptorThread.length; i++) { if (!_threadPool.dispatch(new Acceptor(i))) { Log.warn("insufficient maxThreads configured for {}",this); break; } } } Log.info("Started {}",this); } /* ------------------------------------------------------------ */ @Override protected void doStop() throws Exception { try { close(); } catch (IOException e) { Log.warn(e); } if (_threadPool != _server.getThreadPool() && _threadPool instanceof LifeCycle) ((LifeCycle)_threadPool).stop(); super.doStop(); Thread[] acceptors = null; synchronized (this) { acceptors = _acceptorThread; _acceptorThread = null; } if (acceptors != null) { for (int i = 0; i < acceptors.length; i++) { Thread thread = acceptors[i]; if (thread != null) thread.interrupt(); } } } /* ------------------------------------------------------------ */ public void join() throws InterruptedException { Thread[] threads = _acceptorThread; if (threads != null) for (int i = 0; i < threads.length; i++) if (threads[i] != null) threads[i].join(); } /* ------------------------------------------------------------ */ protected void configure(Socket socket) throws IOException { try { socket.setTcpNoDelay(true); if (_soLingerTime >= 0) socket.setSoLinger(true,_soLingerTime / 1000); else socket.setSoLinger(false,0); } catch (Exception e) { Log.ignore(e); } } /* ------------------------------------------------------------ */ public void customize(EndPoint endpoint, Request request) throws IOException { if (isForwarded()) checkForwardedHeaders(endpoint,request); } /* ------------------------------------------------------------ */ protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException { HttpFields httpFields = request.getConnection().getRequestFields(); // Retrieving headers from the request String forwardedHost = getLeftMostValue(httpFields.getStringField(getForwardedHostHeader())); String forwardedServer = getLeftMostValue(httpFields.getStringField(getForwardedServerHeader())); String forwardedFor = getLeftMostValue(httpFields.getStringField(getForwardedForHeader())); String forwardedProto = getLeftMostValue(httpFields.getStringField(getForwardedProtoHeader())); if (_hostHeader != null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } else if (forwardedHost != null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } else if (forwardedServer != null) { // Use provided server name request.setServerName(forwardedServer); } if (forwardedFor != null) { request.setRemoteAddr(forwardedFor); InetAddress inetAddress = null; if (_useDNS) { try { inetAddress = InetAddress.getByName(forwardedFor); } catch (UnknownHostException e) { Log.ignore(e); } } request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName()); } if (forwardedProto != null) { request.setScheme(forwardedProto); } } /* ------------------------------------------------------------ */ protected String getLeftMostValue(String headerValue) { if (headerValue == null) return null; int commaIndex = headerValue.indexOf(','); if (commaIndex == -1) { // Single value return headerValue; } // The left-most value is the farthest downstream client return headerValue.substring(0,commaIndex); } /* ------------------------------------------------------------ */ public void persist(EndPoint endpoint) throws IOException { } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialPort() */ public int getConfidentialPort() { return _confidentialPort; } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialScheme() */ public String getConfidentialScheme() { return _confidentialScheme; } /* ------------------------------------------------------------ */ /* * @see * org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server * .Request) */ public boolean isIntegral(Request request) { return false; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getConfidentialPort() */ public int getIntegralPort() { return _integralPort; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#getIntegralScheme() */ public String getIntegralScheme() { return _integralScheme; } /* ------------------------------------------------------------ */ /* * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request) */ public boolean isConfidential(Request request) { return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS); } /* ------------------------------------------------------------ */ /** * @param confidentialPort * The confidentialPort to set. */ public void setConfidentialPort(int confidentialPort) { _confidentialPort = confidentialPort; } /* ------------------------------------------------------------ */ /** * @param confidentialScheme * The confidentialScheme to set. */ public void setConfidentialScheme(String confidentialScheme) { _confidentialScheme = confidentialScheme; } /* ------------------------------------------------------------ */ /** * @param integralPort * The integralPort to set. */ public void setIntegralPort(int integralPort) { _integralPort = integralPort; } /* ------------------------------------------------------------ */ /** * @param integralScheme * The integralScheme to set. */ public void setIntegralScheme(String integralScheme) { _integralScheme = integralScheme; } /* ------------------------------------------------------------ */ protected abstract void accept(int acceptorID) throws IOException, InterruptedException; /* ------------------------------------------------------------ */ public void stopAccept(int acceptorID) throws Exception { } /* ------------------------------------------------------------ */ public boolean getResolveNames() { return _useDNS; } /* ------------------------------------------------------------ */ public void setResolveNames(boolean resolve) { _useDNS = resolve; } /* ------------------------------------------------------------ */ /** * Is reverse proxy handling on? * * @return true if this connector is checking the * x-forwarded-for/host/server headers */ public boolean isForwarded() { return _forwarded; } /* ------------------------------------------------------------ */ /** * Set reverse proxy handling. * If set to true, then the X-Forwarded headers (or the headers set in their place) * are looked for to set the request protocol, host, server and client ip. * * @param check * true if this connector is checking the * x-forwarded-for/host/server headers * @set {@link #setForwardedForHeader(String)} * @set {@link #setForwardedHostHeader(String)} * @set {@link #setForwardedProtoHeader(String)} * @set {@link #setForwardedServerHeader(String))} */ public void setForwarded(boolean check) { if (check) Log.debug(this + " is forwarded"); _forwarded = check; } /* ------------------------------------------------------------ */ public String getHostHeader() { return _hostHeader; } /* ------------------------------------------------------------ */ /** * Set a forced valued for the host header to control what is returned by * {@link ServletRequest#getServerName()} and * {@link ServletRequest#getServerPort()}. This value is only used if * {@link #isForwarded()} is true. * * @param hostHeader * The value of the host header to force. */ public void setHostHeader(String hostHeader) { _hostHeader = hostHeader; } /* ------------------------------------------------------------ */ /* * * @see #setForwarded(boolean) */ public String getForwardedHostHeader() { return _forwardedHostHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedHostHeader * The header name for forwarded hosts (default x-forwarded-host) * @see #setForwarded(boolean) */ public void setForwardedHostHeader(String forwardedHostHeader) { _forwardedHostHeader = forwardedHostHeader; } /* ------------------------------------------------------------ */ /** * @return the header name for forwarded server. * @see #setForwarded(boolean) */ public String getForwardedServerHeader() { return _forwardedServerHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedServerHeader * The header name for forwarded server (default * x-forwarded-server) * @see #setForwarded(boolean) */ public void setForwardedServerHeader(String forwardedServerHeader) { _forwardedServerHeader = forwardedServerHeader; } /* ------------------------------------------------------------ */ /** * @see #setForwarded(boolean) */ public String getForwardedForHeader() { return _forwardedForHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedRemoteAddressHeader * The header name for forwarded for (default x-forwarded-for) * @see #setForwarded(boolean) */ public void setForwardedForHeader(String forwardedRemoteAddressHeader) { _forwardedForHeader = forwardedRemoteAddressHeader; } /* ------------------------------------------------------------ */ /** Get the forwardedProtoHeader. * @return the forwardedProtoHeader (default X-Forwarded-For) * @see #setForwarded(boolean) */ public String getForwardedProtoHeader() { return _forwardedProtoHeader; } /* ------------------------------------------------------------ */ /** Set the forwardedProtoHeader. * @param forwardedProtoHeader the forwardedProtoHeader to set (default X-Forwarded-For) * @see #setForwarded(boolean) */ public void setForwardedProtoHeader(String forwardedProtoHeader) { _forwardedProtoHeader = forwardedProtoHeader; } /* ------------------------------------------------------------ */ @Override public String toString() { String name = this.getClass().getName(); int dot = name.lastIndexOf('.'); if (dot > 0) name = name.substring(dot + 1); return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class Acceptor implements Runnable { int _acceptor = 0; Acceptor(int id) { _acceptor = id; } /* ------------------------------------------------------------ */ public void run() { Thread current = Thread.currentThread(); String name; synchronized (AbstractConnector.this) { if (_acceptorThread == null) return; _acceptorThread[_acceptor] = current; name = _acceptorThread[_acceptor].getName(); current.setName(name + " - Acceptor" + _acceptor + " " + AbstractConnector.this); } int old_priority = current.getPriority(); try { current.setPriority(old_priority - _acceptorPriorityOffset); while (isRunning() && getConnection() != null) { try { accept(_acceptor); } catch (EofException e) { Log.ignore(e); } catch (IOException e) { Log.ignore(e); } catch (InterruptedException x) { // Connector has been stopped Log.ignore(x); } catch (ThreadDeath e) { throw e; } catch (Throwable e) { Log.warn(e); } } } finally { current.setPriority(old_priority); current.setName(name); synchronized (AbstractConnector.this) { if (_acceptorThread != null) _acceptorThread[_acceptor] = null; } } } } /* ------------------------------------------------------------ */ public String getName() { if (_name == null) _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()); return _name; } /* ------------------------------------------------------------ */ public void setName(String name) { _name = name; } /* ------------------------------------------------------------ */ /** * @return Get the number of requests handled by this connector since last * call of statsReset(). If setStatsOn(false) then this is * undefined. */ public int getRequests() { return (int)_requestStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationTotal. */ public long getConnectionsDurationTotal() { return _connectionDurationStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Number of connections accepted by the server since statsReset() * called. Undefined if setStatsOn(false). */ public int getConnections() { return (int)_connectionStats.getTotal(); } /* ------------------------------------------------------------ */ /** * @return Number of connections currently open that were opened since * statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpen() { return (int)_connectionStats.getCurrent(); } /* ------------------------------------------------------------ */ /** * @return Maximum number of connections opened simultaneously since * statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpenMax() { return (int)_connectionStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Mean duration in milliseconds of open connections since * statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsDurationMean() { return _connectionDurationStats.getMean(); } /* ------------------------------------------------------------ */ /** * @return Maximum duration in milliseconds of an open connection since * statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationMax() { return _connectionDurationStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Standard deviation of duration in milliseconds of open * connections since statsReset() called. Undefined if * setStatsOn(false). */ public double getConnectionsDurationStdDev() { return _connectionDurationStats.getStdDev(); } /* ------------------------------------------------------------ */ /** * @return Mean number of requests per connection since statsReset() called. * Undefined if setStatsOn(false). */ public double getConnectionsRequestsMean() { return _requestStats.getMean(); } /* ------------------------------------------------------------ */ /** * @return Maximum number of requests per connection since statsReset() * called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsMax() { return (int)_requestStats.getMax(); } /* ------------------------------------------------------------ */ /** * @return Standard deviation of number of requests per connection since * statsReset() called. Undefined if setStatsOn(false). */ public double getConnectionsRequestsStdDev() { return _requestStats.getStdDev(); } /* ------------------------------------------------------------ */ /** * Reset statistics. */ public void statsReset() { updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis()); _requestStats.reset(); _connectionStats.reset(); _connectionDurationStats.reset(); } /* ------------------------------------------------------------ */ public void setStatsOn(boolean on) { if (on && _statsStartedAt.get() != -1) return; Log.debug("Statistics on = " + on + " for " + this); statsReset(); _statsStartedAt.set(on?System.currentTimeMillis():-1); } /* ------------------------------------------------------------ */ /** * @return True if statistics collection is turned on. */ public boolean getStatsOn() { return _statsStartedAt.get() != -1; } /* ------------------------------------------------------------ */ /** * @return Timestamp stats were started at. */ public long getStatsOnMs() { long start = _statsStartedAt.get(); return (start != -1)?(System.currentTimeMillis() - start):0; } /* ------------------------------------------------------------ */ protected void connectionOpened(Connection connection) { if (_statsStartedAt.get() == -1) return; _connectionStats.increment(); } /* ------------------------------------------------------------ */ protected void connectionUpgraded(Connection oldConnection, Connection newConnection) { _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0); } /* ------------------------------------------------------------ */ protected void connectionClosed(Connection connection) { connection.closed(); if (_statsStartedAt.get() == -1) return; long duration = System.currentTimeMillis() - connection.getTimeStamp(); int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0; _requestStats.set(requests); _connectionStats.decrement(); _connectionDurationStats.set(duration); } /* ------------------------------------------------------------ */ /** * @return the acceptorPriority */ public int getAcceptorPriorityOffset() { return _acceptorPriorityOffset; } /* ------------------------------------------------------------ */ /** * Set the priority offset of the acceptor threads. The priority is adjusted * by this amount (default 0) to either favour the acceptance of new threads * and newly active connections or to favour the handling of already * dispatched connections. * * @param offset * the amount to alter the priority of the acceptor threads. */ public void setAcceptorPriorityOffset(int offset) { _acceptorPriorityOffset = offset; } /* ------------------------------------------------------------ */ /** * @return True if the the server socket will be opened in SO_REUSEADDR * mode. */ public boolean getReuseAddress() { return _reuseAddress; } /* ------------------------------------------------------------ */ /** * @param reuseAddress * True if the the server socket will be opened in SO_REUSEADDR * mode. */ public void setReuseAddress(boolean reuseAddress) { _reuseAddress = reuseAddress; } /* ------------------------------------------------------------ */ public boolean isLowResources() { if (_threadPool != null) return _threadPool.isLowOnThreads(); return _server.getThreadPool().isLowOnThreads(); } /* ------------------------------------------------------------ */ private void updateNotEqual(AtomicLong valueHolder, long compare, long value) { long oldValue = valueHolder.get(); while (compare != oldValue) { if (valueHolder.compareAndSet(oldValue,value)) break; oldValue = valueHolder.get(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy