IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY

by

François Bergeon

A DISSERTATION

Submitted to

The University of Liverpool

in partial fulfillment of the requirements for the degree of

MASTER OF SCIENCE Information Technology (Software Engineering)

January 5, 2009

ABSTRACT

IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY by François Bergeon

The subject of this dissertation is the implementation of container classes in shared memory in order to allow concurrent high-level access to easily retrievable structured data by more than one process.

The project extends the sequence containers and associative containers offered by the C++ Standard Template Library (STL) in the form of a “Shared Containers Library” which purpose is to allocate both the containers and the objects they contain in shared memory to make them accessible by other processes running on the same machine.

Shared STL-like containers can be used for a variety of implementations that require high-level sharing of structured and easily retrievable data between processes. Applications of shared containers may include realtime monitoring, metrics gathering, dynamic configuration, distributed processing, process prioritization, parallel cache seeding, etc.

The Shared Container Library presented here consists of a binary library file, a suite of C++ header files, and the accompanying programmers’

documentation. STL-type containers can be allocated in a shared memory segment thanks to a class factory mechanism that abstracts the complexity of the implementation. The Shared Containers Factory offers synchronization for concurrent access to containers in the form of a mechanism offering both exclusive and a non-exclusive locks.

The target platform of the Shared Container Library is the Microsoft implementation of the STL for the 32bit Microsoft Windows operating systems (Win32) as distributed with the Microsoft Visual Studio 2008 development environment and its Microsoft Visual C++ compiler. Further developments of this library may include porting to the Unix platform and designing a similar library for other languages such as Java and C#.

I declare that the dissertation describes original work that has not previously been presented for the award of any other degree of any institution. or writings of another. expressions.DECLARATION I hereby certify that this dissertation constitutes my own product. François Bergeon . ideas. and that appropriate credit is given where I have used the language. that where the language of others is set forth. quotation marks so indicate. Signed.

Thanks to Eddy Luten for his high-resolution timer code that is used in the test scaffolding. Thanks to Ion Gaztañaga for his work on the Boost. A special thanks to Roy Soltoff. kind words and constructive comments.Interprocess library and to Dr. Marc Ronell for his prior work on shared allocators. formerly of Logical Systems Inc. The author thanks the members of the EFSnet development team for their insight and support during the development of an online transaction processing system when the idea of implementing containers in shared memory for monitoring and dynamic configuration was born. a simple C compiler for the TRS-80 with which the author learned the C language in 1982. who authored “LC” (“elsie”).ACKNOWLEDGEMENTS The author wishes to thank Martha McCormick. for her support. . the dissertation advisor for the project. Thanks to Gabriel Fleseriu and Andreas Masur for pointing out the adequacy of segregated storage in custom allocators.

.4.............2...........6 13 13 14 Allocation of container objects in shared memory....TABLE OF CONTENTS Page LIST OF TABLES LIST OF FIGURES LIST OF CODE LISTINGS Chapter 1..............1 4.............3 8 8 8 9 11 Inheritance ...................... encapsulation....2 1............. Theory 3.................2 4.............................14 Allocation of container’s contents in shared memory.......................................4................3 4..............3 1..........15 Reference safety ..............1 4......................................................2............2 Introduction Functional requirements 4....2 3................ Background and review of literature 2......2 Related Work Literature 6 6 6 Chapter 3............................................ Introduction 1.....11 Design patterns ......5 4...........2........4 4...........2 3............1 2......................1 1..................................15 Container attachment from a non-creating process ..............................16 4.......2.1 3..11 Abstraction.....4... Analysis and Design 4................................2...............3 3..3 Non-functional requirements 16 vi .4 Generic Programming and the Standard Template Library Interprocess communication and shared memory Memory allocation and segregated storage Object oriented approaches – design patterns 3..1 3.......14 Coherence of internal data structures between processes ........4 1.... separation of concerns.12 Chapter 4...........16 Concurrent access safety .................5 Overview Scope Problem Statement Approach Outcome ix x xi 1 1 1 2 4 4 Chapter 2..2................

............1 5........................3.................6 shared_map <Key...2 4.......................................................4.............2 7.......................................1 5..........3 Functional evaluation Performance evaluation Concurrency issues 42 42 43 45 Chapter 7.2 38 Unit testing .........3 sync A................23 High-level classes – shared containers and class factories............ Data...........3 Methodology Iterative design steps Shared containers library source code 5........3 7...................................5 Approach Detailed design 4.........6 31 31 31 32 Naming conventions..........32 Structure of library and header files.......1 6..........4 shared_vector<T > A.........3........3.................1 7..............................2 5........................................... Compare> A..................................25 Process flows ........... Shared Container Library Programmers’ Manual (excerpts) A.......... Sequence > A..............................................4 Verification and validation 5.4 17 20 Overview .............5........4....3.............2 5....................39 Chapter 6.............................................4.....35 Synchronization....3..........................32 Interface to shared memory – pointer consistency ...........3 4.................................4 Potential applications Lessons Learned Future Activity Prospects for Further Work 47 47 48 49 50 REFRENCES CITED Appendix A....7 shared_hash_set <Key.....28 Chapter 5..33 Object instantiation and attachment – map of shared containers ...................... Compare> A....5............38 Test scaffolding ......... Results and Evaluation 6..............................2 Shared heap parameters A.............................4 4.................. Methods and Realization 5........20 Low level use cases .5 5.....1 4........2 6............................3 5...............8 SharedVectorFactory<T > 51 54 54 55 56 58 59 61 63 64 vii ............4 5...............37 Reference safety ...................5.......1 5..........1 Introduction to the Shared Containers Library A..38 5.................3..........................................5 shared_stack<T.............. Conclusions 7...5.

h B.5 shared_heap. Compare> A. Sequence> A.9 shared_allocator.1 safe_counter.h B.h B.h B.13 shared_set.10 SharedMapFactory map<Key.h B.h B. Test Scaffolding Source Code D.cpp B.8 shared_pool.3 sync. Performance Evaluation Results Appendix D.cpp 120 121 121 123 viii .A. Shared Containers Library Source Code (Excerpts) B. Data.cpp D.11 SharedHashSetFactory set<Key.9 SharedStackFactory<T .h B.h B.cpp B.cpp B.11 shared_deque.2 MapTest.6 shared_heap.12 shared_stack.h 71 71 72 74 79 79 81 93 94 102 105 113 114 116 117 Appendix C.h B.10 container_factories. Compare> 65 67 69 Appendix B.2 sync.14 shared_hash_map.4 heap_parameters.7 shared_pool.h B.1 VectorTest.

........................... 120 ix ............................LIST OF TABLES Page Table 1: Functional requirements ....................................................................................................................... 44 Table 3: Raw performance evaluation results .. 14 Table 2: Performance comparison ...

............. 28 Figure 9: State chart diagram for the synchronization mechanism ............................................... 29 Figure 10: Sample shared container usage ......................................................................................... 23 Figure 5: Shared container creation......................................................................LIST OF FIGURES Page Figure 1: Top level use cases......... 22 Figure 4: Low level use cases .............................................. 47 x .............. attachment and release ..................................................................................................................................... 40 Figure 13: Monitoring / configuring an application server................. 13 Figure 2: Sample linked list...................... 24 Figure 6: Shared container classes and class factories ... 30 Figure 11: Header file inclusion hierarchy .............................................. 27 Figure 8: High-level use cases .................................................................................................................. 25 Figure 7: Container factories class hierarchy ....... 34 Figure 12: Test scaffolding interactive flow ........................... 15 Figure 3: Layered design class diagram....

...........................h... 119 Listing 18: VectorTest........ 94 Listing 11: shared_pool......................... 81 Listing 9: shared_heap............ 115 Listing 16: shared_set............................ 125 xi ........................................ 37 Listing 4: safe_counter..............................cpp ................................h.....................cpp.................................................................................................................................................cpp ............................................................................................... 104 Listing 13: container_factories...... 78 Listing 7: heap_parameters..............................................................................h ...... 113 Listing 14: shared_deque.................. 117 Listing 17: shared_hash_map.......................................................h .......................................................... 74 Listing 6: sync..............................h ...................................................................................h ............ 114 Listing 15: shared_stack....................................................LIST OF CODE EXCERPTS Page Listing 1: The shared_object sub-class of shared_pool................. 71 Listing 5: sync........ 102 Listing 12: shared_allocator............................. 123 Listing 19: MapTest..................... 36 Listing 3: Shared heap header structure and the get_params method .......................................................h..........................................................................................h.............................. 92 Listing 10: shared_pool.......................cpp..........................................................h ....................................................................... 36 Listing 2: Declaration of the the shared container map ........h .........h..... 79 Listing 8: shared_heap..........................cpp.........

2 Scope The main focus of the project is to extend the sequence containers and associative containers offered by the C++ Standard Template Library (STL) so that the containers and the objects they contain are allocated in a shared memory segment in order to be accessible by other processes. etc. “sets”.). In particular. Inter-process communication. Care was taken to preserve the properties of the container classes when implemented in shared memory. 1. “maps”. Well-known data structures and algorithms are used to store and retrieve data with flexibility and optimal performance.Chapter 1. “Stacks”. “Lists”. “Queues”. This project extends the sequence containers and associative containers offered by the C++ Standard Template Library (STL) in the form of a “Shared Containers Library” which enables programmers to allocate both the containers and the objects they contain in shared memory in order to make them accessible by other processes running on the same machine. Container classes can be found in most object oriented programming languages such as C++ (“vectors”.1 Overview The aim of this project is to offer a practical programming interface that allows developers to implement generic container classes in shared memory. the gap can be filled with a mechanism that makes high-level structured data concurrently accessible by various processes. Such “shared containers” provide concurrent high-level access to easily retrievable structured data by more than one process. on the other hand. offers low-level mechanisms to share data among processes.) Java (“Maps”. A gap has been identified between the high-level access offered by container classes and the low-level nature or typical inter-process communication. “Sets”. INTRODUCTION 1. By implementing container classes in shared memory.) and C# (“Hashtables”. “deques”. etc. etc. the aim was to 1 .

Implementation of container classes in other languages such as Java and C# may be discussed but no specific research for these platforms has been included in the scope of this project.” These classes. “Sets” and “Lists” and C# collections such as “Hashtables”. “deques”. container classes have gained in popularity and similar implementations can now be found in most object oriented programming languages. Access to shared memory was concentrated on the primitives offered by the Win32 platform and no effort was made to research other operating system implementations.3 Problem Statement Container classes are “classes whose purpose is to contain other objects. Since their introduction in the C++ Standard Template Library. “Queues” and “Stacks” all use well-known data structures and algorithms to 2 . Java collections such as “Maps”.maintain the genericity of the containers and to ensure that the different retrieval mechanisms (e.” a programming paradigm popularized by the C++ Standard Template Library (Stroustrup. This project concentrated on the Microsoft implementation of the STL for the 32bit Windows operating system platform as distributed with Microsoft Visual Studio 2008. as quoted by Buchanan 2008). constitute a cornerstone of “generic programming. “sets” and “maps”. C++ containers like “vectors”.g. parameterized “to contain any class of object” (SGI 2006). The following functional requirements have specifically been excluded of this project and no effort was be made to address them: - Data encryption or persistence Data access security Support for shared memory segment re-sizing Garbage collection 1. iterators) and sorting algorithms offered by the Standard Template Library still remain transparently usable when containers are allocated in shared memory.

the aim of this project is to offer developers a practical high-level access to easily retrievable data shared between programs while abstracting the inherent complexity of the implementation. on the other hand. Mechanisms offered by operating systems for inter-process communication. By default. If it is possible for different threads of a same process to access data in these containers provided thread-safe code and adequate synchronization are used. 3 . and are therefore made concurrently accessible to various processes. By coupling the usability. Embracing code reuse and logical encapsulation. it is up to the developer to structure the data stored in a shared memory segment so that various processes can efficiently access it. the generic and parameterized approach of these container classes fosters code reuse by abstracting the complexity of retrieval and sorting algorithms into standardized libraries (Baum & Becker 2000). for example. and accessible concurrently and randomly by various processes. tend to only offer a low-level access to data. none of the aforementioned libraries currently offer a mechanism that allows these structures to be concurrently accessible by more than one process. Like in the case of a raw buffer. The corresponding library classes that implement these containers are widely used in a large number of applications. But this mechanism only supports a low-level access to a common contiguous memory region and requires a somewhat large amount of non-trivial work to be usable.efficiently store and retrieve data in memory with great flexibility and performance. performance and code reuse benefits of container classes along with the inter-process communication nature of shared memory. Shared memory. Furthermore. both data and indexing structures are meant to be stored in the contiguous memory space of the running process. but these well-known software tools have some intrinsic limitations. the approach selected is to extend the container classes offered by the C++ Standard Template Library (STL) so that both the containers and the objects they contain can be located in a segment of shared memory. consists of a finite segment of memory. mapped to a file.

5 Outcome This project offers a practical method to instantiate STL containers in shared memory on the Win32 platform. the C++ language allows for a straightforward implementation of low-level operating system calls. and a highest level programming interface composed of classes derived from STL containers and their corresponding class factories. although containers located in shared memory may present degradation in performance when simple data types are being considered. In addition. Performance evaluation showed that. This language and this platform have been chosen for the availability of a standard set of container classes (the STL) and by the well-documented nature of the shared memory primitives offered by the Windows operating system.4 Approach The main focus of the project is the C++ programming language on a 32-bit Microsoft Windows platform. The implemented design follows a layered approach consisting of a low level interface managing a segment of shared memory. the response time for populating and accessing a container composed of complex data type items – such as strings – is not significantly longer than for similar operations in the process’ main 4 . Good practices of object-oriented analysis and design are embraced in the form of code reuse. a higher-level shared memory pool responsible for allocation and memory management. Functional evaluation demonstrated that containers can be created and populated by one process and concurrently accessed and modified by others. 1.1. The approach and design selected provide a class library that allows programmers to instantiate and access STL-like shared containers with little knowledge or concern about the complexity of the underlying architecture. and abstraction of complexity through encapsulation and separation of concerns. an option that may not be directly available with other object oriented languages if the needed primitives are not exposed in their standard library – which is usually not the case.

5 . strong runtime type safety and garbage collection. Although the code developed for this project lacks some important features such as solid exception handling. it can serve as the basis of a wider-scoped effort to standardize allocation of STL containers in a shared memory on the Windows 32bit operating system or on Unix or Linux platforms.memory.

