View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
    10   *   http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   * Unless required by applicable law or agreed to in writing,
    13   * software distributed under the License is distributed on an
    14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   * KIND, either express or implied.  See the License for the
    16   * specific language governing permissions and limitations
    17   * under the License.
    18   */
    19  package org.apache.myfaces.orchestra.lib;
    20
    21  import org.apache.commons.logging.Log;
    22  import org.apache.commons.logging.LogFactory;
    23
    24
    25  /**
    26   * A reentrant mutual exclusion with the same basic
    27   * behavior and semantics as the implicit monitor lock accessed using
    28   * {@code synchronized} methods and statements.
    29   * <p>
    30   * Serialization of this class behaves in the same way as built-in
    31   * locks: a deserialized lock is in the unlocked state, regardless of
    32   * its state when serialized.
    33   * <p>
    34   * This class exists just for the purposes of Java 1.4 compatibility;
    35   * it is equivalent to the Java 1.5 ReentrantLock class. It probably
    36   * doesn't perform as well as the "real" lock class, but Orchestra
    37   * doesn't use it in any critical paths.
    38   *
    39   * @since 1.1
    40   */
    41  public class _ReentrantLock implements java.io.Serializable
    42  {
    43      private static final long serialVersionUID = 7373984872572414699L;
    44      private static final long WAIT_WARN_MILLIS = 30L * 1000L; // 30 seconds
    45
    46      private final Log log = LogFactory.getLog(_ReentrantLock.class);
    47      private transient Thread lockedBy;
    48      private int lockCount;
    49
    50      public void lockInterruptibly() throws InterruptedException
    51      {
    52          Thread caller = Thread.currentThread();
    53          for(;;)
    54          {
    55              synchronized(this)
    56              {
    57                  if (lockedBy == null)
    58                  {
    59                      lockedBy = caller;
    60                      lockCount = 1;
    61                      return;
    62                  }
    63
    64                  if (lockedBy == caller)
    65                  {
    66                      ++lockCount;
    67                      return;
    68                  }
    69
    70                  long waitedFor;
    71                  try
    72                  {
    73                      if (log.isDebugEnabled())
    74                      {
    75                          log.debug("Waiting for lock " + this.toString()
    76                                  + " which is owned by thread "
    77                                  + lockedBy.getName());
    78                      }
    79                      long beganAt = System.currentTimeMillis();
    80                      this.wait(WAIT_WARN_MILLIS);
    81                      waitedFor = System.currentTimeMillis() - beganAt;
    82                  }
    83                  catch(InterruptedException e)
    84                  {
    85                      throw e;
    86                  }
    87
    88                  if (waitedFor >= WAIT_WARN_MILLIS)
    89                  {
    90                      // Hopefully this will not get invoked very often!
    91                      if (log.isWarnEnabled())
    92                      {
    93                          String lockedByName;
    94                          if (lockedBy != null)
    95                          {
    96                              lockedByName = lockedBy.getName();
    97                          }
    98                          else
    99                          {
    100                             // Unlikely but possible due to race conditions
    101                             lockedByName = "(none)";
    102                         }
    103                         log.warn("Waited for longer than " + WAIT_WARN_MILLIS
    104                             + " milliseconds for access to lock " + this.toString()
    105                             + " which is locked by thread " + lockedByName);
    106                     }
    107                 }
    108             }
    109         }
    110     }
    111
    112     public void unlock()
    113     {
    114         Thread caller = Thread.currentThread();
    115         synchronized(this)
    116         {
    117             if (lockedBy != caller)
    118             {
    119                 throw new IllegalStateException("Unlock on lock not owned by caller");
    120             }
    121
    122             --lockCount;
    123             if (lockCount == 0)
    124             {
    125                 lockedBy = null;
    126                 this.notifyAll();
    127             }
    128         }
    129     }
    130
    131     public boolean isHeldByCurrentThread()
    132     {
    133         Thread caller = Thread.currentThread();
    134         synchronized(this)
    135         {
    136             return lockedBy == caller;
    137         }
    138     }
    139 }