DYT/Tool/3rdParty_x64/include/dcmtk/dcmnet/scppool.h
2024-11-22 23:19:31 +08:00

342 lines
12 KiB
C++

/*
*
* 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