# Modelos Formais de Concorrência para Kotlin/Android ## 1. Modelo CSP (Communicating Sequential Processes) ### Definição Formal Um processo CSP é definido por: ``` P ::= STOP -- processo morto (deadlock) | SKIP -- processo terminado normalmente | a → P -- prefixo: executa evento a, depois P | P □ Q -- choice externo: o ambiente escolhe | P ⊓ Q -- choice interno: P escolhe | P ‖ Q -- composição paralela | P \ A -- ocultação do conjunto A de eventos ``` ### Aplicação a Coroutines Kotlin ```kotlin // Cada coroutine é um processo CSP // launch { } ≡ processo concorrente // channel.send(x) ≡ evento de saída // channel.receive() ≡ evento de entrada // Deadlock clássico em CSP: // P = a → b → STOP // Q = b → a → STOP // P ‖ Q → cada um espera o outro primeiro → DEADLOCK // Equivalente em Kotlin: val channelA = Channel() val channelB = Channel() launch { channelA.send(1); channelB.receive() } // P launch { channelB.send(2); channelA.receive() } // Q // DEADLOCK: ambos bloqueados esperando ``` --- ## 2. Modelo de Atores (Actor Model) ### Definição Cada ator tem: - Caixa postal (mailbox) — fila de mensagens - Comportamento — função: Mensagem → (Estado', [Atores novos], [Mensagens]) - Estado local encapsulado — não compartilhado ### Em Kotlin com Coroutines ```kotlin // Actor via Channel + coroutine fun CoroutineScope.counterActor() = actor { var counter = 0 for (msg in channel) { when (msg) { is IncCounter -> counter++ is GetCounter -> msg.response.complete(counter) } } } // Propriedades formais: // - Sem race conditions: estado encapsulado // - Sem deadlocks: se mailbox unbounded e sem cycles // - Linearizabilidade: operações parecem atômicas para clientes ``` --- ## 3. Modelo de Memória Android (JMM - Java Memory Model) ### Happens-Before Relations ``` Regras do JMM que garantem visibilidade: 1. Program order: a₁ →ₚ a₂ se a₁ vem antes de a₂ no mesmo thread 2. Monitor lock: unlock(m) → lock(m) 3. Volatile: write(v) → read(v) para variável volatile 4. Thread start: start(t) → qualquer ação de t 5. Thread join: qualquer ação de t → join(t) 6. Finalizer: fim do construtor → início do finalize() ``` ### StateFlow e Atomicidade ```kotlin // MutableStateFlow usa CAS (Compare-And-Swap) internamente // Garantia: atualização via compareAndSet é lock-free e wait-free // Leitura de .value é sempre a versão mais recente (volatile semantics) // CORRETO: update atômico _state.update { currentState -> currentState.copy(isRecording = true) } // INCORRETO: read-modify-write não atômico val current = _state.value // read _state.value = current.copy(...) // write separado → race condition! ``` --- ## 4. Análise de Deadlocks em Android ### Padrões de Deadlock Comuns #### Pattern 1: runBlocking em Main Thread ```kotlin // DEADLOCK: runBlocking bloqueia Main, coroutines precisam do Main fun onClickButton() { runBlocking { // bloqueia Main thread viewModel.doSomething() // precisa de Main para updates // DEADLOCK! } } // CORRETO: fun onClickButton() { lifecycleScope.launch { viewModel.doSomething() } } ``` #### Pattern 2: Mutex lock reentrante (não existe em Kotlin) ```kotlin // Kotlin Mutex é NÃO reentrante — diferente de synchronized(this) val mutex = Mutex() suspend fun outer() { mutex.withLock { inner() // tenta adquirir mesmo mutex → DEADLOCK! } } suspend fun inner() { mutex.withLock { // bloqueia esperando outer() liberar // nunca chega aqui } } ``` #### Pattern 3: Channel rendezvous sem consumidor ```kotlin val channel = Channel() // sem buffer launch { channel.send(result) // bloqueia até alguém receber } // Se não há nenhum receiver ativo → coroutine fica suspensa para sempre // Pode causar memory leak se scope sobrevive // CORRETO: usar Channel(BUFFERED) ou garantir receiver existe ``` --- ## 5. Análise de Liveness (Ausência de Starvation) ### Definição Formal ``` Starvation: processo P está em starvation se: ∃ sequência infinita de execuções onde P nunca progride, mesmo sendo elegível para execução. Em termos de LTL: ¬Starvation(P) ≡ GF(ready(P)) → GF(running(P)) ("sempre que P está pronto, eventualmente P executa") ``` ### No Contexto Android/Kotlin ```kotlin // Fairness do scheduler de coroutines: // - Dispatchers.Default: trabalho processor-bound, round-robin entre coroutines // - Dispatchers.IO: thread pool expansível (default 64 threads), fair scheduling // - Dispatchers.Main: fila FIFO no Main thread // Risco de starvation: // 1. Dispatchers.Default com muitas coroutines CPU-bound → novas ficam esperando // 2. Dispatchers.IO.limitedParallelism(n) → n pequeno → fila grande // Exemplo Auri: // VoicePipeline roda em Main (para updates de UI) // LLM requests rodam em IO // Se LLM request bloquear IO thread pool → STT pode ficar esperando ``` --- ## 6. Verificação de Propriedades com TLA+ ### Exemplo para VoicePipeline ```tla VARIABLES state, sttResult, llmResult Init == state = "IDLE" /\ sttResult = "" /\ llmResult = "" StartRecording == /\ state = "IDLE" /\ state' = "RECORDING" /\ UNCHANGED <> StopAndTranscribe == /\ state = "RECORDING" /\ state' = "TRANSCRIBING" /\ UNCHANGED <> STTComplete == /\ state = "TRANSCRIBING" /\ sttResult' \in STRING \ {""} /\ state' = "QUERYING_LLM" /\ UNCHANGED <> -- Propriedade de Safety: NoDeadlock == state \in {"IDLE","RECORDING","TRANSCRIBING", "QUERYING_LLM","SPEAKING","ERROR"} -- Propriedade de Liveness: EventuallyIdle == <>(state = "IDLE") ``` --- ## 7. Race Conditions — Checklist para Kotlin/Android ### Variáveis que precisam de proteção ```kotlin // ❌ INSEGURO: var compartilhado entre coroutines sem sincronização var isConnected: Boolean = false launch(Dispatchers.IO) { isConnected = true } launch(Dispatchers.Default) { if (isConnected) ... } // race! // ✅ SEGURO: @Volatile para leituras/escritas simples @Volatile var isConnected: Boolean = false // ✅ SEGURO: AtomicBoolean para CAS operations val isConnected = AtomicBoolean(false) isConnected.compareAndSet(false, true) // ✅ SEGURO: StateFlow para estado observável private val _isConnected = MutableStateFlow(false) val isConnected = _isConnected.asStateFlow() ``` ### Padrões seguros em Kotlin coroutines ```kotlin // Mutex para seções críticas val mutex = Mutex() mutex.withLock { // seção crítica } // Actor para estado mutável encapsulado val stateActor = actor { ... } // StateFlow para estado reativo val state = MutableStateFlow(initialState) state.update { it.copy(...) } // atômico via CAS ``` --- ## 8. Análise de Memory Leaks em Android ### Context Leaks (mais comum) ```kotlin // ❌ LEAK: Activity context capturada em objeto de longa vida class LlmClient(val context: Context) { // se context = Activity → leak // cliente pode sobreviver à Activity } // ✅ CORRETO: Application context para objetos de longa vida class LlmClient(val context: Context) { init { // usar context.applicationContext para operações longas } } ``` ### Coroutine Leaks ```kotlin // ❌ LEAK: coroutine lançada sem scope adequado fun startRecording() { GlobalScope.launch { // nunca cancelado! // ... } } // ✅ CORRETO: scope vinculado ao ciclo de vida class EarLlmService : Service() { private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) override fun onDestroy() { serviceScope.cancel() // cancela todas as coroutines } } ``` ### Listener Leaks (Bluetooth) ```kotlin // ❌ LEAK: listener registrado mas nunca removido audioManager.registerAudioDeviceCallback(callback, null) // onDestroy esquece de chamar unregisterAudioDeviceCallback // ✅ CORRETO: registro/desregistro simétrico override fun onStart() { register(callback) } override fun onStop() { unregister(callback) } ```