On Thu, Apr 4, 2024 at 3:58 PM Konstantin Kolinko
<knst.koli...@gmail.com> wrote:
>
> чт, 4 апр. 2024 г. в 15:45, <r...@apache.org>:
> >
> > This is an automated email from the ASF dual-hosted git repository.
> >
> > remm pushed a commit to branch main
> > in repository https://gitbox.apache.org/repos/asf/tomcat.git
> >
> >
> > The following commit(s) were added to refs/heads/main by this push:
> >      new fba822229a Further locking harmonizations for main components
> > fba822229a is described below
> >
> > commit fba822229a6f05223768e7d7026cd886d7f8356d
> > Author: remm <r...@apache.org>
> > AuthorDate: Thu Apr 4 14:43:02 2024 +0200
> >
> >     Further locking harmonizations for main components
> >
> >     Also for lifecycle operations.
> > ---
> >  java/org/apache/catalina/core/ContainerBase.java   | 86 
> > +++++++++++++---------
> >  java/org/apache/catalina/core/StandardService.java | 22 +++---
> >  webapps/docs/changelog.xml                         |  5 +-
> >  3 files changed, 66 insertions(+), 47 deletions(-)
> >
> > diff --git a/java/org/apache/catalina/core/ContainerBase.java 
> > b/java/org/apache/catalina/core/ContainerBase.java
> > index 4edd4edf9d..846f4e27d8 100644
> > --- a/java/org/apache/catalina/core/ContainerBase.java
> > +++ b/java/org/apache/catalina/core/ContainerBase.java
> > @@ -129,6 +129,7 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >       * The child Containers belonging to this Container, keyed by name.
> >       */
> >      protected final HashMap<String,Container> children = new HashMap<>();
> > +    private final ReadWriteLock childrenLock = new 
> > ReentrantReadWriteLock();
>
> It is useless to have the above "children" field as protected.
> Nobody will be able to access it in a safe manner, because they have
> no access to childrenLock.
>
> > @@ -632,26 +633,30 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >              log.debug(sm.getString("containerBase.child.add", child, 
> > this));
> >          }
> >
> > -        synchronized (children) {
> > +        childrenLock.writeLock().lock();
> > +        try {
> >              if (children.get(child.getName()) != null) {
> >                  throw new 
> > IllegalArgumentException(sm.getString("containerBase.child.notUnique", 
> > child.getName()));
> >              }
> >              child.setParent(this); // May throw IAE
> >              children.put(child.getName(), child);
> > -        }
>
> Change of behaviour!
> The old sync block ended with the "}" above.
>
> > -        fireContainerEvent(ADD_CHILD_EVENT, child);
> > +            fireContainerEvent(ADD_CHILD_EVENT, child);
> >
> > -        // Start child
> > -        // Don't do this inside sync block - start can be a slow process 
> > and
> > -        // locking the children object can cause problems elsewhere
> > -        try {
> > -            if ((getState().isAvailable() || 
> > LifecycleState.STARTING_PREP.equals(getState())) && startChildren) {
> > -                child.start();
> > +            // Start child
> > +            // Don't do this inside sync block - start can be a slow 
> > process and
>
> The above says "Don't do this inside sync block",
> but your change puts it into a sync block.
>
> (And I see similar changes of behaviour further in this commit.)
>
> The lock was intended to protect changes of the "children" hash table,
> but now it starts to protect
> a) state changes of those children (start/stop/destroy)
> b) notifications.
>
> I think that the state changes (in LifecycleBase) already have their
> own protection.

Yes, it's all synced. This is rather inconsistent overall, and I
thought first about harmonizing the other way. I don't understand why
the lifecycle operation needs to be under the sync (and the
notification also seemed like a bad idea) so it could be better.

Rémy

> While stating/stopping/destroying is in progress,
> that child still remains ours and findChildren() etc. should be
> allowed to be called by other threads.
> A write lock prevents other threads from reading the value.
>
> https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html
>
> Thus I am -1.
> I think it is easier to revert and work on a new patch rather than try
> to amend this one.
>
> > +            // locking the children object can cause problems elsewhere
> > +            try {
> > +                if ((getState().isAvailable() || 
> > LifecycleState.STARTING_PREP.equals(getState())) && startChildren) {
> > +                    child.start();
> > +                }
> > +            } catch (LifecycleException e) {
> > +                throw new 
> > IllegalStateException(sm.getString("containerBase.child.start"), e);
> >              }
> > -        } catch (LifecycleException e) {
> > -            throw new 
> > IllegalStateException(sm.getString("containerBase.child.start"), e);
> > +        } finally {
> > +            childrenLock.writeLock().unlock();
> >          }
> > +
> >      }
> >
> >
> > @@ -688,8 +693,11 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >          if (name == null) {
> >              return null;
> >          }
> > -        synchronized (children) {
> > +        childrenLock.readLock().lock();
> > +        try {
> >              return children.get(name);
> > +        } finally {
> > +            childrenLock.readLock().unlock();
> >          }
> >      }
> >
> > @@ -700,8 +708,11 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >       */
> >      @Override
> >      public Container[] findChildren() {
> > -        synchronized (children) {
> > +        childrenLock.readLock().lock();
> > +        try {
> >              return children.values().toArray(new Container[0]);
> > +        } finally {
> > +            childrenLock.readLock().unlock();
> >          }
> >      }
> >
> > @@ -728,36 +739,40 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >              return;
> >          }
> >
> > +        childrenLock.writeLock().lock();
>
> Why add locking here? There was none.
> The code does not modify the "children" map here. It happens later,
> after the child has been stopped and destroyed.
>
> >          try {
> > -            if (child.getState().isAvailable()) {
> > -                child.stop();
> > +            try {
> > +                if (child.getState().isAvailable()) {
> > +                    child.stop();
> > +                }
> > +            } catch (LifecycleException e) {
> > +                log.error(sm.getString("containerBase.child.stop"), e);
> >              }
> > -        } catch (LifecycleException e) {
> > -            log.error(sm.getString("containerBase.child.stop"), e);
> > -        }
> >
> > -        boolean destroy = false;
> > -        try {
> > -            // child.destroy() may have already been called which would 
> > have
> > -            // triggered this call. If that is the case, no need to 
> > destroy the
> > -            // child again.
> > -            if (!LifecycleState.DESTROYING.equals(child.getState())) {
> > -                child.destroy();
> > -                destroy = true;
> > +            boolean destroy = false;
> > +            try {
> > +                // child.destroy() may have already been called which 
> > would have
> > +                // triggered this call. If that is the case, no need to 
> > destroy the
> > +                // child again.
> > +                if (!LifecycleState.DESTROYING.equals(child.getState())) {
> > +                    child.destroy();
> > +                    destroy = true;
> > +                }
> > +            } catch (LifecycleException e) {
> > +                log.error(sm.getString("containerBase.child.destroy"), e);
> >              }
> > -        } catch (LifecycleException e) {
> > -            log.error(sm.getString("containerBase.child.destroy"), e);
> > -        }
> >
> > -        if (!destroy) {
> > -            fireContainerEvent(REMOVE_CHILD_EVENT, child);
> > -        }
> > +            if (!destroy) {
> > +                fireContainerEvent(REMOVE_CHILD_EVENT, child);
> > +            }
> >
> > -        synchronized (children) {
> >              if (children.get(child.getName()) == null) {
> >                  return;
> >              }
> >              children.remove(child.getName());
> > +        } finally {
> > +            childrenLock.writeLock().unlock();
> > +
> >          }
> >
> >      }
> > @@ -1166,13 +1181,16 @@ public abstract class ContainerBase extends 
> > LifecycleMBeanBase implements Contai
> >
> >      public ObjectName[] getChildren() {
> >          List<ObjectName> names;
> > -        synchronized (children) {
> > +        childrenLock.readLock().lock();
> > +        try {
> >              names = new ArrayList<>(children.size());
> >              for (Container next : children.values()) {
> >                  if (next instanceof ContainerBase) {
> >                      names.add(next.getObjectName());
> >                  }
> >              }
> > +        } finally {
> > +            childrenLock.readLock().unlock();
> >          }
> >          return names.toArray(new ObjectName[0]);
> >      }
> > diff --git a/java/org/apache/catalina/core/StandardService.java 
> > b/java/org/apache/catalina/core/StandardService.java
> > index dbb32e55ce..63eb38e57f 100644
> > --- a/java/org/apache/catalina/core/StandardService.java
> > +++ b/java/org/apache/catalina/core/StandardService.java
> > @@ -230,20 +230,20 @@ public class StandardService extends 
> > LifecycleMBeanBase implements Service {
> >              System.arraycopy(connectors, 0, results, 0, connectors.length);
> >              results[connectors.length] = connector;
> >              connectors = results;
> > +            try {
> > +                if (getState().isAvailable()) {
> > +                    connector.start();
> > +                }
> > +            } catch (LifecycleException e) {
> > +                throw new 
> > IllegalArgumentException(sm.getString("standardService.connector.startFailed",
> >  connector), e);
> > +            }
> >          } finally {
> >              writeLock.unlock();
> >          }
> >
> > -        try {
> > -            if (getState().isAvailable()) {
> > -                connector.start();
> > -            }
> > -        } catch (LifecycleException e) {
> > -            throw new 
> > IllegalArgumentException(sm.getString("standardService.connector.startFailed",
> >  connector), e);
> > -        }
> > -
> >          // Report this property change to interested listeners
> >          support.firePropertyChange("connector", null, connector);
> > +
> >      }
> >
> >
> > @@ -326,13 +326,13 @@ public class StandardService extends 
> > LifecycleMBeanBase implements Service {
> >                  }
> >              }
> >              connectors = results;
> > -
> > -            // Report this property change to interested listeners
> > -            support.firePropertyChange("connector", connector, null);
> >          } finally {
> >              writeLock.unlock();
> >          }
> >
> > +        // Report this property change to interested listeners
> > +        support.firePropertyChange("connector", connector, null);
> > +
> >      }
> >
> >
> > diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
> > index 368ccfa673..0727329c3f 100644
> > --- a/webapps/docs/changelog.xml
> > +++ b/webapps/docs/changelog.xml
> > @@ -139,8 +139,9 @@
> >          Markus Wolfe. (schultz)
> >        </fix>
> >        <fix>
> > -        Improve Service connectors access sync using a 
> > ReentrantReadWriteLock.
> > -        (remm)
> > +        Improve Service connectors and Container children access sync 
> > using a
> > +        ReentrantReadWriteLock. Harmonize locking for their lifecycle
> > +        operations after add and remove. (remm)
> >        </fix>
> >      </changelog>
> >    </subsection>
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> > For additional commands, e-mail: dev-h...@tomcat.apache.org
> >
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to