-1

I'm using Delphi XE2 and my application is used to notify about new records in twitter/rss. In my application I use 2 threads to grab some data from twitter & rss every second.

Here is the code:

type section:

  TWarframeEvent=record
    GUID: String;
    EventType: Byte; // 0 = unknown; 1 = alert; 2 = invasion; 3 = infestation
    Planet: String;
    Mission: String;
    EventDate: TDateTime;
    Time: Integer;
    RewardCredits: LongWord;
    RewardOther: String;
    RewardOtherAmount: Integer;
    Notified: Boolean;
    ItemIndex: Integer;
    Hidden: Boolean;
  end;

  TWarframeNotifyEvent=record
    NotifyTimeLeft: LongWord;
    ID: Integer;
    FlashOnTaskbar: Boolean;
    PlaySound: Boolean;
    Volume: Integer;
    TrayPopupBalloon: Boolean;
  end;

  TWarframeEventList=record
    WarframeEvent: Array of TWarframeEvent;
    WarframeEventCount: Integer;
    NotifyEvent: TWarframeNotifyEvent;
  end;


  TUpdateFromTwitterThread=class(TThread)
    TwitterURL: String;
    Procedure Execute; override;
  end;

  TUpdateFromRSSThread=class(TThread)
    RSS_URL: String;
    Procedure Execute; override;
  end;

var section (module)

  WarframeEventList: TWarframeEventList;

implementation section:

procedure TForm1.TimerUpdateEventsTimer(Sender: TObject);
begin
  UpdateFromTwitterThread:=TUpdateFromTwitterThread.Create(True);
  UpdateFromTwitterThread.TwitterURL:=form2.EditAlertsURL.Text;
  UpdateFromTwitterThread.Start;
  UpdateFromRSSThread:=TUpdateFromRSSThread.Create(True);
  UpdateFromRSSThread.RSS_URL:=form2.EditInvansionsURL.Text;
  UpdateFromRSSThread.Start;
end;

procedure TUpdateFromTwitterThread.Execute;
var
  HTTPClient: TIdHTTP;
  IOHandler: TIdSSLIOHandlerSocketOpenSSL;
  S, S2: String;
  i, l: Integer;
  NewAlertDate: TDateTime;
  ErrorLogFile: TextFile;
