 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Joe Guest
|
Posted: Wed Dec 15, 2004 7:48 pm Post subject: What constitutes a global variable |
|
|
I'm using the TIdTCPServer component in a Windows Service, and I'm a bit
confused at to what constitutes a global variable. The TNTService class has
some private fields defined as well as some private methods. Is it safe to
access these from the TIdTCPServer's Execute event?
Something like this: (slimmed down considerably for demonstration)
type
TCustQRouter = class(TNtService)
sockServer: TIdTCPServer;
procedure sockServerExecute(AThread: TIdPeerThread);
procedure NtServiceStart(Sender: TNtService; var DoAction: Boolean);
procedure NtServiceStop(Sender: TNtService; var DoAction: Boolean);
procedure sockServerConnect(AThread: TIdPeerThread);
procedure sockServerDisconnect(AThread: TIdPeerThread);
private
{ Private declarations }
fConnections : integer;
fRunDirectory : string;
// found in INI
fLogDirectory : string;
fCurrCarrier : string;
fCurService : string;
fSQLAttempts : integer;
procedure TCPLog(sLogFile, sLogMsg: string);
function ReGenerateLabel(var AThread: TIdPeerThread; const sScanID:
string; const sOrderNumber: string; var sRouteCode: string; var sLabelFile:
string; var sErrorMessage: string): boolean;
function ManipulateLabel(const sLabelFile: string; const sOrderNumber:
string; const sRouteCode: string; const sScannerID: string; var sErrMsg:
string): boolean;
function ConnectionString(AThread: TIdPeerThread): string;
public
{ Public declarations }
end;
procedure TCustQRouter.sockServerExecute(AThread: TIdPeerThread);
var
sInput : string;
begin
sScanID := '';
while (not self.Terminated()) and (AThread.Connection.Connected()) do
begin
// make sure the rest of the system get's it's time slices
self.ProcessRequests(false);
// read a line from the client, terminates on CR/LF or times out after 3
seconds
sInput := AThread.Connection.ReadLn(EOL, 3000);
// if the read times out, just loop back to the top
if AThread.Connection.ReadLnTimedOut then
continue;
// if the string is empty, loop back tot he top
if (sInput = '') then
continue;
// log the input received
// since this method is defined at the service level and not the thread
level, do it need to be synchronized???
self.TCPLog(self.FLogDirectory + 'todayslog.log', 'Input Received: ' +
sInput);
// [snip]
end;
end;
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Dec 15, 2004 8:02 pm Post subject: Re: What constitutes a global variable |
|
|
"Joe" <jsheble-NOSPAM (AT) logicor (DOT) com> wrote
| Quote: | The TNTService class has some private fields defined as well as some
private methods. Is it safe to access these from the TIdTCPServer's
Execute event?
|
The OnExecute event is called in the context of a worker thread. Any access
by the event handler to data that can be accessed by other threads, either
directly or indirectly, must be synchronized.
Gambit
|
|
| Back to top |
|
 |
Joe Guest
|
Posted: Wed Dec 15, 2004 8:47 pm Post subject: Re: What constitutes a global variable |
|
|
Then how would I make calls to functions and/or routines from other units,
specifically ones I've written myself? It doesn't seem likely I can call
Synchronize on them because they all either accept parameters or return
values.
Or perhaps I'm mis-understanding something very basic...
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote
| Quote: |
"Joe" <jsheble-NOSPAM (AT) logicor (DOT) com> wrote in message
news:41c0951e (AT) newsgroups (DOT) borland.com...
The TNTService class has some private fields defined as well as some
private methods. Is it safe to access these from the TIdTCPServer's
Execute event?
The OnExecute event is called in the context of a worker thread. Any
access
by the event handler to data that can be accessed by other threads, either
directly or indirectly, must be synchronized.
Gambit
|
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Dec 15, 2004 11:29 pm Post subject: Re: What constitutes a global variable |
|
|
"Joe" <jsheble-NOSPAM (AT) logicor (DOT) com> wrote
| Quote: | Then how would I make calls to functions and/or routines from other
units, specifically ones I've written myself? It doesn't seem likely I can
call Synchronize on them because they all either accept parameters or
return values.
|
That does not negate the use of Synchronize(). You can use TIdSync to help
you pass parameters to, and receive return values from, synchronized
methods.
Your other option is to make the method's internal code thread-safe, using
critical sections, mutexes, etc. Then you don't have to synch the calling
of the methods themselves.
Gambit
|
|
| Back to top |
|
 |
