/usr/include/dcmtk/dcmnet/scppool.h is in libdcmtk-dev 3.6.1~20150924-5.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | /*
*
* Copyright (C) 2012-2014, OFFIS e.V.
* All rights reserved. See COPYRIGHT file for details.
*
* This software and supporting documentation were developed by
*
* OFFIS e.V.
* R&D Division Health
* Escherweg 2
* D-26121 Oldenburg, Germany
*
*
* Module: dcmnet
*
* Author: Michael Onken
*
* Purpose: Class listening for association requests and managing a pool of
* worker threads that each are waiting to take over a single incoming
* association. Thus, the pool can serve as many associations
* simultaneously as the number of threads it is configured to create.
*
*/
#ifndef SCPPOOL_H
#define SCPPOOL_H
#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
#ifdef WITH_THREADS // Without threads this does not make sense...
#include "dcmtk/ofstd/ofthread.h"
#include "dcmtk/dcmnet/scpthrd.h"
#include "dcmtk/dcmnet/scpcfg.h"
#include "dcmtk/dcmnet/assoc.h"
/** Base class for implementing an SCP pool with one thread listening for
* incoming TCP/IP connections and spawning a number of SCP worker threads
* that handle the incoming DICOM association on that connection. This base
* class is abstract.
*/
class DCMTK_DCMNET_EXPORT DcmBaseSCPPool
{
public:
/** Abstract base class that handles forwarding the configuration and
* T_ASC_Association to the actual worker class for each worker thread.
*/
class DCMTK_DCMNET_EXPORT DcmBaseSCPWorker : public OFThread
{
public:
/** Virtual Destructor
*/
virtual ~DcmBaseSCPWorker();
/** Set the association that should be handled by the worker thread.
* This must happen *before* actually calling run() (i.e. start()) on
* the worker.
* @param assoc The association that should be handled by the worker.
* @return EC_Normal if OK, error code otherwise. An error may occur
* if the the function was called before with a valid
* association, or if the given association is NULL.
*/
virtual OFCondition setAssociation(T_ASC_Association* assoc);
/** Set SCP configuration that should be used by the worker in order
* to handle incoming association requests (presentation contexts, etc.).
* @param config A DcmSharedSCPConfig object to be used by this worker.
* @return EC_Normal, if configuration is accepted, error code
* otherwise.
*/
virtual OFCondition setSharedConfig(const DcmSharedSCPConfig& config) = 0;
/** Check whether worker is busy.
* @return OFTrue if worker is busy, OFFalse otherwise.
*/
virtual OFBool busy() = 0;
/** Ends and exits worker thread. Call will not return.
*/
virtual void exit();
protected:
/** Protected constructor which is called within the friend class
* DcmSCPWorkerFactory in order to create a worker.
* @param pool Handle to the SCP pool in order to inform pool
* about exiting the underlying thread, etc.
*/
DcmBaseSCPWorker(DcmBaseSCPPool& pool);
/** Overwrites run() function provided by OFThread. Is automatically
* executed when start() is called (also provided by OFThread).
*/
virtual void run();
/** Starts listening on the given association.
* Note that the underlying TCP connection must be already accepted,
* i.e. ASC_receiveAssociation() must have been called already
* on the association; after that, this listen() function kicks in
* and has to take over full responsibility of the association,
* including accepting it, refusing it, handling incoming DIMSE
* messages, freeing memory of the T_ASC_Association struct,
* and the like.
* @param assoc Pointer to the association that should be handled.
* Must not be NULL.
* @return EC_Normal if association was handled properly (i.e.
* was handled, refused, ... Only in case of connection
* or messaging errors, an error code will be returned
* instead.
*/
virtual OFCondition workerListen(T_ASC_Association* const assoc) = 0;
/// Reference to pool in order to notify pool if thread exits, etc.
DcmBaseSCPPool& m_pool;
/// Temporarily stores association parameter to be available for the
/// run() method. run() will set the pointer immediately to NULL; the
/// deletion takes place inside the actual worker m_worker which starts
/// its operation afterwards in run().
T_ASC_Association* m_assoc;
};
// Needed to keep MS VC6 happy
friend class DcmBaseSCPWorker;
/** Virtual destructor, frees internal memory.
*/
virtual ~DcmBaseSCPPool();
/** Set the number of maximum permitted connections, i.e.\ threads/workers.
* @param maxWorkers Number of threads permitted to exist within pool.
*/
virtual void setMaxThreads(const Uint16 maxWorkers);
/** Get number of maximum permitted connections, i.e.\ threads/workers.
* @return Number of threads permitted to exist within pool.
*/
virtual Uint16 getMaxThreads();
/** Get number of currently active connections.
* @param onlyBusy Return only number of those workers that are busy with a
* connection and not idle, if OFTrue.
* @return Number of connections currently handled within pool
*/
virtual size_t numThreads(const OFBool onlyBusy);
/** Listen for incoming association requests. For each incoming request, a
* new thread is started if number of maximum threads is not reached yet.
* @return DUL_NOASSOCIATIONREQUEST if no connection is requested during
* timeout. Returns other error code if serious error occurs during
* listening. Will not return EC_Normal since listens forever if
* no timeout occurs.
*/
virtual OFCondition listen();
/** Return handle to the SCP configuration that is used to configure how to
* handle incoming associations. For the pool, e.g. by providing settings
* for TCP connection timeout, and for the workers, e.g. by configuration
* presentation contexts and the like.
* @return The SCP configuration(s).
*/
virtual DcmSCPConfig& getConfig();
/** If enabled, the pool will return from listening for incoming requests
* as soon as the last worker is idle, i.e.\ no worker is handling a DICOM
* association any more.
*/
virtual void stopAfterCurrentAssociations();
protected:
/** Constructor. Initializes internal member variables.
*/
DcmBaseSCPPool();
/** Create SCP worker.
* @return The worker created
*/
virtual DcmBaseSCPWorker* createSCPWorker() = 0;
/** Try to find worker to run the association. If worker could be found, a
* side effect is that assoc is set to NULL.
* @param assoc The association to be run. Must be not NULL.
* @param sharedConfig A DcmSharedSCPConfig object to be used by the worker.
* @return EC_Normal if worker could be found and runs the association,
* an error code otherwise.
*/
OFCondition runAssociation(T_ASC_Association* assoc,
const DcmSharedSCPConfig& sharedConfig);
/** Drops association and clears internal structures to free memory
* @param assoc The association to free
*/
virtual void dropAndDestroyAssociation(T_ASC_Association* assoc);
/** Reject association using the given reason, e.g.\ because maximum number
* of connections is currently already served.
* @param assoc The association to reject
* @param reason The rejection reason
*/
void rejectAssociation(T_ASC_Association* assoc,
const T_ASC_RejectParametersReason& reason);
/** Used by thread to tell pool it has terminated
* @param thread The thread that is calling this function and is about to
* exit.
* @param result The final result of the thread.
*/
void notifyThreadExit(DcmBaseSCPWorker* thread,
OFCondition result);
private:
/// Possible run modes of pool
enum runmode
{
/// Listen for new connections
LISTEN,
/// Reserved for later use
STOP,
/// Shutting down worker threads
SHUTDOWN
};
/// Mutex that guards the list of busy and idle workers
OFMutex m_criticalSection;
/// List of all workers running a connection
OFList<DcmBaseSCPWorker*> m_workersBusy;
/// List of all workers being idle, i.e.\ not running a connection
OFList<DcmBaseSCPWorker*> m_workersIdle;
/// SCP configuration to be used by pool and all workers
DcmSCPConfig m_cfg;
/// Maximum number of workers that can exist at a time. Thus limits the
/// maximum number of connections for the pool since every worker serves
/// one connection at a time.
Uint16 m_maxWorkers;
// Not implemented yet: Can be helpful if all workers are busy but incoming
// associations should then not be rejected immediately but only after a
// specific timeout
// Uint16 m_workersBusyTimeout;
// Not implemented yet: list of associations that are waiting for a worker
// becoming available
// OFList<T_ASC_Association*> m_waiting;
/// Current run mode of pool
runmode m_runMode;
};
/** Implementation of DICOM SCP server pool. The pool waits for incoming
* TCP/IP connection requests, accepts them on TCP/IP level and hands the
* connection to a worker thread. The maximum number of worker threads, i.e.
* simultaneous connections, is configurable. The default is 5. At the moment,
* if no free worker slots are available, an incoming request is rejected with
* the error "local limit exceeded", i.e. those requests are not queued. This
* behaviour might change in the future.
* @tparam SCP the service class provider to be instantiated for each request,
* should follow the @ref SCPThread_Concept.
* @tparam SCPPool the base SCP pool class to use. Use this parameter if you
* want to use a different implementation (probably derived from
* DcmBaseSCPPool) as base class for implementing the SCP pool.
* @tparam BaseSCPWorker the base SCP worker class to use. Use this parameter
* if you want to use a different implementation of the SCP worker, e.g. for
* using processes instead of threads or sharing a single thread for
* multiple workers.
*/
template<typename SCP = DcmThreadSCP, typename SCPPool = DcmBaseSCPPool, typename BaseSCPWorker = OFTypename SCPPool::DcmBaseSCPWorker>
class DcmSCPPool : public SCPPool
{
public:
/** Default construct a DcmSCPPool object.
*/
DcmSCPPool() : SCPPool()
{
}
private:
/** Helper class to use any class as an SCPWorker as long as it is a model
* of the @ref SCPThread_Concept.
*/
struct SCPWorker : public BaseSCPWorker
, private SCP
{
/** Construct a SCPWorker for being used by the given DcmSCPPool.
* @param pool the DcmSCPPool object this Worker belongs to.
*/
SCPWorker(DcmSCPPool& pool)
: BaseSCPWorker(pool)
, SCP()
{
}
/** Set the shared configuration for this worker.
* @param config a DcmSharedSCPConfig object to be used by this worker.
* @return the result of the underlying SCP implementation.
*/
virtual OFCondition setSharedConfig(const DcmSharedSCPConfig& config)
{
return SCP::setSharedConfig(config);
}
/** Determine if the Worker is currently handling any request.
* @return OFTrue if the underlying SCP implementation is currently
* handling a request, OFFalse otherwise.
*/
virtual OFBool busy()
{
return SCP::isConnected();
}
/** Perform SCP's duties on an already accepted (TCP/IP) connection.
* @param assoc The association to be run
* @return Returns EC_Normal if negotiation could take place and no
* serious network error has occurred or the given association
* is invalid. Error code otherwise.
*/
virtual OFCondition workerListen(T_ASC_Association* const assoc)
{
return SCP::run(assoc);
}
};
/** Create a worker to be used for handling a request.
* @return a pointer to a newly created SCP worker.
*/
virtual BaseSCPWorker* createSCPWorker()
{
return new SCPWorker(*this);
}
};
#endif // WITH_THREADS
#endif // SCPPOOL_H
|