Needs to be implemented…

Um colega meu chamado Manuel Felício está a fazer um projecto que envolve mexer com a API Java para MIDI. Algures na aplicação ele utiliza o seguinte método (não interessa qual a funcionalidade):

package javax.sound.midi
public class Sequence … {
     public Patch[] getPatchList(){…}
}

Parece que o array devolvido estava sempre vazio, por isso ele foi buscar o código fonte da classe (hoje!) e encontrou esta pérola:

public Patch[] getPatchList(){
     // $$kk: 04.09.99: need to implement!!
     return new Patch[0];
}

Muito bom…

Advertisements

Chamadas assíncronas em WCF: bloqueio de threads de callback

O meu projecto final de curso envolve uma componente de comunicação implementada em WCF que está baseada num binding bidireccional. Num dado ponto, o carregamento de dados por parte de um cliente envolve várias operações assíncronas encadeadas; por exemplo: inicialmente é feito o pedido assíncrono A; quando o pedido A está completo é lançado o pedido B, também assíncrono. Durante a execução surgiu um problema: os pedidos assíncronos eram lançados mas os resultados não eram recolhidos (o callback não era invocado) apesar da operação ser executada no serviço. Tudo indicava um problema no cliente. Depois de algumas cabeçadas na parede chegámos a uma conclusão: o problema era bloquear uma thread de callback! Naquele caso o bloqueio era o ponto sincronização de todos os resultados. Mas então, não posso usar uma thread de callback, que supostamente é uma worker thread (do ThreadPool), para fazer trabalho? Estranho… Mas voltando um pouco atrás, vou tentar replicar aqui o problema num exemplo simples.

Vamos considerar o seguinte serviço WCF, configurado com WsDualHttpBinding

[ServiceContract(Namespace = "WCFTests")]
public interface IHelloWorld
{
    [OperationContract]
    string SayHello(string name);
}

public class MyService : IHelloWorld
{
    public string SayHello(string name)
    {
        return String.Format("Hello {0}!", name);
    }
}
<service name="WCFTests.MyService">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000"  />
          </baseAddresses>
        </host>
        <endpoint address="HelloWorld" 
                  binding="wsDualHttpBinding" 
                  contract="WCFTests.IHelloWorld"/>
      </service>

Para obter um proxy para o serviço com interface assíncrona podemos utilizar a tool svcutil de linha de comando com o switch /async. Ou, mais fácil, podemos adicionar uma ServiceReference através do Visual Studio, indicando nas opções avançadas que pretendemos interface assíncrona.

 

O WCF, como é natural, segue o Asynchronous Programmming Model (APM) do .NET. Assim, o proxy terá um método BeginSayHello – que aceita o parâmetro name e um AsyncCallback – e um método EndSayHello que permite obter o resultado a partir do IAsyncResult devolvido pelo método anterior.

  IAsyncResult BeginSayHello(string name, AsyncCallback callback, object asyncState);       
  string EndSayHello(IAsyncResult result);

O método EndSayHello é bloqueante até que a resposta ao pedido seja obtida pelo que a melhor forma de utilizar este modelo é passar o callback que será invocado por uma thread do ThreadPool quando a operação terminar. Neste callback podemos depois invocar o método EndSayHello e obter o resultado.

Chegando ao problema que é o objectivo deste post, consideremos a seguinte implementação de um cliente para o serviço HelloWorld:

static void Main(string[] args)
{
    HelloWorldClient proxy = new HelloWorldClient();
    proxy.BeginSayHello(
        ".NET", 
        HelloWorldCallback, 
        proxy);
    Thread.Sleep(3000);
    proxy.BeginSayHello(
        "WCF", 
        HelloWorldBlockingCallback, 
        proxy);
    Thread.Sleep(3000);
    proxy.BeginSayHello(
        "never says hello to this one!", 
        HelloWorldCallback, 
        proxy);
    Console.ReadLine();
}