begin
  HTTPClient:=TIdHTTP.Create(nil);
  IOHandler:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  try
    try
      HTTPClient.IOHandler:=IOHandler;
      HTTPClient.HandleRedirects:=True;
      S:=HTTPClient.Get(self.TwitterURL);
    except
      Assign(ErrorLogFile, AppPath+'Error.log');
      if not(FileExists(AppPath+'Error.log')) then
        Rewrite(ErrorLogFile);
      Append(ErrorLogFile);
      Writeln(ErrorLogFile, DateTimeToStr(Now)+' '+LS_ErrorConnection+' '+self.TwitterURL+'; Error code: '+IntToStr(HTTPClient.ResponseCode));
      Close(ErrorLogFile);
      self.Terminate;
      HTTPClient.Free;
      IOHandler.Free;
    end;
  finally
    HTTPClient.Free;
    IOHandler.Free;
  end;

  if Application.Terminated or self.Terminated then exit;

  S:=copy(S, pos('<b>WarframeAlerts</b></span>', S), Length(S)-pos('<b>WarframeAlerts</b></span>', S));

  while pos('tweet-timestamp js-permalink js-nav', S)>0 do begin
    S:=copy(S, pos('tweet-timestamp js-permalink js-nav', S)+35, Length(S)-pos('tweet-timestamp js-permalink js-nav', S)-35);
    S2:=copy(S, pos('data-time="', S)+11, Length(S)-pos('data-time="', S)-11);
    NewAlertDate:=StrToInt(copy(S2, 1, pos('"', S2)-1));
    S2:=copy(S, pos('<p class="js-tweet-text tweet-text">', S)+36, pos('</p>', S)-pos('<p class="js-tweet-text tweet-text">', S)-36);
    for i:= 0 to WarframeEventList.WarframeEventCount-1 do
      if (WarframeEventList.WarframeEvent[i].EventDate=NewAlertDate) and (WarframeEventList.WarframeEvent[i].Planet=copy(S2, 1, pos(')', S2))) then
        NewAlertDate:=0;

    if NewAlertDate=0 then continue;

    Inc(WarframeEventList.WarframeEventCount);
    SetLength(WarframeEventList.WarframeEvent, WarframeEventList.WarframeEventCount);

    for i:= 0 to WarframeEventList.WarframeEventCount-2 do begin
      if WarframeEventList.WarframeEvent[i].EventDate>NewAlertDate then begin
        for l:=WarframeEventList.WarframeEventCount-1 downto i+1 do
          WarframeEventList.WarframeEvent[l]:=WarframeEventList.WarframeEvent[l-1];
        Break;
      end;
    end;

    if i<=WarframeEventList.NotifyEvent.ID then Inc(WarframeEventList.NotifyEvent.ID);

    WarframeEventList.WarframeEvent[i].GUID:='';
    WarframeEventList.WarframeEvent[i].ItemIndex:=-2;
    WarframeEventList.WarframeEvent[i].EventType:=1;
    WarframeEventList.WarframeEvent[i].Planet:=copy(S2, 1, pos(')', S2));
    S2:=copy(S2, Length(WarframeEventList.WarframeEvent[i].Planet)+3, Length(S2)-Length(WarframeEventList.WarframeEvent[i].Planet));
    WarframeEventList.WarframeEvent[i].Mission:=copy(S2, 1, pos(' -', S2)-1);
    WarframeEventList.WarframeEvent[i].EventDate:=NewAlertDate;
    S2:=copy(S2, Length(WarframeEventList.WarframeEvent[i].Mission)+4, Length(S2)-Length(WarframeEventList.WarframeEvent[i].Mission));
    WarframeEventList.WarframeEvent[i].Time:=StrToInt(copy(S2, 1, pos('m', S2)-1))-1;
    S2:=copy(S2, pos('-', S2)+2, Length(S2)-pos('-', S2));
    WarframeEventList.WarframeEvent[i].RewardCredits:=StrToInt(copy(S2, 1, pos('cr', S2)-1));
    WarframeEventList.WarframeEvent[i].RewardOther:=copy(S2, pos('cr', S2)+5, Length(S2)-pos('cr', S2));
    WarframeEventList.WarframeEvent[i].RewardOtherAmount:=1;
    WarframeEventList.WarframeEvent[i].Notified:=False;
    WarframeEventList.WarframeEvent[i].Hidden:=False;
  end;

  self.Free;
end;

procedure TUpdateFromRSSThread.Execute;
var
  HTTPClient: TIdHTTP;
  IOHandler: TIdSSLIOHandlerSocketOpenSSL;
  S, S2, S3: String;
  i: Integer;
  NewEventType: Byte;
  ErrorLogFile: TextFile;
