|
| 1 | +# Thread-Safe Tasks: API Analysis Reference (DRAFT) |
| 2 | + |
| 3 | +This document provides a list of .NET APIs that should not be used or should be used with caution in thread-safe tasks. These APIs are problematic because they either rely on or modify process-level state, which can cause race conditions in multithreaded execution. |
| 4 | + |
| 5 | +The APIs listed in this document will be detected by Roslyn analyzers and/or MSBuild BuildCheck to help identify potential threading issues in tasks that implement `IMultiThreadableTask`. |
| 6 | + |
| 7 | +**Note**: The analyzers rely on **static code analysis** and may not catch all dynamic scenarios (such as reflection-based API calls). |
| 8 | + |
| 9 | +## API Issues Categories |
| 10 | + |
| 11 | +Categories of threading issues with .NET API usage in thread-safe tasks to be aware of: |
| 12 | + |
| 13 | +1. **Working Directory Modifications and Usage**, such as file system operations with relative paths. |
| 14 | +1. **Environment Variables Modification and Usage** |
| 15 | +1. **Process Culture Modification and Usage**, which can affect data formatting. |
| 16 | +1. **Assembly Loading** |
| 17 | +1. **Static Fields** |
| 18 | + |
| 19 | +### Best Practices |
| 20 | + |
| 21 | +Instead of the problematic APIs listed below, thread-safe tasks should: |
| 22 | + |
| 23 | +1. **Use `TaskEnvironment`** for all file system operations, environment variable changes, and working directory changes. |
| 24 | +1. **Always use absolute paths** when still using some standard .NET file system APIs. |
| 25 | +1. **Explicitly configure external processes** using `TaskEnvironment`. |
| 26 | +1. **Never modify process culture**: Avoid modifying culture defaults. |
| 27 | + |
| 28 | +## Detailed API Reference |
| 29 | + |
| 30 | +The following tables list specific .NET APIs and their threading safety classification: |
| 31 | + |
| 32 | +### System.IO.Path Class |
| 33 | + |
| 34 | +| API | Level | Short Reason | Recommendation | |
| 35 | +|-----|-------|--------------|-------| |
| 36 | +| `Path.GetFullPath(string path)` | ERROR | Uses current working directory | Use MSBuild API | |
| 37 | + |
| 38 | +### System.IO.File Class |
| 39 | + |
| 40 | +| API | Level | Short Reason | Recommendation | |
| 41 | +|-----|-------|--------------|-------| |
| 42 | +| All methods | ERROR | Uses current working directory | Use absolute paths | |
| 43 | + |
| 44 | +### System.IO.Directory Class |
| 45 | + |
| 46 | +| API | Level | Short Reason | Recommendation | |
| 47 | +|-----|-------|--------------|-------| |
| 48 | +| All methods | ERROR | Uses current working directory | Use absolute paths | |
| 49 | + |
| 50 | +### System.Environment Class |
| 51 | + |
| 52 | +| API | Level | Short Reason | Recommendation | |
| 53 | +|-----|-------|--------------|-------| |
| 54 | +| All properties setters | ERROR | Modifies process-level state | Use MSBuild API | |
| 55 | +| `Environment.CurrentDirectory` (getter, setter) | ERROR | Accesses process-level state | Use MSBuild API | |
| 56 | +| `Environment.Exit(int exitCode)` | ERROR | Terminates entire process | Return false from task or throw exception | |
| 57 | +| `Environment.FailFast` all overloads | ERROR | Terminates entire process | Return false from task or throw exception | |
| 58 | +| All other methods | ERROR | Modifies process-level state | Use MSBuild API | |
| 59 | + |
| 60 | +### System.IO.FileInfo Class |
| 61 | + |
| 62 | +| API | Level | Short Reason | Recommendation | |
| 63 | +|-----|-------|--------------|-------| |
| 64 | +| Constructor `FileInfo(string fileName)` | WARNING | Uses current working directory | Use absolute paths | |
| 65 | +| `CopyTo` all overloads | WARNING | Destination path relative to current directory | Use absolute paths | |
| 66 | +| `MoveTo` all overloads | WARNING | Destination path relative to current directory | Use absolute paths | |
| 67 | +| `Replace` all overloads | WARNING | Paths relative to current directory | Use absolute paths | |
| 68 | + |
| 69 | +### System.IO.DirectoryInfo Class |
| 70 | + |
| 71 | +| API | Level | Short Reason | Recommendation | |
| 72 | +|-----|-------|--------------|-------| |
| 73 | +| Constructor `DirectoryInfo(string path)` | WARNING | Uses current working directory | Use absolute paths | |
| 74 | +| `MoveTo(string destDirName)` | WARNING | Destination path relative to current directory | Use absolute paths | |
| 75 | + |
| 76 | +### System.IO.FileStream Class |
| 77 | + |
| 78 | +| API | Level | Short Reason | Recommendation | |
| 79 | +|-----|-------|--------------|-------| |
| 80 | +| Constructor `FileStream` all overloads | WARNING | Uses current working directory | Use absolute paths | |
| 81 | + |
| 82 | +### System.IO Stream Classes |
| 83 | + |
| 84 | +| API | Level | Short Reason | Recommendation | |
| 85 | +|-----|-------|--------------|-------| |
| 86 | +| Constructor `StreamReader` all overloads | WARNING | Uses current working directory | Use absolute paths | |
| 87 | + |
| 88 | +### System.Diagnostics.Process Class |
| 89 | + |
| 90 | +| API | Level | Short Reason | Recommendation | |
| 91 | +|-----|-------|--------------|-------| |
| 92 | +| All properties setters | ERROR | Modifies process-level state | Avoid | |
| 93 | +| `Process.GetCurrentProcess().Kill()` | ERROR | Terminates entire process | Avoid | |
| 94 | +| `Process.GetCurrentProcess().Kill(bool entireProcessTree)` | ERROR | Terminates entire process | Avoid | |
| 95 | +| `Process.Start` all overloads | ERROR | May inherit process state | Use MSBuild API | |
| 96 | + |
| 97 | +### System.Diagnostics.ProcessStartInfo Class |
| 98 | + |
| 99 | +| API | Level | Short Reason | Recommendation | |
| 100 | +|-----|-------|--------------|-------| |
| 101 | +| Constructor `ProcessStartInfo()` all overloads | ERROR | May inherit process state | Use MSBuild API | |
| 102 | + |
| 103 | +### System.Threading.ThreadPool Class |
| 104 | + |
| 105 | +| API | Level | Short Reason | Recommendation | |
| 106 | +|-----|-------|--------------|-------| |
| 107 | +| `ThreadPool.SetMinThreads(int workerThreads, int completionPortThreads)` | ERROR | Modifies process-wide settings | Avoid | |
| 108 | +| `ThreadPool.SetMaxThreads(int workerThreads, int completionPortThreads)` | ERROR | Modifies process-wide settings | Avoid | |
| 109 | + |
| 110 | +### System.Globalization.CultureInfo Class |
| 111 | + |
| 112 | +| API | Level | Short Reason | Recommendation | |
| 113 | +|-----|-------|--------------|-------| |
| 114 | +| `CultureInfo.DefaultThreadCurrentCulture` (setter) | ERROR | Affects new threads | Modify the thread culture instead | |
| 115 | +| `CultureInfo.DefaultThreadCurrentUICulture` (setter) | ERROR | Affects new threads | Modify the thread culture instead | |
| 116 | + |
| 117 | +### Static |
| 118 | + |
| 119 | +| API | Level | Short Reason | Recommendation | |
| 120 | +|-----|-------|--------------|-------| |
| 121 | +| Static fields | WARNING | Shared across threads, can cause race conditions | Avoid | |
| 122 | + |
| 123 | +### Assembly Loading (System.Reflection.Assembly class, System.Activator class) |
| 124 | +Tasks that load assemblies dynamically in the task host may cause version conflicts. Version conflicts in task assemblies will cause build failures (previously these might have been sporadic). Both dynamically loaded dependencies and static dependencies can cause issues. |
| 125 | + |
| 126 | +| API | Level | Short Reason | Recommendation | |
| 127 | +|-----|-------|--------------|-------| |
| 128 | +| `Assembly.LoadFrom(string assemblyFile)` | WARNING | May cause version conflicts | Be aware of potential conflicts, use absolute paths | |
| 129 | +| `Assembly.LoadFile(string path)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 130 | +| `Assembly.Load` all overloads | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 131 | +| `Assembly.LoadWithPartialName(string partialName)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 132 | +| `Activator.CreateInstanceFrom(string assemblyFile, string typeName)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 133 | +| `Activator.CreateInstance(string assemblyName, string typeName)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 134 | +| `AppDomain.Load` all overloads | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 135 | +| `AppDomain.CreateInstanceFrom(string assemblyFile, string typeName)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 136 | +| `AppDomain.CreateInstance(string assemblyName, string typeName)` | WARNING | May cause version conflicts | Be aware of potential conflicts | |
| 137 | + |
| 138 | +### P/Invoke |
| 139 | + |
| 140 | +**Concerns**: |
| 141 | +- P/Invoke calls may use process-level state like current working directory |
| 142 | +- Native code may not be thread-safe |
| 143 | +- Native APIs may modify global process state |
| 144 | + |
| 145 | +| API | Level | Short Reason | Recommendation | |
| 146 | +|-----|-------|--------------|-------| |
| 147 | +| `[DllImport]` attribute | WARNING | Not covered by analyzers | Review for thread safety, use absolute paths | |
0 commit comments