{"action":"created","issue":{"url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/24","labels_url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/24/labels{/name}","comments_url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/24/comments","events_url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/24/events","html_url":"https://github.com/OpenHFT/Chronicle-Map/issues/24","id":82337046,"number":24,"title":"ChronicleSet is not thread safe","user":{"login":"rsmilyanov","id":6984182,"avatar_url":"https://avatars.githubusercontent.com/u/6984182?v=3","gravatar_id":"","url":"https://api.github.com/users/rsmilyanov","html_url":"https://github.com/rsmilyanov","followers_url":"https://api.github.com/users/rsmilyanov/followers","following_url":"https://api.github.com/users/rsmilyanov/following{/other_user}","gists_url":"https://api.github.com/users/rsmilyanov/gists{/gist_id}","starred_url":"https://api.github.com/users/rsmilyanov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rsmilyanov/subscriptions","organizations_url":"https://api.github.com/users/rsmilyanov/orgs","repos_url":"https://api.github.com/users/rsmilyanov/repos","events_url":"https://api.github.com/users/rsmilyanov/events{/privacy}","received_events_url":"https://api.github.com/users/rsmilyanov/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":15,"created_at":"2015-05-29T08:47:42Z","updated_at":"2015-05-29T13:09:47Z","closed_at":null,"body":"Hello,\r\n\r\nI am running some tests for ChronicleSet of strings that is supposed to be used concurrent by 5 threads and with the following code I am getting \"net.openhft.chronicle.hash.locks.IllegalInterProcessLockStateException: Must not acquire update lock, while read lock is already held by this thread\"\r\n\r\nHere the code snippet:\r\n\r\npublic class TestChronicleSet {\r\n\r\n    public static <H extends ChronicleHash<K,?>,  B extends ChronicleHashBuilder<K, H, B>, K> H init(B builder, int entrySize, int averageKeySize, String fileName) {\r\n\r\n        String tmp = System.getProperty(\"java.io.tmpdir\");\r\n        String pathname = tmp + \"/\" + fileName;\r\n        File file = new File(pathname);\r\n        try {\r\n            H result = builder.entries(entrySize).averageKeySize(averageKeySize).createPersistedTo(file);\r\n            return result;\r\n        } catch (IOException ioe) {\r\n            throw new RuntimeException(ioe);\r\n        }\r\n    }\r\n\r\n    public synchronized static <A> ChronicleSet<A> initSet(Class<A> entryClass, int entrySize, int averageKeySize, String fileName) {\r\n        return init(ChronicleSetBuilder.of(entryClass), entrySize, averageKeySize, fileName);\r\n    }\r\n\r\n    public static void main(String[] args) {\r\n        ChronicleSet<String> set = initSet(String.class, 1_000_000, 30, \"stringSet.dat\");\r\n        ExecutorService executor = Executors.newFixedThreadPool(5);\r\n        for (int i = 0; i < 10; i++) {\r\n            Runnable worker = new WorkerThread(set);\r\n            executor.execute(worker);\r\n        }\r\n        executor.shutdown();\r\n        while (!executor.isTerminated()) {\r\n\r\n        }\r\n        System.out.println(\"Finished all threads\");\r\n\r\n\r\n    }\r\n\r\n    public static class WorkerThread implements Runnable {\r\n        private final ChronicleSet<String> set;\r\n        public WorkerThread(ChronicleSet<String> set){\r\n            this.set = set;\r\n        }\r\n\r\n        @Override\r\n        public void run() {\r\n            System.out.println(Thread.currentThread().getName()+\" Start. Command = \" + set.size());\r\n            processCommand();\r\n            System.out.println(Thread.currentThread().getName()+\" End.\");\r\n        }\r\n\r\n        private void processCommand() {\r\n            Set<String> nomenclatures = new HashSet<>();\r\n            for(int i = 0; i < 10; i++) {\r\n                String nomenclature = \"#############################\" + i;\r\n                nomenclatures.add(nomenclature);\r\n\r\n            }\r\n\r\n            set.addAll(nomenclatures);\r\n\r\n\r\n            Set<String> strings = CollectionUtil.randomSubset(nomenclatures, new Random());\r\n\r\n            set.addAll(strings);\r\n\r\n\r\n            Set<String> toRemove = new HashSet<>();\r\n            Random generator = new Random();\r\n            for (int j = 0; j < 3; j++) {\r\n                int i = generator.nextInt(10);\r\n                String nomenclature = \"#############################\" + i;\r\n                toRemove.add(nomenclature);\r\n            }\r\n            set.removeAll(toRemove);\r\n\r\n            for (String s : set) {\r\n                System.out.println(s);\r\n            }\r\n\r\n            strings = CollectionUtil.randomSubset(nomenclatures, new Random());\r\n\r\n            set.addAll(strings);\r\n\r\n            for (String s : set) {\r\n                System.out.println(s);\r\n            }\r\n        }\r\n\r\n    }\r\n}\r\n\r\nSince ChronicleSet is almost the same as ChronicleMap I expect that it will be thread safe. Can you please have a look at the example code and let me know whether it is a bug into ChronicleSet implementation or I am supposed to synchronize the code for addAll/removeAll operations on the application side?\r\n\r\nThanks,\r\nRadoslav"},"comment":{"url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/comments/106798948","html_url":"https://github.com/OpenHFT/Chronicle-Map/issues/24#issuecomment-106798948","issue_url":"https://api.github.com/repos/OpenHFT/Chronicle-Map/issues/24","id":106798948,"user":{"login":"bobymicroby","id":927575,"avatar_url":"https://avatars.githubusercontent.com/u/927575?v=3","gravatar_id":"","url":"https://api.github.com/users/bobymicroby","html_url":"https://github.com/bobymicroby","followers_url":"https://api.github.com/users/bobymicroby/followers","following_url":"https://api.github.com/users/bobymicroby/following{/other_user}","gists_url":"https://api.github.com/users/bobymicroby/gists{/gist_id}","starred_url":"https://api.github.com/users/bobymicroby/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bobymicroby/subscriptions","organizations_url":"https://api.github.com/users/bobymicroby/orgs","repos_url":"https://api.github.com/users/bobymicroby/repos","events_url":"https://api.github.com/users/bobymicroby/events{/privacy}","received_events_url":"https://api.github.com/users/bobymicroby/received_events","type":"User","site_admin":false},"created_at":"2015-05-29T13:09:47Z","updated_at":"2015-05-29T13:09:47Z","body":"@BoundedBuffer \r\n\r\nHi Rob, can I safely guess that the context scope of a put method is the put method itself. And that the put method is atomic and thread-safe ? \r\n\r\nCan you please elaborate more on \r\n\r\n>the put() method locks the maps segment, but it does not hold the lock until the end of the context scope. \r\n\r\nAs far as I can see acquireUsingLocked creates a default value if the key is absent and accepts a instance  of the Value class that will be populated with either the default value for that map or with the existing value behind the key.  \r\n\r\nSo I hope that I don't have to do something like \r\n```java\r\nfinal Value v = new  Value() \r\n\r\ntry( Context c = map.acquireUsingLocked(non_existing_key,v)) {\r\nv.a = 1\r\n\r\n} /**  Here 1 should be atomically inserted into the map ?*/\r\n\r\ntry( Context c = map.acquireUsingLocked(non_existing_key,v)) {\r\nv.a = 2\r\n\r\n} /**  Here 2 should be atomically inserted into the map ?*/\r\n\r\n```\r\n\r\nSome of methods provide you with a Context directly and other like put doesn't. This is adding a bit of confusion .\r\n\r\n\r\n \r\n\r\n\r\n"}}