Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Execute method isn't called in thread under Android...



Permlink Replies: 2 - Last Post: Jul 27, 2017 8:28 PM Last Post By: Peter Stefanos
Peter Stefanos

Posts: 5
Registered: 11/30/00
Execute method isn't called in thread under Android...
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 7:20 PM
Hello All,

In Builder C++ V10.2, I have a simple timer adding list view items to a list view in 2 ways. The first way adds a list view item to a list view in the main GUI thread directly, the second does the same via a thread and calling the Synchronize method. Both messages appear if I run this code on Win32, but only the non-threaded message appears running under Android. Under Android, if I add a break point at the Execute() method, it never gets there.
I have probably missed the obvious. Any suggestions?

Thanks in advance,
Peter.

In the header file:
class TThreadedMessage : public TThread
{
public:
	String Message;
 
	__fastcall TThreadedMessage();
	void __fastcall Execute();
	void __fastcall MessageFunction(void);
};


and in the main file:

//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
	TListViewItem *Item = Form1->ListView->Items->Add();
	Item->Text = String("Regular Hello");
 
	ThreadedMessage("Threaded Hello");
}
 
//---------------------------------------------------------------------------
void ThreadedMessage(String TheMessage)
{
	// Instantiate class and save the parameter away.
	TThreadedMessage *LM = new TThreadedMessage();
	try {
		LM->Message = TheMessage;
	}
	catch (const Exception &E) {
		delete LM;
		throw;
	}
	// LM self destroys... allegedly, so no need to call delete.
}
 
//---------------------------------------------------------------------------
__fastcall TThreadedMessage::TThreadedMessage() :
TThread(false)
{
}
 
//---------------------------------------------------------------------------
void __fastcall TThreadedMessage::Execute()
{
	Synchronize(MessageFunction);
}
 
//---------------------------------------------------------------------------
void __fastcall TThreadedMessage::MessageFunction(void)
{
	TListViewItem *Item = Form1->ListView->Items->Add();
	Item->Text = Message;
}
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Execute method isn't called in thread under Android...
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 8:03 PM   in response to: Peter Stefanos in response to: Peter Stefanos
Peter Stefanos wrote:

In Builder C++ V10.2, I have a simple timer adding list view items to
a list view in 2 ways. The first way adds a list view item to a list
view in the main GUI thread directly, the second does the same via a
thread and calling the Synchronize method.

Why are you running a thread whose sole purpose is to call
Synchronize()? That really defeats the pupose of a thread. You may as
well just use the new TThread::ForceQueue() method that was added in
10.2, which will get you the same effect without using a thread at all.

In any case, there are a couple of bugs with your ThreadedMessage()
implementation.

First, you are constructing the TThread object with
CreateSuspended=false, so it will start running automatically after the
constructor exits. Thus, it is possible that the thread's Execute()
method to be called before you have had a chance to assign the thread's
Message value. Your use of Synchronize() is protecting the Message
from being used until after Timer1Timer() exits, but this is bad
design. You should be setting the Message value before the thread ever
starts running, either by:

1. constructing the TThread object with CreateSuspended=true, then
calling its Start() method after setting its Message:

void ThreadedMessage(String TheMessage)
{
    TThreadedMessage *LM = new TThreadedMessage();
    try {
        LM->Message = TheMessage;
        LM->Start();
    }
    catch (...) {
        delete LM;
        throw;
    }
}
 
__fastcall TThreadedMessage::TThreadedMessage()
    : TThread(true)
{
}


2. construct the TThread with CreateSuspended=false, and pass the
desired string value to the TThread constructor.

void ThreadedMessage(String TheMessage)
{
    TThreadedMessage *LM = new TThreadedMessage(TheMessage);
}
 
__fastcall TThreadedMessage::TThreadedMessage(String TheMessage)
    : TThread(false), Message(TheMessage)
{
}


Second, you have a memory leak on Windows, because your thread object
IS NOT SELF DESTROYING, like you claim! You have to explicitly set the
thread's FreeOnTerminate property to true for that to happen, but you
are not doing that:

__fastcall TThreadedMessage::TThreadedMessage(...)
    : ...
{
    FreeOnTerminate = true; // <-- add this
}


However, on ARC-based platforms, like Android, Delphi-style objects are
reference counted, and your LM variable is likely going out of scope
before the thread actually starts running. Execute() is not called if
the thread's Terminated property is already set to true when the thread
starts running, and the TThread destructor does call Terminate() and
wait for the thread to terminate, so that could explain why you don't
see Execute() being called.

Also, TThread internally increments its own reference count on ARC
platforms (so the thread can still run even if noone is referencing
it), however it does not perform that increment until the thread
actually starts running, so you need to maintain an active reference to
your thread object until that time, or else you will get a premature
destruction. This issue does not exist on Windows.

ARC bites again!

--
Remy Lebeau (TeamB)
Peter Stefanos

Posts: 5
Registered: 11/30/00
Re: Execute method isn't called in thread under Android...
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jul 27, 2017 8:28 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Peter Stefanos wrote:

In Builder C++ V10.2, I have a simple timer adding list view items to
a list view in 2 ways. The first way adds a list view item to a list
view in the main GUI thread directly, the second does the same via a
thread and calling the Synchronize method.

Why are you running a thread whose sole purpose is to call
Synchronize()? That really defeats the pupose of a thread. You may as
well just use the new TThread::ForceQueue() method that was added in
10.2, which will get you the same effect without using a thread at all.
Hi Remy,

Thanks for the response. I probably should have explained that I have a much bigger program that uses a TIdTCPClient instance to connect to a server. Logging messages from various other threads wasn't working so I created a simple project that illustrated the observed behaviour. (probably wasn't a great example project)
I have implemented your changes and now it works.

Thank you!
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02