begin
  HTTPClient:=TIdHTTP.Create(nil);
  IOHandler:=TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  try
    try
      HTTPClient.IOHandler:=IOHandler;
      HTTPClient.HandleRedirects:=True;
      S:=HTTPClient.Get(self.RSS_URL);
    except
      Assign(ErrorLogFile, AppPath+'Error.log');
      if not(FileExists(AppPath+'Error.log')) then
        Rewrite(ErrorLogFile);
      Append(ErrorLogFile);
      Writeln(ErrorLogFile, DateTimeToStr(Now)+' '+LS_ErrorConnection+' '+self.RSS_URL+'; Error code: '+IntToStr(HTTPClient.ResponseCode));
      Close(ErrorLogFile);
      self.Terminate;
      HTTPClient.Free;
      IOHandler.Free;
    end;
  finally
    HTTPClient.Free;
    IOHandler.Free;
  end;

  if Application.Terminated or self.Terminated then exit;

    for i:= 0 to WarframeEventList.WarframeEventCount-1 do
      if (WarframeEventList.WarframeEvent[i].EventType=2) or (WarframeEventList.WarframeEvent[i].EventType=3) then
        WarframeEventList.WarframeEvent[i].Time:=0;

  while pos('<item>', S)>0 do begin
    S:=copy(S, pos('<item>', S)+6, Length(S)-pos('<item>', S)-6);
    S2:=LowerCase(copy(S, pos('<author>', S)+8, pos('</author>', S)-pos('<author>', S)-8));
    NewEventType:=0;
    if S2='alert' then
      NewEventType:=1;
    if S2='invasion' then
      NewEventType:=2;
    if S2='outbreak' then
      NewEventType:=3;

    if NewEventType=1 then
      Continue;

    S2:=LowerCase(copy(S, pos('<guid>', S)+6, pos('</guid>', S)-pos('<guid>', S)-6));
    for i:= 0 to WarframeEventList.WarframeEventCount-1 do
      if ((WarframeEventList.WarframeEvent[i].EventType=2) or (WarframeEventList.WarframeEvent[i].EventType=3)) and (WarframeEventList.WarframeEvent[i].GUID=S2) then begin
        WarframeEventList.WarframeEvent[i].Time:=1;
        NewEventType:=255;
      end;

    if NewEventType=255 then
      Continue;

    Inc(WarframeEventList.WarframeEventCount);
    SetLength(WarframeEventList.WarframeEvent, WarframeEventList.WarframeEventCount);

    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].ItemIndex:=-2;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].GUID:=S2;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].EventType:=NewEventType;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Time:=1;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:='';
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=0;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=0;
    S2:=copy(S, pos('<title>', S)+7, pos('</title>', S)-pos('<title>', S)-7);
    if NewEventType=2 then begin
      S2:=Copy(S2, 1, pos(' - ', S2)-1);
      WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:=S2;
      S3:=Copy(S2, 1, pos('VS.', S2)-1);
      S3:=Copy(S3, pos('(', S3), Length(S3)-pos('(', S3)+1);
      if pos('x ', S3)>0 then
        WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S3, 2, pos('x ', S3)-2));
      if pos('K)', S3)>0 then
        WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000;
      S3:=Copy(S2, pos('VS.', S2)+4, Length(S2)-pos('VS.', S2)-3);
      S3:=Copy(S3, pos('(', S3), Length(S3)-pos('(', S3)+1);
      if pos('x ', S3)>0 then
        if WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount<StrToInt(copy(S3, 2, pos('x ', S3)-2)) then
          WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S3, 2, pos('x ', S3)-2));
      if pos('K)', S3)>0 then
        if WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits<StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000 then
          WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S3, 2, pos('K)', S3)-2))*1000;
    end;
    if NewEventType=3 then begin
      S2:=Copy(S2, 1, pos(' - ', S2)-1);
      WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:='';
      if pos('x ', S2)>0 then begin
        WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOtherAmount:=StrToInt(copy(S2, 1, pos('x ', S2)-1));
        WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardOther:=S2;
      end;
      if copy(S2, Length(S2), 1)='K' then
        WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].RewardCredits:=StrToInt(copy(S2, 1, Length(S2)-1))*1000;
      S2:=copy(S2, 1, Length(S2)-1);
    end;
    S2:=copy(S, pos('<title>', S)+7, pos('</title>', S)-pos('<title>', S)-7);
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Planet:=Copy(S2, pos(' - ', S2)+3, Length(S2)-pos(' - ', S2));
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Mission:=copy(S, pos('<author>', S)+8, pos('</author>', S)-pos('<author>', S)-8);
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Notified:=False;
    WarframeEventList.WarframeEvent[WarframeEventList.WarframeEventCount-1].Hidden:=False;
  end;

  self.Free;
end;

The questions are:

1) Is there a way to re-use threads without destroying/re-creating them every minute? Needless to say, this causes memory fragmentation and programs starts to bloat after a day of non-stop work. I do not ask how to make a looped thread since they are not applicable here.

2) When is the right time to free threads? Should I free it before they finish their work or before I create them in timer handler (the one which fires every minute)? Or maybe thread suicides after execute is finished?

