segunda-feira, 28 de junho de 2010

Atribuindo nomes às threads

Para quem trabalha com programação multi-thread sabe que a depuração do código é um tarefa bem complicada.
Percorrer o caminho inverso do fluxo de execução de uma tarefa que é intercalada por threads requer tempo de  habilidade.
O Delphi 2010 trás uma novidade interessante que promete ajudar nessa árdua tarefa, agora é possível nomear as threads.
Observe a imagem abaixo:



No Delphi 2010 foi introduzido o método NameThreadForDebugging na classe TThread e é utilizado tanto para nomear quanto para renomear uma thread específica.
Sua assinatura trás dois parâmetros: AThreadName e AThreadID.
O primeiro é o nome que será dado à thread e o segundo parâmetro é o ID da thread a ser nomeada. No caso do segundo parâmetro ser omitido, ou ser -1, a operação será realizada sob a corrente thread que está rodando.
Outro detalhe é que o nome atribuído é do tipo AnsiString, ou seja, pode ser necessário um typecast explícito.

Vamos ver na prática como o recurso é aplicado.

procedure TMyThread.Execute;
var
  lThreadName: AnsiString;
begin
  {$IFDEF VER210}
  lThreadName := AnsiString(Self.ClassName + '_' + IntToStr(FCount));
  NameThreadForDebugging(lThreadName);
  {$ENDIF}

  while not Terminated do
  begin

    Sleep(300);
  end;
end;

Não se esqueça que esse método só está disponível no Delphi 2010 e, provavelmente, nas versões superiores. No caso do código fonte ser utilizado em versões anteriores, use utilize a diretiva de compilação {$IFDEF VER210} para evitar erro de compilação/compatibilidade.

Apesar desse recurso ser introduzido somente na versão 2010, existe um método que pode ser aplicado para as versões anteriores.

{$IFDEF MSWINDOWS}

procedure SetDebbugerThreadName(const AThreadName: AnsiString; const AThreadID: LongWord = $FFFFFFFF);
type
  TThreadNameInfo = record
    FType:     LongWord;  // Deve ser sempre 0x1000
    FName:     PAnsiChar; // Ponteiro para o nome da thread
    FThreadID: LongWord;  // Thread ID (-1 indica a corrente thread)
    FFlags:    LongWord;  // Não utilizado, deve ser zero.
  end;
var
  lThreadInfo: TThreadNameInfo;
begin
  lThreadInfo.FType     := $1000;
  lThreadInfo.FName     := PAnsiChar(AThreadName);
  lThreadInfo.FThreadID := AThreadID;
  lThreadInfo.FFlags    := 0;

  try
    RaiseException($406D1388, 0, SizeOf(lThreadInfo) div SizeOf(LongWord), @lThreadInfo);
  except
  end;
end;

{$ENDIF}

O uso dessa função tem exatamente o mesmo efeito que o método NameThreadForDebugging.

Um exemplo completo utilizando ambas formas pode ser baixada aqui.

terça-feira, 8 de junho de 2010

Customizando o Help Insight no Delphi

Recentemente li um artigo sobre Help Insight que chamou bastante minha atenção.
Acredito que todos saibam o que é ou já tiveram contato com essa funcionalidade, mas para aqueles que não estão acostumado com o nome Help Insight, esse é um recurso do IDE do Delphi que apresenta um popup com uma breve descrição sobre o identificador (classe, método, função, variável, ...) na qual o cursor do mouse está posicionado.
Também é possível invocar o Help Insight através do pressionamento das teclas CTRL + SHIFT + H.
Por padrão é apresentado nesse popup menu as seguintes informações:
  • Nome do identificador
  • Categoria do identificador (Campo, variável, método, classe, propriedade, ...)
  • Localização (nome da unit seguido pelo número da linha e da coluna)
  • Tipo (Se o identificador é string, integer, ...)

No caso do identificador ser um método, function ou procedure, a descrição é extendida apresentando também o(s) parâmetro(s) e o valor de retorno.

