I've attached a patch for review, I'll create a Jira issue shortly.

Regards,

Peter.


On 8/05/2013 7:40 AM, Peter wrote:
I agree, I think we need to review this TCK test against the specification.

I suspect more than one person created this test, perhaps there was confusion; 
at some point the resolution was changed to seconds (before Apache River's 
time).

This meant if insufficient ServiceItem's are found, lookup could return up to 3 
seconds early or 30 seconds later than the waitDur (specified in ms).  This 
seems unreasonable, if the method waits that 3 seconds, it might find more 
ServiceItems.

On the other hand if sufficient ServiceItems are found, it checks they return 
as soon as possible by confirming the number of services returned.  To pass on 
this occassion, the test must return more than one second prior to waitDur's 
expiry.  This also seems unreasonable, one because another thread may obtain 
the monitor first (if so it finds more than minMatches) and two because it 
should pass if it returns in the waitDur time window.

I think the ms time ranges you've mentioned are more appropriate, they are 
achievable in practise (I checked).

I'll submit a patch for review.

Peter.





----- Original message -----
Hi Peter:

I'd certainly apply some tolerance band to the test result.  Failing for
3 ms difference is unreasonable.  I'd expect probably 50-100ms
variance.  The ms clock isn't assumed to be that accurate.  Matter of
fact, waiting for a task switch could cause at least that much variance.

Cheers,

Greg.

On Tue, 2013-05-07 at 06:17, Peter Firmstone wrote:
This test smells broken?

I modified the output to read in milliseconds.

Relevant ServiceDiscoveryManager method under test:

public ServiceItem[] lookup(ServiceTemplate tmpl,
                                                                   int 
minMatches,
                                                                   int 
maxMatches,
                                                                   
ServiceItemFilter filter,
                                                                   long waitDur)
                                                                     throws 
InterruptedException,
                                                                                
   RemoteException {...}



To summarise what's happens (occassionally):

CASE 1:

         * ServiceDiscoveryManager.lookup  is called and blocks waiting until
             it receives the minimum number of ServiceItems.
         * If the required number of ServiceItem's are discovered late, the
             waitDur limit will be approached when lookup returns.
                     o The resolution of the test is to the nearest second, not
                         millisecond.


         Firstly I don't believe this is the intent of the Jini Standard
         SD4.1.3 The blocking feature of lookup, which states:

                 As noted above, each category contains a version of |lookup|
                 that provides a feature in which the entity can request that if
                 the number of service references found throughout the available
                 lookup services does not fall into a desired range, the method
                 will wait a finite period of time until either an acceptable
                 minimum number of service references are discovered or the
                 specified time period has passed.


         In this case the specified time period hasn't quite passed, however
         an acceptable minimum number of service references are returned and
         the test fails.  This failure is rare.


CASE 2:

                             * 3. while lookup() is blocking, if enough new 
services are
                             *      registered so that the acceptable minimum 
is achieved,
                             *      lookup() will return immediately; that is, 
even if there
                             *      is more time left on the wait period, 
lookup() will not
                             *      wait for more services beyond the minimum.
                             *
                             *      For example, if 3 services are initially 
registered and
                             *      lookup is called with min = 4 and max = 7, 
then lookup()
                             *      will find the 3 services and then wait for 
more
services to
                             *      be registered. Suppose that while lookup() 
is blocking
                             *      another 5 services are registered, bringing 
the total
number
                             *      of services to 8. In this case, lookup() 
will stopping
                             *      waiting and return 4 services (the 
minimum), not the
                             *      maximum 7.

While the test performs this check, it isn't implemented in
ServiceDiscoveryManager, instead it's left up to chance that the lookup
method, waiting on an object monitor will obtain the monitor as soon as
it's notified.  However if multiple threads contend for the lock, it's
very unlikely the thread scheduler will give the object monitor to the
waiting thread, instead the most recently blocking thread is likely to
get the monitor.

This requirment in the test appears to be additional to the
specification and would require ServiceDiscoveryManager be modified, to
use some kind of fair Lock scheme.  However doing this would
unnecessarily complicate ServiceDiscoveryManager, instead it would be
simpler to return the number of ServiceItem's found up to the maxMatches
at the time the lock is obtained.