3) Should I make

HTTPClient: TIdHTTP;
IOHandler: TIdSSLIOHandlerSocketOpenSSL;

an external (global) objects to avoid destroying/re-creating them every minute?

4) When I try to close my application, it deadlocks if threads were active. Is there any way to terminate threads even if they didn't finish their work or maybe detach them?

  • I think there are too many questions here. I think I could answer some of them but can't face trying to answer them all. – David Heffernan Apr 19 '14 at 11:53
  • Do not periodically create and destroy them. Make a loop like `while not Terminated do` inside the `Execute` method and wait for an exit event for some period of time (which you want to use for checking those online channels). If the waiting function returns timeout result, you can check those channels. If it returns signalled state of the event, you can exit the `Execute` method which ends the thread context. – TLama Apr 19 '14 at 11:54
  • In pseudo-code e.g. [`like this`](http://pastebin.com/36Mz616N). – TLama Apr 19 '14 at 13:05
  • If you "free" a thread then you can't "reuse" it. – Jerry Dodge Apr 19 '14 at 14:05
  • Ouch. You cannot call `Free` from inside a thread's `Execute` method. There are many large problems with this code. There's some double frees and clearly dangerous data races. You are simply asking the wrong questions. You need to make your code correct. Once you do that you don't need to worry about creating new threads every minute. No problem at all to do that. You've misdiagnosed your problem. Your problem is that you code is horribly broken. – David Heffernan Apr 19 '14 at 15:55
  • Regarding this statement, "I do not ask how to make a looped thread since they are not applicable here.". How do you expect the thread to perform repeated tasks without a loop? I think perhaps you need to define what you mean by "re-use". But there's really two completely different issues here. There's the original general question which mjn answered well. And there's your very broken code. – David Heffernan Apr 19 '14 at 15:58
  • Thanks for the straight answer Jerry. What if I don't free it after execetion and use .Start every minute? Will it actually start a thread or just run it in main thread of application? – Sonic Son'edit Apr 19 '14 at 15:59
  • @sonic A thread runs its Execute method and then it is done. Eventually you will realise that you need a loop. At some point you'll start accepting help. – David Heffernan Apr 19 '14 at 16:01
  • David, I know I can't free thread from inside it. This was one of desperate attempts to fix deadlocks. Secondly, free checks if object is not nil before destroying, so it's safe to use multiple free - one for success and one for exception. Anything else about my code being "broken" except self.free? – Sonic Son'edit Apr 19 '14 at 16:02
  • `Free` does indeed perform a check for `nil`. But when you call in the finally, it won't be `nil`. – David Heffernan Apr 19 '14 at 16:06
  • TLama, and this is why I'm here. I checked your suggestion - I can see you use a timeout timer (event), I tried this to resolve a deadlock, to no avail. I also don't see how do you suggest to create a one minute delay. Thanks for input buffer hint. – Sonic Son'edit Apr 19 '14 at 16:08
  • David, What exactly do you mean? That finally will fire after exception and try to destroy an object that already free? – Sonic Son'edit Apr 19 '14 at 16:09
  • You call `Free` twice for each of those objects. Once in the exception handler and once in the finally block. Both will execute in case of exception. You simply need to remove the calls to `Free` in the exception handler. – David Heffernan Apr 19 '14 at 16:12
  • David, thanks for that, I thought it will be nil next time free is called, but you are right, it actually is not nil. This should look something like this? http://pastebin.com/cWGbJUzE – Sonic Son'edit Apr 19 '14 at 16:18
  • Yes that avoids double free. Calling Terminate is pointless though. Just call exit. Anyway I think your entire approach is wrong. My answer tries to explain. – David Heffernan Apr 19 '14 at 16:19
  • @Sonic, thread constructor's `UpdateTime` parameter is the time in ms, so to have 1 minute period would mean pass there value 60000. If you read both answers here and look at the code I've posted, you'll see it's just an implementation of those advices. But it seems you still tend to waste system resources for killing and creating new threads. Never mind. Good luck! ;-) – TLama Apr 19 '14 at 16:27
  • 1
    TLama, thanks! I think I will indeed re-approach the problem and try to implement looped thread. – Sonic Son'edit Apr 19 '14 at 16:28

