title: ReentrantLock gets mysteriously unlocked within the same thread, even while no other threads are accessing it
body: <p>I have some code that I thought would be very straightforward:</p>
  
  <pre><code>public int internalWrite(byte[] data, int offset, int size) throws InterruptedException {
      lock.lockInterruptibly();
      try {
          if (state == State.RELEASED) throw new TrackReleasedException();
          return track.write(data, offset, size, AudioTrack.WRITE_NON_BLOCKING);
      } finally {
          if (!lock.isHeldByCurrentThread()) {
              Log.e("phonographnative", "internalWrite() lock is not held by current thread! " + Thread.currentThread());
          } else lock.unlock();
      }
  }
  </code></pre>
  
  <p>The <code>lock</code> is a <code>fair</code> ReentrantLock, but the issue also occurs with a non-<code>fair</code> one. <code>track</code> is an Android AudioTrack; its <code>write</code> method is mostly native code (but nothing to do with threading). It has no access to the <code>lock</code> anyway. The exception is practically never thrown in practice (and never while investigating this behaviour). What happens is that, very reproducibly (more on this later), the lock will mysteriously get unlocked within the same thread, causing the log message to occur. Previously, when I didn't have this check in place, an IllegalMonitorStateException would, expectedly, be thrown. After this happened a couple of times, there would be a <code>java.lang.AssertionError: Attempt to repark</code>, within the lock's code itself. Some exemplary log output:</p>
  
  <pre><code>2019-03-20 12:20:37.428 8097-8181/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
  2019-03-20 12:20:37.428 8097-8184/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
  2019-03-20 12:20:37.428 8097-8181/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
  2019-03-20 12:20:37.428 8097-8184/com.kabouzeid.gramophone.debug E/phonographnative: internalWrite() lock is not held by current thread! Thread[phonographnative-decoding-65308.0,5,main]
  2019-03-20 12:20:37.430 8097-8184/com.kabouzeid.gramophone.debug E/AndroidRuntime: FATAL EXCEPTION: phonographnative-decoding-65308.0
      Process: com.kabouzeid.gramophone.debug, PID: 8097
      java.lang.AssertionError: Attempt to repark
          at java.lang.Thread.parkFor$(Thread.java:2143)
          at sun.misc.Unsafe.park(Unsafe.java:325)
          at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
          at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
          at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
          at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1220)
          at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:312)
          at com.kabouzeid.gramophone.service.ffmpeg.AudioContext.internalWrite(AudioContext.java:197)
          at com.kabouzeid.gramophone.service.ffmpeg.AudioContext.write(AudioContext.java:177)
          at com.kabouzeid.gramophone.service.ffmpeg.FFmpegPlayer.decodeAndPlayAudio(Native Method)
          at com.kabouzeid.gramophone.service.ffmpeg.FFmpegPlayer.lambda$new$0(FFmpegPlayer.java:72)
          at com.kabouzeid.gramophone.service.ffmpeg.-$$Lambda$FFmpegPlayer$MKAlsDZBJzprKYoChfgA-0JlIi8.run(lambda)
          at java.lang.Thread.run(Thread.java:761)
  </code></pre>
  
  <p>This happens even if no other threads are trying to run this code, and even if I comment out all other attempts to lock/unlock this lock (by other threads, in different code sections). This problem rarely occurs intermittently, but reliably when I try to <em>unpause</em> the AudioTrack that is being written to. It does not happen when initially writing to it, or when doing anything else (like starting playback from scratch). This unpausing happens on an entirely different thread, and I have not been able to determine the causal connection between the two things. It might just be some random scheduler madness.</p>
  
  <p>The <code>internalWrite</code> method is called very frequently, on the order of thousands of times per second. I have a feeling that this is just a bug within Android's implementation of <code>ReentrantLock</code>, given that the assertion being hit is entirely within JVM code. (What does "Attempt to repark" even mean?) But I can't rule out that I missed some other detail in my own code, and I'd appreciate any thoughts about this!</p>
  
  <p>The full code can be found <a href="https://gitlab.com/meew0/Phonograph/blob/cafe13db63de670568b6b94225b8cca21ec80da8/app/src/main/java/com/kabouzeid/gramophone/service/ffmpeg/AudioContext.java#L187" rel="noreferrer">here</a>. I have also tracked down the relevant sections in Android code: <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java#2267" rel="noreferrer">AudioTrack#write</a>, <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/jni/android_media_AudioTrack.cpp#656" rel="noreferrer">native_write_byte</a> (the method is called differently), <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/jni/android_media_AudioTrack.cpp#628" rel="noreferrer">writeToTrack</a>, <a href="https://android.googlesource.com/platform/frameworks/av/+/refs/heads/master/media/libaudioclient/AudioTrack.cpp#1779" rel="noreferrer">AudioTrack->write</a>. However, they have not helped me at all in elucidating this error.</p>