/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed 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.xnio.nativeimpl; import static java.lang.Thread.currentThread; import static org.xnio.nativeimpl.Log.log; /** * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a> */ final class AcceptChannelHandle extends NativeDescriptor { private final Runnable freeTask; private final NativeAcceptChannel<?> server; private int count; private int low; private int high; private int tokenCount = -1; private boolean stopped; AcceptChannelHandle(final NativeAcceptChannel<?> server, final int fd, final NativeWorkerThread thread, final int low, final int high) { super(thread, fd); this.server = server; this.low = low; this.high = high; freeTask = new Runnable() { public void run() { freeConnection(); } }; } protected void handleReadReady() { if (Native.EXTRA_TRACE) log.tracef("Invoke accept handler on %s", this); server.invokeAcceptHandler(); } protected void handleWriteReady() { } void resume() { final NativeWorkerThread thread = this.thread; if (Native.EXTRA_TRACE) log.tracef("Request resume on %s", this); if (thread == currentThread()) { if (! stopped && server.resumed) { thread.doResume(this, true, false, false); if (Native.EXTRA_TRACE) log.tracef("Complete resume on %s", this); } else { if (Native.EXTRA_TRACE) log.tracef("Resume cancelled on %s", this); } } else { thread.execute(new Runnable() { public void run() { resume(); } }); } } void suspend() { final NativeWorkerThread thread = this.thread; if (Native.EXTRA_TRACE) log.tracef("Request suspend on %s", this); if (thread == currentThread()) { if (stopped || ! server.resumed) { thread.doResume(this, false, false, false); if (Native.EXTRA_TRACE) log.tracef("Complete suspend on %s", this); } else { if (Native.EXTRA_TRACE) log.tracef("Suspend cancelled on %s", this); } } else { thread.execute(new Runnable() { public void run() { suspend(); } }); } } void channelClosed() { final NativeWorkerThread thread = this.thread; if (thread == currentThread()) { freeConnection(); } else { thread.execute(freeTask); } } void freeConnection() { assert currentThread() == thread; if (Native.EXTRA_TRACE) log.tracef("Freeing connection on %s", this); if (count-- <= low && tokenCount != 0 && stopped) { stopped = false; if (server.resumed) { if (Native.EXTRA_TRACE) log.tracef("Freeing connection on %s -> resume", this); thread.doResume(this, true, false, false); } } } void setTokenCount(final int newCount) { NativeWorkerThread workerThread = thread; if (workerThread == currentThread()) { if (Native.EXTRA_TRACE) log.tracef("Set token count on %s (tokenCount: %d)", this, tokenCount); if (tokenCount == 0) { tokenCount = newCount; if (count <= low && stopped) { stopped = false; if (server.resumed) { if (Native.EXTRA_TRACE) log.tracef("Accept resumed on %s (count: %d, tokenCount: %d)", this, count, tokenCount); thread.doResume(this, true, false, false); } } return; } workerThread = workerThread.getNextThread(); } if (Native.EXTRA_TRACE) log.tracef("Delegating token set from %s to %s", this, workerThread.getName()); setThreadNewCount(workerThread, newCount); } private void setThreadNewCount(final NativeWorkerThread workerThread, final int newCount) { final int number = workerThread.getNumber(); workerThread.execute(new Runnable() { public void run() { server.getHandle(number).setTokenCount(newCount); } }); } void initializeTokenCount(final int newCount) { NativeWorkerThread workerThread = thread; if (workerThread == currentThread()) { tokenCount = newCount; if (newCount == 0) { stopped = true; if (Native.EXTRA_TRACE) log.tracef("Token count set on %s (stopped; no tokens)", this); workerThread.doResume(this, false, false, false); } else { if (Native.EXTRA_TRACE) log.tracef("Token count set on %s (initial tokenCount: %d)", this, newCount); } } else { workerThread.execute(new Runnable() { public void run() { initializeTokenCount(newCount); } }); } } boolean getConnection() { assert currentThread() == thread; if (stopped) { return false; } if (tokenCount != -1 && --tokenCount == 0) { if (Native.EXTRA_TRACE) log.tracef("Passing token on %s", this); setThreadNewCount(thread.getNextThread(), server.getTokenConnectionCount()); } if (++count >= high || tokenCount == 0) { stopped = true; if (Native.EXTRA_TRACE) log.tracef("Accept stopped on %s (count: %d, tokenCount: %d)", this, count, tokenCount); thread.doResume(this, false, false, false); } return true; } void executeSetTask(final int high, final int low) { final NativeWorkerThread thread = this.thread; if (thread == currentThread()) { if (Native.EXTRA_TRACE) log.tracef("Setting low/high to %d/%d on %s", low, high, this); this.high = high; this.low = low; if (count >= high && ! stopped) { stopped = true; if (Native.EXTRA_TRACE) log.tracef("Setting low/high -> suspend on %s", this); suspend(); } else if (count <= low && stopped) { stopped = false; if (server.resumed) { if (Native.EXTRA_TRACE) log.tracef("Setting low/high -> resume on %s", this); resume(); } } } else { thread.execute(new Runnable() { public void run() { executeSetTask(high, low); } }); } } int getConnectionCount() { assert currentThread() == this.thread; return count; } }