2 Answers2

6

You've asked a lot of questions here, and then added a large amount of code. The code is very badly broken. I can see:

  • Erroneous call to Self.Free from inside Execute. That is simply wrong. You must use FreeOnTerminate if you want a self-destroying thread.
  • You have double free of HTTPClient and IOHandler in case of exception.
  • There appears to be a dangerous data race in the list that you mutate in those two threads.

I am sure that there are many other errors in your code.

But I don't want to get into that. And I don't want to address all four questions that you asked directly. I will restrict myself to one only. Deal with that and all other problems go away I believe.

Is there a way to re-use threads without destroying/re-creating them every minute?

In the comments you also say:

What if I don't free it after exception and call Start every minute?

This seems to me to indicate the fundamental misunderstanding that you have. A thread's code is nothing more than a single function call. In the case of TThread that function is Execute. When the thread starts, Execute is called. When Execute returns, that thread's useful life is over. It cannot be restarted once Execute returns.

What this means is that if you want a single thread to perform multiple repetitions of a single task, then you need to implement a loop inside the Execute method.

You also state that:

I do not ask how to make a looped thread since they are not applicable here.

I'm sorry, but that is not correct. The way that you make a thread perform a task multiple times is by looping.


More generally I feel that you would be well served by availing yourself of a higher level parallel library. The best available is OTL.

If you do not wish to take that advice then here is how I would go about structuring your program:

  1. Remove the timer.
  2. Create two threads, and have them looping indefinitely until terminated. That's a standard while not Terminated loop in the Execute method.
  3. Have the threads do their work in each iteration of the loop.
  4. When they are done working, they need to wait for a minute (or however long). Do that by waiting on an event for a specified timeout.
  5. That event can be known to the controlling master thread which uses the event to cancel the threads when it is time to quit.
  6. So in order to quit the master thread calls Terminate on the two threads, sets the cancel event, and calls Free on the two threads. Then the application is safe to terminate.

If you architect your program like this all the other questions go away. You only create two threads for the entire life of the program. That deals with questions 1, 2 and 4. As for question 3, you keep those objects as local variables. They are local to the thread and should be locals under Execute. Or better still, locals in helper methods that Execute calls. Your Execute methods are very large.

Separate to this, is the data race on the list. I cannot give you detailed advice on how to fix that. Clearly some serialization is called for.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks David, this is exactly what I wanted to hear - what happens to thread after execute is done. Your answer basically covers all I wanted to know - what is possible and what is not. Thank you for your detailed reply and sorry for acting like that before. About data race: there was only one thread before, so there was no reason for semaphore, but yes, I need it now. – Sonic Son'edit Apr 19 '14 at 16:24
  • Ok, no probs. Often when you don't get the answer that you expect it indicates something up with the question. And then both sides need to try to work out where the disconnect was. That's what I was trying to do. I think we got there in the end and thanks for staying with us. – David Heffernan Apr 19 '14 at 16:26
  • Thanks a lot for the answer once again! There is just one more question I wanted to ask - if I understand correctly, you suggest to use master thread to control child threads instead of letting them self-control? Why not just set termination event in procedure which is called before terminating application? – Sonic Son'edit Apr 19 '14 at 16:36
  • I don't really understand that. But clearly there is a master thread. You create two other threads. Is your app a service or a background app? Self-control sounds wrong though. The threads work. Then something external to them needs to tell them to quit. – David Heffernan Apr 19 '14 at 16:42
  • @David, that master thread should except signalling termination event also cancel a running HTTP client request. That's what you can safely do in some `Cancel` like method, which can be called by the master thread. – TLama Apr 19 '14 at 16:47
  • @TLama OK. That's not my area of expertise as I'm sure you know. Thanks. I might be tempted to do ExitProcess but suggesting it might lead to flames! – David Heffernan Apr 19 '14 at 16:49
  • David, yes, it's a background. David, TLama, thanks a lot for your answers! I assume that _not_ canceling a running http request may be the cause of the deadlock? – Sonic Son'edit Apr 19 '14 at 16:55