CASE 1 test results:

Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Time is Mon May 06 22:17:31 EST 2013
Starting test in separate process with command:
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja
 
r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3
 .jar
-ea -esa -client '-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
-Dcom.sun.jini.qa.harness.runjiniserver=true
-Dcom.sun.jini.qa.harness.runkitserver=true
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
-Dcom.sun.jini.qa.harness.testhosts=
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.test.port=9082
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
'-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
com.sun.jini.qa.harness.MasterTest
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
-- requested block = 60000 millisecond(s), actual block = 59999
millisecond(s)
           at
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
           at
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
           at
com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
           at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
           at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)

TIME: 10:20:02 PM

Test process was destroyed and returned code 1
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Test Failed: Test Failed: com.sun.jini.qa.harness.TestException:  --
blocked longer than expected -- requested block = 60000 millisecond(s),
actual block = 59999 millisecond(s)

Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Running com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
Time is Tue May 07 10:40:30 EST 2013
Time is Tue May 07 10:40:30 EST 2013
Starting test in separate process with command:
Starting test in separate process with command:
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja
 
r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3
 .jar
-ea -esa -client '-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
-Dcom.sun.jini.qa.harness.runjiniserver=true
-Dcom.sun.jini.qa.harness.runkitserver=true
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
-Dcom.sun.jini.qa.harness.testhosts=
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.test.port=9082
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
'-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
com.sun.jini.qa.harness.MasterTest
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
'C:\Program Files\Java\jdk1.6.0_26\jre\bin\java'
-Djava.security.manager=org.apache.river.api.security.CombinerSecurityManager
-Djava.security.policy=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/policy/defaulttest.policy
-Djava.rmi.server.codebase=http://medusa:9082/qa1-servicediscovery-dl.jar -cp
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-platform.ja
 
r;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\jsk-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\high-scale-lib.jar;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib\custard-apple-1.0.3
 .jar
-ea -esa -client '-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
-Dcom.sun.jini.jsk.port=9080 -Dcom.sun.jini.qa.port=9081
-Dcom.sun.jini.jsk.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy
-Dcom.sun.jini.qa.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.qa.harness.harnessJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jiniharness.jar
-Dcom.sun.jini.qa.harness.testJar=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib\jinitests.jar
-Dcom.sun.jini.qa.harness.runjiniserver=true
-Dcom.sun.jini.qa.harness.runkitserver=true
-Djava.security.properties=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/harness/trust/dynamic-policy.properties
-Dcom.sun.jini.qa.harness.testhosts=
-Djava.util.logging.config.file=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\resources\qa1.logging
-Dcom.sun.jini.test.home=C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa
-Dcom.sun.jini.test.port=9082
-Dcom.sun.jini.qa.harness.policies=file:/C:/Users/peter/Documents/NetBeansProjects/peterConcurrentPolicy/qa/src/com/sun/jini/test/resources/jinitest.policy
'-Djava.ext.dirs=C:\Program
Files\Java\jdk1.6.0_26\jre\lib\ext;C:\windows\Sun\Java\lib\ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\lib-ext;C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\lib-ext'
com.sun.jini.qa.harness.MasterTest
com/sun/jini/test/spec/servicediscovery/lookup/LookupMinLessMax.td
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
-- requested block = 60000 millisecond(s), actual block = 59997
millisecond(s) wait error = 0
com.sun.jini.qa.harness.TestException:  -- blocked longer than expected
-- requested block = 60000 millisecond(s), actual block = 59997
millisecond(s) wait error = 0
           at
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.verifyBlocking(LookupMinEqualsMax.java:288)
           at
com.sun.jini.test.spec.servicediscovery.lookup.LookupMinEqualsMax.applyTestDef(LookupMinEqualsMax.java:120)
           at
com.sun.jini.test.spec.servicediscovery.AbstractBaseTest.run(AbstractBaseTest.java:549)
           at com.sun.jini.qa.harness.MasterTest.doTest(MasterTest.java:256)
           at com.sun.jini.qa.harness.MasterTest.main(MasterTest.java:144)




