Testing Atomicity and Linearizability of Concurrent Operations in Data Structures
270 likes | 408 Vues
This work examines the challenges of testing atomicity in concurrent data structures, particularly focusing on composed operations within environments like TOMCAT. We present a modular approach that generates simple, controlled traces to verify linearizability effectively. Our study highlights issues with large trace sizes, the complexity of reconstructing operations, and rare bugs linked to specific key values. By employing a modular testing strategy, we enhance the reliability of concurrent collections in modern programming languages, ensuring they meet atomic semantics requirements.
Testing Atomicity and Linearizability of Concurrent Operations in Data Structures
E N D
Presentation Transcript
Testing Atomicity of Composed Concurrent Operations Ohad ShachamTel Aviv University Nathan Bronson Stanford University Alex Aiken Stanford University Mooly Sagiv Tel Aviv University Martin Vechev ETH & IBM Research Eran Yahav Technion
Concurrent Data Structures Writing highly concurrent data structures is complicated Modern programming languages provide efficient concurrent collections with atomic semantics … … … … … … .. … …
Challenge Testing the atomicity of composed operations … … … … … … … …
TOMCAT Motivating Example TOMCAT 5.* TOMCAT 6.* attr = new HashMap(); … Attribute removeAttribute(String name){ Attribute val = null; synchronized(attr) { found = attr.containsKey(name) ; if (found) { val = attr.get(name); attr.remove(name); } } return val; } attr = new ConcurrentHashMap(); … Attribute removeAttribute(String name){ Attribute val = null; /* synchronized(attr) { */ found = attr.containsKey(name) ; if (found) { val = attr.get(name); attr.remove(name); } /* } */ return val; } Invariant: removeAttribute(name) returns the value it removes from attr or null
removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”); o
removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.put(“A”, o); attr.remove(“A”); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { return val; attr.remove(“A”); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { return val; attr.put(“A”, o); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”); attr.remove(“A”); Linearizability null o o null null o null null null o o null
Challenge Testing the linearizabiliy of composed operations … … … … … … … …
removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”); • Reconstruction in TOMCAT extremely challenging • Large traces • Large number of traces • Bugs occur in rare cases with specific key values
Our Solution Generates simple traces Modularity Enables Env control Base linearizability Restrict generated traces Influence Restrict generated traces
Modular Checking removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”);
Challenge Testing Linearizability of composed operation in a modular fashion … … … … … … … …
removeAttribute(“O”) { • Attribute val = null; • found = attr.containsKey(“O”) • if (found) { • val = attr.get(“O”); • attr.remove(“O”); • } • return val; removeAttribute(“GGG”) { Attribute val = null; found = attr.containsKey(“GGG”) if (found) { return val; • removeAttribute(“GGG”) { • Attribute val = null; • found = attr.containsKey(“GGG”) • if (found) { • val = attr.get(“GGG”); • attr.remove(“GGG”); • } • return val; • removeAttribute(“K”) { • Attribute val = null; • found = attr.containsKey(“K”) • if (found) { • val = attr.get(“K”); • attr.remove(“K”); • } • return val; • removeAttribute(“P”) { • Attribute val = null; • found = attr.containsKey(“P”) • if (found) { • val = attr.get(“P”); • attr.remove(“P”); • } • return val; removeAttribute(“K”) { Attribute val = null; found = attr.containsKey(“K”) if (found) { return val; removeAttribute(“B”) { Attribute val = null; found = attr.containsKey(“B”) ; if (found) { return val; • removeAttribute(“LL”) { • Attribute val = null; • found = attr.containsKey(“LL”) • if (found) { • val = attr.get(“LL”); • attr.remove(“LL”); • } • return val; • removeAttribute(“L”) { • Attribute val = null; • found = attr.containsKey(“L”) • if (found) { • val = attr.get(“L”); • attr.remove(“L”); • } • return val; • removeAttribute(“G”) { • Attribute val = null; • found = attr.containsKey(“G”) • if (found) { • val = attr.get(“G”); • attr.remove(“G”); • } • return val; • removeAttribute(“R”) { • Attribute val = null; • found = attr.containsKey(“R”) • if (found) { • val = attr.get(“R”); • attr.remove(“R”); • } • return val; removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { return val; • removeAttribute(“P”) { • Attribute val = null; • found = attr.containsKey(“P”) • if (found) { • val = attr.get(“P”); • attr.remove(“P”); • } • return val; attr.put(“O”, o’); attr.put(“K”, o’); attr.put(“G”, o’); attr.put(“L”, o’); attr.put(“A”, o); attr.put(“R”, o’); attr.put(“GGG”, o); attr.put(“P”, o’); attr.put(“P”, o’); attr.put(“LL”, o’); attr.put(“K”, o’); attr.remove(“R”);
Influence Base Environment removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”);
Running Example Attribute removeAttribute(String name){ Attribute val = null; found = attr.containsKey(name) ; if (found) { val = attr.get(name); attr.remove(name); } return val; }
removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”); Attribute removeAttribute(String name){ Attribute val = null; found = attr.containsKey(name) ; if (found) { val = attr.get(name); attr.remove(name); } return val; }
attr.put(“A”, o); attr.remove(“A”); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { return val; removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { return val; attr.put(“A”, o); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; attr.put(“A”, o); attr.remove(“A”); attr.remove(“A”); removeAttribute(“A”) { Attribute val = null; found = attr.containsKey(“A”) ; if (found) { val = attr.get(“A”); attr.remove(“A”); } return val; null attr.put(“A”, o); o attr.remove(“A”); o null null o null null o null o null
program COLT CO extractor library spec Timeout Non-Lin candidateCOs CO instrument linearizability checking Execution key/value driver influence driver
Benchmark • Used simple static analysis to extract composed operations • 19% needed manual modification • Extracted 112 composed operations from 55 applications • Apache Tomcat, Cassandra, MyFaces – Trinidad, etc… • Extracted all composed operations per application • We did not find additional composed operations • Using Google Code and Koders
59Non Linearizable 53Unknown
17Open Non Linearizable 53Unknown 42Non Linearizable
17Open Non Linearizable 27Linearizable 42Non Linearizable 26Globals
Globals … … … … … … … … m.put(k,v) … … … … … … … … … … …
27Linearizable 85Non-linearizable
Easy Detection Attribute removeAttribute(String name){ Attribute val = null; found = attr.containsKey(name) ; if (found) { val = attr.get(name); attr.remove(name); } return val; }
Current Work • Define a class of data-independent composed operations • Use small model reduction to prove linearizability of data-independent composed operations • Empirical study for the ratio of real life data-independent composed operations
Summary Writing concurrent data structures is hard Employing atomic library operations is error prone Modular linearizability checking Leverage influence Sweet spot Identify many important bugs together with a traces showing and explaining the violations Hard to find Simple and efficient technique