예제 #1
0
    // Recursively determine if a cycle exists in the Crst type dependency graph rooted at the 'rootCrst'
    // type. The 'currCrst' indicates the next dependency to be examined (it will be the same as the
    // 'rootCrst' when we're first called). The 'cycleList' argument contains a list of Crst types we've
    // already examined in this branch of the algorithm and serves both to avoid checking the same node twice
    // and to provide a list of the involved Crst types should a cycle be detected.
    // Note that this algorithm is not designed to detect general cycles in the graph, only those that involve
    // the 'rootCrst' directly. This is somewhat inefficient but gives us a simple way to generate clear error
    // messages.
    bool FindCycle(CrstType rootCrst, CrstType currCrst, List <CrstType> cycleList)
    {
        // Add the current Crst type to the list of those we've seen.
        cycleList.Add(currCrst);

        // Look through all the dependencies of the current Crst type.
        foreach (CrstType childCrst in currCrst.AcquiredBeforeList)
        {
            // If we find a reference back to the root Crst type then we've found a cycle. Start backing out
            // from the recursion (keeping the list of nodes we visited intact) by returning true.
            if (childCrst == rootCrst)
            {
                return(true);
            }

            // Otherwise iterate over the dependencies of the current node and for each one that we haven't
            // already seen and recursively extend the search.
            if (!cycleList.Contains(childCrst))
            {
                if (FindCycle(rootCrst, childCrst, cycleList))
                {
                    return(true);
                }
            }
        }

        // Didn't find any cycles involving the root and this node; remove this node from the potential cycle
        // list and return up to our caller indicating such.
        cycleList.RemoveAt(cycleList.Count - 1);

        return(false);
    }
예제 #2
0
    // Parse a list of Crst type names. Any other token terminates the list (without error and without
    // consuming that token from the stream). The list of tokens is returned as a list of corresponding
    // CrstTypes (which are auto-vivified in the output dictionary if they haven't been declared yet).
    void ParseList(List <CrstType> list)
    {
        // Parse tokens until we find a non-indentifier.
        while (true)
        {
            Token token = NextToken();
            if (token.Id != KeywordId.Id)
            {
                // We found the list terminator. Push the non-identifier token back into the stream for our
                // caller to parse correctly.
                UnwindToken();
                return;
            }

            // Look up or add a new CrstType corresponding to this type name.
            CrstType crst;
            if (m_crsts.ContainsKey(token.Text))
            {
                crst = m_crsts[token.Text];
            }
            else
            {
                crst = new CrstType(token.Text);
                m_crsts[crst.Name] = crst;
            }

            // Add the type to the output list we're building.
            list.Add(crst);
        }
    }
예제 #3
0
    // Declare a SameLevelAs relationship between the two Crst types given. Groups will be assigned, created
    // or merged as required to maintain our guarantees (each CrstType is a member of at most one group and
    // all CrstTypes involved in the same transitive closure of a SameLevelAs relationship are members of one
    // group).
    public static void Join(CrstType crst1, CrstType crst2)
    {
        CrstTypeGroup group;

        if (crst1 == crst2)
        {
            // In this case the type refers to itself. Create a singleton group for this type if it doesn't
            // already exist.
            if (crst1.Group == null)
            {
                group = new CrstTypeGroup();
                group.m_members.Add(crst1);

                s_groups.Add(group);

                crst1.Group = group;
            }
        }
        else if (crst1.Group == null && crst2.Group == null)
        {
            // Neither types belong to a group already. So we can create a new one and add both types to it.
            group = new CrstTypeGroup();
            group.m_members.Add(crst1);
            group.m_members.Add(crst2);

            s_groups.Add(group);

            crst1.Group = group;
            crst2.Group = group;
        }
        else if (crst1.Group == null)
        {
            // The first type doesn't belong to a group yet but the second does. So we can simply add the
            // first type to the second group.
            group = crst2.Group;
            group.m_members.Add(crst1);

            crst1.Group = group;
        }
        else if (crst2.Group == null)
        {
            // As for the case above but the group/no-group positions are reversed.
            group = crst1.Group;
            group.m_members.Add(crst2);

            crst2.Group = group;
        }
        else if (crst1.Group != crst2.Group)
        {
            // Both types belong to different groups so we'll have to merge them. Add the members of group 2
            // to group 1 and throw away group 2.
            group = crst1.Group;
            CrstTypeGroup absorbGroup = crst2.Group;
            foreach (CrstType crst in absorbGroup.m_members)
            {
                group.m_members.Add(crst);
                crst.Group = group;
            }

            s_groups.Remove(absorbGroup);
        }

        // The only case left is when both types are already in the same group and there's no work needed in
        // this case.
    }