After contacting the author – who maintains a SourceForge project for a shared allocator – it appears that the initiative suffered from a roadblock in the form of an impossibility for some versions of the GNU C++ compiler (GCC) to adequately instantiate shareable objects with the “placement new” directive. shared memory allocator for the Standard Template Library.Interprocess library.1 Related Work Ion Gaztañaga has extended the Boost initative with the Boost. but according to the apparently rarely updated SourceForge website. the project seems to be stalled and no contact is provided for its author or caretaker to verify its progress. Ronell proposes an architecture for sharing STL containers between processes (Ronell 2003). a C++ class library that offers a suite of containers similar to the STL’s and that can be instantiated in shared memory (Gaztañaga 2008). the aim of this project differs from Gaztañaga’s approach and one could argue that modifying the code of the STL classes or designing all new classes may be considered contrary to object orientation and code reuse principles.). 6 . Ronell went as far as submitting a bug report to the GCC committee before putting the project on indefinite hold pending resolution of the issue and due to conflicting work schedule (Ronell 2006).” Dr. Other initiatives range from various flavors or STL custom allocators to libraries implementing standalone data structures in shared memory with various degrees of success.d. One project of interest is the STLshm library which published aim is to “create and use STL containers in […] shared memory” (anonymous n. BACKGROUND AND REVIEW OF LITERATURE 2. 2. Dr.2 Literature Numerous books on the C++ language exist and the most commonly referenced is certainly “The C++ Programming Language” authored by its creator (Stroustrup 2000). In a paper entitled “A C++ pooled.Chapter 2. However.

a 2003 C++ User’s Group Journal article reprinted by DrDobb’s magazine offers a special-purpose alocator to make STL containers available in shared memory (Ketema 2003). for example. Given gthe potential usefulness of the subject. In addition to Dr. it comes as no surprise that such an implementaton has been attempted before.The standard documentation of the STL provides some basic insights as to container classes and their usage (SGI 2006). A simple implementation of a custom allocator using “segregated storage” (Stroustrup 2000. Austern 1999).570-573) is the subject of an article containing sample pseudo-code by Fleseriu & Masur (2004). 7 . p. Additional literature provides extensive coverage of implementation details of the STL (e. Academic papers and public websites present a number of initiatives related to containers implemented in shared memory. In his book “Effective STL” Scott Meyers (2001) details some of the idiosyncrasies of the STL. In particular. Ronell’s paper mentioned above (Ronell 2003). Charles Petzold’s “Programming Windows” (1999) covers Win32 mechanisms that can be used for synchronizing access to shared memory between concurrent processes such as mutexes and semaphores. the issue of pointer compatibility between processes. But one may argue that the author only describes a sample high-level architecture that falls short on concrete details for an implementation and lacks coverage of some functional requirements like. this reading gives an understanding of some important limitations placed upon custom allocators.q.

THEORY 3. to create and manage mutexes and semaphores. This same mechanism can be used to develop a custom memory allocation mechanism that differs from the default heap allocation offered by the STL. Stepanov: “Generic programming centers around the idea of abstracting from concrete. giving access to flags and counters meant to be used for synchronization purposes (Downey 2008). Custom STL allocators were “originally developed as an abstraction for memory models […] to ignore the distinction between near and far pointers” (Meyers 2001). 3. for example. It is a commonly accepted idea that the abstraction mechanisms offered by the STL provide great flexibility for data storage and retrieval using parameterized container classes. One example of such a beneficial abstraction mechanism of particular interest to this project’s aim is the availability of custom allocators. In fact. which was in turn incorporated in the standard C++ library (Austern 1999). efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software” (Musser & Stepanov. it can also be used in to redirect memory allocation to a managed segment of shared memory (Ketema 2003).Chapter 3. Musser and Alexander A.1 Generic Programming and the Standard Template Library Containers are an integral part of the Standard Template Library and some may argue that generic programming represent a defining feature of the C++ language. 1988). The authors went on to create the Ada Generic Library (Musser & Stepanov 1989) followed a few years later by the C++ Standard Template Library (Stepanov & Lee 1995). the STL draws its origins from the Ada Generic Library that pioneered the concepts of generic programming as described by David R. In addition to changing the memory allocation strategy. Other mechanisms exist to transfer data 8 .2 Interprocess communication and shared memory Mechanisms are offered by operating systems to allow processes to communicate between each-other. Built-in primitives exist.

a target memory-mapped file is first created for this very purpose. one can also argue that there is no memory allocation strategy that addresses all aspects of performance and resource utilization efficiency in one simple solution. shared memory had a humble start by relying at first on a specific data segment declared in a Dynamic Linked Library (DLL). 3. On the Microsoft Windows platform. then in Windows 95 – “true” shared memory capability was added to the operating system in the form of memory-mapped files: “Memory-mapped files (MMFs) offer a unique memory management feature that allows applications to access files on disk in the same way they access dynamic memory—through pointers” (Kath 1993). Given the diverse choice of available allocation algorithms.between processes sequentially such as with named pipes on the Unix operating system. One of the fundamental components of this project is the implementation of a low-level shared memory pool able to satisfy the allocation needs of a custom STL allocator. With the introduction of the Win32 Application Programming Interface (API) – first in Windows NT. a view into the File-Mapping object can be mapped into the process address space. The programmer could allocate character strings in that segment and access them from the various processes having loaded the DLL (Petzold 1999).3 Memory allocation and segregated storage If it can be said that “memory management is one of the most fundamental areas of computer programming” (Bartlett 2004). the determination was made to implement a simple segregated storage memory pool to manage a segment of shared memory. Then. 9 . Shared memory is a more flexible mechanism that can be used to “allow multiple processes to share virtual memory space” (Gray 1997). A Windows File-Mapping object is created or accessed by a call to CreateFileMapping or OpenFileMapping with the target memory-mapped file handle as a parameter. To access a segment of shared memory. using Win32 API’s MapViewOfFileEx function.

no effort is made to reuse the unemployed memory. For this reason. 1970). If the required size is larger than one chunk. too small a chunk size and allocation/deallocation of multiple-chunk buffers may eventually fail because of high fragmentation. every fixed-size chunk is available and points to the next contiguous chunk of memory in a manner similar to a linked list. they are removed from the available pool by having the previous free chunk point to the next free chunk and the address of the first chunk is returned. When the memory pool is first initialized. however. This algorithm presents the advantage of simplicity and rapid allocation and deallocaiton response time (Purdom et al. If the required allocation size is smaller than one allocation chunk. the released allocation chunks are inserted at the beginning of the free chunk list. When an allocation request is processed. 10 . the fist available chunk is considered and the next available chunk becomes the first available chunk for the next request. chunk size has to be chosen with great care: too large a chunk size and a lot of memory is wasted for the most frequent allocations. But numerous allocation and deallocation of various size blocks tend to fragment the memory pool and may lead to its inability to satisfy subsequent allocation requests of more than one chunk. dynamic memory sub-segments. If enough contiguous chunks are found. part of the scope of this project to design such a garbage collection mechanism and the segregated storage memory pool developed for the shared memory segment of this project does not make any effort at optimizing subsequent allocations.Segregated storage revolves around a “first-fit” allocation of memory chunks of fixed size rather than allocation of variable size. A hardened segregated storage algorithm would therefore not be complete without an adequate “garbage collection” mechanism that guarantees that released memory chunks are returned to the pool in the most efficient way while preserving the highest ratio of contiguous free space. a search for contiguous chunks with a total size at least that of the required allocation size is performed in the memory pool. It is not. When previously allocated memory is released into the pool. on the other hand.

the following principles of Object-Oriented Programming have been embraced during the design and implementation phases: 3. separation of concerns As defined by Bennett et al. a number of commonalities have been identified between the constructors of the various STL containers. encapsulation. the multiple inheritance feature offered by the C++ language allows for each STL container class to be derived into a shared version that also derives from a synchronization class. In addition.1 Inheritance According to Peter Frohlïch (2002) “inheritance is an incremental modification mechanism that transforms an “ancestor” class into a “descendent” class by augmenting it in various ways. 3.4 Object oriented approaches – design patterns Of particular interest of this project. Generalization and specialization through object inheritance is also present in the hierarchy of shared container class factories.” One of the aims of this project is to mask the internal complexity of allocating containers in shared memory. 11 .4. If a one-to-one relationship exists between the type of shared container and its corresponding class factory. During the normal iterative refactoring process for this project.” In the spirit of this definition. Abstraction is used to isolate the inherent complexity of interfacing with shared memory from the programmer by encapsulating complex tasks into the lowest levels of the implementation and exposing a simplified interface in the higher levels. (2006) “encapsulation is a kind of abstraction that […] focuses on the external behaviour of something and ignores the internal details of how the behaviour is produced.4. associative containers and hash-based containers. The class factory hierarchy is built along these same lines. inheritance allows for the reuse of the STL container code into derived classes tailored to be allocated in shared memory.3.2 Abstraction. containers nevertheless fall into simple sequence containers.

87). The singleton pattern is applied in order to ensure that a only one shared heap object is instantiated so as to provide a single access point of access for all shared memory allocators (Gamma et al. p. 1977). In particular.. With the observation that different people sometimes solved similar problems in similar ways. and that a single segment of shared memory is targeted by the Shared Containers Library. – Shared containers factories used in the design fall into this definition by allowing creation.To use a term coined by Edsger W. and then describes the core of the solution to that problem. 2002. some well-known creational design patterns have found their way in the architecture defined for this project. the singleton and the abstract class factory. separation of concerns was used during the analysis phase to ensure that responsibilities of the different objects falls along the lines corresponding to a logically layered approach (Dijkstra 1974). p. in such a way that you can use this solution a million times over. The abstract class factory pattern was used to design the mechanism responsible for instantiating shared containers or connecting to existing ones.127). This behavior is required by the fact that STL custom allocators are not permitted to have non-static members (Meyer 2001. two well-known design patterns were used during the design phase of this project. 3. p 54).4. without ever doing it the same way twice” (Alexander et al.3 Design patterns “Each pattern describes a problem which occurs over and over again in our environment. attachment and release of shared containers. 2002. 12 . Dijkstra. Class factories are defined as providing an interface for creating families of related objects while abstracting the complexity related to object instantiation (Gamma et al.

access it and release its resources when it is no longer needed. not only the object itself needs to be residing in shared memory.1 Introduction From a high level approach. 13 . implementing container classes in shared memory needs to satisfy a finite number of functional requirements. in the case of shared containers. Figure 1 details the top level use cases for an implementation of a container object in shared memory. attach to an existing ones. but all the objects it contains along with its internal data structures have to be consistently accessible and modifiable from all processes as well. In order to locate a container object in a segment of shared memory. As it is the case for non-shared containers. But. a process should be able to create a shared container. access and modify them concurrently. the shared container objects needs to be located in a segment of memory that is commonly accessible between processes. processes need also to attach to an existing container created by another process and safely access it concurrently. Figure 1: Top level use cases For various processes to create new container objects. and then release their resources.Chapter 4. ANALYSIS AND DESIGN 4.

2 Allocation of container’s contents in shared memory Along its lifespan.2 Functional requirements At a lower level. it becomes clear that a mechanism is required to create and access a segment of shared memory and manage variable size memory allocation within this segment.4.2. a container object allocates and deallocates memory for objects it contains as they are inserted or removed. the requirement of creating.1 Allocation of container objects in shared memory For a container object to be accessible by more than one process. A mechanism is therefore needed to allow the 14 . it needs to be located inside a segment of shared memory. It also allocates memory for its own internal data strucutres and storage needs. 4. attaching to and accessing a shared container can be divided into items related to allocation of the container and items that provide access to it (table 1).2. A mechanism is therefore needed to allocate a buffer of shared memory and build a container object in this buffer. Considering a lower level of requirements. Allocation requirements Allocation of container objects in shared memory Allocation of container’s contents in shared memory Coherence of internal data structures between processes Access requirements Container attachment from a non-creating process Reference safety Concurrent access safety Table 1: Functional requirements 4.

4 Container attachment from a non-creating process Creating a container object located with its contents in shared memory and providing a mechanism that ensures consistency of its internal data structures between concurrent 15 .container object to interface with the above mentioned mechanism in order to allocate variable size buffers in shared memory as needed. Specifically.2. Without a mechanism devised to address this issue. Figure 2: Sample linked list 4.3 Coherence of internal data structures between processes It is the author’s contention that the internal data structure coherence requirement is less trivial than the other functional requirements and may easily be overseen when first considering this project. 4. a linked list built by process “A” would contain pointers to elements which are valid in process “A” but point to invalid memory addresses in process “B”. If the nodes are located in shared memory. consider the case of a simple linked list where each node contains a pointer to the next element (figure 2).2. the internal data structures of shared containers need to consistently point to the valid memory locations regardless of the process in which they are accessed. To illustrate the discussion. The inherent complexity of this requiement resides in ensuring proper function of container objects used by concurrent processes. the pointers they contain need to consistently address the same memory locations – regardless of the process in which they are accessed – or the linked list is invalid.

4. A reference tracking mechanism is required to identify the last process that uses a particular shared container in order to destroy the container and release the memory it occupies once that process no longer needs it. or number of concurrent client processes. 4.processes would be useless without a way for a process to gain access to a container object created by another process. But other processes may still require access to it. and it cannot be destroyed without first verifying that it is no longer in use. The requirement of container attachment from a noncreating process ensures that a process that did not create a shared container can nevertheless obtain access to it.2. 16 . overall size or individual size of containers. it is desirable to release memory allocated to that object.5 Reference safety Once a process no longer has the need to access a shared container object.2. This mechanism should guarantee that the writing process is the only one accessing the container and that no other process is accessing the same container at the time. the internal data structures of the container may be in an intermediary state that would lead to unexpected results or access violations. but if a process attempts to access the contents of a shared container while another one modifies it. 4. but ensures exclusive access to a process before it is allowed to modify a shared container.3 Non-functional requirements No limitation should be imposed on the shared memory segment in terms of number of shared containers it holds.6 Concurrent access safety Two or more processes could be granted read access to a shared container (without attempting to modify its contents) without penalty. An access mechanism is therefore needed that allows any number of concurrent reader processes.

The programming interface presented to users/developers should be consistent with that of the STL and laid out in a clear. Note that. in its quality of focal point for communication among the different processes.255256). the necessity of implementing a mechanism to manage a memory pool in shared memory was considered. Once a memory pool mechanism that allocates and manages a segment of shared memory exists. This language extension allows the “new” keyword to target a specific memory address instead of allocating memory from the process heap (Stroustrup 2000. and to ensure that memory subsequently released returns to the pool for possible reallocation. Care should be taken to mask undue complexity and favor practicality whenever possible. The aim of this memory management mechanism is multiple. p. Finally. First. with the use of the placement new operator. it can offer a centralized mechanism to link container objects to unique identifiers that can be used by non-creating processes to obtain access to existing containers. final destruction of the shared object cannot be performed by invoking the C++ “delete” instruction but rather should be done first by calling the object’s destructor method and then by releasing the memory buffer from the pool. In this regard. concise and logical way. the memory management mechanism can also be used to help satisfy the requirement of container attachment from a non-creating process. 17 .4 Approach To satisfy the requirements set forth above. Secondly. creating a container object in shared memory can be performed by invoking the “placement new” C++ instruction to instantiate the desired object in this buffer. 4. it should create and map a segment of shared memory to the calling process address space and facilitate consistent access among processes. it is responsible for implementing a memory allocation scheme that allows variable length buffers to be allocated.

The highest level interfaces directly with the STL and offers a practical programming interface. After a superficial analysis. It is outside the scope of this discussion to compare the merits of different memory allocation strategies. The lower level of this mechanism is in charge of interfacing directly with the shared memory segment. Such a custom allocator can be designed to use the above mentioned shared memory pool for its allocation need. the decision was made to use a form of segregated storage as documented by Stroustrup (2000. pp. this idea does not stand further scrutiny as it could solve the issue of virtualization of the contents but it would not address the need to virtualize the container’s internal data structures as illustrated by the linked-list example (figure 2). Changing the code of the Standard 18 . whereas the higher level manages memory allocation within the segment and facilitates low level communication between like-mechanisms in concurrent processes. In this regards.The functional requirement of allocating the container’s contents in shared memory is addressed by using a mechanism provided by the STL in the form of a “custom allocator. In this regards. If this approach would indeed ensure consistency of pointers within each process’ address space. The diverse responsibilities are given to the shared memory management mechanism and points to a layered implementation in order to ensure a proper separation of concerns. one could envision the need for a modification of the container classes so that they use a relative pointer for their internal data structures as well. ” A custom allocator is a class used as a parameter in the creation of a STL container class that exposes allocation methods used by the container to allocate memory in a custom location chosen by the programmer. it would be tempting to conclude that virtualization of memory references between processes can simply be achieved by wrapping the container’s contents in relative pointers adjusted within each process for the offset where the shared memory segment is mapped. The issue of consistency of the container’s internal data structures among processes is of particular concern for the success of this project. But this approach requires a customized behavior on the part of the container classes that can only be achieved with a modification of their source code.570-573).

Template Library clearly falls outside the scope of this project. The class factory approach masks the complexity of the implementation and provides a high-level interface for accessing a shared container by a non-creating process. and one may argue that it goes against the core principles of reuse and encapsulation defined as some tenets of object oriented programming. But. Although seemingly restrictive and apparently difficult to achieve. To achieve the non-functional requirements of simplicity and accessibility of the programming interface. obtain a pointer to a shared container object. other options were researched to satisfy this requirement. This behavior guarantees consistency of the shared containers’ internal data structures between the processes while at the same time reducing the complexity of the implementation. For these reason. the approach selected is to use a class factory mechanism that can be invoked to create a new container. while allowing a writer process to obtain an exclusive lock that ensure that no other writer and no readers concurrently access the container. 19 . It interfaces with the lower levels of the implementation to allocate memory in the shared segment. it was desirable to design a mechanism that allowed multiple read-only processes to obtain a non-exclusive lock ensuring that no writers were currently accessing the container. and release unused resources thanks to a built-in reference safety mechanism. unlike the simplest existing forms of locking solutions. By forcing mapping of the shared memory segment to the same address in each process space. the approach selected is in fact to ensure that the segment of shared memory is mapped at the very same offset in each process. attach to an existing one or detach from a container. and this solves the pointer consistency issue altogether. each byte of shared memory is guaranteed to be located exactly at the same offset in every process. The requirement for concurrent access safety is satisfied by implementing a locking mechanism.

Interface to an allocated segment of shared memory is layered between a low-level interface implemented by the shared_heap class and a higher level interface implemented by the shared_pool class. The header also holds thread safe variables used by the memory allocation algorithm for synchronization.4. along with custom parameters common to all process and used by higher level objects through its getparams method. As a static class.1 Overview Problem domain analysis for the functional requirements set forth led to the selection of a layered architecture approach.5. The shared heap uses a discrete header allocated at the beginning of the shared memory segment. The shared pool is a higher level interface that implements access to the segment of shared memory that is tailored to be used by shared containers. The shared heap is implemented as a singleton class that offers a low-level interface to an allocated segment of shared memory. The highest level of implementation is materialized at the shared container factory level with the help of the shared_allocator custom allocator class (figure 3). This header allows the shared heap to map the shared memory segment to the same address space in all processes in order to ensure pointer consistency between processes. A mechanism is also provided to detect when the last process releases the shared memory segment in order to perform resource deallocation and cleanup. it enforces the singleton nature of the shared heap by forwarding allocation and 20 . The shared heap object implements the mechanism for segregated storage allocation within the shared memory segment. The shared heap manages the shared memory segment and generic allocation whereas the shared pool manages allocation of objects and containers. Other responsibilities of the shared heap include connecting client processes to the same segment of shared memory while ensuring consistency between processes address space and offering a way for the higher level interface to access common parameters.5 Detailed design 4.

A custom allocator class. The shared pool uses the ptrparam parameter of the shared heap’s header exposed through the getparams method to connect concurrent processes to the shared container map. shared_allocator.deallocation requests to a single static shared heap object. The shared map resides in shared memory and is indexed by a unique container object name (string). and detach from identifiable instances of shared containers. attach to. It contains instances of shared_object. is used as a template parameter for STL containers to target the segment of shared memory for their allocation needs. a simple class defined to hold parameters and references to each shared container. The shared_pool class maintains a map of shared containers allocated in the segment of shared memory in order to offer a mechanism for all class factories to create. The highest level of the interface consists of class factories deriving from a generalized _SharedContainerFactory class. 21 . Class factories are used to instantiate and attach to shared container object.

Figure 3: Layered design class diagram 22 .

2 Low level use cases The different low level use cases of the system are depicted in figure 4. The shared allocator is a STL custom allocator that uses the shared pool to interface to the shared memory segment.5. The shared pool interface is then called upon to remove the corresponding entry in the container map. attachment by another process and release. it is destroyed and the shared memory it utilizes is freed by the class factory. The shared pool keeps a reference counter updated for each shared container. When no more processes hold any reference to a particular shared container. 23 . Shared container factories also use the shared pool to build new container objects in shared memory and insert newly created shared containers or find existing ones in the container map that is kept in shared memory.4. Figure 4: Low level use cases The sequence diagram in figure 5 details a typical shared container creation.

attachment and release 24 .Figure 5: Shared container creation.

Figure 6: Shared container classes and class factories 25 .3. or detach from a previously attached instance. Similarly. attach to an existing one.1 Overview Instantiation of shared containers is performed by using derived shared container classes and their corresponding class factories. The corresponding class factory allows the programmer to create a new shared container. the shared_map class derives from the STL map class and is exposed through SharedMapFactory (figure 6). Each newly created shared container is identified with a user-assigned alphanumeric name so that other processes can identify it and attach to it.3 High-level classes – shared containers and class factories 4. a corresponding shared container class and a class factory are defined. As an illustration. The shared container class derives from the base STL container class and from the sync class which provides a synchronization mechanism for concurrent access.5. For each container class defined in the STL. the shared_vector class derives from the STL vector class and uses the SharedVectorFactory for instance creation and access.5.4.

as the vector class is an STL sequence container. Each container type offers specific constructors that are exposed by the corresponding class factories. Container-specific class factories derive from one of these base classes according to the type of container considered. implemented Another set in of the derived for the _SharedSequenceContainerFactory associative containers are constructors in implemented _SharedAssociativeContainerFactory class. in turn. the SharedVectoryFactory class derives from the _SharedSequenceContainerFactory class. a set of constructors specific to hash-based containers are implemented in the _SharedHashContainerFactory class. For example. A set of constructors used by sequence containers are class. associative and hash-based. This class.The shared container factory classes have been generalized along the lines of the three generic types of containers defined in the STL: sequential. Each shared container-specific class factory derives from the corresponding type-generic container factory class. Figure 7 details the generalization and specialization relationships between container factory classes. 26 . derives from the generalized _SharedContainerFactory class. Common constructors are implemented in the base _SharedContainerFactoryClass. Finally.

Figure 7: Container factories class hierarchy 27 .

3. it releases it back.4 Process flows Figure 9 is a state chart diagram detailing the different states and transitions of a sync object. The client process also needs to be able to release the locks. 4. Before modifying a shared container. thereby signaling that no more access will be performed on this object. Before accessing the container it needs to ensure no other process is modifying it by obtaining a non-exclusive read lock. Figure 8: High-level use cases A client process can create a new shared container or attach to an existing one created by another process.5.5. 28 .4. it needs to ensure that no other process is accessing it by obtaining an exclusive write lock. When its use of the shared container is over.2 High level use cases The high level use cases of the system are depicted in figure 8.

Because the second process was the last user. The first process creates the shared vector and obtains a write lock on it. or it can downgrade an exclusive lock into a non-exclusive one. the second process obtains the read lock. The first process releases the shared container.Figure 9: State chart diagram for the synchronization mechanism A process can obtain an exclusive or non-exclusive lock on the object. The second process attaches to the shared vector by name and attempts to get a read lock on it. The following UML sequence diagram (figure 10) details a simple shared container creation by one process and access by another one. Once the first process releases the write lock. 29 . The second process releases the read lock and releases the shared container. the shared container is then destroyed and all shared memory occupied by it is released. it can attempt to upgrade an existing non-exclusive lock to an exclusive on.

Figure 10: Sample shared container usage 30 .

an iterative approach similar to Boehm’s “spiral model” was selected for analysis.Chapter 5. With this approach. access by non-creating process) and may have proven to be less intuitive to use by the programmer than the class factories. class shAlloc> where “T” was an STL container class and “shAlloc” was an implementation of a custom STL allocator managing a segment of shared memory.1 Methodology Due to the very nature of this project. In an attempt to address the requirements in a most elegant way. design. 5.g. This approach proved valuable when addressing the issue of pointer consistency. a preliminary draft of the design evolved from a class wrapper around STL containers to a parameterized class in the form: shContainer<class T. implementation and testing (Boehm 1986).shAlloc> shINTVECTOR. Literature research showed that similar efforts to virtualize pointers in each process were not being successful or required rewrite or modification of the STL 31 .shAlloc>. The issue of pointer consistency among processes was researched and initial design experimented with the concept of virtual pointers similar in concept to the auto_prt STL class.2 Iterative design steps Original analysis identified the functional requirements for this project and the issue of pointer consistency among processes was researched in depth. a declaration for a shared vector of integers would have consisted of: typedef shContainer<vector<int. In the end. for example. METHODS AND REALIZATION 5. it was determined that this approach would have fallen short of implementing some of the requirements (e. This approach allowed for incremental discovery of the functional requirements as a better understanding of the different issues was obtained. and when the concurrency issue linked to multi-core processors was unveiled.

Due to the parameterized nature of the shared container classes. Additional.h.3 Shared containers library source code 5. most class instantiation logic is contained in the class_factories.3. BiCapitalisation is used for class factory names to differentiate them from regular classes. First-line header files match corresponding STL container header files and are meant to be included by programmers in their source code (shared_vector.container classes and the decision was made to align the shared memory segment on the same offset in all the processes.h header files rather than a compiled object library file.cpp files. attributes and operations. Lower-level interface to the shared memory pool is implemented in the shared_pool.1 Naming conventions The following naming conventions are used throughout the source code: lowercase is used for most class names. The 32 .h header file (figure 11).h.g. lower-level header files are included by the container_factories. Most header files include the corresponding STL header and the container_factories.h. etc).cpp and shared_heap. generalized class factories) 5.2 Structure of library and header files The shared container library consists of a binary library file – shared_containers.h header file.h header file due to their adaptor nature. Each shared container class is declared in its corresponding header file. underscore_separated_words are used for class names and operations.lib – and a series of C++ header files. shared_map.3. for consistency with the STL. One exception is the shared_stack and shared_queue classes that are declared in their respective headers but include the shared_deque. 5. shared_hash_set. _leading_underscores are used for class attributes and for class names meant to be derived (e.

is used for a second. 33 .cpp file.h header file contains a one-time declaration of heap variables that need to be present at link time (listing 7). the segment of shared memory is in fact mapped twice. 5. The shared header contains. separate mapping of the shared memory segment which extends for the remainder of the segment size. All cpp files are compiled into the binary library file for distribution along with the headers. including Windows XP (Microsoft 2008 [1]). This header can be included one time in any module of the C++ project. This value. The external heap variables can be customized at compile time and are used by the shared_heap object to open and initialize the shared memory segment. In addition to the library and traditional header files. among other things.3.3 Interface to shared memory – pointer consistency To solve the issue of pointer consistency between processes it has been decided to map the shared memory segment at the same offset in each process. Consistent mapping is achieved by using the MapViewOfFileEx Win32 API calls that accepts a forced mapping offset as an optional parameter and is available in Microsoft Windows 2000 and newer operating systems. First the shared header is mapped at an address determined automatically by the operating system so as not to conflcit with the rest of the process memory. stored in the _beaseaddress member of the shared header (listing 3). the heap_parameters. the offset at which the rest of the shared memory segment should be mapped in every process in order to ensure pointer coherence. These variables include the size and identifier of the segment of shared memory.synchronization class is implemented in the sync. To achieve consistent mapping among processes. the size of memory chunks used by the segregated storage algorithm and the target process’ base address for mapping the shared memory segment.

Figure 11: Header file inclusion hierarchy 34 .

and a reference counter. 5. a process memory space is limited to an addressable 2 Gb of memory for a highest 32-bit address of 0x7fffffff (Kath 1993). This master list exists in the form of a STL map container that contains the name of each shared container along with a structure containing a pointer to the shared container object. the offset should be high enough in the process memory space that it does not interfere with the process heap. // Size of object safe_counter _refcount.3.Selecting an appropriate offset for mapping the segment of shared memory at the same address in every process is non-trivial and no guarantee is made that mapping can be obtained without errors. To facilitate consistent mapping. Base offset (0x01100000) + heap size (0x04000000) = 0x051000000. // Pointer to object size_t _size. while at the same time providing enough “headroom” for a large enough segment of shared memory. its typeof size. In the current configuration. an arbitrary value of 0x01100000 has been selected for the 32-bit mapping offset of the shared memory segment along with a heap size of 64 Mb or 0x04000000 bytes (listing 7). Any value corresponding to the size of the shared memory segment subtracted from the maximum address would be appropriate as the starting offset of the mapped segment of shared memory. but low enough so that it falls in the allowable addressing space of the process.4 Object instantiation and attachment – map of shared containers The requirement of container attachment from a non-creating process is addressed by the existence of a “master” list of shared containers allocated in the segment of shared memory. a sub-class of the shared_pool class which is responsible for managing the shared map (listing 1): class shared_pool::shared_object { public: void* _object. This structure is implemented in the form of shared_object. In Win32. // Usage count 35 . which is a lower memory address than the process’ highest address of 0x7fffffff.

void shared_heap::get_params(long** longparam. shared_object() {}. Compare. The shared_pool class uses the _ptrparam void* header member (returned in the ptrparam parameter) to store the address of the shared map and make it accessible among processes. _size(size). }. size_t size): _object(object). static container_map* _containermap. // Number of processes safe_counter _allocating. class Data. ~shared_object() {}. // Common base address void* _head. 36 . the get_params method returns a pointer to user-defined parameters stored in the shared heap header (listing 3). Listing 1: The shared_object sub-class of shared_pool The shared map is then defined as shared map of string and pointers to shared_object objects which is allocated in the segment of shared memory and uses the shared_allocator custom allocator for its allocation needs (listing 2). Designed to use an approach similar to the parameters used in Windows messaging (Petzold 1999). // 1 if allocation in progress char _filename[MAX_PATH].shared_object(void* object. // First available chunk size_t _heapsize. Listing 2: Declaration of the the shared container map The mechanism implemented in order to grant access to the shared map from all processes uses the get_params method exposed in the shared_heap class. typedef shared_map<std::string. Data>>> {}. // Size of heap and chunk size long _longparam. Data. // Client pointer parameter safe_counter _numprocesses. _chunksize. public std::map<Key. template<class Key. shared_allocator<std::pair<const Key. _refcount(1) {}. class Compare=std::less<Key>> class shared_map : public sync. void*** ptrparam) { *longparam = &_header->_longparam. // Client long parameter void* _ptrparam. __declspec(align(32)) volatile class shared_heap::shared_header { public: char _signature[sizeof(SHAREDHEAP_SIGNATURE)]. shared_object*> container_map. // Signature void* _baseaddress. // Temp filename }.

and for downgrading from an exclusive write lock to a non-exclusive read lock. 5. To this extent. each sync object maintains a reference of the process and thread number that owns an exclusive write lock in order to accept subsequent write lock and unlock 37 . thread-safe counter using Win32’s interlocked primitives (listing 4). In addition. Note that the safe_counter type is also used in the shared heap’s header to facilitate synchronization of allocation operations.*ptrparam = &_header->_ptrparam. It provides a mechanism for obtaining a non-exclusive read lock or an exclusive write lock. } Listing 3: Shared heap header structure and the get_params method In addition. This weak verification is far from perfect and does not guarantee the denial of attachment of an incorrect shared container type.5 Synchronization Interprocess synchronization is provided in the form of a low-level sync class from which every shared container class derives using C++’s multiple inheritance mechanism (listings 5 & 6). the size of the container object returned by the sizeof operator is validated against the size stored in the corresponding member in the shared_object object in order to prevent mapping a pointer to a container of one type to an existing shared container of a different type.3. The sync class uses safe_counter members that are designed as a wrapper aroung a simple. along with a mechanism for upgrading from a non-exclusive read lock to an exclusive write lock. Downgrade is a guaranteed operation. but it is designed as a first line of defense in a more complex mechanism that can be subsequently developed. two threads attempting to upgrade a non-exclusive lock simultaneously). The sync class maintains a safe_counter for read locks and a separate one for write locks. but upgrade is not. the shared_pool class performs simple verification when attaching to an existing shared container. and it does not support an infinite timeout to avoid possible dead-lock scenarios (e.g.

4. validation of the Shared Container Library was limited to the author’s perception based on prior experience with STL containers. A fail-safe mechanism is provided to prevent any lock counters to attain a negative value. Unit testing was performed on each component as an initial quality assurance step and a test scaffolding program was designed to verify that the key functional requirements of the project were met.3. Some key qualitative properties were confirmed during the implementation of the test scaffolding software. Regression testing was performed with the help of the test scaffolding at each incremental phase of the development effort. 5.4 Verification and validation Due to the unavailability of an industry sponsor for this project.requests from the same thread/process without error. The counter is decremented each time a process detaches from the container. 5. Due to the low-level nature of the produced code. in particular in terms of ease of deployment and abstraction of complexity for the programmer-user. the decision was made to rely on the step-by-step feature of the development environment’s built-in debugger to conduct most of the initial testing instead of using repeatable unit testing tools such as “Cunit” which would have required an extensive simulated running environment. The counter is set to one when the shared container is created and incremented when a process attaches to it.1 Unit testing Each component of the library was unit tested with an emphasis on structural coverage and in particular branch coverage analysis. 5. When the counter reaches zero the container’s destructor is called and the memory it occupied is released by the shared_pool. 38 .6 Reference safety The shared_object class used in the shared container attachment mechanism contains a safe_counter object used by the shared_pool for reference counting.

4.5. a test scaffolding program was developed to exercise a variety of STL containers operations and types. verify and detach operations in additional processes. populate. the test scaffolding first offers the choice to consider containers allocated in the default process heap or in a shared memory segment. Multi-process functional evaluations consists in create and populate operations in the main process followed by concurrent attach.2 Test scaffolding For the purpose of evaluating the functionalities and the performance of the produced library. verify and destroy operations. Then the user is asked to choose what type of container is to be exercised among the following: . A typical functional evaluation consists in create.queue The user is then given a choice of operation to perform on the selected container: - create/attach : create a new container or attach to an existing one populate : populate the container with discrete values verify : verify that discrete values populate the container destroy/detach : destroy the container or detach if still in use by another process Each operation is timed with a millisecond timer in order to measure performance. Queue functional testing varies from other containers for populates makes one process push values into the queue while verify makes another process pop and verify values from the queue. In its interactive flow (figure 12).vector . Timeouts are adjusted for queue testing in order for the popping process 39 . The test scaffolding program was designed to be instantiated in more than one process in order to test concurrency behavior.map .

non-repetitive string values are obtained by considering an ASCII character value varying with the index number modulo 26. Figure 12: Test scaffolding interactive flow In order to populate the containers with non-repetitive discrete values.Similarly. and building a string consisting of the obtained character followed by a numeric representation of the index value (listing 19). The pseudocode form of this algorithm would be: 40 .to wait for the pushing process without generating a verification error if the queue is empty. the following approach was used: Non-repetitive numeric values are obtained by adding the order of the value to the previous result to obtain a suite in the form “ni = n(i-1) + i” for i > 0 where n0 = 0 (listing 18).

"Z25". "C2". 41 . … .“Char(‘A’ + i%26) & i” for i ≥ 0 which would provides the following suite of values: "A0". "A26". "B27". etc. "B1".

populating it with non-repetitive but predictable discrete values and then verifying the presence and correctness of the values in the container. One process “pushed” 10. and then re-creating it. RESULTS AND EVALUATION 6.000.Chapter 6.1 Functional evaluation Functional evaluation of the shared containers was performed by instantiating a container. Evaluation results were positive with the successful verification of the contents of a container by one process while it had been populated by a different process. destroying it. The inefficiency of the segregated storage algorithm in terms of reallocation of multiplechunk buffers was evidenced by the original failure of a test consisting in creating and populating a large map of strings and integers. After destroying the map object.000 integer values into the queue while another process “popped” values from it. In each case. both for a shared vector of integers and a shared map of string and integers. The test scaffolding was also used to perform concurrent testing on a queue adapter to a shared deque. and. This resulted in the inability of the algorithm to allocate multiple-chunk buffers and led to a failure of the test 42 . The test scaffolding was used for testing a vector-type container of integer values and a map-type container of variable-length string keys and integer values. the number of iteration was variable with final evaluation performed on 1. The fact that the simple segregated storage algorithm does not offer any garbage collection mechanism led to high fragmentation of the shared memory pool. more specifically. few of which were consolidated in the correct order with their immediate neighbors. to the deallocation of multiple chunks of memory in random order. It was noted that push and pop operations on a shared deque (or a stack or queue adapter) required exclusive access (write lock) due to the fact that the first and last element of the deque were constantly being modified and internal iterators were thus being invalidated. the shared pool consisted in free chunks of memory.000 items.

verify and release by another process. not concurrent access) Table 2 details the results of this comparison. the response time of standard operations on shared containers was compared to the performance delivered by “pure” STL containers during the same allocation. - No synchronization delays (successive.2 Performance evaluation For performance evaluation. The comparison was made using the following parameters: Local memory: - Instantiation of pure STL vector and map containers Shared memory with create: - Instantiation of shared_vector and shared_map containers.program. then release of the container by the creating process for deallocation. First instantiation was not measured because it triggers creation and initialization of the shared memory segment and associated segregated storage pool Shared memory with attach: - Creation of shared_vector and shared_map containers by one process followed by attach. After analysis of the cause of the failure. Test was performed allocating 1 million integer items into a vector and 1 million string and integer pairs into a map. 6. 43 . insertion of records. it was decided to double the size of the allocation chunk in the shared heap mechanism from its initial testing value of 32 bytes thereby greatly reducing the number of multiple-chunk allocations (listing 7). This change solved the reallocation error issue and multiple creation and destruction of large maps of strings was subsequently successful. retrieval of records and release. populate.

destroy / detach map<string.699ms 1. Response time for “attach” in the “Shared memory w/attach” case reflects simple attachment to an existing shared container.683ms 1. Similarly.populate .create / attach . The creation operation does not include initialization of the shared memory segment and its corresponding segregated storage memory pool which was independently measured as adding less than 100ms for a 64Mb shared pool with a chunk size of 64 bytes. 44 .verify . the time required to attach to an existing container is O(log n).int> . Once the shared memory segment is initialized.107ms 393ms Table 2: Performance comparison Local memory results were obtained by instantiating native STL classes. The “Shared memory w/attach” results were obtained by creating a container in one process and then measuring attach.create / attach . the time O(log n) required to insert a new container into the shared container map’s balanced binary tree structure needs to be taken into account (where n is the number of shared containers present in the container map). The “Shared memory w/creation” column corresponds to standalone shared container classes. populate.Local memory vector<int> . the tests have been run several times and the average results are being presented here (see appendix C for complete results). verify and destroy operations from another process.752ms 1.populate . In each case. the time required to retrieve the container by name from the shared container map (Cormen et al. creation of a shared container is performed in a similar time than the time required to create a local container.261ms 193ms Shared memory w/attach < 1ms 17ms 1ms < 1ms < 1ms 1. 2001).317ms < 1ms < 1ms 13ms 1ms < 1ms < 1ms 1. Creation time of the container objects in the “Shared memory w/creation” case is on par with that of other cases. To this duration.destroy / detach Shared memory w/create < 1ms 18ms 1ms < 1ms < 1ms 1.verify .

or even with any regularity” (Cohen & Woodring 1998) was verified during the testing phase of this project. In this case. a fact that may be explained by the fact that the segregated storage algorithm used in this implementation does not make any attempt at garbage collection whereas the default heap allocator may perform cleanup tasks when memory is deallocated. a vector of integers) but does not significantly differ for complex types (e. Performance related to populating a container and verifying its contents does seem to suffer a 30-40% degradation when simple data types are used (e. Of course. initial multi-process testing on the desktop computer with a debug version of the code terminated with a fatal exception: “DEBUG_ERROR("ITERATOR LIST CORRUPTED!")” 45 .g. response time to populate and verify a container in shared memory does not show significant degradation compared to locally allocated containers. Destruction of a shared memory allocated container proves to be faster than that of a local one. Although concurrent multi-process testing on the laptop did not show any issue. except when considering very simple data types.6 Ghz was used for further testing and text editing. Testing was conducted on two machines both running the same version of Windows XP professional with the latest Microsoft updates.3 Concurrency issues The postulate that “multithreaded programs are deterministic by nature […] many serious bugs will not happen all the time.13 Ghz was used for development and testing.One important finding is that. possible delays due to high disk activity or concurrency locking requests are not taken into account in the above measured results. A desktop computer with a dual-core Pentium 6400 processor running at 2. a map of strings) whether the container is located on the process heap or in shared memory. In addition.g. 6. and a laptop with a Pentium M processor at 1. the short time observed for detach in the “Shared memory w/attach” case (<1ms) can be explained by the fact that the shared container is not actually erased as the creating process still holds a reference to the object. the reference counter of the shared container is simply decremented.

no additional faults were noted during subsequent multi process concurrent testing on the desktop computer. Further research showed that the error was in fact linked to an iterator debugging feature specific to this implementation of the STL. Because the error only occurred on the desktop and not on the laptop. 46 .J.The error was traced to the “xutility” header of the Dinkumware implementation of the STL (based on code written by P. Once these macros were applied and the test code recompiled. Microsoft 2008 [3]). A solution was found in the form of defining the macros “_HAS_ITERATOR_DEBUGGING” and “_SECURE_SCL” both with a value of zero to turn these additional features off at compile time (Microsoft 2008 [2]. this issue may only exist on multi-core processors capable of true multitasking as opposed to single core variants that only simulate multitasking by the use of time-sharing techniques. Plauger) distributed with Microsoft Visual C++ 2008.

Chapter 7. CONCLUSIONS
7.1 Potential applications

Shared STL-like containers can be used for a variety of implementations that require high-level access to structured data between processes.

Figure 13: Monitoring / configuring an application server

47

Such needs are frequently encountered in concurrent interactive applications where users are allowed to exchange data. Other applications include monitoring of online transaction processing system. Databases and local files can be used in lieu of shared containers for these applications, but these solutions may add overhead, additional failure points and/or increased code complexity compared to shared containers.

One practical example of shared container could be for a transaction processing system where one could envision each worker thread of an application server updating a given set of metrics residing in a shared map that can be read by another process. This other process could, for example, expose these metrics via the Simple Network Management (SNMP) protocol. The same SNMP interface could use another shared map to dynamically modify the internal parameters representing the configuration used by the worker threads, thereby providing a path to dynamic configuration (figure 13). One could also envision a separate process using the metrics harvested by worker threads in the shared container to dynamically change the configuration of the working system in order to react to connection errors or system-wide congestion (e.g. automated database failover, etc).

7.2

Lessons Learned

The subject of implementing STL containers in shared memory has attracted quite a bit of attention over time, possibly because it may look easily feasible to the casual observer, but proves to be in fact more challenging than originally thought. It would be an oversimplification to think that implementing a custom allocator targeting a shared memory segment would be sufficient to reach the goals of this project. In fact, allocating the data held by the containers in shared memory is only one of several functional requirements – and maybe one of the easiest to satisfy. Other requirements include placing the internal indexing structures of the containers in shared memory and making them consistently accessible by various processes. In addition, such an implementation would not be complete without an adequate synchronization and reference counting mechanism that guarantees safe concurrent access to the containers and their contents

48

but releases resources used by objects that are no longer in use. One could therefore argue that the challenges presented by a project like this one may remain unrecognized until one starts performing a detailed analysis of the implementation’s requirements.

Functional evaluation on the shared containers library led to a surprising finding in the form of satisfactory performance on a computer equipped with a single core processor but an unexpected behavior on a double-core equipped machine. The lesson here is that multithreaded or multi-process applications should be tested on architectures where true concurrency can occur – in this case a dual-core Pentium processor – to guarantee that all potential synchronizing issues have been addressed. Processors with a high degree of compatibility can still have significant architectural differences that may cause issues in software programs to remain hidden for a long time.

7.3

Future Activity

The code implemented for this project lacks strong exception handling and this should be added before the library is used in real-life applications. The simple segregated storage algorithm does not currently offer a garbage collection mechanism and one could argue that its usefulness would consequently be limited if numerous allocation/deallocation of large memory buffers take place. A garbage collection mechanism would be a welcome addition to the library.

In addition, the current library implementation only performs very limited runtime type checking when attaching to an existing shared container. Only the memory footprint of the container object is taken into account (its size returned by sizeof) to verify that the container expected from a call to ContainerFactory::attach is of the correct type. One could envision using C++’s runtime type information (RTTI) mechanism using the typeid operator to verify the container’s type upon attachment by a non-creating process.

49

neither languages supports multiple inheritance which is extensively used in this implementation.4 Prospects for Further Work The current implementation of the shared container library is limited to the Win32 operating system and the STL library distributed with Microsoft Visual Studio 2008. 50 . no real-life testing of the shared container library has been performed at this time and it would stand to reason to think that early practical usage of the library may unveil issues that were not uncovered during initial testing. For example. albeit one that would pose its own set of challenges and may lead to a different approach. 7. if parameterized classes can be declared in newer versions of Java and C# by using “generics” similar in concept and syntax to C++’s templates (Bracha 2004 & Microsoft 2008 [4]).Finally. Porting the shared containers library to other operating systems such as Unix or Linux should prove feasible by developing platform-specific versions of the shared heap and the safe counter classes. Developing a similar library for other languages such as Java and C# would also be an interesting endeavor. Additional desirable functionalities can be envisioned such as the possibility to use more than one memory mapped file – a current limitation of this implementation – the possibility to persist the memory mapped file on disk once all processes have terminated execution and the addition of security and data encryption for the shared memory segments and the containers they hold. Another direction for further work could be to widen the features of the Shared Containers Library.

et al. 2008) 51 . (1999) . 2008) Fleseriu G."A Pattern Language" .Sun Microsystems . 2008) Baum.Proceedings of the 37th International Conference on Technology of Object-Oriented Languages and Systems . Irvine .ieee.ISBN 0-7695-0918-5 – pp."Inheritance Decomposed" . J.liv.ISBN ISBN 0-387-90652-5 Downey.5/pdf/generics-tutorial.The choices.ISBN 9780195019193 – p. A.codeguru. 2008) . (1977) . and implementations of dynamic allocation" ."Object-Oriented Systems Analysis And Design Using UML" p.CodeGuru . 1986) ."Inside memory management .REFRENCES CITED Alexander C. A.B.net/Introduction. tradeoffs.Oxford University Press .) .ac. (2008) .ddj."The Little Book of Semaphores" .IEEE Computer .Addison Wesley .pdf?tp=&arnumber=59&isnu mber=6 (Accessed December 18.pdf (Accessed December 17. (May 5. (February 10. P."STLshm" .Department of Information and Computer Science University of California. J (March 27. 2004) .ISBN 978-0-26203293-3 – p.html (Accessed November 25.[Online] Available at: http://www.sun."A spiral model of software development and enhancement " . (1998) . et al."Allocators (STL)" . Dobb's Journal .W. 2008) Bracha G.x anonymous (n.uk/iel1/2/6/00000059.[Online] Available at: http://stlshm. (November 16.fi/~sakkinen/inhws/papers/Froehlich.O'Reilly . 2008) Buchanan.W.IBM developerWorks .[Online] Available: http://www. M. A. et al. (2002) . M.266-277 Bennet S. 2004) ."Generic Programming and the STL" . (2001) . L.pdf (Accessed December 18. Inc. B. 2008) Cohen.H.Dr."Win32 Multithreaded Programming" . (2006) .SourceForge. .570 Cormen.[Online] Available at: http://greenteapress."Generics in the Java Programming Language" .[Online] Available at: http://www. M."On the role of scientific thought .Springer-Verlag New York.com/semaphores/downey08semaphores.com/j2se/1.jyu.McGraw Hill .d. & Woodring. & Masur.Green Tea Press .cs.ibm.[Online] Available: http://java.com/developerworks/linux/library/l-memory/ (Accessed December 14. 2008) Frohlïch. T.com/Cpp/Cpp/cpp_mfc/stl/article.ISBN 13-978-0-07-711000-0 Boehm.org. (July 5. 2008) Austern.253 Dijkstra E.[Online] Available at: http://ieeexplore."Generic components to foster reuse" .php/c4079/ (Accessed October 13. (2000) .Selected writings on Computing: A Personal Perspective" . & Becker. 2004) .ISBN 0201-30956-4 Bartlett.ISBN 1-56592-296-4 – p.226 .pdf (Accessed December 10."An Interview with Bjarne Stroustrup" ."Introduction to Algorithms" .com/cpp/207000124 (Accessed November 17.ezproxy.[Online] Available at: http://www.McGraw Hill .net .sourceforge. (1974) .

psu. 2008) Gray. J.[Online] Available at: http://citeseerx.com/en-us/library/ms810613.cgi?id=21251 (Accessed November 21. I.microsoft."Boost."GCC Bugzilla Bug 21251 . (April 1."Checked Iterators" . (May 29.ISBN 0-201-74962-9 – pp.Gamma E.edu/techreports/1970/TR98.com/enus/library/0x6a29h6(VS. 2008) Musser."Programming Windows" .A. R.35.Elements of Reusable Object-Oriented Software" . (2001) .80).[Online] Available at: http://allocator.Microsoft Corp.net/ (Accessed November 21. (August 1. 2008) 52 .Microsoft Corp. ."Design Patterns .aspx (Accessed December 10."Interprocess Communications in Unix" .microsoft. P.Pearson Education . S.[Online] Available at: http://msdn.The University of Wisconsin Madison [Online] Available at: http://www.Addison Wesley .W. 2008) Ronell.cs. et al. (2002) .[Online] Available at: http://msdn.Microsoft Corp."MapViewOfFileEx Function" . 2008 [1]) . et al.Microsoft Press .S."Creating STL Containers in Shared Memory" .A. 2008) Meyers.[Online] Available at: http://gcc.ISBN 81-7808-135-0 Gaztañaga . .[Online] Available at: https://svn. M. 2006) .aspx (Accessed October 7.edu/viewdoc/download?doi=10.Microsoft Developer Network Technology Group .wisc."The Ada Generic Library: Linear List Processing Packages” .R. 2003) .Placement into shared memory" GNU .gnu. .Prentice Hall . 2008) Microsoft (2008 [2]) .Computer Science Dept.aspx (Accessed November 25.Dr.193 Kath.ist.ISBN 0-13186891-8 – p.microsoft.ddj. (February 9.sourceforge. 2008) Ketema.80). 2008) Musser. (1999) .pdf (Accessed December 14."Statistical Investigation of Three Storage Allocation Algorithms" .[Online] Available at: http://msdn.80).Proceeds of the First International Conference of ISSAC-88 and AAECC-6 . (1997) ."A C++ Pooled.com/cpp/184401639 (Accessed October 7."Debug Iterator Support" .de/lenne3d/lib/boost/1. 2008) Microsoft (2008 [3]) . (March 28. 1970) ."Generic programming" . D. D.Dobb's [Online] Available at: http://www.51.85).Proceedings of the Fifth Real Time Linux Workshop . & Stepanov A."Managing Memory-Mapped Files in Win32" . (1988) .org/bugzilla/show_bug. M.microsoft. G.R. [Online] Available at: http://msdn.48-58 Microsoft (November 6.html (Accessed November 25.ISBN 1-57231-995-X Purdom. 1993) . 2008) Ronell.zib. (2003) . .Microsoft Corp. 2008) Microsoft (2008 [4]) .1. (1989) .aspx (Accessed December 18."Introduction to Generics (C# Programming Guide" .aspx (Accessed December 10.0/doc/html/interprocess. 2008) ."Effective STL" .[Online] Available at: http://msdn. C.Springer Compass International – ISBN: 0387971335 Petzold.com/en-us/library/aa985982(VS.1.com/en-us/library/aa366763(VS. & Stepanov A.Interprocess" – boost C++ libraries .1780&rep=rep1&type=pdf (Accessed December 8. Shared Memory Allocator For The Standard Template Library" .com/en-us/library/aa985965(VS.microsoft.

html (Accessed September 25.sgi."Standard Template Library Programmer's Guide .1.1.7776&rep=rep1&type=pdf (Accessed December 8.108.ISBN 0201-70073-5 53 ."The C++ Programming Language" . 2008) Stepanov A.SGI (2006) .Silicon Graphics. (1995) . 2008) Stroustrup.[Online] Available at: http://citeseerx. Inc.ist. .Introduction to the Standard Template Library" .Hewlett-Packard Company .edu/viewdoc/download?doi=10.[Online] Available at: http://www. (2000) . B.Addison Wesley . & Lee M.com/tech/stl/stl_introduction.psu."The standard template library" .Hewlett-Packard Company .

provided that the below copyright notice appears in all copies and that both the copyright notice and this permission notice appear in supporting documentation. modify. Containers offered by the Shared Containers Library are derived from the containers offered in the Standard Template Library (STL).com to inquire about possible commercial use of this library.francois. copy.Appendix A.1 Introduction to the Shared Containers Library The Shared Containers Library is a C++ library of container classes implemented in shared memory. With the Shared Containers Library. Permission to use. Copyright © 2008 François Bergeon 54 . UK (Martha McCormick Dissertation Advisor).fr/containers/doc. A. It is provided "as is" without express or implied warranty. The author makes no representations about the suitability of this software for any purpose.perso. programmers are able to instantiate STL containers and the objects they contain in a segment of shared memory accessible by concurrent processes. and distribute this software and its documentation for any non-commercial purpose is hereby granted without fee. Programmers are responsible for using the provided synchronization mechanisms to avoid access and update conflicts between concurrent processes accessing a shared container.html. The Shared Containers Library was written by François Bergeon for his dissertation for a Master of Science in Information Technology (Software Engineering) at the University of Liverpool. Please contact the author at fbergeon@gmail. SHARED CONTAINER LIBRARY PROGRAMMERS’ MANUAL (EXCERPTS) The latest version of the programmer’s documentation for the shared container library is available online at: http://bergeon.neuf.

485. 55 . an arbitrary high offset in the process space. The default value is 32 bytes. size_t _heapsize _heapsize is the size of the shared heap in bytes.2 Shared heap parameters Description Shared heap parameters need to be defined in the code in order to be present at link time. char* _heapname _heapname is an arbitrary name assigned to the shared heap. Definition These values are defined in the header heap_parameters.760 bytes or 10 Mbytes.A.h. The default value is “SHARED_CONTAINERS”. Default values are defined in the header heap_paramters. The default value is 0x01100000. size_t _chunksize _chunksize is the size of each allocation chunk in bytes. void* _baseaddress _baseaddress is the address in the process address space where the shared heap is to be mapped. The default value is 10.h that can be included in the source code or modified to suit.

3 sync Description sync is a synchronization class designed to be used as-is or derived by classes that require synchronization. … if (s. A corresponding call to write_unlock is required for each successful call to write_lock.upgrade_lock()) s. A call to upgrade_lock is functionally equivalent to a thread-safe call to read_unlock followed by a call to write_lock. If the same thread calls write_lock more than once. Downgrade from an exclusive lock to non-exclusive lock is always guaranteed.read_lock().A. The timeout parameter is rounded down to the closest increment of 10ms. Example sync s. the operation succeeds. sync offers both exclusive (write) locks and non-exclusive (read) locks. else s. An infinite timeout should never be used in a call to upgrade due to the risk of a possible race condition. Definition Defined in the header sync.write_unlock(). Upgrade from a non-exclusive lock to an exclusive lock cannot be guaranteed. Lock behavior follows the following matrix: Request\Existing lock read_lock write_lock upgrade_lock downgrade_lock is_locking_thread none success success n/a n/a false read lock success wait for release of all read locks wait for release of all other read locks n/a false write lock wait for release of write lock wait for release of write lock n/a success true if thread owns the lock For A corresponding call to read_unlock is required for each call to read_lock. s.h. A call to is_locking_thread returns true if the calling thread and process is the current owner of the exclusive lock.read_unlock(). A value of -1 is equivalent to an infinite timeout. A call to downgrade_lock is functionally equivalent to a thread-safe call to write_unlock followed by a call to read_lock. 56 . A value of less than 10 returns immediately.

0 for immediate or -1 for infinite. Returns true if lock obtained. void write_unlock() bool upgrade_lock(long timeout = 0) Release write lock Attempt to upgrade an existing non-exclusive read lock to an exclusive write lock within timeout milliseconds or 0 for immediate. Downgrade from an existing exclusive write lock to a nonexclusive read lock.Members Member bool read_lock(long timeout = -1) Description Obtain non-exclusive read lock within timeout milliseconds. the non-exclusive read lock is not released. 0 for immediate or -1 for infinite. Returns true if the lock was successfully upgraded. Release read lock Void read_unlock() bool write_lock(long timeout = -1) Obtain exclusive write lock within timeout milliseconds. Returns true if lock obtained. If the upgrade fails. void downgrade_lock() 57 .

4 shared_vector<T > Description A shared_vector is a shared memory implementation of a STL vector container. attached to and released from by calling the static methods of the SharedVectorFactory class. V->insert(V->begin(). Example shared_vector<int> *V = SharedVectorFactory<int>::create(“name”).h. shared_vector also offers synchronization methods that should be used if other processes are susceptible to access it concurrently. V->read_lock(). Public base classes sync Members All public members of the vector class and of the sync class are exposed. Template parameters Parameter T Description The shared vector's value type: the type of object that is stored in the vector. shared_vector offers the same functionalities and properties than vector. Default Type requirements Same requirements than for vector. Please refer to the STL documentation for information on the vector class. V->write_unlock(). shared_vector objects are be created. Definition Defined in the header shared_vector. assert(V->size() == 1 && V->capacity() >= 1 && *V[0] == 3). V->write_lock(). V->read_unlock(). 3).A. 58 .

Sequence > Description A shared_stack is a shared memory implementation of a STL stack adapter. S->pop(). Type requirements 59 . shared_deque<T> Default Sequence The type of the underlying container used to implement the stack.A. assert(S->size() == 3). S->push(8).5 shared_stack<T. assert(S->empty()). assert(S->top() == 7). Please refer to the STL documentation for information on the stack class. assert(S->top() == 8). S->write_unlock(). SharedStackFactory<int>::detach(“name”). Example int main() { shared_stack<int> *S = SharedStackFactory<int>::create(“name”). attached to and released from by calling the static methods of the SharedStackFactory class. S->push(7). assert(S->top() == 4). S->write_lock(). } Definition Defined in the header shared_stack. S->pop(). S->pop().h. shared_stack also offers synchronization methods that should be used before accessing the container if other processes are susceptible to access it concurrently. shared_stack objects are created. Template parameters Parameter T Description The shared deque's value type: the type of object that is stored in the deque. S->push(4). A shared_stack offers the same functionalities and properties than the stack.

Same requirements than for stack. Public base classes sync Members All public members of the stack class and of the sync class are exposed. 60 .

Please refer to the STL documentation for information on the map class. *months["november"] = 30. ltstr>::iterator next = cur. int. *months["april"] = 30. cout << "Next (in alphabetical order) is " << (*next). int main() { shared_map<const char*. cout << "Previous (in alphabetical order) is " << (*prev). } Definition 61 . ltstr>.first << endl. shared_map<const char*. --prev. *months["february"] = 28. A shared_map offers the same functionalities and properties than the map. } }. *months["july"] = 31. shared_map also offers synchronization methods that should be used before accessing the container if other processes are susceptible to access it concurrently.create(“my map”). ltstr>::iterator cur = months. months->write_lock(). int. shared_map<const char*. *months["june"] = 30. Compare> Description A shared_map is a shared memory implementation of a STL map container.find("june"). shared_map<const char*. months->read_unlock(). *months["march"] = 31. int.first << endl. int. *months["october"] = 31. *months["august"] = 31. Example struct ltstr { bool operator()(const char* s1. *months["september"] = 30. Data. s2) < 0. ltstr> *months = SharedMapFactory<const char*.6 shared_map <Key. *months["december"] = 31. *months["may"] = 31. shared_map objects are created. int. months->write_unlock(). ++next. months->read_lock(). *months["january"] = 31. attached to and released from by calling the static methods of the SharedMapFactory class. cout << "june -> " << months["june"] << endl.A. const char* s2) const { return strcmp(s1. ltstr>::iterator prev = cur.

a Strict Weak Ordering whose argument type is key_type.h. 62 .Defined in the header shared_map. The key comparison function. This is also defined as shared_map::key_compare less<Key> Default Type requirements Same requirements than for map. Public base classes sync Members All public members of the map class and of the sync class are exposed. Template parameters Parameter Key Data Compare Description The map's key type and value type. it returns true if its first argument is less than its second argument. This is also defined as shared_map::data_type. This is also defined as shared _map::key_type The map's data type. and false otherwise.

Public base classes sync Members All public members of the hash_set class and of the sync class are exposed. a Strict Weak Ordering whose argument less<Key> type is key_type. and false otherwise. Compare> Description A shared_hash_set is a shared memory implementation of a STL hash_set container. This is also defined as shared_hash_set::key_compare and shared_hash_set::value_compare.h. Please refer to the STL documentation for information on the hash_set class. Template parameters Parameter Key Description The set's key type and value type.7 shared_hash_set <Key. This is also defined as shared _hash_set::key_type and shared_hash_set::value_type Default Compare The key comparison function. Definition Defined in the header shared_hash_set. shared_hash_set objects are created. shared_hash_set also offers synchronization methods that should be used before accessing the container if other processes are susceptible to access it concurrently. 63 . it returns true if its first argument is less than its second argument.A. attached to and released from by calling the static methods of the SharedHash_setFactory class. A shared_hash_set offers the same functionalities and properties than the hash_set. Type requirements Same requirements than for hash_set.

Template parameters Parameter T Description The shared vector's value type: the type of object that is stored in the vector. . SharedVectorFactory<int>::detach(“name”). Process 2: shared_vector<int> *V = SharedVectorFactory<int>::attach(“name”). Type requirements Same requirements than for vector. const T& t) void detach(char* name) Description Attach to an existing shared_vector named name Create new shared_vector named name Copy constructor Create new shared_vector named name with n elements Create new shared_vector named name with n copies of t Detach from shared_vector named name 64 . shared_vector&) shared_vector* create(char* name.A.. attach to or detach from a shared_vector. size_t n. size_t n) shared_vector* create(char* name. Example Process 1: shared_vector<int> *V = SharedVectorFactory<int>::create(“name”). Definition Defined in the header shared_vector. Shared vectors are named entities to be identifiable by other processes.. Members Member shared_vector* attach(char* name) shared_vector* create(char* name) shared_vector* create(char* name.8 SharedVectorFactory<T > Description SharedVectorFactory is a static class used to create. Process 1: SharedVectorFactory<int>::detach(“name”).h.

h. Process 1: SharedStackFactory<int>::detach(“name”). . Example Process 1: shared_stack<int> *S = SharedStackFactory<int>::create(“name”). Sequence> Description SharedStackFactory is a static class used to create.A. SharedStackFactory<int>::detach(“name”). Definition Defined in the header shared_stack. Type requirements Same requirements than for stack. Shared stacks are named entities to be identifiable by other processes. attach to or detach from a shared_stack. shared_deque<T> Default Sequence The type of the underlying container used to implement the stack. Process 2: shared_stack<int> *S = SharedStackFactory<int>::attach(“name”). Members Member shared_stack* attach(char* name) shared_stack* create(char* name) shared_stack* create(char* name.. shared_stack&) shared_stack* create(char* name.9 SharedStackFactory<T . Description Attach to an existing shared_stack named name Create new shared_stack named name Copy constructor Create new shared_stack named 65 . Template parameters Parameter T Description The shared stack's value type: the type of object that is stored in the stack..

size_t n) shared_stack* create(char* name, size_t n, const T& t) void detach(char* name)

name with n elements Create new shared_stack named name with n copies of t Detach from shared_stack named name

66

A.10 SharedMapFactory map<Key, Data, Compare> Description
SharedMapFactory is a static class used to create, attach to or detach from a shared_map. Shared maps are named entities to be identifiable by other processes.

Example
Process 1: shared_map<std::string, int> *S = SharedMapFactory<std::string, int>::create(“name”); Process 2: shared_map<std::string, int> *S = SharedMapFactory<std::string, int>::attach(“name”); ... SharedMapFactory<std::string, int>::detach(“name”); Process 1: SharedMapFactory<std::string, int>::detach(“name”);

Definition
Defined in the header shared_map.h.

Template parameters
Parameter Key Data Description The map's key type and value type. This is also defined as shared _map::key_type and shared_map::value_type The map's data type. This is also defined as shared_map::data_type. Default

Compare The key comparison function, a Strict Weak Ordering whose argument less<Key> type is key_type; it returns true if its first argument is less than its second argument, and false otherwise. This is also defined as shared_map::key_compare and shared_map::value_compare.

Type requirements

Same requirements than for map.

Members
Member shared_map* attach(char* name) shared_map* create(char* name) Description Attach to an existing shared_map named name Create new shared_map named name

67

shared_map* create(char* name, const Creates an empty shared_map named key_compare& comp) name, using comp as the key_compare object. template <class InputIterator> shared_map* create(char* name, InputIterator f, InputIterator l) template <class InputIterator> shared_map* create(char* name, InputIterator f, InputIterator l, const key_compare& comp) shared_map* create(char* name, shared_map&) void detach(char* name) Creates a shared_map named name with a copy of a range. Creates a shared_map named name with a copy of a range, using comp as the key_compare object. Copy constructor Detach from shared_map named name

68

Compare> Description SharedHashSetFactory is a static class used to create. Example Process 1: shared_hash_set<int> *S = SharedHashSetFactory<int>::create(“name”). it returns true if its first argument is less than its second argument. Members Member shared_hash_set* attach(char* name) shared_hash_set* create(char* name) shared_hash_set* create(char* name. Type requirements Same requirements than for hash_set. and false otherwise.A. SharedHashSetFactory<int>::detach(“name”). Shared hash sets are named entities to be identifiable by other processes. a Strict Weak Ordering whose argument less<Key> type is key_type.11 SharedHashSetFactory set<Key. Process 2: shared_hash_set<int> *S = SharedHashSetFactory<int>::attach(“name”). . using comp as the 69 . Process 1: SharedHashSetFactory<int>::detach(“name”). attach to or detach from a shared_hash_set. Definition Defined in the header shared_hash_set. const key_compare& comp) Description Attach to an existing shared_hash_set named name Create new shared_hash_set named name Creates an empty shared_hash_set named name... This is also defined as shared _hash_set::key_type and shared_hash_set::value_type Default Compare The key comparison function.h. This is also defined as shared_hash_set::key_compare and shared_hash_set::value_compare. Template parameters Parameter Key Description The set's key type and value type.

Creates a shared_hash_set named name with a copy of a range and a bucket count of at least n. shared_hash_set* create(char* name. InputIterator f. const key_compare& comp) shared_hash_set* create(char* name. size_t n) template <class InputIterator> shared_hash_set* create(char* name. const key_compare& named name. Creates a shared_hash_set named name with a copy of a range and a bucket count of at least n. shared_hash_set&) void detach(char* name) Creates a shared_hash_set named name with a copy of a range. size_t n. InputIterator l) template <class InputIterator> shared_hash_set* create(char* name. using comp) comp as the key_compare object.key_compare object. Copy constructor Detach from shared_hash_set named name 70 . size_t n) Create new shared_ash_set named name with at least n buckets shared_hash_set* create(char* Creates an empty shared_hash_set name. using comp as the key_compare object. InputIterator l. template <class InputIterator> shared_hash_set* create(char* name. InputIterator f. InputIterator l. with at least n buckets. size_t n. InputIterator f.

Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include <windows. }.Appendix B. public: // Constructor .h 71 . }.Thread-safe counters * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .1 safe_counter. // Cast operator to const long inline operator const long() { return _counter. */ class safe_counter { private: // Counter is volatile and needs to be aligned // on a 32bit boundary for interlocked operations _declspec(align(32)) volatile LONG _counter.fr/containers/src.h . n). // Increment and decrement operators inline long operator++() { return InterlockedIncrement(&_counter).h> /* * Wrapper class around an interlocked counter * .no need for interlocked access at this time inline safe_counter(long n = 0) : _counter(n) {}.zip B.Class is platform dependent. }.francois. // Assignment operator inline safe_counter& operator=(long n) { InterlockedExchange(&_counter. Listing 4: safe_counter. inline long operator--() { return InterlockedDecrement(&_counter). return *this. }. SHARED CONTAINERS LIBRARY SOURCE CODE (EXCERPTS) The latest version of the source code for the shared container library is available online at: http://bergeon. }.h /* * safe_counter.neuf.perso.

safe_counter _writelock.implements a synchronization for shared containers * . */ class sync { private: // Internal read/write sempahores safe_counter _readlock. long _lockingprocessid.h" /* * sync class . 72 .Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include "safe_counter.h . long _lockingthreadid.2 sync. // Standard loop delay is 10ms static const unsigned DELAY = 10.B.Simple read/write locking mechanism * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .h /* * sync.Class is platform dependent.

Thread must already own a read lock // .time out in milliseconds. 0 for no wait // inline bool write_lock(long timeout = -1) { return write_lock(timeout. // Obtain exclusive write lock with timeout // . // Upgrade from a non-exclusive read lock to an // exclusive write lock // . 0). // Downgrade from an exclusive write lock to a non-exclusive read lock // . // Release non-exclusive read lock void read_unlock(). 0 for no wait (default) // bool upgrade_lock(long timeout = 0).More than one read lock is allowed at any single time // timeout . // Release exclusive write lock void write_unlock(). }.public: // Obtain non-exclusive read lock with timeout // . 0 for no wait // bool read_lock(long timeout = -1).time out in milliseconds.Read lock is guaranteed // void downgrade_lock().Thread must already own a write lock // . private: 73 . -1 for infinite (default). // Check if the current thread owns the write lock bool is_locking_thread(). -1 for infinite (default).No read locks and only one write lock // is allowed at any single time // timeout .If another thread is already waiting for a wite lock it will be preempted // timeout .time out in milliseconds.

More than one read lock is allowed at any single time // timeout . } }.3 sync. Listing 5: sync.cpp /* * sync.Simple read/write locking mechanism * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .h B. } inline long get_processid() { return GetCurrentProcessId().// Internal write lock with acceptable number of existing locks bool write_lock(long timeout. // Get thread and process id inline long get_threadid() { return GetCurrentThreadId().Software Engineering * * Martha McCormick Dissertation Advisor * */ #include "sync.time out in milliseconds. 1 for no delay // 74 . long existing).h" // Obtain non-exclusive read lock with timeout // . 0 for infinite.cpp .

} 75 . } // Timeout expired? if (timeout >= 0 && --n == 0) return false. } }. // Wait Sleep(DELAY)..) { // Check that no write lock has been requested if (_writelock == 0) { // Obtain read lock ++_readlock. for (. // Has no write lock been requested in the meantime? if (_writelock == 0) return true.bool sync::read_lock(long timeout) { // Obtain maximum number of iterations to run long n = (timeout / DELAY) + 1. // Release read lock and try again --_readlock. // Release non-exclusive read lock void sync::read_unlock() { // Safety if (--_readlock == -1) ++_readlock.

// Wait Sleep(DELAY).) { // Try to obtain an exclusive write lock // or preempt other threads if we are upgrading // from a non-exclusive one while (++_writelock > 1 && existing == 0) { --_writelock.// Obtain exclusive write lock with timeout // . } // Obtain maximum number of iterations to run long n = (timeout / DELAY) + 1. 0 for infinite. 1:upgrade) // bool sync::write_lock(long timeout. 1 for no delay // existing .time out in milliseconds.. // Timeout expired? if (timeout >= 0 && --n == 0) return false. for (.acceptable number of existing locks (0:normal. long existing) { // Has this thread already obtained an exclusive write lock? if (is_locking_thread()) { // Increment lock counter so that the next call to // write_unlock() does not release the lock ++_writelock.No read locks and only one write lock is allowed at any single time // timeout . return true. } // Then wait for all readers to release their non-exlusive read locks while (_readlock > existing) 76 .

} }. // Reset thread and process id if (n == 0) _lockingthreadid = _lockingprocessid = -1.{ // Timeout expired? if (timeout >= 0 && --n == 0) { // Release write lock --_writelock. } // Wait Sleep(DELAY). } // Write lock obtained. // Safety if (n == -1) ++_writelock. return false. _lockingprocessid = get_processid(). // Check if the current thread owns the write lock bool sync::is_locking_thread() { return (_writelock > 0 && _lockingthreadid == get_threadid() && _lockingprocessid == get_processid()). } // Release exclusive write lock void sync::write_unlock() { long n = --_writelock. return true. store locking thread id _lockingthreadid = get_threadid(). } 77 .

cpp 78 . // Release our read lock read_unlock().Thread must already own a read lock // .Read lock is guaranteed // void sync::downgrade_lock() { ++_readlock.An infinite timeout may lead to a running condition // timeout . write_unlock().time out in milliseconds. } // Downgrade from an exclusive write lock to a non-exclusive read lock // .If another thread is already waiting for a wite lock it will be preempted // . // Lock upgraded return true. 0 for no wait // bool sync::upgrade_lock(long timeout) { // Obtain a write lock with one existing read lock (ours) if (!write_lock(timeout. } Listing 6: sync.Thread must already own a write lock // .// Upgrade from non-exclusive read lock to exclusive write lock // . 1)) return false.

B. // 64 bytes void* _baseaddress = (void*)0x01100000.h /* * heap_parameters.h /* * shared_heap.h> // These variables can be modified to suit specific needs char* _heapname = "SHARED_CONTAINERS".5 shared_heap.Parameters for shared heap * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT . // 64Mb size_t _chunksize = 64.Software Engineering * * Martha McCormick Dissertation Advisor * */ #include <stddef.h .h .Heap implementation in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---- 79 . // Arbitrary heap name size_t _heapsize = 67108864.h B.4 heap_parameters. // Arbitrary high for debug Listing 7: heap_parameters.

implements a heap in shared memory * * This class is platform dependent. #ifdef WIN32 HANDLE _mapping.h> #endif /* * shared_heap class . // Forward class declaration // Class members shared_header* _header. #endif // Pointer to shared header // Address of shared heap // Last error caught // Handle of Win32 mapping object // Standard loop delay is 20ms static const unsigned DELAY = 20. 80 . void* _heap.* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT . long _lasterror.Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #ifdef WIN32 #include <windows. * */ class shared_heap { private: class shared_header.

h B. size_t heapsize = 0x100000. }. #ifdef _DEBUG void dump().cpp /* * shared_heap.public: // Public methods shared_heap(const char* heapname.Heap implementation in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a 81 . void*** ptrparam). void deallocate(void* head. ~shared_heap(void). void lock(). size_t size). void unlock(). void* allocate(size_t size). Listing 8: shared_heap. #endif private: // Private methods void cleanup().cpp .6 shared_heap. size_t chunksize = 0x1000. void get_params(long** longparam. void* baseaddress = NULL).

b) ((a)%(b)==0?(a):((((a)/(b))+1)*(b))) // // The shared header is located at the beginning of the shared memory mapped segment // All processes accessing shared containers rely on the shared header to map // shared memory to the same virtualized memory space and to access existing containers // __declspec(align(32)) volatile class shared_heap::shared_header { public: char _signature[sizeof(SHAREDHEAP_SIGNATURE)]. // Size of heap and chunk size 82 .h" #include "safe_counter. * It uses WIN32 primitives and is platform dependent. // Common base address to be used by all processes void* _head.Software Engineering * * Martha McCormick Dissertation Advisor * * This class implements a heap of segregated storage in shared memory.* Master of Science in IT . * */ #include "shared_heap. // Pointer to first available chunk size_t _heapsize. // Signature that identifies view as a shared heap void* _baseaddress. _chunksize.h" #ifndef BYTE #define BYTE unsigned char #endif // The signature is used to confirm that an existing memory // mapping has been created by this same code #define SHAREDHEAP_SIGNATURE "**FBSH**" // Indicator of last chunk in chunk list #define LAST_CHUNK ((LPVOID)~0) // Macro to roundup value #define ROUNDUP(a.

