Re: [livetribe-scm] [461] garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua: Fix for LIVETRIBE-61: made AbstractServer thread safe.

View: New views
3 Messages — Rating Filter:   Alert me  

Parent Message unknown Re: [livetribe-scm] [461] garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua: Fix for LIVETRIBE-61: made AbstractServer thread safe.

by Alan.Cabrera :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Sweet.  And it still fails fast.


Regards,
Alan

On Jul 31, 2008, at 5:42 AM, sbordet@... wrote:

Diff

Modified: garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/AbstractServer.java (460 => 461)

--- garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/AbstractServer.java	2008-07-27 17:48:22 UTC (rev 460)
+++ garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/AbstractServer.java	2008-07-31 12:42:53 UTC (rev 461)
@@ -15,7 +15,7 @@
  */
 package org.livetribe.slp.spi;
 
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -24,50 +24,55 @@
  */
 public abstract class AbstractServer implements Server
 {
+    private enum Status
+    {
+        READY, STARTING, RUNNING, STOPPING, STOPPED
+    }
+
     protected final Logger logger = Logger.getLogger(getClass().getName());
+    private final AtomicReference<Status> status = new AtomicReference<Status>(Status.READY);
 
-    private final AtomicBoolean starting = new AtomicBoolean(false);
-    private volatile boolean running;
-
     public boolean isRunning()
     {
-        return running;
+        return status.get() == Status.RUNNING;
     }
 
-    public void start()
+    public boolean start()
     {
-        if (!starting.compareAndSet(false, true) || isRunning())
+        if (!status.compareAndSet(Status.READY, Status.STARTING))
         {
-            if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " is already running");
-            return;
+            if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " is not ready");
+            return false;
         }
 
         if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " starting...");
 
         doStart();
 
-        running = true;
+        status.set(Status.RUNNING);
+        if (logger.isLoggable(Level.FINE)) logger.fine("Server " + this + " started successfully");
 
-        if (logger.isLoggable(Level.FINE)) logger.fine("Server " + this + " started successfully");
+        return true;
     }
 
     protected abstract void doStart();
 
-    public void stop()
+    public boolean stop()
     {
-        if (!starting.compareAndSet(true, false) && !isRunning())
+        if (!status.compareAndSet(Status.RUNNING, Status.STOPPING))
         {
-            if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " is already stopped");
-            return;
+            if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " is not running");
+            return false;
         }
 
         if (logger.isLoggable(Level.FINER)) logger.finer("Server " + this + " stopping...");
 
         doStop();
 
-        running = false;
+        status.set(Status.STOPPED);
+        if (logger.isLoggable(Level.FINE)) logger.fine("Server " + this + " stopped successfully");
 
-        if (logger.isLoggable(Level.FINE)) logger.fine("Server " + this + " stopped successfully");
+        return true;
     }
 
     protected abstract void doStop();

Modified: garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/Server.java (460 => 461)

--- garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/Server.java	2008-07-27 17:48:22 UTC (rev 460)
+++ garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/spi/Server.java	2008-07-31 12:42:53 UTC (rev 461)
@@ -20,9 +20,9 @@
  */
 public interface Server
 {
-    public void start();
+    public boolean start();
 
     public boolean isRunning();
 
-    public void stop();
+    public boolean stop();
 }

Modified: garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua/StandardUserAgent.java (460 => 461)

--- garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua/StandardUserAgent.java	2008-07-27 17:48:22 UTC (rev 460)
+++ garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua/StandardUserAgent.java	2008-07-31 12:42:53 UTC (rev 461)
@@ -93,9 +93,9 @@
         this.directoryAgentAddresses = directoryAgentAddresses;
     }
 
-    public void start()
+    public boolean start()
     {
-        server.start();
+        return server.start();
     }
 
     public boolean isRunning()
@@ -103,15 +103,16 @@
         return server.isRunning();
     }
 
