Metastatic.Analysis.BusinessLogic.UnmanagedTask
(Metastatic v0.10.4)
View Source
Detects unsupervised async operations that can cause memory leaks.
This analyzer identifies spawning of async tasks/threads without proper supervision or error handling - a common concurrency anti-pattern.
Cross-Language Applicability
This is a universal concurrency anti-pattern across all async/concurrent systems:
- Python/asyncio:
asyncio.create_task()without task group - Python/threading:
Thread(target=fn).start()without join - JavaScript:
new Promise()without.catch() - JavaScript/Node: Promise without rejection handler
- Elixir:
Task.async()withoutTask.Supervisor - Go:
go func()without wait group or context - C#:
Task.Run()without continuation or error handling - Java:
CompletableFuture.runAsync()without exception handling - Rust:
tokio::spawn()without join handle management
Problem
Unmanaged async operations can:
- Leak memory if tasks never complete
- Silently fail without error propagation
- Cause race conditions without synchronization
- Leave orphaned processes after parent exits
- Exhaust resources (threads, file handles, connections)
Examples
Bad (Python)
async def handler():
asyncio.create_task(background_work()) # Fire and forget!
# Task may fail silently, no cleanupGood (Python)
async def handler():
async with asyncio.TaskGroup() as tg:
tg.create_task(background_work())
# Tasks are supervised, errors propagateBad (JavaScript)
function handler() {
new Promise(async (resolve) => {
await riskyWork(); // No error handling!
});
}Good (JavaScript)
function handler() {
Promise.resolve()
.then(() => riskyWork())
.catch(handleError);
}Bad (Elixir)
def handle_event(event) do
Task.async(fn -> process(event) end) # Unmanaged!
endGood (Elixir)
def handle_event(event) do
Task.Supervisor.async_nolink(MySupervisor, fn ->
process(event)
end)
endBad (Go)
func handler() {
go riskyWork() // No wait, no error handling
}Good (Go)
func handler(ctx context.Context) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
riskyWork(ctx)
}()
wg.Wait()
}Bad (C#)
void Handler() {
Task.Run(() => RiskyWork()); // Fire and forget
}Good (C#)
async Task Handler() {
await Task.Run(() => RiskyWork())
.ContinueWith(t => HandleError(t.Exception));
}Detection Strategy
Detects MetaAST pattern:
{:async_operation, :spawn, lambda}Or function calls matching async spawn patterns:
Task.async,asyncio.create_task,Promise.resolvego func(),Task.Run,spawn
Without subsequent supervision/await/join in same scope.
Limitations
- Cannot detect if supervision exists in parent scope
- Heuristic-based: may have false positives
- Difficult to detect await in different block