Isso tudo é feito automaticamente pelo IDE em tempo de edição, ou seja, nem necessitar que o código seja compilado.
O que eu não sabia, e é o objetivo desse artigo, é que o conteúdo do popup menu pode ser customizado exibindo informações mais detalhadas a respeito do identificador em questão.
Essa customização deve estar situada imediatamente acima do identificador a qual se deseja trabalhar e tem seu início demarcado através de um comentário coringa (///, três barras) seguido por uma tag XML reconhecida pelo Help Insight viewer.

As tags XML atualmente reconhecida são:
  • summary
  • param
  • returns
  • permission
  • remarks
  • comments
Agora vamos ver um exemplo prático contendo todas essas tags.

/// <summary>    Verificar se um determinado componente/field teve ou não seu conteúdo
///              alterado. O método é utilizado dentro do framework para sinalizar os
///              componentes que foram modificados durante uma operação de update.</summary>
/// <param name="FieldComp"> Registro contendo informações sobre o field/componente. </param>
/// <param name="Index">    Índice do registro dentro da lista (<c>FFieldList</c>). </param>
/// <param name="Modified">  Parâmetro de saída que deve ser setado para True/False, dependendo
///                          do status verificado pelo método. Por padrão, o parâmetro vem
///                          com o valor True. </param>
/// <returns>    O valor retornado deve significar que o método obteve sucesso ao comparar o
///              valor original com o valor atual do componente/field, ou seja, que o valor
///              do parâmetro Modified é válido.</returns>
/// <permission> Esse método é utilizado internamente apenas quando o formulário está em estado
///              de alteração de registro. A implementação original trás suporte para os
///              componentes mais comumente utilizados, entretanto, em caso de customização ou
///              extenção do suporte a outros componentes, esse método pode sempre ser
///              reimplementado nos formulários filhos. </permission>
/// <remarks>    Originalmente esse método foi concebido para ser utilizado pelo framework como
///              um serviço. Assim, evite chamá-lo diretamente a menos que saiba com exatidão
///              o que está fazendo. </remarks>
/// <comments>  Caso necessite reimplementar o método, é recomendado que faça uma chamada à
///              implementação da classe pai (Exemplo: Result := inherited;). </comments>

Observe que foi utilizado também as tags <c> e </c>. Essas tags delimitam um área de texto que será formatada no estilo de código fonte do Delphi (por padrão, Courier New).
O resultado é:

Convenhamos que se você está navegando pelo código, um help assim é muito, mas muito mais útil do que simplesmente visualizar a assinatura do identificador (padrão do Delphi), certo? Além do mais, você não precisa se deslocar até a definição/implementação/declaração do identificador para obter maiores detalhes, o que quase sempre resulta na interrupção da linha de raciocínio durante alguma investigação.
Uma outra vantagem que notei sobre a utilização do Help Insigh é poder ter dois tipos de documentação a respeito de um método sobre dois pontos de vista.
Deixa eu exemplificar. Observe o código fonte abaixo.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
  private
    { Private declarations }

  protected
    function FieldIsModified(var FieldComp: TFieldItem; const Index: Integer; out Modified: Boolean): Boolean; virtual;

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

{_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\
  Definition.: Protected          Strict [ ]     Virtual/Override [X]     Event [ ]
  Ojective...:
  Usefulness.:
  Parameters.: [ I ] FieldComp =
               [ I ] Index     =
               [ O ] Modified  =
  Result.....:
  Comments...:
_/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\__/-\}
function TForm1.FieldIsModified(var FieldComp: TFieldItem; const Index: Integer; out Modified: Boolean): Boolean;
begin
  // ...
end;

end.

Creio que a prática mais comum de documentação de código seja a inclusão de comentários imediatamente acima da implementação dos métodos/funções (como apresentando no exemplo acima), ficando a área de interface da unit com apenas as definições.

O código fonte acima contém a estrutura do bloco de comentário que costumo utilizar para documentar um método ou uma function (removi o texto para ficar mais fácil sua visualização). As informações que acrescento nesse bloco estão relacionadas principalmente com seu funcionamento, com sua dependência perante a solução como um todo e com cuidados que devem ser tomados na sua utilização.
Quando penso no Help Inside, penso em um ponto de vista onde saber como é o funcionamento interno de um método não é tão importante, naquele momento, quanto saber o que o método faz, suas entradas e suas saídas. Assim, quando digo que o Help Insight proporciona um segundo tipo de comentários é justamente a possibilidade de documentar o código fonte sob uma perspectiva mais focada no conceito do que em detalhes intrínsecos ao funcionamento.

Apesar de muito útil, fique um tanto desconfortável com relação a legibilidade (ou melhor, à falta de legibilidade) que a estrutura de definição das tags causa ao layout da classe.
Por exemplo, se você possui um classe com vários métodos e propriedades. Acrescentar um help insigh para cada definição irá dinimuir a visão do todo por exigir constante deslocamento entre as linhas para navegar pela classe (aqui o layout struct do Delphi se mostrou ainda mais útil).
Fiz alguns testes e notei que o IDE aceita a utilização de REGIONS para delimitar a área do Help Insight, o que pareceu uma forma bem razoável para resolver ou, no mínimo, minimizar a área visível ocupada.


Um segundo ponto com relação a legibilidade é que não há uma formatação específica para as tags.
  a) É possível escrever a descrição de uma tag utilizando multi-linhas ou em apenas uma única linha que o conteúdo será apresentado igualmente no popup;
  b) Pode-se acrescentar espaços vários espaços em brancos, mas os excedentes serão ignorados (assim como em HTML);
  c) Qualquer quebra de linha será ignorada, o que dá margem para formatar melhor o texto na unit, mas na hora de exibir o popup, as frases serão concatenadas em um único parágrafo.
  Isso faz com que não seja possível escrever um pequeno trecho de código, por exemplo.

Em outras palavras, fiquei com a impressão que é mais legível ler as descrições do popup do que diretamente do código fonte.

Caso deseje analisar o código fonte completo da unit criada durante a escrita desse artigo, faça o download nesse link.


A última dica é para aqueles de desejam alterar a saída do popup para um layout mais adequado ou apenas queiram fazer pequenos incrementos. Nesse caso basta editar o arquivo HelpInsight.xsl (sub-diretório ObjRepos do diretório de instalação do RAD Studio, para mim é C:\Program Files\Embarcadero\RAD Studio\7.0\ObjRepos). Lá você encontrará também outras opções não descrita nesse artigo.

Boa sorte.