GetSystemInfo(&SystemInfo). // // // // // Client long parameter Client pointer parameter Number of processes currently accessing shared memory 1 if allocation in progress.size of shared heap in bytes (defaults to 0x100000 = 1Mb) // chnksize . MAX_PATH). // Initialize members _lasterror = 0. // Size of shared header is rounded up to proper allocation granularity SYSTEM_INFO SystemInfo. void* baseaddress) { // Flag for creation of new shared heap BOOL bCreateNew = FALSE. }.name of shared heap // heapsize . size_t heapsize. // Buffer for temp filename and wide char heap name TCHAR szTmpFile[MAX_PATH]. 0. size_t chunksize. TCHAR szHeapName[MAX_PATH]. safe_counter _allocating.dwAllocationGranularity). heapname. _header = NULL. char _filename[MAX_PATH]. szHeapName. _heap = NULL.address of shared mempory mapping of NULL if default // shared_heap::shared_heap(const char* heapname. // Convert heap name to wide char for Win32 API calls MultiByteToWideChar(CP_ACP.long _longparam. 0 otherwise Temp filename // // Shared heap constructor // // heapname .size of segregated storage chunks (defauts to 0x1000 = 4Kb) // baseaddress . SystemInfo. _mapping = NULL. safe_counter _numprocesses. -1. DWORD dwHeaderSize = ROUNDUP(sizeof(shared_header). void* _ptrparam. 83 .

