Search code examples
c++builderindy

How To Sychronize Code In TIdTCPServer::Execute


I have VCL code in the TIdTCPServer::Execute method that needs to be Syncronized, how can I do this?

void __fastcall Tdata::bcServerExecute(TIdContext *AContext)
{
  String json = AContext->Connection->Socket->ReadLn();
  TJSONObject* jo = (TJSONObject*)TJSONObject::ParseJSONValue(json);
  if (jo != 0)
  {
    TJSONPair* jp = (TJSONPair*)jo->Get("text");
    String text = jp->JsonValue->Value();

    jp = (TJSONPair*)jo->Get("font_size");
    int font_size = _wtoi(jp->JsonValue->Value().c_str());

    int tw = 0;
    double text_width = 0.0;
    //================================
    TCanvas* canvas = new TCanvas();
    if (canvas)
    {
      canvas->Handle = GetDC(0);
      canvas->Font->Size = font_size;
      tw = canvas->TextWidth(text);
      double ppi = Screen->PixelsPerInch;
      text_width = double(tw) / ppi;
      ReleaseDC(0,canvas->Handle);

      delete canvas;
    }
    String buf = String(text_width);
    //==================================
    AContext->Connection->IOHandler->WriteLn(buf); 
  }
}

The code is not surprisingly periodically deadlocking.


Solution

  • Nothing in this code needs to be delegated to the main UI thread via Synchronize.

    However, when using a TCanvas in a worker thread, you do need to Lock() it while you are using it, otherwise the main UI thread will interfere with it in the background (UPDATE: specifically, the main UI thread frequently releases HDCs belonging to unlocked TBitmap.Canvas and TControlCanvas objects, even ones being used by worker threads. You are OK if you use TCanvas itself, though).

    Alternatively, simply replace the TCanvas with the corresponding Win32 API function calls instead (CreateFontIndirect(), GetTextExtentPoint32()), then you don't have to worry about locking anything.