PV Access to PV Access protocol gateway (aka. proxy) Theory of Operation The GW maintains a Channel Cache, which is a dictionary of client side channels (shared_ptr instances) in the NEVER_CONNECTED or CONNECTED states. Each entry also has an activity flag and reference count. The activity flag is set each time the server side receives a search request for a PV. The reference count is incremented for each active server side channel. Periodically the cache is iterated and any client channels with !activity and count==0 are dropped. In addition the activity flag is unconditionally cleared. Name search handling The server side listens for name search requests. When a request is received the channel cache is searched. If no entry exists, then one is created and no further action is taken. If an entry exists, but the client channel is not connected, then it's activiy flag is set and no further action is taken. If a connected entry exists, then an affirmative response is sent to the requester. When a channel create request is received, the channel cache is checked. If no connected entry exists, then the request is failed. Structure associations and ownership ```c struct ServerContextImpl { vector > providers; // GWServerChannelProvider }; struct GWServerChannelProvider : public pva::ChannelProvider { ChannelCache cache; }; struct ChannelCache { weak_pointer server; map > entries; epicsMutex cacheLock; // guards entries }; struct ChannelCacheEntry { ChannelCache * const cache; shared_ptr channel; // InternalChannelImpl set interested; }; struct InternalChannelImpl { // PVA client channel shared_ptr requester; // ChannelCacheEntry::CRequester }; struct ChannelCacheEntry::CRequester { weak_ptr chan; }; struct GWChannel { shared_ptr entry; shared_ptr requester; // pva::ServerChannelRequesterImpl }; struct pva::ServerChannelImpl : public pva::ServerChannel { shared_ptr channel; // GWChannel }; ``` Threading and Locking ServerContextImpl BeaconServerStatusProvider ? 2x BlockingUDPTransport (bcast and mcast, one thread each) calls ChannelProvider::channelFind with no locks held BlockingTCPAcceptor BlockingServerTCPTransportCodec -> BlockingAbstractCodec (send and recv threads) ServerResponseHandler calls ChannelProvider::channelFind calls ChannelProvider::createChannel calls Channel::create* InternalClientContextImpl 2x BlockingUDPTransport (bcast listener and ucast tx/rx, one thread each) BlockingTCPConnector BlockingClientTCPTransportCodec -> BlockingSocketAbstractCodec (send and recv threads) ClientResponseHandler calls MonitorRequester::monitorEvent with MonitorStrategyQueue::m_mutex locked TODO ServerChannelRequesterImpl::channelStateChange() - placeholder, needs implementation the send queue in BlockingAbstractCodec has no upper bound Monitor ServerChannelImpl calls GWChannel::createMonitor with a MonitorRequester which is ServerMonitorRequesterImpl The MonitorRequester is given a Monitor which is GWMonitor GWChannel calls InternalChannelImpl::createMonitor with a GWMonitorRequester GWMonitorRequester is given a Monitor which is ChannelMonitorImpl Updates originate from the client side, entering as an argument when GWMonitorRequester::monitorEvent is called, and exiting to the server when passed as an argument of a call to ServerMonitorRequesterImpl::monitorEvent. When an update is added to the monitor queue ServerMonitorRequesterImpl::monitorEvent is called, as notification that the queue is not empty, which enqueues itself for transmission. The associated TCP sender thread later calls ServerMonitorRequesterImpl::send(), which calls GWMonitor::poll() to de-queue an event, which it encodes to the senders bytebuffer. It then reschedules itself.