Top Up Prev Next Bottom Contents Index Search

3.8 Sharing Data Structures Across Multiple Stars

It is sometimes desirable to have a set of stars that share and manipulate a common data structure1. A simple way to accomplish this is to define a star that contains a static member. Suppose, for example, you wish to define a class of stars that create a shared list of pointers, one to each instance of this type of star. Thus, every star of this type would be able to access every other star of this type. Consider the following implementation:

defstar {
name { Share }
domain { SDF }
desc { A star with a shared data structure }
hinclude { "DataStruct.h" }
private {
static SequentialList starList;
}
output {
name { howmany }
type { int }
}
code {
SequentialList SDFShare::starList;
}
begin {
starList.append(this);
}
go {
howmany%0 << starList.size();
}
wrapup {
starList.initialize();
}
}
This star has a static private member of type SequentialList with name starList. The "static" in C++ ensures that there will be no more than one instance of the SequentialList object. That instance will be accessible to every instance of the star, but not to any other object (because the member is private). That one instance is actually declared by the lines:

code {
SequentialList SDFShare::starList;
}
The declaration will get put into the file SDFShare.cc by the preprocessor. Notice that the class name of the star is SDFShare not just Share. The begin method simply adds to the sequential list a pointer to the star that invoked the begin method (this). Note that you should use the begin method here rather than the setup method because the begin method is always invoked exactly once, while the setup method might be invoked more than once when the simulation starts up. The go method sends to the output (named howmany) the size of the list. This will be equal to the number of stars of this type in the universe.

The wrapup method has the only tricky part of this code. It reinitializes the SequentialList so that subsequent runs do not just simply add to a list created by previous runs. However, note that the wrapup method will not be invoked if an error occurs during the run. Pigi ensures correct operation nonetheless by deleting all instances of the stars and recreating them if an error occurred on the previous run. Thus, between invocations of the begin method, either the wrapup method or the constructor for the star (and all its members) will be invoked. The constructor for SequentialList also initializes the list, so the list is always initialized before the first begin method is called.

The above approach is somewhat limited. You may want more than one type of star to share a data structure. In this case, you should create a common base class for all the stars that will share the data structure. The shared data structure should be a protected member, rather than a private member, so that it is accessible to derived stars.

Alternatively, you might want arbitrary subsets of stars to share distinct data structures, one for each subset. This can be accomplished by defining a static list that is indexed by a string, and using a parameter in the star to identify to which subset it belongs. An efficient data structure to use for this is the HashTable. So for example, suppose we wanted to modify the above star to create lists of stars with common values of a parameter "mySubset", and to output the number of stars in their subset. The above code becomes:

defstar {
name { BetterShare }
domain { SDF }
desc { A star with a shared data structure }
hinclude { "DataStruct.h" }
hinclude { "HashTable.h" }
output {
name { howmany }
type { int }
}
state {
name { mySubset }
default { "subset A" }
type { string }
}
private {
static HashTable listOfLists;
SequentialList* myList;
}
code {
HashTable SDFBetterShare::listOfLists;
}
begin {
if (listOfLists.hasKey((char*)mySubset)) {
myList = listOfLists.lookup((char*)mySubset);
} else {
myList = new SequentialList;
listOfLists.insert((char*)mySubset,myList);
}
myList->append(this);
}
go {
howmany%0 << myList->size();
}
wrapup {
if (listOfLists.hasKey((char*)mySubset)) {
listOfLists.remove((char*)mySubset);
delete myList;
}
}
}
In addition to the static private member listOfLists, we also have a pointer myList to a SequentialList. The begin method is a bit more complicated now. It first checks to see whether an entry in the hash table has already been created with a key equal to the value of the state "mySubset". If it has, then the SequentialList pointer myList is set equal to the value of that entry. If it has not, then a new SequentialList is allocated and inserted into the hash table with the appropriate key. The last action is simply to insert a pointer to the star instance into myList.

The go method is similar to before.

The wrapup method is slightly more complicated, because it needs to free the memory allocated when the new SequentialList was allocated. However, it should free that memory only once, and there may be several star instances pointing to it. Thus, it first checks the hash table to see whether there exists an entry with key equal to mySubset. If there does, then it removes that entry and deletes the SequentialList myList.



Top Up Prev Next Bottom Contents Index Search

1 See the SDFWriteVar and SDFReadVar stars for a similar implementation.

Copyright © 1990-1997, University of California. All rights reserved.