O que eu aprendi da minha primeira contribuição ao núcleo do Node.js

Yael Hermon Blocked Unblock Seguir Seguindo 26 de dezembro de 2018 drenar

Algumas semanas atrás, meu primeiro PR para o Node.js foi mesclado! Alguns dias depois, decidi twittar sobre isso e compartilhar como essa experiência foi positiva, na esperança de encorajar outros a contribuir também.

Mais tarde, Uri Shaked sugeriu que eu compartilhasse minha experiência em um post curto. Uri sempre tem ótimas idéias. Obrigado Uri!

Como acabei com esse PR?

Estou feliz que você tenha perguntado. Deixe-me começar com um pouco de fundo. Eu amo o Node.js, e contribuir para isso foi realmente na minha lista de desejos por um tempo muito longo. Nunca cheguei a fazê-lo porque sempre dizia a mim mesmo que não tinha tempo para isso, ou que talvez não estivesse qualificado o suficiente, ou outras desculpas esfarrapadas.

A reviravolta na história aconteceu quando eu estava trabalhando em uma palestra para um encontro JavaScript-Israel sobre o Garbage Collector V8 . Benjamin Gruenbaum , um dos principais colaboradores do Node.js, perguntou se gostaria que ele me conectasse com engenheiros V8 para revisar meus slides. Umm… obviamente eu faria.

Então ele fez, o que ficou muito legal. Benji também me perguntou se eu estava interessado em contribuir para o núcleo do Node.js. Mais uma vez, eu disse sim. Não há mais desculpas. As coisas só se tornaram reais.

Configurando o ambiente

Primeiro tive que construir o Node.js na minha máquina. Foi surpreendentemente fácil, graças aos excelentes documentos que o Node.js tem. Em seguida, decidi brincar e começar a depurá-lo.

Eu estaria mentindo se dissesse que foi tranquilo desde o começo. A última vez que trabalhei com o C ++ foi em 2012. Eu estava enferrujado. Além disso, naquela época eu tinha um ambiente completamente diferente do que tenho agora. Eu tinha um PC com Windows com o Visual Studio, enquanto agora estou executando o VSCode em um Mac.

Eu amo o VSCode, então eu queria configurar o VSCode para este projeto também. Logo encontrei uma extensão e configurei as coisas para funcionar. Para minha configuração de depuração, acabei configurando um depurador de nó e um depurador lldb para anexar ao processo Node. Isso funcionou muito bem.

Trabalhando em um problema real!

Então Benji me conectou com Anna , que é a colaboradora principal do Node.js que implementou ' worker_threads '. Benji também me apontou essa questão . Eu olhei para o problema e tentei reproduzi-lo com o menor código possível, apenas para me livrar do ruído.

Eu lutei com a criação de um caso de teste que reproduzisse o problema, já que ele era causado por uma condição de corrida. O código que falhou ao executar dentro do Node.js não falharia no meu ambiente de teste. Eventualmente, encontrei algo que falhou em todas as minhas corridas. Embora possa não falhar em todas as máquinas, ou todas as vezes, Anna confirmou que era bom o suficiente. Em seguida, comecei a depurá-lo para ver o que realmente estava acontecendo lá.

Se você nunca ouviu falar de 'threads de trabalho', provavelmente é porque eles são novos e estão atualmente em um estado experimental. Os funcionários permitem criar vários ambientes em execução em encadeamentos independentes. Eles são úteis para executar operações JavaScript intensivas de CPU, sem bloquear o thread principal.

O thread principal e o thread de trabalho podem se comunicar entre si por meio de um canal de mensagens entre eles. Além desse canal de mensagens, há outro canal de mensagens no qual as mensagens internas são enviadas, como stdout do worker. Quando você console.log dentro do worker, ele chega ao thread principal através deste canal interno de mensagens e o thread principal manipula-o, empurrando-o para seu fluxo stdout.

O problema é que estávamos chamando a função kDispose na classe de trabalho JS antes de esperar que todas as mensagens do stdio do trabalhador fossem processadas pelo thread principal através da porta de mensagens interna. Portanto, quando o segmento de trabalho terminou, perdemos as referências para os fluxos stdio do lado pai, e uma mensagem para o pai possivelmente poderia chegar depois disso.

No início, eu tentei várias abordagens diferentes para consertar isso, incluindo a configuração de uma promessa a ser resolvida quando a porta de mensagens foi concluída, aguardando antes de descartá-la e passando os retornos de chamada JS para a camada C ++.

Conversar com Anna sobre isso revelou-me que existia um método de drenagem para o MessagePort e emitia todas as mensagens recebidas de forma síncrona. Então, no final, todas as mensagens seriam processadas. Na verdade, o dreno já era chamado para o MessagePort externo. Como eu não tinha visto essa função todo esse tempo? ? Eu adicionei uma chamada para drenar também no MessagePort interno. A correção foi simples assim.

Uma coisa importante a lembrar é – é totalmente bom experimentar abordagens estranhas ao longo do caminho. É assim que você aprende. E depois de depurar lotes de código worker_threads, posso dizer que eu conheço algumas das suas bases de código muito bem agora 🙂

Benji e Anna foram muito receptivos desde o começo. Esta foi uma grande experiência. Eu aprendi muito com Anna e com o código, o que foi muito desafiador. Definitivamente não é algo que eu costumo lidar no meu dia-a-dia.

Eu não posso esperar para trabalhar na minha próxima edição!