-    public void stop()
+    public boolean stop()
     {
-        server.stop();
+        return server.stop();
     }
 
     protected void doStart()
     {
         udpConnectorServer.addMessageListener(listener);
         udpConnectorServer.start();
+
         notificationConnectorServer.addMessageListener(listener);
         notificationConnectorServer.start();
 

Added: garden/livetribe-slp/trunk/src/test/java/org/livetribe/slp/spi/AbstractServerTest.java (0 => 461)

--- garden/livetribe-slp/trunk/src/test/java/org/livetribe/slp/spi/AbstractServerTest.java	                        (rev 0)
+++ garden/livetribe-slp/trunk/src/test/java/org/livetribe/slp/spi/AbstractServerTest.java	2008-07-31 12:42:53 UTC (rev 461)
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2008-2008 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.livetribe.slp.spi;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.testng.annotations.Test;
+
+/**
+ * @version $Revision$ $Date$
+ */
+public class AbstractServerTest
+{
+    private void await(CyclicBarrier barrier)
+    {
+        try
+        {
+            barrier.await();
+        }
+        catch (Exception x)
+        {
+            throw new AssertionError(x);
+        }
+    }
+
+    /**
+     * Tests that calling concurrently start() results in only one call to doStart().
+     */
+    @Test
+    public void testT1StartT2Start() throws Exception
+    {
+        ExecutorService pool = Executors.newCachedThreadPool();
+        final CyclicBarrier barrier = new CyclicBarrier(2);
+        final AtomicInteger start = new AtomicInteger(0);
+
+        final AbstractServer server = new AbstractServer()
+        {
+            protected void doStart()
+            {
+                logger.finer("Server " + this + " in doStart");
+                if (start.compareAndSet(0, 1))
+                {
+                    // Rendez-vous on barrier1 with thread 'main'
+                    await(barrier);
+                    // Rendez-vous on barrier2 with thread 'main'
+                    await(barrier);
+                }
+            }
+
+            protected void doStop()
+            {
+                logger.finer("Server " + this + " in doStop");
+            }
+        };
+
+        Callable<Boolean> starter = new Callable<Boolean>()
+        {
+            public Boolean call() throws Exception
+            {
+                return server.start();
+            }
+        };
+
+        Future<Boolean> t1 = pool.submit(starter);
+        // Rendez-vous on barrier1 with thread 't1'
+        barrier.await();
+        barrier1:
+        {}
+
+        Future<Boolean> t2 = pool.submit(starter);
+        assert !t2.get();
+        // Rendez-vous on barrier2 with thread 't1'
+        barrier.await();
+        barrier2:
+        {}
+
+        assert t1.get();
+    }
+
+    @Test
+    public void testT1StartT2StopT2Start() throws Exception
+    {
+        ExecutorService pool = Executors.newCachedThreadPool();
+        final CyclicBarrier barrier = new CyclicBarrier(2);
+        final AtomicInteger start = new AtomicInteger(0);
+
+        final AbstractServer server = new AbstractServer()
+        {
+            protected void doStart()
+            {
+                logger.finer("Server " + this + " in doStart");
+                if (start.compareAndSet(0, 1))
+                {
+                    // Rendez-vous on barrier1 with thread 'main'
+                    await(barrier);
+                    // Rendez-vous on barrier2 with thread 'main'
+                    await(barrier);
+                    // Rendez-vous on barrier3 with thread 'main'
+                    await(barrier);
+                }
+            }
+
+            protected void doStop()
+            {
+                logger.finer("Server " + this + " in doStop");
+            }
+        };
+
+        Callable<Boolean> starter = new Callable<Boolean>()
+        {
+            public Boolean call() throws Exception
+            {
+                return server.start();
+            }
+        };
+
+        Callable<Boolean> stopper = new Callable<Boolean>()
+        {
+            public Boolean call() throws Exception
+            {
+                return server.stop();
+            }
+        };
+
+        Future<Boolean> t1 = pool.submit(starter);
+        // Rendez-vous on barrier1 with thread 't1'
+        barrier.await();
+        barrier1:
+        {}
+
+        Future<Boolean> t2 = pool.submit(stopper);
+        assert !t2.get();
+        // Rendez-vous on barrier2 with thread 't1'
+        barrier.await();
+        barrier2:
+        {}
+
+        t2 = pool.submit(starter);
+        assert !t2.get();
+        // Rendez-vous on barrier3 with thread 't1'
+        barrier.await();
+        barrier3:
+        {}
+
+        assert t1.get();
+    }
+}
Property changes on: garden/livetribe-slp/trunk/src/test/java/org/livetribe/slp/spi/AbstractServerTest.java
___________________________________________________________________
Name: svn:keywords
   + Author Date Id Revision
Name: svn:eol-style
   + native


To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email



Re: Re: [livetribe-scm] [461] garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua: Fix for LIVETRIBE-61: made AbstractServer thread safe.

by simonebordet :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi,

On Thu, Jul 31, 2008 at 4:53 PM, Alan Cabrera <adc@...> wrote:
> Sweet.  And it still fails fast.

It does not work ?

Simon
--
http://bordet.blogspot.com
---
Finally, no matter how good the architecture and design are,
to deliver bug-free software with optimal performance and reliability,
the implementation technique must be flawless.   Victoria Livschitz

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Re: Re: [livetribe-scm] [461] garden/livetribe-slp/trunk/src/main/java/org/livetribe/slp/ua: Fix for LIVETRIBE-61: made AbstractServer thread safe.

by Alan.Cabrera :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Jul 31, 2008, at 8:47 AM, Simone Bordet wrote:

> Hi,
>
> On Thu, Jul 31, 2008 at 4:53 PM, Alan Cabrera <adc@...>  
> wrote:
>> Sweet.  And it still fails fast.
>
> It does not work ?

It does.  Sorry, I should have provided more detail.

I just like how if I run two starts at the same time one will return  
immediately rather than wait for the other start to complete.

BTW, my preference is to throw an IllegalStateException rather than  
return a boolean.   If people really care about what/if something went  
wrong I think exceptions are the way to go.


Regards,
Alan


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email