docs: ReleaseResult change(SemaphoreEndpoint endpoint, UUID invocationUid, int permits) {
          if (permits == 0) {
              return ReleaseResult.failed(Collections.<AcquireInvocationKey>emptyList());
          }
  
          Collection<AcquireInvocationKey> cancelled = cancelWaitKeys(endpoint, invocationUid);
  
          long sessionId = endpoint.sessionId();
          if (sessionId != NO_SESSION_ID) {
              SessionSemaphoreState state = sessionStates.get(sessionId);
              if (state == null) {
                  state = new SessionSemaphoreState();
                  sessionStates.put(sessionId, state);
              }
  
              long threadId = endpoint.threadId();
              if (state.containsInvocation(threadId, invocationUid)) {
                  Collection<AcquireInvocationKey> c = Collections.emptyList();
                  return ReleaseResult.successful(c, c);
              }
  
              state.invocationRefUids.put(threadId, Tuple2.of(invocationUid, permits));
          }
  
          available += permits;
          initialized = true;
  
          Collection<AcquireInvocationKey> acquired =
                  permits > 0 ? assignPermitsToWaitKeys() : Collections.<AcquireInvocationKey>emptyList();
  
          return ReleaseResult.successful(acquired, cancelled);
      }