Relevant Section of Jini Specification:


                     SD.4.1.3 The |lookup| Method

The |lookup| method queries each available lookup service in the managed
set for service reference(s) that match criteria defined by the entity
that invokes this method. Entities typically employ this method when
they need infrequent access to services and when the cost of making
remote queries is outweighed by the overhead of maintaining a local
cache (for example, because of resource limitations).

The |lookup| method has four versions, each version falling into one of
two categories: those versions of this method that return a single
instance of |ServiceItem| and those versions that return a set of
service references as an array of |ServiceItem| objects.

Two arguments are common to all versions of this method: an instance of
|ServiceTemplate| and an instance of |ServiceItemFilter|.

Within each category, the versions of |lookup| differ only in whether or
not a particular version provides what is referred to as a "wait" (or
blocking) feature. That is, each category contains both a non-blocking
version of |lookup| which returns immediately when unable to find the
desired service, and a blocking version which returns only after waiting
a specified amount of time for the desired service to be discovered. The
particular version of |lookup| that an entity employs is typically
determined by the entity's intended usage pattern.

The descriptions that follow refer to all versions of the |lookup|
method, except where explicitly noted.

The |tmpl| argument and the |filter| argument both have semantics
identical to that defined for these arguments in the description of the
|createLookupCache| method above. In particular,

         * A |null| reference value for the |tmpl| parameter is treated as
             the equivalent of a "wildcarded" |ServiceTemplate|.

         * If |null| is the value for the |filter| parameter, only template
             matching will be employed to find the desired services.

         * The effects of modifying the contents of the |tmpl| parameter
             while the invocation is in progress are unpredictable and 
undefined.

If no service can be found that matches the desired criteria, then the
versions of |lookup| from the first category--those that return a single
instance of |ServiceItem|--will return |null|, whereas the versions from
the second category--those that return an array of |ServiceItem|
instances--will return an empty array.

The versions of |lookup| from the first category can be used in a
fashion similar to the first form of the |lookup| method defined in the
|ServiceRegistrar| interface described in the /Jini Lookup Service
Specification/. That is, an entity would typically invoke one of these
versions of |lookup| when it wishes to find a /single/ service reference
and the particular lookup service with which that service reference is
registered is unimportant to the entity.

Each version of |lookup| defined in the |ServiceDiscoveryManager|
differs with the corresponding version of |lookup| in |ServiceRegistrar|
in the following ways:

         * The versions of |lookup| defined in the |ServiceDiscoveryManager|
             query /multiple/ lookup services (the order in which the lookup
             services are queried is dependent on the implementation).

         * The versions of |lookup| defined in the |ServiceDiscoveryManager|
             can apply additional selection criteria, in the form of a filter
             object, when deciding whether a service reference found through
             standard template matching should be returned to the entity.

The versions of |lookup| that return an array of |ServiceItem| objects
can be used in a fashion similar to the second form of |lookup| defined
in the |ServiceRegistrar| interface. That is, an entity would typically
invoke these versions of |lookup| when it wishes to find /multiple/
service references that satisfy the input criteria. Each of the versions
of |lookup| that return an array of |ServiceItem| objects takes as one
of its arguments an |int| parameter, |maxMatches|, that represents the
maximum number of matches that should be returned. The array returned by
these methods will contain no more than |maxMatches| service references,
although it may contain fewer than that number.

As with the versions of |lookup| that return a single instance of
|ServiceItem|, multiple queries and filtering are also notable
differences between the second-category versions of this method and
their counterpart in |ServiceRegistrar|.

For each version of |lookup|, whenever a lookup service query returns a
|null| service reference, the filter is bypassed, and the service
reference is excluded from the return object. On the other hand, if the
query returns a non-|null| service reference in which the associated
array of attribute contains one or more |null| elements, the filter is
still applied and the service reference is included in the return object.

Each version of |lookup| may be confronted with duplicate references
during a search for a service of interest. This is because the same
service may register with more than one lookup service in the managed
set. As with the cache, when a set of service references is returned by
|lookup|, each service reference in the return set will be unique with
respect to all other service references in the set, as determined by the
|equals| method provided by each reference.