예제 #4
0
    // Parse a single Crst type definition.
    void ParseCrst()
    {
        // The next token had better be an identifier (the Crst type name).
        Token token = NextToken();

        if (token.Id != KeywordId.Id)
        {
            throw new UnexpectedTokenError(token, KeywordId.Id);
        }

        // The Crst instance might already exist in the dictionary (forward references to a Crst type cause
        // these entries to auto-vivify). But in that case the entry better not be marked as 'Defined' which
        // would indicate a double declaration.
        CrstType crst;

        if (m_crsts.ContainsKey(token.Text))
        {
            crst = m_crsts[token.Text];
            if (crst.Defined)
            {
                throw new ParseError(String.Format("Duplicate definition for CrstType '{0}'", token.Text), token);
            }
        }
        else
        {
            // Otherwise this Crst type hasn't been seen thus far so we allocate a new instance and add it to
            // the dictionary.
            crst = new CrstType(token.Text);
            m_crsts.Add(crst.Name, crst);
        }

        // We're defining, not just referencing this type.
        crst.Defined = true;

        // Parse any attributes inside this definition (until we see an 'End' token).
        bool parsingCrst = true;

        while (parsingCrst)
        {
            // Get the next token. Either some attribute keyword or 'End'.
            token = NextToken();
            List <CrstType> list;

            switch (token.Id)
            {
            case KeywordId.AcquiredBefore:
                // Simply parse the following list of Crst types into the current type's AcquiredBefore list.
                ParseList(crst.AcquiredBeforeList);
                break;

            case KeywordId.AcquiredAfter:
                // AcquiredAfter is trickier. To make the ranking algorithm's life easier we actually
                // normalize all rules to the AcquiredBefore form (see LevelCrsts() for the reasoning). So we
                // capture the list of Crst types that follow the AcquiredAfter keyword and then append the
                // current type to the AcquiredBefore list of each type found.
                list = new List <CrstType>();
                ParseList(list);
                foreach (CrstType priorCrst in list)
                {
                    priorCrst.AcquiredBeforeList.Add(crst);
                }
                break;

            case KeywordId.SameLevelAs:
                // Parse the following list of Crst types them let the CrstTypeGroup class handle the
                // resulting updates to the type groups we're currently maintaining. See the comments for the
                // CrstTypeGroup class for more details.
                list = new List <CrstType>();
                ParseList(list);
                foreach (CrstType sameLevelCrst in list)
                {
                    CrstTypeGroup.Join(crst, sameLevelCrst);
                }
                break;

            case KeywordId.Unordered:
                crst.Level = CrstType.CrstUnordered;
                break;

            case KeywordId.End:
                parsingCrst = false;
                break;

            default:
                throw new UnexpectedTokenError(token,
                                               KeywordId.AcquiredBefore,
                                               KeywordId.AcquiredAfter,
                                               KeywordId.SameLevelAs,
                                               KeywordId.Unordered);
            }
        }
    }
예제 #5
0
 // Predicate method used with List<T>.FindAll() to locate Crst types that haven't had their rank assigned
 // yet.
 static bool Unleveled(CrstType crst)
 {
     return(crst.Level == CrstType.CrstUnassigned);
 }