szTmpPath). FILE_ATTRIBUTE_TEMPORARY. szHeapName. GetTempFileName(szTmpPath. FILE_SHARE_WRITE. // Get temp dir & file GetTempPath(MAX_PATH-14. // // // // // // // Temp file name RW Access Share mode Optional security attributes Create file Temporary file Flags & attributes 84 . NULL. // Check requested heap size if (heapsize == NULL) { SetLastError(ERROR_BAD_LENGTH).try { // Open file mapping _mapping = OpenFileMapping(FILE_MAP_WRITE. // Name of file mapping object // Obtain last system error _lasterror = GetLastError(). // Obtain last system error _lasterror = GetLastError(). // Create temp file HANDLE hFile = CreateFile(szTmpFile. throw. szTmpFile). 0. CREATE_ALWAYS. GENERIC_WRITE | GENERIC_READ. // Handle cannot be inherited szHeapName). attempt to create temp file if (_mapping == NULL && _lasterror == ERROR_FILE_NOT_FOUND) { // Create file mapping bCreateNew = TRUE. // Unable to open file mapping. } // Create temporary file for mapping TCHAR szTmpPath[MAX_PATH-14]. // RW access FALSE. NULL).

// Header size NULL). dwSizeHigh. // Hi dword // Low dword // // // // // // Handle to temp file Optional security attributes Page protection Hi-order DWORD of size Low-order DWORD of size Name of file mapping object 85 . // Close underlying temp file CloseHandle(hFile). NULL. // Any base address // Unable to map view of file: cleanup & exit if (_header == NULL) { _lasterror = GetLastError(). // RW access 0. // Obtain last system error _lasterror = GetLastError(). // Create new file mapping object _mapping = CreateFileMapping(hFile. } // Exit function if OpenFileMapping or CreateFileMapping failed if (_mapping == NULL) throw. // File mapping object FILE_MAP_WRITE. // Add header size to desired size __int64 qwMaxSize = heapsize + dwHeaderSize. // Offset 0 dwHeaderSize. DWORD dwSizeLow = (qwMaxSize & 0x00000000FFFFFFFF). szHeapName). // Map view of file to obtain/create header _header = (shared_header *)MapViewOfFileEx(_mapping. 0.// Exit if we could not create temp file if (hFile == INVALID_HANDLE_VALUE) throw. dwSizeLow. throw. PAGE_READWRITE. DWORD dwSizeHigh = (qwMaxSize & 0xFFFFFFFF00000000) >> 32.

sizeof(void**)). NULL. // FILE_MAP_WRITE. // baseaddress). // Convert & store temp filename // Initialize segregated storage deallocate(_heap. if (_heap == NULL) throw. throw. // 0. SHAREDHEAP_SIGNATURE) != 0) { _lasterror = ERROR_BAD_FORMAT. // No allocation currently in progress WideCharToMultiByte(CP_ACP. NULL). // Chunk size _header->_longparam = 0. // Insert signature last to prevent race conditions with other processes strcpy_s(_header->_signature. -1. // Set client LPVOID parameter _header->_numprocesses = 1. MAX_PATH. // Currently 1 process _header->_allocating = 0. szTmpFile. // Heap size _header->_chunksize = max(chunksize. // 0. // Storage not initialized _header->_heapsize = heapsize.} if (bCreateNew) { // Map view of file to target base address _heap = MapViewOfFileEx(_mapping. SHAREDHEAP_SIGNATURE). // Set client DWORD parameter _header->_ptrparam = NULL. heapsize). dwHeaderSize. // File mapping object RW access Offset (must be aligned to allocation granularity) Full size Target base address or NULL // Save last error if failed _lasterror = GetLastError(). 0. _header->_filename. // Signature } else { // Check signature or fail if (strcmp(_header->_signature. // Now common base address _header->_head = LAST_CHUNK. 86 . sizeof(SHAREDHEAP_SIGNATURE). // Populate header _header->_baseaddress = _heap.