Remy Lebeau (TeamB) Guest
|
Posted: Wed Dec 15, 2004 11:35 pm Post subject: Re: What constitutes a global variable |
|
|
"Joe" <jsheble-NOSPAM (AT) logicor (DOT) com> wrote
| Quote: | while (not self.Terminated()) and (AThread.Connection.Connected()) do
begin
|
Do not do that inside an Indy OnExecute handler. The handler itself is
already looped by TIdTCPServer. The code inside the handler is supposed to
handle just a single iteration of the loop.
| Quote: | // make sure the rest of the system get's it's time slices
self.ProcessRequests(false);
|
Get rid of that. It is not needed. The service runs in its own thread,
separate from the thread that is triggering the OnExecute handler. Your
OnExecute code will not be blocking the service from processing its requests
queue, so there is no reason to invoke the queue processing directly like
you are.
Use this code instead:
procedure TCustQRouter.sockServerExecute(AThread: TIdPeerThread);
var
sInput, sScanID : string;
begin
// read a line from the client, terminates on CR/LF or times out
after 3 seconds
sInput := AThread.Connection.ReadLn(EOL, 3000);
// if the read times out, or if the string is empty, just loop back
to the top
if (AThread.Connection.ReadLnTimedOut) or (sInput = '') then
Exit;
// log the input received
TCPLog(FLogDirectory + 'todayslog.log', 'Input Received: ' +
sInput);
end;
| Quote: | // since this method is defined at the service level and not the
thread
level, do it need to be synchronized???
self.TCPLog(self.FLogDirectory + 'todayslog.log', 'Input Received: ' +
sInput);
|
That depends on what TCPLog() is actually doing internally. One thing I do
see is that you are accessing the FLogDirectory value. When is that value
assigned, and is that value ever changed during the service's lifetime? If
the value is ever changed, then you must synch access to it.
Gambit
|
|
| Back to top |
|
 |
Joe Guest
|
Posted: Thu Dec 16, 2004 3:10 pm Post subject: Re: What constitutes a global variable |
|
|
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote
| Quote: | while (not self.Terminated()) and (AThread.Connection.Connected()) do
begin
Do not do that inside an Indy OnExecute handler. The handler itself is
already looped by TIdTCPServer. The code inside the handler is supposed
to
handle just a single iteration of the loop.
|
When does the OnExecute handler fire off then? I had used several examples
I had seen online in putting this together, but I guess I need to research
it a bit more, eh...
| Quote: |
// make sure the rest of the system get's it's time slices
self.ProcessRequests(false);
Get rid of that. It is not needed. The service runs in its own thread,
separate from the thread that is triggering the OnExecute handler. Your
OnExecute code will not be blocking the service from processing its
requests
queue, so there is no reason to invoke the queue processing directly like
you are.
|
Ok, thanx... actually, I'm considering researching a bit more, overriding
the TIdPeerThread to come up with my own ThreadClass, and re-writing my
Windows Service using it. But I guess I need to go through my own routines
and make them thread safe...
| Quote: | That depends on what TCPLog() is actually doing internally. One thing I
do
see is that you are accessing the FLogDirectory value. When is that value
assigned, and is that value ever changed during the service's lifetime?
If
the value is ever changed, then you must synch access to it.
|
It gets assigned upon service start up, and it doesn't change thoughout the
lifetime of the service, it remains the same. Accessing global variables
(or TNTService properties/fields) read-only is certainly permissible, right?
|
|
| Back to top |
|
 |
Joe Guest
|
Posted: Thu Dec 16, 2004 4:38 pm Post subject: Re: What constitutes a global variable |
|
|
Ok, I'm still just a bit confused about creating thread-safe code. I have a
routine I use in practically all my apps, and this particular procedure gets
called from each individual thread. I'll list the routine, then finish with
my questions afterwards...
function WriteLog(sFile, sContent: string; blOverWrite: boolean = false):
boolean;
var
fHnd: TextFile;
begin
AssignFile(fHnd, sFile);
if FileExists(sFile) and blOverWrite then
ReWrite(fHnd)
else if FileExists(sFile) and (not blOverWrite) then
Append(fHnd)
else
ReWrite(fHnd);
WriteLn(fHnd, sContent);
CloseFile(fHnd);
Result := true;
end;
This routine could be called from each thread safely (the sFile variable
would be different for each thread), and since it wouldn't be accessing the
same file, then it could be conidered thread-safe, right?
"Remy Lebeau (TeamB)" <no.spam (AT) no (DOT) spam.com> wrote
| Quote: |
"Joe" <jsheble-NOSPAM (AT) logicor (DOT) com> wrote in message
news:41c0951e (AT) newsgroups (DOT) borland.com...
while (not self.Terminated()) and (AThread.Connection.Connected()) do
begin
Do not do that inside an Indy OnExecute handler. The handler itself is
already looped by TIdTCPServer. The code inside the handler is supposed
to
handle just a single iteration of the loop.
// make sure the rest of the system get's it's time slices
self.ProcessRequests(false);
Get rid of that. It is not needed. The service runs in its own thread,
separate from the thread that is triggering the OnExecute handler. Your
OnExecute code will not be blocking the service from processing its
requests
queue, so there is no reason to invoke the queue processing directly like
you are.
Use this code instead:
procedure TCustQRouter.sockServerExecute(AThread: TIdPeerThread);
var
sInput, sScanID : string;
begin
// read a line from the client, terminates on CR/LF or times out
after 3 seconds
sInput := AThread.Connection.ReadLn(EOL, 3000);
// if the read times out, or if the string is empty, just loop back
to the top
if (AThread.Connection.ReadLnTimedOut) or (sInput = '') then
Exit;
// log the input received
TCPLog(FLogDirectory + 'todayslog.log', 'Input Received: ' +
sInput);
end;
// since this method is defined at the service level and not the
thread
level, do it need to be synchronized???
self.TCPLog(self.FLogDirectory + 'todayslog.log', 'Input Received: '
+
sInput);
That depends on what TCPLog() is actually doing internally. One thing I
do
see is that you are accessing the FLogDirectory value. When is that value
assigned, and is that value ever changed during the service's lifetime?
If
the value is ever changed, then you must synch access to it.
Gambit
|
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|