예제 #6
0
    // Declare a SameLevelAs relationship between the two Crst types given. Groups will be assigned, created
    // or merged as required to maintain our guarantees (each CrstType is a member of at most one group and
    // all CrstTypes involved in the same transitive closure of a SameLevelAs relationship are members of one
    // group).
    public static void Join(CrstType crst1, CrstType crst2)
    {
        CrstTypeGroup group;

        if (crst1 == crst2)
        {
            // In this case the type refers to itself. Create a singleton group for this type if it doesn't
            // already exist.
            if (crst1.Group == null)
            {
                group = new CrstTypeGroup();
                group.m_members.Add(crst1);

                s_groups.Add(group);

                crst1.Group = group;
            }
        }
        else if (crst1.Group == null && crst2.Group == null)
        {
            // Neither types belong to a group already. So we can create a new one and add both types to it.
            group = new CrstTypeGroup();
            group.m_members.Add(crst1);
            group.m_members.Add(crst2);

            s_groups.Add(group);

            crst1.Group = group;
            crst2.Group = group;
        }
        else if (crst1.Group == null)
        {
            // The first type doesn't belong to a group yet but the second does. So we can simply add the
            // first type to the second group.
            group = crst2.Group;
            group.m_members.Add(crst1);

            crst1.Group = group;
        }
        else if (crst2.Group == null)
        {
            // As for the case above but the group/no-group positions are reversed.
            group = crst1.Group;
            group.m_members.Add(crst2);

            crst2.Group = group;
        }
        else if (crst1.Group != crst2.Group)
        {
            // Both types belong to different groups so we'll have to merge them. Add the members of group 2
            // to group 1 and throw away group 2.
            group = crst1.Group;
            CrstTypeGroup absorbGroup = crst2.Group;
            foreach (CrstType crst in absorbGroup.m_members)
            {
                group.m_members.Add(crst);
                crst.Group = group;
            }

            s_groups.Remove(absorbGroup);
        }

        // The only case left is when both types are already in the same group and there's no work needed in
        // this case.
    }
예제 #7
0
    // Emit the CrstTypes.h output file.
    void WriteHeaderFile(string fileName)
    {
        FileStream   stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
        StreamWriter writer = new StreamWriter(stream);

        // Create a collection based on all the Crst types we've stored in the hash. We do this so we can sort
        // the Crst types we emit (lexically, based on type name).
        Dictionary <string, CrstType> .ValueCollection crstCollection = m_crsts.Values;
        CrstType[] crsts = new CrstType[crstCollection.Count];
        crstCollection.CopyTo(crsts, 0);
        Array.Sort(crsts);

        // Emit the header. Contains copyright information, the usual goop to avoid multiple inclusion and a
        // header comment to discourage direct editing and point the user at the CrstTypes.def file instead
        // (where all will be explained in greater detail).
        writer.WriteLine("//");
        writer.WriteLine("// Licensed to the .NET Foundation under one or more agreements.");
        writer.WriteLine("// The .NET Foundation licenses this file to you under the MIT license.");
        writer.WriteLine("// See the LICENSE file in the project root for more information.");
        writer.WriteLine("//");
        writer.WriteLine();
        writer.WriteLine("#ifndef __CRST_TYPES_INCLUDED");
        writer.WriteLine("#define __CRST_TYPES_INCLUDED");
        writer.WriteLine();
        writer.WriteLine("// **** THIS IS AN AUTOMATICALLY GENERATED HEADER FILE -- DO NOT EDIT!!! ****");
        writer.WriteLine();
        writer.WriteLine("// This file describes the range of Crst types available and their mapping to a numeric level (used by the");
        writer.WriteLine("// runtime in debug mode to validate we're deadlock free). To modify these settings edit the");
        writer.WriteLine("// file:CrstTypes.def file and run the clr\\artifacts\\CrstTypeTool utility to generate a new version of this file.");
        writer.WriteLine();

        // Emit the CrstType enum to define a value for each crst type (along with the kNumberOfCrstTypes
        // constant).
        writer.WriteLine("// Each Crst type is declared as a value in the following CrstType enum.");
        writer.WriteLine("enum CrstType");
        writer.WriteLine("{");
        for (int i = 0; i < crsts.Length; i++)
        {
            writer.WriteLine("    Crst" + crsts[i].Name + " = " + i.ToString() + ",");
        }
        writer.WriteLine("    kNumberOfCrstTypes = " + crsts.Length.ToString());
        writer.WriteLine("};");
        writer.WriteLine();

        // This is the end of the regular part of the header included by most files.
        writer.WriteLine("#endif // __CRST_TYPES_INCLUDED");
        writer.WriteLine();

        // There is a second section of the header intended for inclusion only by vm\Crst.cpp. This contains
        // some data tables used to map crst type to rank or name. We could instead define two separate
        // headers, but on the whole it seems simpler to do it this way.
        writer.WriteLine("// Define some debug data in one module only -- vm\\crst.cpp.");
        writer.WriteLine("#if defined(__IN_CRST_CPP) && defined(_DEBUG)");
        writer.WriteLine();

        // Emit the crst type to rank mapping table.
        writer.WriteLine("// An array mapping CrstType to level.");
        writer.WriteLine("int g_rgCrstLevelMap[] =");
        writer.WriteLine("{");
        foreach (CrstType crst in crsts)
        {
            string crstLine = "    " + crst.Level + ",";
            crstLine = crstLine + new string(' ', 16 - crstLine.Length);
            writer.WriteLine(crstLine + "// Crst" + crst.Name);
        }
        writer.WriteLine("};");
        writer.WriteLine();

        // Emit the crst type to name mapping table.
        writer.WriteLine("// An array mapping CrstType to a stringized name.");
        writer.WriteLine("LPCSTR g_rgCrstNameMap[] =");
        writer.WriteLine("{");
        foreach (CrstType crst in crsts)
        {
            writer.WriteLine("    \"Crst" + crst.Name + "\",");
        }
        writer.WriteLine("};");
        writer.WriteLine();

        // Emit the constant Crst.cpp uses to record an unordered rank.
        writer.WriteLine("// Define a special level constant for unordered locks.");
        writer.WriteLine("#define CRSTUNORDERED (-1)");
        writer.WriteLine();

        // Emit a couple of inline helpers to map type to rank or name (and validate the type while they're at
        // it).
        writer.WriteLine("// Define inline helpers to map Crst types to names and levels.");
        writer.WriteLine("inline static int GetCrstLevel(CrstType crstType)");
        writer.WriteLine("{");
        writer.WriteLine("    LIMITED_METHOD_CONTRACT;");
        writer.WriteLine("    _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);");
        writer.WriteLine("    return g_rgCrstLevelMap[crstType];");
        writer.WriteLine("}");
        writer.WriteLine("inline static LPCSTR GetCrstName(CrstType crstType)");
        writer.WriteLine("{");
        writer.WriteLine("    LIMITED_METHOD_CONTRACT;");
        writer.WriteLine("    _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);");
        writer.WriteLine("    return g_rgCrstNameMap[crstType];");
        writer.WriteLine("}");
        writer.WriteLine();

        // And that's the end of the second section of the header file.
        writer.WriteLine("#endif // defined(__IN_CRST_CPP) && defined(_DEBUG)");

        writer.Close();
        stream.Close();
    }