// Offset (must be aligned to allocation granularity) 0.. // File mapping object FILE_MAP_WRITE.) { // Cleanup cleanup(). if (_heap == NULL) throw. dwHeaderSize. // Re-set error code SetLastError(_lasterror). // RW access 0.. // Common base address // Save last error if failed _lasterror = GetLastError(). } // Map view of file to target base address _heap = MapViewOfFileEx(_mapping. } // // Cleanup is called by destructor or 87 . throw.} // Increment number of client processes if (++_header->_numprocesses == 1) { // A process is currently closing the shared memory _lasterror = ERROR_FILE_NOT_FOUND. // Full size _header->_baseaddress). } } // Standard destructor shared_heap::~shared_heap(void) { cleanup(). } } catch (.

// Unmap header if (_header != NULL) { // Are we the last process to use this shared memory file? if (--_header->_numprocesses == 0) { // If so.// if an exception is caught in the constructor // void shared_heap::cleanup() { // Buffer for temp filename TCHAR szTmpFile[MAX_PATH]. // Initialize as an empty string szTmpFile[0] = '\0'. // Reset member variables _heap = NULL. } // Close file mapping if (_mapping != NULL) CloseHandle(_mapping). // Delete temp file if we are the last user process 88 . convert temp filename so we can delete it MultiByteToWideChar(CP_ACP. _mapping = NULL. 0. // Unmap heap if (_heap != NULL) UnmapViewOfFile(_heap). _header = NULL. MAX_PATH). } // Unmap header UnmapViewOfFile((LPVOID)_header). _header->_filename. -1. szTmpFile.