If it is determined that a lookup service is unavailable (due to an
exception or some other non-fatal error) while interacting with a lookup
service from the managed set, all versions of |lookup| will invoke the
|discard| method on the instance of |DiscoveryManagement| being employed
by the |ServiceDiscoveryManager|. Doing so will result in the
unavailable lookup service being discarded and made eligible for
rediscovery.

Recall that the propagation of modifications to a service's attributes
across a set of lookup services typically occurs asynchronously. It is
for this reason that while invoking |lookup| to find a set of matching
services, it is possible that the set returned may contain multiple
references having the same service ID with different attributes. Note
that although this sort of inconsistent state can also occur if the
entity employs a cache, the cache will eventually reflect the correct
state.

The Blocking Feature of |lookup|

As noted above, each category contains a version of |lookup| that
provides a feature in which the entity can request that if the number of
service references found throughout the available lookup services does
not fall into a desired range, the method will wait a finite period of
time until either an acceptable minimum number of service references are
discovered or the specified time period has passed.

The versions of |lookup| providing this blocking feature each takes as
one of its parameters a value of type long that represents the number of
milliseconds to wait for the service to be discovered. In addition to
|RemoteException| (described previously for these methods), each of
these versions of |lookup| may throw an |InterruptedException|.

One of these blocking versions of |lookup| implicitly uses a value of
one for both the acceptable minimum and the allowable maximum number of
service references to discover. The other blocking version requires that
the entity specify the range through the |minMatches| and |maxMatches|
parameters, respectively.

Prior to blocking, each of these versions of |lookup| first queries each
available lookup service in an attempt to retrieve a satisfactory number
of matching services. Whether or not the method actually blocks is
dependent on how many matching service references are found during the
query process. Blocking occurs only if after querying /all/ of the
available lookup services, the number of matching services found is less
than the acceptable minimum. If the waiting period (measured from when
blocking first begins) passes before that minimum number of service
references is found, the method will return the service references that
have been discovered up to that point. If the waiting period passes and
no services have been found, |null| or an empty array (depending on the
version of |lookup|) will be returned.

If, after querying all of the available lookup services, the number of
services found to satisfy the desired criteria is greater than or equal
to the specified minimum but less than the specified maximum, the method
will return the currently discovered service references without
blocking. If the initial query process produces the desired maximum
number of service references, the method will return the results
immediately.

The blocking versions of |lookup| are quite useful to entities that
cannot proceed until such a service of interest is found. If a
non-positive value is input to the |waitDur| argument, then the method
will not wait. It will simply query the available lookup services and
employ the return semantics described above.

The values of the |minMatches| and |maxMatches| arguments must both be
positive, and |maxMatches| must be greater than or equal to
|minMatches|; otherwise, an |IllegalArgumentException| will be thrown.

The blocking versions of |lookup| make a concurrency guarantee with
respect to the discovery of new lookup services during the wait period.
That is, while waiting for the desired service reference(s) to be
discovered, if one or more of the targeted--but previously
unavailable--lookup services is discovered and added to the managed set,
those new lookup services will also be queried for the service(s) of
interest.

In addition, the blocking versions of |lookup| throw
|InterruptedException|. When an entity invokes either version with valid
parameters, the entity may decide during the wait period that it no
longer wishes to wait the entire period for the method to return. Thus,
while the method is blocking on the discovery of matching service(s), it
may be interrupted by invoking the |interrupt| method from the |Thread|
class. The intent of this mechanism is to allow the entity to interrupt
a blocking |lookup| in the same way it would a sleeping thread.