예제 #8
0
    // Parse a list of Crst type names. Any other token terminates the list (without error and without
    // consuming that token from the stream). The list of tokens is returned as a list of corresponding
    // CrstTypes (which are auto-vivified in the output dictionary if they haven't been declared yet).
    void ParseList(List<CrstType> list)
    {
        // Parse tokens until we find a non-indentifier.
        while (true)
        {
            Token token = NextToken();
            if (token.Id != KeywordId.Id)
            {
                // We found the list terminator. Push the non-identifier token back into the stream for our
                // caller to parse correctly.
                UnwindToken();
                return;
            }

            // Look up or add a new CrstType corresponding to this type name.
            CrstType crst;
            if (m_crsts.ContainsKey(token.Text))
                crst = m_crsts[token.Text];
            else
            {
                crst = new CrstType(token.Text);
                m_crsts[crst.Name] = crst;
            }

            // Add the type to the output list we're building.
            list.Add(crst);
        }
    }
예제 #9
0
    // Parse a single Crst type definition.
    void ParseCrst()
    {
        // The next token had better be an identifier (the Crst type name).
        Token token = NextToken();
        if (token.Id != KeywordId.Id)
            throw new UnexpectedTokenError(token, KeywordId.Id);

        // The Crst instance might already exist in the dictionary (forward references to a Crst type cause
        // these entries to auto-vivify). But in that case the entry better not be marked as 'Defined' which
        // would indicate a double declaration.
        CrstType crst;
        if (m_crsts.ContainsKey(token.Text))
        {
            crst = m_crsts[token.Text];
            if (crst.Defined)
                throw new ParseError(String.Format("Duplicate definition for CrstType '{0}'", token.Text), token);
        }
        else
        {
            // Otherwise this Crst type hasn't been seen thus far so we allocate a new instance and add it to
            // the dictionary.
            crst = new CrstType(token.Text);
            m_crsts.Add(crst.Name, crst);
        }

        // We're defining, not just referencing this type.
        crst.Defined = true;

        // Parse any attributes inside this definition (until we see an 'End' token).
        bool parsingCrst = true;
        while (parsingCrst)
        {
            // Get the next token. Either some attribute keyword or 'End'.
            token = NextToken();
            List<CrstType> list;

            switch (token.Id)
            {

            case KeywordId.AcquiredBefore:
                // Simply parse the following list of Crst types into the current type's AcquiredBefore list.
                ParseList(crst.AcquiredBeforeList);
                break;

            case KeywordId.AcquiredAfter:
                // AcquiredAfter is trickier. To make the ranking algorithm's life easier we actually
                // normalize all rules to the AcquiredBefore form (see LevelCrsts() for the reasoning). So we
                // capture the list of Crst types that follow the AcquiredAfter keyword and then append the
                // current type to the AcquiredBefore list of each type found.
                list = new List<CrstType>();
                ParseList(list);
                foreach (CrstType priorCrst in list)
                    priorCrst.AcquiredBeforeList.Add(crst);
                break;

            case KeywordId.SameLevelAs:
                // Parse the following list of Crst types them let the CrstTypeGroup class handle the
                // resulting updates to the type groups we're currently maintaining. See the comments for the
                // CrstTypeGroup class for more details.
                list = new List<CrstType>();
                ParseList(list);
                foreach (CrstType sameLevelCrst in list)
                    CrstTypeGroup.Join(crst, sameLevelCrst);
                break;

            case KeywordId.Unordered:
                crst.Level = CrstType.CrstUnordered;
                break;

            case KeywordId.End:
                parsingCrst = false;
                break;

            default:
                throw new UnexpectedTokenError(token,
                                               KeywordId.AcquiredBefore,
                                               KeywordId.AcquiredAfter,
                                               KeywordId.SameLevelAs,
                                               KeywordId.Unordered);
            }
        }
    }
