This is an automated email from the ASF dual-hosted git repository.

markt-asf 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 8e3a010e02 Review "Guard the sender recovery trigger against a 
concurrent stop"
8e3a010e02 is described below

commit 8e3a010e024259509e65ff062785e721813038cf
Author: Mark Thomas <[email protected]>
AuthorDate: Thu Oct 22 14:26:19 2026 +0100

    Review "Guard the sender recovery trigger against a concurrent stop"
    
    Close some timing gaps
---
 .../tribes/membership/LocalStrings.properties      |  1 +
 .../tribes/membership/McastServiceImpl.java        | 55 +++++++++++++++++-----
 2 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/java/org/apache/catalina/tribes/membership/LocalStrings.properties 
b/java/org/apache/catalina/tribes/membership/LocalStrings.properties
index a4224bdc6d..9676d1ad20 100644
--- a/java/org/apache/catalina/tribes/membership/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/membership/LocalStrings.properties
@@ -41,6 +41,7 @@ mcastServiceImpl.messageError=Unable to decode message
 mcastServiceImpl.receive.running=McastService.receive already running.
 mcastServiceImpl.recovery=Tribes membership running recovery thread. 
Multicasting is not functional.
 mcastServiceImpl.recovery.failed=Recovery attempt number [{0}] failed. Trying 
again in [{1}] seconds.
+mcastServiceImpl.recovery.halted=Recovery halted as membership service has 
been explicitly stopped.
 mcastServiceImpl.recovery.startFailed=Recovery thread failed to start 
membership service.
 mcastServiceImpl.recovery.stopFailed=Recovery thread failed to stop membership 
service.
 mcastServiceImpl.recovery.successful=Membership recovery was successful.
diff --git a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java 
b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
index 60935bb95f..e5c99ed582 100644
--- a/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
+++ b/java/org/apache/catalina/tribes/membership/McastServiceImpl.java
@@ -40,9 +40,9 @@ import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
 /**
- * A <b>membership</b> implementation using simple multicast. This class is 
responsible for maintaining a list of
- * active cluster nodes in the cluster. If a node fails to send out a 
heartbeat, the node will be dismissed. This is the
- * low level implementation that handles the multicasting sockets.
+ * A <b>membership</b> implementation using simple multicast. This class is 
responsible for maintaining a list of active
+ * cluster nodes in the cluster. If a node fails to send out a heartbeat, the 
node will be dismissed. This is the low
+ * level implementation that handles the multicasting sockets.
  */
 public class McastServiceImpl extends MembershipProviderBase {
 
@@ -54,9 +54,8 @@ public class McastServiceImpl extends MembershipProviderBase {
     protected static final int MAX_PACKET_SIZE = 65535;
 
     /**
-     * The maximum payload size of a packet that can be sent. This is lower 
than the maximum
-     * packet size because the protocol limit for a UDP datagram includes the 
IP and UDP headers,
-     * and the IP header may be extended by options.
+     * The maximum payload size of a packet that can be sent. This is lower 
than the maximum packet size because the
+     * protocol limit for a UDP datagram includes the IP and UDP headers, and 
the IP header may be extended by options.
      */
     protected static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - 48;
 
@@ -537,7 +536,8 @@ public class McastServiceImpl extends 
MembershipProviderBase {
      * Send a packet.
      *
      * @param checkexpired whether to check for expired members
-     * @param packet the packet to send
+     * @param packet       the packet to send
+     *
      * @throws IOException if send fails
      */
     public void send(boolean checkexpired, DatagramPacket packet) throws 
IOException {
@@ -567,6 +567,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Get the service start time.
+     *
      * @return the service start time
      */
     public long getServiceStartTime() {
@@ -575,6 +576,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Get the recovery counter.
+     *
      * @return the recovery counter
      */
     public int getRecoveryCounter() {
@@ -583,6 +585,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Check if recovery is enabled.
+     *
      * @return true if recovery is enabled
      */
     public boolean isRecoveryEnabled() {
@@ -591,6 +594,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Get the recovery sleep time.
+     *
      * @return the recovery sleep time
      */
     public long getRecoverySleepTime() {
@@ -599,6 +603,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Get the channel.
+     *
      * @return the channel
      */
     public Channel getChannel() {
@@ -607,6 +612,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Set the channel.
+     *
      * @param channel the channel to set
      */
     public void setChannel(Channel channel) {
@@ -687,6 +693,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
         /**
          * Constructor.
+         *
          * @param time the send interval
          */
         public SenderThread(long time) {
@@ -739,6 +746,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
         /**
          * Recover the mcast service.
+         *
          * @param parent the parent service
          */
         public static synchronized void recover(McastServiceImpl parent) {
@@ -769,6 +777,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
         /**
          * Constructor.
+         *
          * @param parent the parent service
          */
         public RecoveryThread(McastServiceImpl parent) {
@@ -777,6 +786,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
         /**
          * Stop the service.
+         *
          * @return true if stopped successfully
          */
         public boolean stopService() {
@@ -791,12 +801,15 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
         /**
          * Start the service.
+         *
+         * @param recoveryLevel Services to (re)start
+         *
          * @return true if started successfully
          */
-        public boolean startService() {
+        public boolean startService(int recoveryLevel) {
             try {
                 parent.init();
-                parent.start(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ);
+                parent.start(recoveryLevel);
                 return true;
             } catch (Exception e) {
                 
log.warn(sm.getString("mcastServiceImpl.recovery.startFailed"), e);
@@ -814,10 +827,23 @@ public class McastServiceImpl extends 
MembershipProviderBase {
                         log.info(sm.getString("mcastServiceImpl.recovery"));
                     }
                     // Non short-circuit AND since we want both stop and start 
to execute
-                    if (stopService() & startService()) {
-                        success = true;
-                        if (log.isInfoEnabled()) {
-                            
log.info(sm.getString("mcastServiceImpl.recovery.successful"));
+                    synchronized (parent) {
+                        int recoveryLevel = parent.startLevel;
+                        if (recoveryLevel > 0) {
+                            if (stopService() & startService(recoveryLevel)) {
+                                success = true;
+                                if (log.isInfoEnabled()) {
+                                    
log.info(sm.getString("mcastServiceImpl.recovery.successful"));
+                                }
+                            } else {
+                                // This is a hack to enable detection of an 
explicit stop() call between recovery attempts
+                                parent.startLevel = recoveryLevel;
+                            }
+                        } else {
+                            success = true;
+                            if (log.isInfoEnabled()) {
+                                
log.info(sm.getString("mcastServiceImpl.recovery.halted"));
+                            }
                         }
                     }
                     try {
@@ -839,6 +865,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Set the recovery counter.
+     *
      * @param recoveryCounter the recovery counter
      */
     public void setRecoveryCounter(int recoveryCounter) {
@@ -847,6 +874,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Set whether recovery is enabled.
+     *
      * @param recoveryEnabled whether recovery is enabled
      */
     public void setRecoveryEnabled(boolean recoveryEnabled) {
@@ -855,6 +883,7 @@ public class McastServiceImpl extends 
MembershipProviderBase {
 
     /**
      * Set the recovery sleep time.
+     *
      * @param recoverySleepTime the recovery sleep time
      */
     public void setRecoverySleepTime(long recoverySleepTime) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to