/** Disconnect from the PV. OK to call more than once. */ private void disconnect() { // Releasing the _last_ channel will close the context, // which waits for the JCA Command thread to exit. // If a connection or update for the channel happens at that time, // the JCA command thread will send notifications to this PV, // which had resulted in dead lock: // This code locked the PV, then tried to join the JCA Command thread. // JCA Command thread tried to lock the PV, so it could not exit. // --> Don't lock while calling into the PVContext. RefCountedChannel channel_ref_copy; synchronized (this) { // Never attempted a connection? if (channel_ref == null) return; channel_ref_copy = channel_ref; channel_ref = null; connected = false; } try { PVContext.releaseChannel(channel_ref_copy, this); } catch (final IllegalStateException ile) { logger.warn("exception when disconnecting pv " + name, ile); } catch (final Throwable e) { logger.error("exception when disconnecting pv " + name, e); } fireDisconnected(); }
/** Try to connect to the PV. OK to call more than once. */ private void connect() throws Exception { logger.debug("pv of" + this.name + " connectting"); PVContext.scheduleCommand( this.name, this.jcaCommandThreadId, this.channel_ref, "connect", new Runnable() { @Override public void run() { // try { state = PVConnectionState.Connecting; // Already attempted a connection? synchronized (this) { if (channel_ref == null) { channel_ref = PVContext.getChannel( name, EPICS_V3_PV.this.jcaCommandThreadId, EPICS_V3_PV.this); } fireConnectionRequestMade(); if (channel_ref.getChannel().getConnectionState() == ConnectionState.CONNECTED) { handleConnected(channel_ref.getChannel()); } else { } } } catch (Exception e) { logger.error("exception when connecting pv " + name, e); } } }); }
/** * Generate an EPICS PV. * * @param name The PV name. * @param plain When <code>true</code>, only the plain value is requested. No time etc. Some PVs * only work in plain mode, example: "record.RTYP". */ private EPICS_V3_PV( final String name, final boolean plain, ConfigService configservice, int jcaCommandThreadId) { this.name = name; this.plain = plain; this.configservice = configservice; this.jcaCommandThreadId = jcaCommandThreadId; PVContext.setConfigservice(configservice); }
/** ConnectionListener interface. */ @Override public void connectionChanged(final ConnectionEvent ev) { logger.debug("Connection changed for pv " + this.name); // This runs in a CA thread if (ev.isConnected()) { // Transfer to JCACommandThread to avoid // deadlocks // The connect event can actually happen 'right // away' // when the channel is created, before we even // get to assign // the channel_ref. So use the channel from the // event, not // the channel_ref which might still be null. // // EngineContext.getInstance().getScheduler().execute(new Runnable() PVContext.scheduleCommand( this.name, this.jcaCommandThreadId, this.channel_ref, "Connection changed connected", new Runnable() { @Override public void run() { handleConnected((Channel) ev.getSource()); } }); } else { state = PVConnectionState.Disconnected; connected = false; PVContext.scheduleCommand( this.name, this.jcaCommandThreadId, this.channel_ref, "Connection changed disconnected", new Runnable() { @Override public void run() { unsubscribe(); fireDisconnected(); } }); } }
/** {@inheritDoc} */ @Override public void stop() { running = false; PVContext.scheduleCommand( this.name, this.jcaCommandThreadId, this.channel_ref, "stop", new Runnable() { @Override public void run() { logger.debug("Stopping channel " + EPICS_V3_PV.this.name); unsubscribe(); disconnect(); } }); }
@Override public void getCompleted(final GetEvent event) { // This runs in a CA // thread if (event.getStatus().isSuccessful()) { state = PVConnectionState.GotMetaData; final DBR dbr = event.getDBR(); totalMetaInfo.applyBasicInfo( EPICS_V3_PV.this.name, dbr, EPICS_V3_PV.this.configservice); } else { logger.error("The meta get listener was not successful for EPICS_V3_PV " + name); } PVContext.scheduleCommand( EPICS_V3_PV.this.name, EPICS_V3_PV.this.jcaCommandThreadId, EPICS_V3_PV.this.channel_ref, "getCompleted", new Runnable() { @Override public void run() { subscribe(); } }); }