예제 #10
0
 // Predicate method used with List<T>.FindAll() to locate Crst types that haven't had their rank assigned
 // yet.
 static bool Unleveled(CrstType crst)
 {
     return crst.Level == CrstType.CrstUnassigned;
 }
예제 #11
0
    // Recursively determine if a cycle exists in the Crst type dependency graph rooted at the 'rootCrst'
    // type. The 'currCrst' indicates the next dependency to be examined (it will be the same as the
    // 'rootCrst' when we're first called). The 'cycleList' argument contains a list of Crst types we've
    // already examined in this branch of the algorithm and serves both to avoid checking the same node twice
    // and to provide a list of the involved Crst types should a cycle be detected.
    // Note that this algorithm is not designed to detect general cycles in the graph, only those that involve
    // the 'rootCrst' directly. This is somewhat inefficient but gives us a simple way to generate clear error
    // messages.
    bool FindCycle(CrstType rootCrst, CrstType currCrst, List<CrstType> cycleList)
    {
        // Add the current Crst type to the list of those we've seen.
        cycleList.Add(currCrst);

        // Look through all the dependencies of the current Crst type.
        foreach (CrstType childCrst in currCrst.AcquiredBeforeList)
        {
            // If we find a reference back to the root Crst type then we've found a cycle. Start backing out
            // from the recursion (keeping the list of nodes we visited intact) by returning true.
            if (childCrst == rootCrst)
                return true;

            // Otherwise iterate over the dependencies of the current node and for each one that we haven't
            // already seen and recursively extend the search.
            if (!cycleList.Contains(childCrst))
                if (FindCycle(rootCrst, childCrst, cycleList))
                    return true;
        }

        // Didn't find any cycles involving the root and this node; remove this node from the potential cycle
        // list and return up to our caller indicating such.
        cycleList.RemoveAt(cycleList.Count - 1);

        return false;
    }