# This patch file was generated by NetBeans IDE
# Following Index: paths are relative to: 
C:\Users\peter\Documents\NetBeansProjects\peterConcurrentPolicy\qa\src\com\sun\jini\test\spec\servicediscovery\lookup
# This patch can be applied using context Tools: Patch action on respective 
folder.
# It uses platform neutral UTF-8 encoding and \n newlines.
# Above lines and this line are ignored by the patching process.
Index: LookupMinEqualsMax.java
--- LookupMinEqualsMax.java Base (BASE)
+++ LookupMinEqualsMax.java Locally Modified (Based On LOCAL)
@@ -57,9 +57,9 @@
  */
 public class LookupMinEqualsMax extends AbstractBaseTest {
 
-    protected long waitDur = 30*1000;
-    protected int  minMatches = 0;
-    protected int  maxMatches = 0;
+    protected volatile long waitDur = 30*1000;
+    protected volatile int  minMatches = 0;
+    protected volatile int  maxMatches = 0;
 
     /** Performs actions necessary to prepare for execution of the 
      *  current test.
@@ -176,7 +176,85 @@
                                                          waitDur);
        long endTime = System.currentTimeMillis();
        long actualBlockTime = endTime-startTime;
-       long waitError = (actualBlockTime-waitDur)/1000;
+         /* 7th May 2013 - Peter Firmstone.
+          * According to section SD4.1.3 The blocking feature of lookup
+          * waitDur is measured in milliseconds.
+          * 
+          * Quote:
+              * the method will wait a finite period of time until either an 
+              * acceptable minimum number of service references are discovered 
+              * or the specified time period has passed. 
+          * 
+          * waitError is specified incorrectly here, since it is converted to
+          * seconds and hence requires that the lookup method wait no longer 
than one second less
+          * of waitDur if the acceptable minimum number of services are 
+          * discovered, this is appears incorrect.
+          * 
+          * long waitError = (actualBlockTime-waitDur)/1000
+          * 
+          * } else { //(nExpectedSrvcs>=minMatches)
+         *  // Blocking time should be less than the full amount
+         *  if(waitError >= 0) {
+          *    throw new TestException(" -- blocked longer than expected "
+          *                      +"-- requested block = "
+          *                      + waitDurSecs +" second(s), actual "
+          *                      +"block = "+(actualBlockTime/1000)
+          *                      +" second(s)");
+         *     }
+          * }//endif(nExpectedSrvcs<maxMatches)
+          * 
+          * In addition it was previously considered an error if the wait 
period
+          * was exactly equal to waitDur after being rounded to seconds,
+          * this is in conflict with the spec that specifies UNTIL either an 
+          * acceptable minimum number of service references are discovered 
+          * or the specified time period has passed. 
+          * 
+          * The test failed to allow for a corner case where an acceptable 
minimum
+          * number of service references are discovered concurrently to the 
+          * specified time period elapsing.
+          * 
+          * Additionally however, Object.wait(waitDur) may not wake up at the
+          * correct wait time if another object happens to be holding the lock.
+          * 
+          * The spec doesn't specify limits on the wait period, except that it
+          * has passed.  Originally this test allowed 30 seconds to elapse 
after
+          * waitDur
+          * 
+          * I have changed waitDur to milliseconds to comply with the spec
+          * on the 7th of May 2013
+          * 
+          * The limits were changed to allow 300 milliseconds to elapse after 
the 
+          * wait period completes.
+          * 
+          * In addtion Item number 3 below states:
+          * 
+             * 3. while lookup() is blocking, if enough new services are
+             *    registered so that the acceptable minimum is achieved,
+             *    lookup() will return immediately; that is, even if there
+             *    is more time left on the wait period, lookup() will not
+             *    wait for more services beyond the minimum. 
+             *
+             *    For example, if 3 services are initially registered and
+             *    lookup is called with min = 4 and max = 7, then lookup()
+             *    will find the 3 services and then wait for more services to
+             *    be registered. Suppose that while lookup() is blocking
+             *    another 5 services are registered, bringing the total number
+             *    of services to 8. In this case, lookup() will stopping
+             *    waiting and return 4 services (the minimum), not the
+             *    maximum 7.
+          * 
+          * The problem with this statement is if additonal threads are waiting
+          * for the object monitor, there's no guarantee the waiting thread can
+          * obtain the monitor first and return the minimum number.  In fact
+          * the jvm is more likely to prefer a thread that's still
+          * in cpu cache rather than a thread that's been waiting for a long 
+          * time and isn't loaded in the cpu cache.
+          * 
+          * The specification doesn't make this distinction, it only limits
+          * the result to maxMatches.  So we need to check that maxMatches 
isn't
+          * exceeded.
+          */
+       long waitError = (actualBlockTime-waitDur);
        /* Delay to allow all of the services to finish registering */
        DiscoveryServiceUtil.delayMS(regCompletionDelay);
        /* populate the expected info after lookup to prevent delay */
@@ -206,23 +284,12 @@
         *    first is less than the acceptable minimum, then lookup()
         *    will wait for the desired services to be registered with
         *    the lookups
-        * 3. while lookup() is blocking, if enough new services are
-        *    registered so that the acceptable minimum is achieved,
-        *    lookup() will return immediately; that is, even if there
-        *    is more time left on the wait period, lookup() will not
-        *    wait for more services beyond the minimum. 
-        *
-        *    For example, if 3 services are initially registered and
-        *    lookup is called with min = 4 and max = 7, then lookup()
-        *    will find the 3 services and then wait for more services to
-        *    be registered. Suppose that while lookup() is blocking
-        *    another 5 services are registered, bringing the total number
-        *    of services to 8. In this case, lookup() will stopping
-        *    waiting and return 4 services (the minimum), not the
-        *    maximum 7.
+        * 3. DELETED 7th May 2013 see comments
         * 4. if the minimum number of services have not been registered
         *    during the wait period, lookup() will return what it has
         *    found.
+         * 5. ADDED 7th May 2013 The maximum number of services returned 
+         *    never exceeds maxMatches.
         *
         * Below, determine the number of services to expect based on
         * the specified behavior described above.    
@@ -245,16 +312,19 @@
        } else {//(nPreReg >= minMatches) ==> won't block
            logger.log(Level.FINE, ""
                       +":   lookup() will NOT block");
-           if(nPreReg == minMatches) {//return min immediately
-               nExpectedSrvcs = minMatches;
-           } else {//(nPreReg > minMatches)
+            /* 7th May 2013: This requirement complicates 
+             * thread synchronization and is superflous to the specification.
+             */
+//         if(nPreReg == minMatches) {//return min immediately
+//             nExpectedSrvcs = minMatches;
+//         } else {//(nPreReg > minMatches)
                if(nPreReg < maxMatches) {
                    nExpectedSrvcs = nPreReg;
                } else {//(nPreReg >= maxMatches)
                    nExpectedSrvcs = maxMatches;
                }//endif
+//         }//endif
            }//endif
-       }//endif            
        logger.log(Level.FINE, ""
                   +":   minMatches       = "+minMatches);
        logger.log(Level.FINE, ""
@@ -272,24 +342,24 @@
            if(waitError<-3) {
                throw new TestException(" -- failed to block requested "
                                  +"time -- requested block = "
-                                 +waitDurSecs+" second(s), actual "
-                                 +"block = "+(actualBlockTime/1000)
-                                 +" second(s)");
-           } else if(waitError>30) {
+                                 +waitDur+" millisecond(s), actual "
+                                 +"block = "+(actualBlockTime)
+                                 +" millisecond(s)");
+           } else if(waitError>300) {
                throw new TestException(" -- exceeded requested block "
                                  +"time -- requested block = "
-                                 +waitDurSecs+" second(s), actual "
-                                 +"block = "+(actualBlockTime/1000)
-                                 +" second(s)");
+                                 +waitDur+" millisecond(s), actual "
+                                 +"block = "+(actualBlockTime)
+                                 +" millisecond(s)");
            }//endif
        } else { //(nExpectedSrvcs>=minMatches)
-           /* Blocking time should be less than the full amount */
-           if(waitError >= 0) {
+           /* Blocking time will likely be less than the full wait period */
+           if(waitError > 300) {
                throw new TestException(" -- blocked longer than expected "
                                  +"-- requested block = "
-                                 +waitDurSecs+" second(s), actual "
-                                 +"block = "+(actualBlockTime/1000)
-                                 +" second(s)");
+                                 + waitDur +" millisecond(s), actual "
+                                 +"block = "+(actualBlockTime)
+                                 +" millisecond(s) wait error = " + waitError);
            }
        }//endif(nExpectedSrvcs<maxMatches)
        verifyServiceItems(srvcItems,

Reply via email to