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.jar;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.jar;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.jar;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. >