Request for review : 7121314 : Behavior mismatch between AbstractCollection.toArray(T[] ) and its spec
David Holmes
david.holmes at oracle.com
Fri Mar 9 09:53:42 UTC 2012
Hi Sean,
That seems to implement the required semantics.
Minor style nit: }else{ -> } else {
Not sure about the testcase ... Can size() not remove some elements
directly but return the original size?
David
On 9/03/2012 6:16 PM, Sean Chou wrote:
> Hi all,
>
> AbstractCollection.toArray(T[] ) might return a new array even if
> the given array has enough room for the returned elements when it is
> concurrently modified. This behavior violates the spec documented in
> java.util.Collection .
> This patch checks the size of returned array and copies the
> elements to return to the given array if it is large enough.
>
> The webrev is at :
> http://cr.openjdk.java.net/~zhouyx/7121314/webrev.00/
>
> The discussion about this bug happened several months ago, the link
> to the start of that discussion is:
> http://mail.openjdk.java.net/pipermail/core-libs-dev/2011-December/008686.html
>
> And people in that discussion are also in cc list. David mentioned
> it might be simple and better to change the implementation note in last
> mail of that discussion, however the code change is simple too.
>
> There is a test case in the previous discussion. It is not included
> in the webrev, because the testcase is heavily implementation dependent.
> I will add it if it is requested.
>
> Also paste the testcase here:
>
> ////////////////////////////////////////////////////////////
>
> import java.util.Map;
> import java.util.concurrent.ConcurrentHashMap;
>
> public class CollectionToArrayTest {
>
> static volatile Map<String, String> map = new TConcurrentHashMap<String, String>();
>
> static volatile boolean gosleep = true;
>
> static class TConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
> public int size() {
> int oldresult = super.size();
> System.out.println("map size before concurrent remove is"
> + oldresult);
> while (gosleep) {
> try {
> // Make sure the map is modified during toArray is called,
> // between getsize and being iterated.
> Thread.sleep(1000);
> // System.out.println("size called, size is" + oldresult +
> //" take a sleep to make sure the element is deleted before size is returned.");
> } catch (Exception e) {
> }
> }
> return oldresult;
> }
> }
>
> static class ToArrayThread implements Runnable {
> public void run() {
> for (int i = 0; i< 5; i++) {
> String str = Integer.toString(i);
> map.put(str, str);
> }
> String[] buffer = new String[4];
> String[] strings = map.values().toArray(buffer);
> // System.out.println("length is" + strings.length);
> if (strings.length<= buffer.length) {
> System.out.println("given array size is"
> + buffer.length
> +" \nreturned array size is"
> + strings.length
> +", \nbuffer should be used according to spec. Is buffer used :"
> + (strings == buffer));
> }
> }
> }
>
> static class RemoveThread implements Runnable {
> public void run() {
> String str = Integer.toString(0);
> map.remove(str);
> gosleep = false;
> }
> }
>
> public static void main(String args[]) {
> CollectionToArrayTest app = new CollectionToArrayTest();
> app.test_concurrentRemove();
> }
>
> public void test_concurrentRemove() {
> System.out.println("//////////////////////////////////////////////\n" +
> "The spec says if the given array is large\n" +
> "enough to hold all elements, the given array\n" +
> "should be returned by toArray. This \n" +
> "testcase checks this case. \n" +
> "//////////////////////////////////////////////");
>
> Thread[] threads = new Thread[2];
> threads[0] = new Thread(new ToArrayThread());
> threads[1] = new Thread(new RemoveThread());
>
> threads[0].start();
>
> try {
> // Take a sleep to make sure toArray is already called.
> Thread.sleep(1200);
> } catch (Exception e) {
> }
>
> threads[1].start();
> }
> }
>
> ////////////////////////////////////////////////
>
>
> --
> Best Regards,
> Sean Chou
>
More information about the core-libs-dev
mailing list