// 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); }
// 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); } }
// 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. }
// 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); } } }
// 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); }
// 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(); }
// 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); } }
// 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); } } }
// 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; }
// 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; }
// 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(); }