2
  1. the easiest way to re-use the thread is to run a loop within Execute (instead of executing the operation only once and then terminate), and checking while not Terminated or waiting for an event in the loop to allow clean shutdown
  2. free the threads when you no longer need them: you can either use Terminate or a signal to indicate that they should end their work. If FreeOnTerminateis True, there is no need for additional termination and cleanup code in the main thread
  3. no, local (private) variables within the thread are better because the thread should have as little external dependencies as possible
  4. there can be many reasons for deadlocks, but the risk of deadlocks is reduced if you can make them fully independent from "outer resources"
mjn
  • 36,362
  • 28
  • 176
  • 378
  • -Wow. Like I never seen this answer before. I know just perfectly how to make looped threads, I used them for sockets in other application. Now look at the code and tell me how exactly do I run it in the loop in gracefull way in what I posted above. -Thanks for info about FreeOnTerminate. If I understand correctly, It will call self.free after execute is finished? -Yes, I know this, but once again - creating/destroying large objects like the ones in code above every once a minute causes program to bloat due to memory fragmentation. – Sonic Son'edit Apr 19 '14 at 15:24
  • @sonic Please be more polite. Your first sentence in the comment above is condescending. If you already knew the answer why did you ask the question? – David Heffernan Apr 19 '14 at 15:27
  • David, the reason why my first comment is agressive is because my answer is misunderstood. I do not need an answer how to make a looped thread which uses "if (Self.Terminated) or (Application.Terminated) then exit;", my question is how to reuse a thread. Finally, abour 4) - As you can see my threads checks if it's terminated before working with outer resources (a db where it stores parsed data) and it never overlaps with GUI. It _should_ not deadlock, yet it does. – Sonic Son'edit Apr 19 '14 at 15:29
  • 2
    You reuse a thread by not destroying it in the first place. Which means looping and waiting for new work. Your deadlock is because your code is broken. You did not show any code so don't expect any detailed advice. You asked too many questions all at once. Your question needs attention. When somebody tried to help you told them that they were stupid (that's how it reads) and that you already knew what you were being told. – David Heffernan Apr 19 '14 at 15:39
  • David, now you are being quite agressive. Anyway, I believe I clearly stated my question: is it possible to reuse a thread, not "how to create a looped thread". I never called anyone stupid, I just think that people should answer the question which was asked, not the one they make up themselves, don't you think so? – Sonic Son'edit Apr 19 '14 at 15:42
  • Secondly, I will add full code. None of this code relates to the question, but if you want a wall of text in question, so be it. Now please tell me, where it is broken. – Sonic Son'edit Apr 19 '14 at 15:43
  • Nobody has made a question up. What mjn wrote in his section 1 is how you use a thread for multiple tasks. How else are you going to do it? Clearly you need to loop. Fundamentally you've asked a bad question. Until you admit that to yourself you can expect responses like this. As I said from the off the question is actually four questions. It's way too broad. And it seems that you did not even ask the question you meant to. Please try harder. And no, we don't want a wall of code. – David Heffernan Apr 19 '14 at 15:47
  • David, you just said: >Your deadlock is because your code is broken. You did not show any code so don't expect any detailed advice. And then you say you don't want a wall of code. I omitted all code which _can not_ cause a deadlock, but you just said "no, give this code back" and now you say "no we actually dont need it". Please select one. Secondly, I clearly stated "reuse" thread. I think it's pretty much different from "using indefinitely". And finally, all of my questions are related to the same problem. One more thing - if you accuse someone, please argument your statements. – Sonic Son'edit Apr 19 '14 at 15:49
  • @SonicSon'edit 1) "Wall of code" means a whole bunch of code, but on Stack Overflow, we expect an SSCCE, 2) It wasn't clear what you actually meant by "reuse", it seems you have a different definition than we do. – Jerry Dodge Apr 20 '14 at 04:20