// Return former head chunk unlock(). } // // Allocate memory // // size . _header->_chunksize).number of bytes to allocate // void* shared_heap::allocate(size_t size) { // Synchronization lock(). // Special case: allocation of a single chunk if (size == _header->_chunksize) { // Obtain head chunk void** p = (void**)_header->_head. return (void*)p. // Out of memory? if (_header->_head == LAST_CHUNK) { unlock(). } // Number of contiguous chunks to allocate if > 1 89 . } // Roundup allocation size to a multiple of chunk size size = ROUNDUP(size. // Move head to point to next chunk _header->_head = *p.if (szTmpFile[0] != '\0') DeleteFile(szTmpFile). return NULL.

// Index pointer to chunks unsigned n. } // // Deallocate buffer . n = 1. p = (void**)*p. return head. return NULL. // Start of contiguous block void** previous = &_header->_head. ++n) { // Return if we found enough contiguous chunks if (n == chunks) { // Set chunk before block to point to next chunk *previous = *p. // Stop when we reach the end of the heap } while (head != LAST_CHUNK). // Start at head chunk and look for contiguous block of chunks void* head = _header->_head.can also be used to initialize segregated 90 . head = *p. // Return start of chunk block unlock().unsigned chunks = size / _header->_chunksize. allocation failed unlock(). } } // Continue searching from next chunk forward previous = p. // Counter for contiguous chunks // Loop through contiguous chunks for (p = (void**)head. // Pointer to previous chunk // Loop do { // Loop variables void** p. *p == ((BYTE*)p + _header->_chunksize). // No contiguous chunks found.