예제 #12
0
    // Emit the CrstTypes.h output file.
    void WriteHeaderFile(string fileName)
    {
        FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
        StreamWriter writer = new StreamWriter(stream);

        // Create a collection based on all the Crst types we've stored in the hash. We do this so we can sort
        // the Crst types we emit (lexically, based on type name).
        Dictionary<string, CrstType>.ValueCollection crstCollection = m_crsts.Values;
        CrstType[] crsts = new CrstType[crstCollection.Count];
        crstCollection.CopyTo(crsts, 0);
        Array.Sort(crsts);

        // Emit the header. Contains copyright information, the usual goop to avoid multiple inclusion and a
        // header comment to discourage direct editing and point the user at the CrstTypes.def file instead
        // (where all will be explained in greater detail).
        writer.WriteLine("//");
        writer.WriteLine("// Copyright (c) Microsoft. All rights reserved.");
        writer.WriteLine("// Licensed under the MIT license. See LICENSE file in the project root for full license information.");
        writer.WriteLine("//");
        writer.WriteLine();
        writer.WriteLine("#ifndef __CRST_TYPES_INCLUDED");
        writer.WriteLine("#define __CRST_TYPES_INCLUDED");
        writer.WriteLine();
        writer.WriteLine("// **** THIS IS AN AUTOMATICALLY GENERATED HEADER FILE -- DO NOT EDIT!!! ****");
        writer.WriteLine();
        writer.WriteLine("// This file describes the range of Crst types available and their mapping to a numeric level (used by the");
        writer.WriteLine("// runtime in debug mode to validate we're deadlock free). To modify these settings edit the");
        writer.WriteLine("// file:CrstTypes.def file and run the clr\\bin\\CrstTypeTool utility to generate a new version of this file.");
        writer.WriteLine();

        // Emit the CrstType enum to define a value for each crst type (along with the kNumberOfCrstTypes
        // constant).
        writer.WriteLine("// Each Crst type is declared as a value in the following CrstType enum.");
        writer.WriteLine("enum CrstType");
        writer.WriteLine("{");
        for (int i = 0; i < crsts.Length; i++)
            writer.WriteLine("    Crst" + crsts[i].Name + " = " + i.ToString() + ",");
        writer.WriteLine("    kNumberOfCrstTypes = " + crsts.Length.ToString());
        writer.WriteLine("};");
        writer.WriteLine();

        // This is the end of the regular part of the header included by most files.
        writer.WriteLine("#endif // __CRST_TYPES_INCLUDED");
        writer.WriteLine();

        // There is a second section of the header intended for inclusion only by vm\Crst.cpp. This contains
        // some data tables used to map crst type to rank or name. We could instead define two separate
        // headers, but on the whole it seems simpler to do it this way.
        writer.WriteLine("// Define some debug data in one module only -- vm\\crst.cpp.");
        writer.WriteLine("#if defined(__IN_CRST_CPP) && defined(_DEBUG)");
        writer.WriteLine();

        // Emit the crst type to rank mapping table.
        writer.WriteLine("// An array mapping CrstType to level.");
        writer.WriteLine("int g_rgCrstLevelMap[] =");
        writer.WriteLine("{");
        foreach (CrstType crst in crsts)
            writer.WriteLine("    " + crst.Level + ",\t\t\t// Crst" + crst.Name);
        writer.WriteLine("};");
        writer.WriteLine();

        // Emit the crst type to name mapping table.
        writer.WriteLine("// An array mapping CrstType to a stringized name.");
        writer.WriteLine("LPCSTR g_rgCrstNameMap[] =");
        writer.WriteLine("{");
        foreach (CrstType crst in crsts)
            writer.WriteLine("    \"Crst" + crst.Name + "\",");
        writer.WriteLine("};");
        writer.WriteLine();

        // Emit the constant Crst.cpp uses to record an unordered rank.
        writer.WriteLine("// Define a special level constant for unordered locks.");
        writer.WriteLine("#define CRSTUNORDERED (-1)");
        writer.WriteLine();

        // Emit a couple of inline helpers to map type to rank or name (and validate the type while they're at
        // it).
        writer.WriteLine("// Define inline helpers to map Crst types to names and levels.");
        writer.WriteLine("inline static int GetCrstLevel(CrstType crstType)");
        writer.WriteLine("{");
        writer.WriteLine("    LIMITED_METHOD_CONTRACT;");
        writer.WriteLine("    _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);");
        writer.WriteLine("    return g_rgCrstLevelMap[crstType];");
        writer.WriteLine("}");
        writer.WriteLine("inline static LPCSTR GetCrstName(CrstType crstType)");
        writer.WriteLine("{");
        writer.WriteLine("    LIMITED_METHOD_CONTRACT;");
        writer.WriteLine("    _ASSERTE(crstType >= 0 && crstType < kNumberOfCrstTypes);");
        writer.WriteLine("    return g_rgCrstNameMap[crstType];");
        writer.WriteLine("}");
        writer.WriteLine();

        // And that's the end of the second section of the header file.
        writer.WriteLine("#endif // defined(__IN_CRST_CPP) && defined(_DEBUG)");

        writer.Close();
        stream.Close();
    }