static void HelloWorldCallback(IAsyncResult iar)
{
    HelloWorldClient proxy = (HelloWorldClient)iar.AsyncState;
    Console.WriteLine(proxy.EndSayHello(iar));
}

static void HelloWorldBlockingCallback(IAsyncResult iar)
{
    HelloWorldClient proxy = (HelloWorldClient)iar.AsyncState;
    Console.WriteLine(proxy.EndSayHello(iar));
    // Bloquear a thread de callback indefinidamente
    Monitor.Enter(proxy);
    Monitor.Wait(proxy);
}

O resultado da execução deste programa nunca mostra a última mensagem na consola devido à segunda invocação do método BeginSayHello, que utiliza um callback que bloqueia a thread de callback. Desta forma, não serão processadas mais mensagens de resposta a pedidos assíncronos anteriores. Note-se que mesmo que o bloqueio seja temporário (realizar algum trabalho de forma síncrona, por exemplo), durante esse tempo não são processadas mensagens de resposta. Não sei o motivo desta situação nem se é um bug ou uma limitação de implementação. Se alguém tiver uma justificação, diga! O mais curioso é que se mudarmos o binding para um binding unidireccional (tipo BasicHttpBinding ou WsHttpBinding) o problema não existe.

Nos bindings que dão problemas a ideia que dá é que uma mensagem não é processada enquanto o callback da anterior não terminar. Talvez exista apenas uma thread a fazer o dispatch que, ao ficar bloqueada, impede o dispatch das restantes mensagens.

Vou tentar descobrir o motivo, mas até agora não tive muita sorte nem encontrei um padrão. Se alguém souber, diga! 🙂

C# 3.0

A versão 3.0 da linguagem C# já está por cá há algum tempo mas ainda assim há bastante gente que desconhece algumas das novas features: propriedades automáticas, extension methods, inferência de tipos, tipos anónimos, expressões lambda, LINQ, object and collection initializers, etc. As novidades não são fruto de alterações à plataforma .NET mas sim o resultado do trabalho da nova versão do compilador em conjunto com algumas novas bibliotecas. Algumas das funcionalidades são só umas “coisitas”, outras dão bastante jeito e tornam o código mais limpo e “bonito”.

class Person
{
    public string Name { get; set; }
    public ushort Age { get; set; }
}

static class StringExtensions
{
    public static string MyStringExtension(this string s)
    {
        return s + " after my extension";
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<Person> persons = new List<Person>
        { 
            new Person { Name = "João", Age = 18 },
            new Person { Name = "Sara", Age = 21 },
            new Person { Name = "Luís", Age = 22 }
        };

        var x = from p in persons
                where p.Age > 20
                select p.Name;

        foreach (string s in x)
            Console.WriteLine(s.MyStringExtension());
            // "Sara after my extension"
            // "Luís after my extension"
    }
}

Muito “estranho”? 🙂 Como já muito foi escrito sobre o assunto, deixo aqui o link para um conjunto de tutorials escritos pelo David Hayden, um MVP na área do C#, que abordam desde os pontos mais simples até aos mais complexos, incluindo alguns vídeos e apresentações.

Microsoft Student Partners 08/09

Chegando ao fim o ano lectivo 07/08 aproxima-se também do final a edição correspondente do programa MSP. Chegando ao fim esta edição, é altura de formar uma nova euipa para o novo ano. Dexo aqui um link para um post do Chaves com mais informação sobre as candidaturas e sobre o programa em si.
 
Enquanto MSP posso dizer que o programa é um "gerador de oportunidades" a nível pessoal, técnico e assim-assim =): conhecer outros estudantes, conhecer pessoas da Microsoft, criar contactos, ter um canal amplo para troca de conhecimento/informação, ter incentivo e apoio para fazer mais e melhor, acesso a alguns eventos e conteúdos,  etc etc etc… Tudo depende da vontade que cada um tem de crescer, aproveitar a oportunidade, partilhar o seu conhecimento, envolver a comunidade académica na tecnologia..como diria o grande Miguel Vicente: "ter vontade de ser excepcional". Tens essa vontade?