Uploaded image for project: 'Java Driver'
  1. Java Driver
  2. JAVA-3978

Introduce self-managing reference counting to AsyncConnection and AsyncConnectionSource

    • Type: Icon: Improvement Improvement
    • Resolution: Unresolved
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: None
    • Component/s: Internal
    • Fully Compatible
    • Not Needed

      The idea of reference counting breaks when a resource may be used without the need to have a reference to it. For example, consider the following code

      asyncConnection.commandAsync((result, exception) ->
          //process result without touching the connection
      )
      

      On one hand, the callback does not store a reference to asyncConnection, so the caller of the commandAsync method does not see a reason to retain asyncConnection;
      on the other hand, the asyncConnection should not be released until the asynchronous logic completes, otherwise we may never receive a result.
      Clearly, asyncConnection must be retained by someone, but not by the caller who cannot deduce this based only on thinking about references (and this is all he should be thinking about to preserve mental health).

      To cope with the situation, we can put the burden of retaining and releasing asyncConnection on the connection itself.

      This improvement does not have effects observable to our users and is intended to make our code more robust by reducing the reference counting burden that lies on the code writer.

      Currently, we have to write the following code:

      /* In this example the code that creates/borrows connectionSource
       * and the code that releases connectionSource
       * is not executed by the same method,
       * and may be even be located in different classes
       * depending on how we create the callback.
       * This makes the code less readable and more error-prone.*/
      AsyncConnectionSource connectionSource = ...;//count is 1
      connectionSource.getConnectionAsync((connection, problem) -> {
          try {
              connectionSource.release();//count is 0; connectionSource must have stayed retained until its asynchronous logic that obtains a connection completes
          } finally {
              if (problem == null) {
                  //handle the problem
              } else {
                  //use connection and release it
              }
          }
      });
      

      it would have saved us some trouble if we were not have to remember to keep connectionSource retained when we call getConnectionAsync, and if AsyncConnectionSource itself retained it and released when it's no longer needed:

      /* In this example the code that creates/borrows connectionSource
       * and the code that releases connectionSource
       * is executed by and located in the same method.
       * This simplifies reading/writing the code, thus making it more robust.*/
      AsyncConnectionSource connectionSource = ...;//count is 1
      try {
          connectionSource.getConnectionAsync((connection, problem) -> {
              //connectionSource.count is either 0 or 1 depending on the execution, but eventually it is guaranteed to be 0
              if (problem == null) {
                  //handle the problem
              } else {
                  //use connection and release it
              }
          });
      } finally {
          connectionSource.release();//count is either 0 or 1 depending on the execution, but eventually it is guaranteed to be 0
      }
      

            Assignee:
            Unassigned Unassigned
            Reporter:
            valentin.kovalenko@mongodb.com Valentin Kavalenka
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: