001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2013-2014 ForgeRock AS. 015 */ 016package org.opends.server.core; 017 018import java.util.concurrent.atomic.AtomicInteger; 019 020import org.opends.server.types.DirectoryException; 021import org.opends.server.types.Operation; 022 023/** 024 * A QueueingStrategy that concurrently enqueues a bounded number of operations 025 * to the DirectoryServer work queue. If the maximum number of concurrently 026 * enqueued operations has been reached or if the work queue if full, then the 027 * operation will be executed on the current thread. 028 */ 029public class BoundedWorkQueueStrategy implements QueueingStrategy 030{ 031 032 /** 033 * The number of concurrently running operations for this 034 * BoundedWorkQueueStrategy. 035 */ 036 private final AtomicInteger nbRunningOperations = new AtomicInteger(0); 037 /** Maximum number of concurrent operations. 0 means "unlimited". */ 038 private final int maxNbConcurrentOperations; 039 040 /** 041 * Constructor for BoundedWorkQueueStrategy. 042 * 043 * @param maxNbConcurrentOperations 044 * the maximum number of operations that can be concurrently enqueued 045 * to the DirectoryServer work queue 046 */ 047 public BoundedWorkQueueStrategy(Integer maxNbConcurrentOperations) 048 { 049 if (maxNbConcurrentOperations != null) 050 { 051 this.maxNbConcurrentOperations = maxNbConcurrentOperations; 052 } 053 else 054 { 055 int cpus = Runtime.getRuntime().availableProcessors(); 056 this.maxNbConcurrentOperations = 057 Math.max(cpus, getNumWorkerThreads() * 25 / 100); 058 } 059 } 060 061 /** 062 * Return the maximum number of worker threads that can be used by the 063 * WorkQueue (The WorkQueue could have a thread pool which adjusts its size). 064 * 065 * @return the maximum number of worker threads that can be used by the 066 * WorkQueue 067 */ 068 protected int getNumWorkerThreads() 069 { 070 return DirectoryServer.getWorkQueue().getNumWorkerThreads(); 071 } 072 073 /** {@inheritDoc} */ 074 @Override 075 public void enqueueRequest(final Operation operation) 076 throws DirectoryException 077 { 078 if (!operation.getClientConnection().isConnectionValid()) 079 { 080 // do not bother enqueueing 081 return; 082 } 083 084 if (maxNbConcurrentOperations == 0) 085 { // unlimited concurrent operations 086 if (!tryEnqueueRequest(operation)) 087 { // avoid potential deadlocks by running in the current thread 088 operation.run(); 089 } 090 } 091 else if (nbRunningOperations.getAndIncrement() > maxNbConcurrentOperations 092 || !tryEnqueueRequest(wrap(operation))) 093 { // avoid potential deadlocks by running in the current thread 094 try 095 { 096 operation.run(); 097 } 098 finally 099 { 100 // only decrement when the operation is run synchronously. 101 // Otherwise it'll be decremented twice (once more in the wrapper). 102 nbRunningOperations.decrementAndGet(); 103 } 104 } 105 } 106 107 /** 108 * Tries to add the provided operation to the work queue if not full so that 109 * it will be processed by one of the worker threads. 110 * 111 * @param op 112 * The operation to be added to the work queue. 113 * @return true if the operation could be enqueued, false otherwise 114 * @throws DirectoryException 115 * If a problem prevents the operation from being added to the queue 116 * (e.g., the queue is full). 117 */ 118 protected boolean tryEnqueueRequest(Operation op) throws DirectoryException 119 { 120 return DirectoryServer.tryEnqueueRequest(op); 121 } 122 123 private Operation wrap(final Operation operation) 124 { 125 if (operation instanceof AbandonOperation) 126 { 127 return new AbandonOperationWrapper((AbandonOperation) operation) 128 { 129 @Override 130 public void run() 131 { 132 runWrapped(operation); 133 } 134 }; 135 } 136 else if (operation instanceof AddOperation) 137 { 138 return new AddOperationWrapper((AddOperation) operation) 139 { 140 @Override 141 public void run() 142 { 143 runWrapped(operation); 144 } 145 }; 146 } 147 else if (operation instanceof BindOperation) 148 { 149 return new BindOperationWrapper((BindOperation) operation) 150 { 151 @Override 152 public void run() 153 { 154 runWrapped(operation); 155 } 156 }; 157 } 158 else if (operation instanceof CompareOperation) 159 { 160 return new CompareOperationWrapper((CompareOperation) operation) 161 { 162 @Override 163 public void run() 164 { 165 runWrapped(operation); 166 } 167 }; 168 } 169 else if (operation instanceof DeleteOperation) 170 { 171 return new DeleteOperationWrapper((DeleteOperation) operation) 172 { 173 @Override 174 public void run() 175 { 176 runWrapped(operation); 177 } 178 }; 179 } 180 else if (operation instanceof ExtendedOperation) 181 { 182 return new ExtendedOperationWrapper((ExtendedOperation) operation) 183 { 184 @Override 185 public void run() 186 { 187 runWrapped(operation); 188 } 189 }; 190 } 191 else if (operation instanceof ModifyDNOperation) 192 { 193 return new ModifyDNOperationWrapper((ModifyDNOperation) operation) 194 { 195 @Override 196 public void run() 197 { 198 runWrapped(operation); 199 } 200 }; 201 } 202 else if (operation instanceof ModifyOperation) 203 { 204 return new ModifyOperationWrapper((ModifyOperation) operation) 205 { 206 @Override 207 public void run() 208 { 209 runWrapped(operation); 210 } 211 }; 212 } 213 else if (operation instanceof SearchOperation) 214 { 215 return new SearchOperationWrapper((SearchOperation) operation) 216 { 217 @Override 218 public void run() 219 { 220 runWrapped(operation); 221 } 222 }; 223 } 224 else if (operation instanceof UnbindOperation) 225 { 226 return new UnbindOperationWrapper((UnbindOperation) operation) 227 { 228 @Override 229 public void run() 230 { 231 runWrapped(operation); 232 } 233 }; 234 } 235 else 236 { 237 throw new RuntimeException( 238 "Not implemented for " + operation == null ? null : operation 239 .getClass().getName()); 240 } 241 } 242 243 /** 244 * Execute the provided operation and decrement the number of currently 245 * running operations after it has finished executing. 246 * 247 * @param the 248 * operation to execute 249 */ 250 private void runWrapped(final Operation operation) 251 { 252 try 253 { 254 operation.run(); 255 } 256 finally 257 { 258 nbRunningOperations.decrementAndGet(); 259 } 260 } 261}