/** 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();
         }
       });
 }