// Calculate address of last chunk to be deallocated void** toe = (void**)((BYTE*)head + size . // Store address of new previous chunk previous = (void**)p. p <= toe. // Roundup size to an integer number of chunks size = ROUNDUP(size. } // Make last chunk point to previous head *toe = _header->_head. // Address of previous chunk // Index pointer to chunks // Go through all chunks from head to toe for (p = head. size_t size) { // Synchronization lock().pointer to buffer // size . void* p. } // 91 . _header->_chunksize).// storage for the entire heap // // head . unlock()._header->_chunksize). // Make head point to first chunk _header->_head = head. p = ((BYTE*)p + _header->_chunksize)) { // Update previous chunk so it points to current chunk if (previous != NULL) *previous = p. // Loop variables void** previous = NULL.buffer size // void shared_heap::deallocate(void* head.

void*** ptrparam) { *longparam = &_header->_longparam.cpp 92 . *ptrparam = &_header->_ptrparam. } Listing 9: shared_heap. Sleep(DELAY).pointer to pointer parameter // void shared_heap::get_params(long** longparam. } } } // Unlock shared heap void shared_heap::unlock() { if (_header != NULL) --_header->_allocating.// Obtain client parameters from shared header // // longparam . } // Lock shared heap.pointer to long parameter // ptrparam . Wait if busy. No timeout void shared_heap::lock() { if (_header != NULL) { while (++_header->_allocating > 1) { if (--_header->_allocating == 0) continue.

B.Object allocation in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .h" "sync.implements the map class with the shared_allocator template parameter */ template<class Key. Data>>> {}.h /* * shared_pool. class Data. public std::map<Key. /* * Shared pool class . shared_allocator<std::pair<const Key.h" "shared_allocator. Data.Provides singleton allocation interface * to shared heap and manages map of shared objects * */ class shared_pool 93 .Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include #include #include #include #include <map> <string> "shared_heap.7 shared_pool.derives from the map and sync classes * . Compare.h .h" /* * shared_map class * . class Compare=std::less<Key>> class shared_map : public sync.

static void* retrieve(char *name. void* object.cpp . size_t size). // Shared heap and map of shared objects static shared_heap* _heap. private: // Lazy initialization static void init(). public: // Allocator interface to shared heap static void* allocate(size_t size). // Insert. retrieve and release objects in shared map static bool insert(char *name. // Map of objects stored in shared memory typedef shared_map<std::string.{ private: // shared_object class describes each shared objects class shared_object. static void* release(char *name).8 shared_pool.cpp /* * shared_pool. shared_object*> container_map.h B. static container_map* _containermap.Object allocation in shared memory * 94 . static size_t max_size(). static void deallocate(void* buffer. }. Listing 10: shared_pool. size_t size). size_t size).

// Arbitrary heap name extern size_t _heapsize. // Arbitrary high for debug // Class for shared object stored in container map class shared_pool::shared_object { public: void* _object. // 10Mb extern size_t _chunksize.* * * * * * * * * * */ ------ (c) Francois Bergeon 2008 University of Liverpool ------ IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY A dissertation for the completion of a Master of Science in IT .h" // External parameters used to construct a shared heap // These variables can be modified to suit specific needs extern char* _heapname. // Pointer to object size_t _size. 95 . // Size of object safe_counter _refcount. size_t size): _object(object). _size(size). // 32 bytes extern void* _baseaddress.Software Engineering Martha McCormick Dissertation Advisor // Force Microsoft VC++ 6 to turn off iterator checking #ifdef WIN32 #define _HAS_ITERATOR_DEBUGGING 0 #define _SECURE_SCL 0 #endif #include "shared_pool. // Usage count shared_object(void* object. _refcount(1) {}.

shared_object() {}. shared_pool::container_map* shared_pool::_containermap = NULL. // Deallocate buffer _heap->deallocate(buffer. 96 . // // Allocate buffer from shared heap // // size . // Initialize static members shared_heap* shared_pool::_heap = NULL. size_t size) { // Shared heap must be initialized if (_heap == NULL) return. size). }.pointer to buffer // size . ~shared_object() {}.number of bytes to allocate // void* shared_pool::allocate(size_t size) { // Perform lazy intialization if shared heap is not initialized if (_heap == NULL) init(). // Allocate memory and return return _heap->allocate(size). } // // Deallocate buffer // // head .buffer size // void shared_pool::deallocate(void* buffer.

void* object. return false.size of object // bool shared_pool::insert(char *name.}.pointer to object // size .obtain non-exclusive lock first // Return failure if a container with the same name is already present in the shared container map std::string s(name). _containermap->read_lock(). if (_containermap->find(s) != _containermap->end()) { // Release read lock and return failure _containermap->read_unlock(). // // Return heap size // size_t shared_pool::max_size() { return _heapsize. } // // Insert shared object in container map // // name .name of object // object . // Look for entry . } // Allocate and build new shared object void* p = allocate(sizeof(shared_object)). if (p == NULL) 97 . size_t size) { // Perform lazy intialization if shared heap is not initialized if (_heap == NULL) init().

} // // Retrieve shared object from container map // 98 . sizeof(shared_object)). deallocate(p. return false. // Upgrade to an exclusive lock if (!_containermap->upgrade_lock()) { // If upgrade failed we have to do it manually // First release the read lock . _containermap->write_unlock(). } shared_object* so = new(p) shared_object(object.{ // Release read lock and return failure _containermap->read_unlock(). return failure _containermap->write_unlock(). size). } } // Insert shared object and release lock (*_containermap)[s] = so. return false. // Check for name again in case it was inserted concurrently if (_containermap->find(s) != _containermap->end()) { // Release write lock and deallocate object.this may give another process a write lock _containermap->read_unlock(). // Then obtain a write lock the old fashioned way _containermap->write_lock(). // Return success return true.

obtain non-exclusive lock _containermap->read_lock(). return NULL. std::string s(name). // Increment refcount and return pointer to object ++so->_refcount. container_map::iterator it = _containermap->find(s). If refcount reaches zero. 99 . // Look for entry . } // // Release object by decrementing its refcount.size of object // void* shared_pool::retrieve(char *name. // Check shared object and return NULL if size does not match if (so == NULL || so->_size != size) return NULL. _containermap->read_unlock().name of object // size .// name . size_t size) { // Perform lazy intialization if shared heap is not initialized if (_heap == NULL) init(). return so->_object. // Return NULL if no container with that name is present in the shared container map if (it == _containermap->end()) { // Release read lock and return failure _containermap->read_unlock(). } // Obtain pointer to shared object and release read lock shared_object* so = it->second.

} // Obtain pointer to shared object and decrement refcount shared_object* so = it->second.// return pointer to object so its destructor can be called. } // // refcount == 0 .name of object // void* shared_pool::release(char *name) { // Shared heap must be initialized if (_heap == NULL) return NULL. // Return NULL if no container with that name is present in the shared container map if (it == _containermap->end()) { // Release read lock and return failure _containermap->read_unlock(). _containermap->read_lock(). // Look for entry . container_map::iterator it = _containermap->find(s). if (so == NULL || --so->_refcount > 0) { // Release read lock and return NULL if object still in use _containermap->read_unlock().discard object // // Retain pointer to object 100 . return NULL. return NULL. // // name .obtain non-exclusive lock first std::string s(name).

} // // Lazy initialization of shared heap // void shared_pool::init() { // Pointers to shared client parameters 101 .this may give another process a write lock _containermap->read_unlock(). // Upgrade to an exclusive lock if (!_containermap->upgrade_lock()) { // If upgrade failed we have to do it manually // First release the read lock . _containermap->write_unlock(). // Return pointer to object for destruction return p.void* p = so->_object. } } // Erase map entry and release lock _containermap->erase(it). // Get shared object again in case it was removed or attached concurrently if (_containermap->find(s) == _containermap->end() || so->_refcount > 0) { // Return NULL if object not found or still in use _containermap->write_unlock(). // Then obtain a write lock the old fashioned way _containermap->write_lock(). sizeof(shared_object)). // Deallocate object deallocate(so. return NULL.

_chunksize.h /* * shared_allocator. &ptrparam). // Obtain pointers to client parameters in shared header _heap->get_params(&longparam. // Create container map if not initialized if (*ptrparam == NULL) { _containermap = new(_heap->allocate(sizeof(container_map))) container_map. } } Listing 11: shared_pool. _baseaddress). // Build new shared heap from external parameters _heap = new shared_heap(_heapname.h .long* longparam.9 shared_allocator. // Store pointer to map in pParam *ptrparam = _containermap.cpp B. _heapsize.STL allocator in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY 102 . } else { // Or attach to existing container map _containermap = (container_map*)*ptrparam. void** ptrparam.

const void* hint=0) { return(static_cast<pointer> (shared_pool::allocate(n*sizeof(T)))). typedef const T& const_reference. typedef T* pointer. typedef ptrdiff_t difference_type. pointer address(reference r) const { return &r. typedef T& reference. // space for n Ts pointer allocate(size_t n. } 103 .* * A dissertation for the completion of a * Master of Science in IT . ~shared_allocator() throw() {}.Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include "shared_pool. template<class T> class shared_allocator { public: typedef T value_type. typedef unsigned int size_type.h" class shared_pool.} shared_allocator() throw() {}. } const_pointer address(const_reference r) const {return &r. template<class U> shared_allocator(const shared_allocator<U>& t) throw() {}. typedef const T* const_pointer.

n*sizeof(T)). don't destroy void deallocate(pointer p. template<class T> bool operator ==(const shared_allocator<T>& a. } template<class T> bool operator !=(const shared_allocator<T>& a. }. return. const T& val) { new(p) T(val).// deallocate n Ts. } // initialize *p by val void construct(pointer p. } Listing 12: shared_allocator. } size_type max_size() const throw() { return shared_pool::max_size(). size_type n) { shared_pool::deallocate((LPVOID)p. }. } // destroy *p but don't deallocate void destroy(pointer p) { p->~T(). const shared_allocator<T>& b) throw() { return(TRUE). const shared_allocator<T>& b) throw() { return(FALSE). } template<class U> struct rebind { typedef shared_allocator<U> other.h 104 .

Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include "shared_pool.Used to create new shared containers or attach to existing ones.this type is available to all derived classes // Create new shared container and assign specified name // name . */ template<class C> class _SharedContainerFactory { public: typedef C container_type.parameterized abstract factory class * .Class is meant to be derived into specific shared container factory classes. * .h /* * containers_factories. // Container's type .name of shared container // static container_type* create(char* name) 105 . * .Methods of this class are static and call static methods of shared_pool.10 container_factories.h .STL containers in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .h" // Placement new in the shared pool #define SHARED_NEW new(shared_pool::allocate(sizeof(container_type))) /* * SharedContainerFactory .B.

// Detach from shared container .name of shared container // cont .name of shared container // static void detach(char* name) { // Detach from container in sahred pool container_type* c = (container_type*)shared_pool::release(name). }. }. c). // shared_pool returns pointer to the container if we are the last user. }. // Add container to shared pool and return return insert_container(name. // Attach to existing shared container by name // name . // Create new shared container with copy constructor // name .release if last user // name . const container_type& cont) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(cont).container to copy from // static container_type* create(char* name.name of shared container // static container_type *attach(char* name) { // Retrieve container from shared pool return (container_type*)shared_pool::retrieve(name.{ // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(). // Add container to shared pool and return return insert_container(name. sizeof(container_type)). c). NULL otherwise if (c != NULL) 106 .

return NULL.name of shared container // c . } // Returned newly created container return c.destroy_container(c). // Deallocate memory used by shared container object shared_pool::deallocate(c. /* * SharedSequenceContainerFactory . container_type *c) { // Add container to shared pool .pointer to shared container // static container_type* insert_container(char* name.Implements the pre-allocation and replication constructors offered * by sequence containers. c.Specialization of the SharedContainerFactory class for sequence containers.parameterized abstract factory class * . * . sizeof(container_type))) { destroy_container(c). 107 .pointer to shared container // static void destroy_container(container_type* c) { // Call destructor on shared container c->~container_type(). } // Destroy container and deallocate // c . }. sizeof(container_type)). }. } protected: // Attempt to insert container in shared pool // name .destroy it and return NULL if insertion failed if (!shared_pool::insert(name.

c).number of objects to replicate // t . // Container's value type // Container's size_type // Redefinition of base class static create methods static container_type* create(char* name) { return _SharedContainerFactory<container_type>::create(name).*/ template<class C> class _SharedSequenceContainerFactory : public _SharedContainerFactory<C> { public: typedef typename container_type::value_type value_type.name of shared container // n . static container_type* create(char* name. }. // Add container to shared pool and return return insert_container(name. c).name of shared container // n . value_type& t) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(n. c). size_type n. typedef typename container_type::size_type size_type. size_type n) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(n). // Create new shared container with replication // name . // Add container to shared pool and return return insert_container(name. }. // Create new shared container with pre-allocation // name . 108 .number of objects to pre-allocate // static container_type* create(char* name. }.object to replicate // static container_type* create(char* name. const container_type& c) { return _SharedContainerFactory<container_type>::create(name. t).

parameterized abstract factory class * .last iterator 109 .Implements constructors with copy of range and specified key_compare function * offered by associative containers.name of shared container // comp .Specialization of the SharedContainerFactory class for associative containers. */ template<class C> class _SharedAssociativeContainerFactory : public _SharedContainerFactory<C> { public: typedef typename container_type::key_compare key_compare.key_compare object // static container_type* create(char* name. }. /* * SharedAssociativeContainerFactory .name of shared container // f . // Create new shared container with key_compare object // name . static container_type* create(char* name. }. const key_compare& comp) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(comp).}. } // Create new shared container with copy of range // name . // Container's key_compare function // Redefinition of base class static create methods static container_type* create(char* name) { return _SharedContainerFactory<container_type>::create(name). * . }. container_type& c) { return _SharedContainerFactory<container_type>::create(name. // Add container to shared pool and return return insert_container(name.first iterator // l . c). c).

InputIterator f. // Create new shared container with copy of range and key_compare object // name . InputIterator f. */ template<class C> class _SharedHashContainerFactory : public _SharedContainerFactory<C> { 110 . * hash and key_equal functions offered by hash-based associative containers. l).Implements constructors with copy of range and specified number of buckets. }.parameterized abstract factory class * .key_compare object // template<class InputIterator> static container_type* create(char* name.name of shared container // f .Specialization of the SharedContainerFactory class for hash-based associative containers. * . c).first iterator // l . c).// template<class InputIterator> static container_type* create(char* name.last iterator // comp . l. /* * SharedHashContainerFactory . InputIterator l) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type<InputIterator>(f. // Add container to shared pool and return return insert_container(name. InputIterator l. const key_compare& comp) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type<InputIterator>(f. comp). }. // Add container to shared pool and return return insert_container(name. }.

}. // Add container to shared pool and return return insert_container(name. // Container's size_type // Container's hash function // Redefinition of base class static create methods static container_type* create(char* name) { return _SharedContainerFactory<container_type>::create(name). // Create new shared container with a number of buckets // name . const key_compare& comp) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(n. c). } // Create new shared container with a number of buckets and a hash function // name . c). comp).key comparison function // static container_type* create(char* name. container_type& c) { return _SharedContainerFactory<container_type>::create(name. static container_type* create(char* name. size_type n) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type(n).number of buckets // comp .name of shared container // n . c). typedef typename container_type::key_compare key_compare.name of shared container // n .public: typedef typename container_type::size_type size_type. // Add container to shared pool and return return insert_container(name. } // Create new shared container with copy of range // name .number of buckets // static container_type* create(char* name. }. size_type n.name of shared container 111 .

// f - first iterator // l - last iterator // template<class InputIterator> static container_type* create(char* name, InputIterator f, InputIterator l) { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type<InputIterator>(f, l); // Add container to shared pool and return return insert_container(name, c); }; // Create new shared container with copy of range and number of buckets // name - name of shared container // f - first iterator // l - last iterator // n - number of buckets // template<class InputIterator> static container_type* create(char* name, InputIterator f, InputIterator l, { // Allocate new container in shared memory container_type* c = SHARED_NEW container_type<InputIterator>(f, l, n); // Add container to shared pool and return return insert_container(name, c); }; // Create new shared container with copy of range, number of buckets // and hash function // name - name of shared container // f - first iterator // l - last iterator // n - number of buckets // comp - key comparison function // template<class InputIterator> static container_type* create(char* name, InputIterator f, InputIterator l, size_type n, const key_compare& comp)

size_type n)

112

{ // Allocate new container in shared memory container_type* c = SHARED_NEW container_type<InputIterator>(f, l, n, comp); // Add container to shared pool and return return insert_container(name, c); }; }; Listing 13: container_factories.h

B.11 shared_deque.h
/* * shared_deque.h - STL containers in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT - Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include <deque> #include "container_factories.h" /* * shared_deque class * - derives from the deque and sync classes

113

* - implements the deque class with the shared_allocator template parameter */ template<class T> class shared_deque : public sync, public std::deque<T, shared_allocator<T>> {};

/* * ShareddequeFactory - factory class to create shared deques * - Used to create new shared deques or attach to existing ones. * - Derives from the _SharedSequenceContainerFactory to implement * pre-allocation and replication constructors */ template<class T> class SharedDequeFactory : public _SharedSequenceContainerFactory<shared_deque<T>> {};

Listing 14: shared_deque.h

B.12 shared_stack.h
/* * shared_stack.h - STL containers in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT - Software Engineering * * Martha McCormick Dissertation Advisor * */

114

Default sequence class is shared_deque */ template<class T. class Sequence=shared_deque<T>> class SharedStackFactory : public _SharedSequenceContainerFactory<shared_stack<T.factory class to create shared stacks * . /* * SharedStackFactory .derives from the stack and sync classes * .#pragma once #include <stack> #include "shared_deque. * . public std::stack<T.Used to create new shared stacks or attach to existing ones. Listing 15: shared_stack.h 115 .h" /* * shared_stack class * .Derives from the _SharedSequenceContainerFactory to implement * pre-allocation and replication constructors * . class Sequence=shared_deque<T>> class shared_stack : public sync. Sequence>> {}. Sequence> {}.default sequence class is shared_deque */ template<class T.

B.h /* * shared_set.STL containers in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT . shared_allocator<Key>> {}.implements the set class with the shared_allocator template parameter */ template<class Key. public std::set<Key.13 shared_set.implements the multiset class with the shared_allocator template parameter */ 116 .h" /* * shared_set class * .h . /* * shared_multiset class * .derives from the set and sync classes * . Compare.Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include <set> #include "container_factories.derives from the multiset and sync classes * . class Compare=std::less<Key>> class shared_set : public sync.

/* * SharedSetFactory .h B. Compare.h . */ template<class Key.Derives from the _SharedAssociativeContainerFactory to implement * constructors with copy of range and specified key_compare function * offered by associative containers. Listing 16: shared_set.factory class to create shared sets * . public std::multiset<Key.14 shared_hash_map. class Compare=std::less<Key>> class shared_multiset : public sync.Used to create new shared sets or attach to existing ones.template<class Key. Compare>> {}. * .factory class to create shared multisets * . shared_allocator<Key>> {}.Derives from the _SharedAssociativeContainerFactory to implement * constructors with copy of range and specified key_compare function * offered by associative containers.h /* * shared_hash_map. */ template<class Key. Compare>> {}.STL containers in shared memory * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* 117 . class Compare=std::less<Key>> class SharedSetFactory : public _SharedAssociativeContainerFactory<shared_set<Key. * .Used to create new shared multisets or attach to existing ones. /* * SharedMultisetFactory . class Compare=std::less<Key>> class SharedMultisetFactory : public _SharedAssociativeContainerFactory<shared_multiset<Key.

factory class to create shared hash maps * . Data. * . Data>>> {}.* IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .implements the hash_map class with the shared_allocator template parameter */ template<class Key. class Compare=stdext::hash_compare<Key. Compare. public stdext::hash_map<Key. std::less<Key>>> class shared_hash_multimap : public sync. std::less<Key>>> class shared_hash_map : public sync. class Compare=stdext::hash_compare<Key.h" /* * shared_hash_map class * . public stdext::hash_multimap<Key. Compare. /* * shared_hash_multimap class * . shared_allocator<std::pair<const Key.Software Engineering * * Martha McCormick Dissertation Advisor * */ #pragma once #include <hash_map> #include "container_factories. Data. /* * SharedHashMapFactory . class Data.Derives from the _SharedHashContainerFactory to implement * constructors with copy of range and specified key_compare function * offered by associative containers. shared_allocator<std::pair<const Key.derives from the hash_map and sync classes * .derives from the hash_multimap and sync classes * . Data>>> {}. class Data.implements the hash_multimap class with the shared_allocator template parameter */ template<class Key. 118 .Used to create new shared hash maps or attach to existing ones.

* . Compare>> {}. Listing 17: shared_hash_map.Derives from the _SharedHashContainerFactory to implement * constructors with copy of range and specified key_compare function * offered by associative containers. Compare>> {}.*/ template<class Key.Used to create new shared hash multimaps or attach to existing ones. class Data. */ template<class Key.h 119 . class Compare=stdext::hash_compare<Key. Data. std::less<Key>>> class SharedHashMapFactory : public _SharedHashContainerFactory<shared_hash_map<Key. class Data. std::less<Key>>> class SharedHashMultimapFactory : public _SharedHashContainerFactory<shared_hash_multimap<Key.factory class to create shared hash multimaps * . Data. /* * SharedHashMultimapFactory . class Compare=stdext::hash_compare<Key.

PERFORMANCE EVALUATION RESULTS Table 3 presents the raw performance evaluation results discussed above (averages are in bold).int> 1.000.000.Appendix C.000 non-repetitive elements 394 0 1512 1107 187 0 1777 1306 0 394 0 1657 1261 194 0 1729 1307 0 394 0 1754 1301 198 0 1790 1317 0 376 0 1758 1327 194 0 1734 1314 0 407 0 1736 1307 193 0 1730 1341 0 393 0 1683 1261 193 0 1752 1317 0 Table 3: Raw performance evaluation results create 1 2 3 4 5 6 7 8 9 10 11 12 13 Avg 1 2 3 4 5 Avg 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 120 . Local memory populate verify 17 12 12 12 12 12 14 12 12 12 13 12 13 13 1670 1685 1726 1702 1713 1699 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1091 1091 1091 1123 1138 1107 Shared memory (create) Shared memory (attach) erase create populate verify erase attach populate verify detach vector<int> 1.000 non-repetitive elements 0 0 17 1 0 0 14 1 0 0 0 21 1 0 0 18 1 0 0 0 18 1 0 0 15 1 0 0 0 22 1 0 0 21 1 0 0 0 29 1 0 0 4 1 0 0 0 5 1 0 0 31 1 0 0 0 5 1 0 0 5 1 0 0 0 40 1 0 0 40 1 0 0 0 5 1 0 0 4 1 0 0 0 4 1 0 0 4 1 0 0 0 5 1 0 0 5 1 0 0 0 57 1 0 0 60 1 0 0 0 4 1 0 0 5 1 0 0 0 18 1 0 0 17 1 0 map<string.

fr/containers/test. _s = SharedVectorFactory<int>::create(_name).perso.Appendix D. TEST SCAFFOLDING SOURCE CODE The latest version of the shared container library test scaffolding is available online at: http://bergeon.cpp /* * VectorTest. } // Map to a STL vector for testing _c = (std::vector<int>*)_s. } // Error checking 121 .1 VectorTest.cpp . _c = new std::vector<int>.francois.h" // Shared container global name static char* _name = "my vector". // Create/attach vector double VectorTest::create() { // In shared memory if (_shared) { // Instantiate a shared_vector<int> object start_timer(). _s = SharedVectorFactory<int>::attach(_name). } // In local memory else { // Instantiate a vector<int> object start_timer(). // Attempt attach if creation failed if (_s == NULL) { start_timer().neuf.Software Engineering * * Martha McCormick Dissertation Advisor * * This class implements a vector test * */ #include "VectorTest.Test scaffolding vector test * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT .zip D.

0. i < ITER. return end_timer(). } // Verify contents double VectorTest::verify() { int i. // Exclusive lock if (_shared) _s->write_lock(). } // Populate vector double VectorTest::populate() { int i. return -1. i < ITER. // Verify for (i = 0. else _c->push_back(n). if (_shared) _c->push_back(n). } } // Release non-exclusive locks if (_shared) _s->read_unlock(). i++) { // ni = n(i-1) + i n += i. int n = 0. // Non-exclusive lock if (_shared) _s->read_lock(). // Start start_timer().if (_c == NULL) return -1. return end_timer(). // Fail if no match if ((*_c)[i] != n) { if (_shared) _s->read_unlock(). // Populate for (i = 0. i++) { // ni = n(i-1) + i n += i. return end_timer(). } // Release exclusive lock if (_shared) _s->write_unlock(). } 122 . // Start start_timer(). int n = 0.0.

// Destroy/detach vector double VectorTest::destroy() { start_timer().Software Engineering * * Martha McCormick Dissertation Advisor * * This class implements a vector test * */ #include "MapTest.cpp D. return end_timer(). _s = SharedMapFactory<std::string.int> object start_timer(). _s = SharedMapFactory<std::string.int>*)_s. if (_s == NULL) { start_timer(). } Listing 18: VectorTest.2 MapTest.int>::create(_name).int> object 123 . } else { // Instantiate a map<string.h" // Shared container global name static char* _name = "my map".cpp .Test scaffolding map test * * --(c) Francois Bergeon 2008 --* ---University of Liverpool ---* * IMPLEMENTING CONTAINER CLASSES IN SHARED MEMORY * * A dissertation for the completion of a * Master of Science in IT . if (_shared) SharedVectorFactory<int>::detach(_name). // Create/attach map double MapTest::create() { // In shared memory if (_shared) { // Instantiate a shared_map<string.cpp /* * MapTest. } // Map to a STL map for testing _c = (std::map<std::string.int>::attach(_name). else delete _c.

BUFSIZ. i++) { // “Char(‘A’ + i%26) & i” char c = 'A' + (i%26). } // Verify contents double MapTest::verify() { int i. n += i. std::string str. for (i = 0. "%c%d". str = std::string(buffer). int n = 0. char buffer[BUFSIZ]. } // Release exclusive lock if (_shared) _s->write_unlock(). sprintf_s(buffer. // Start start_timer(). i). str = std::string(buffer). else (*_c)[str] = n. } // Populate map double MapTest::populate() { int i. i < ITER. i++) { // “Char(‘A’ + i%26) & i” char c = 'A' + (i%26). } // Error checking if (_c == NULL) return -1. 124 . i). i < ITER. sprintf_s(buffer. "%c%d". std::string str. BUFSIZ. return end_timer(). // Start start_timer().0. _c = new std::map<std::string. n2. n1 += i. // Exclusive lock if (_shared) _s->write_lock().int>. // Non-exclusive lock if (_shared) _s->read_lock(). // Populate for (i = 0. c.start_timer(). c. int n1 = 0. if (_shared) (*_s)[str] = n. return end_timer(). char buffer[BUFSIZ].

0. // Fail if no match if (n1 != n2) { if (_shared) _s->read_unlock(). } // Destroy/detach map double MapTest::destroy() { start_timer(). } Listing 19: MapTest.int>::detach(_name). return end_timer(). else n2 = (*_c)[str]. return -1. return end_timer(). else delete _c.cpp 125 . if (_shared) SharedMapFactory<std::string. } } // Release non-exclusive locks if (_shared) _s->read_unlock().if (_shared) n2 = (*_s)[str].

Sign up to vote on this title
UsefulNot useful