exception class : EAccessViolation
exception message : Access violation at address 7C918FEA in module
'ntdll.dll'. Write of address 00000010.
thread $834:
7c918fea +5b ntdll.dll
RtlpWaitForCriticalSection
7c901046 +41 ntdll.dll RtlEnterCriticalSection
0046b310 +10 Server.exe Classes 3135 +1 TThreadList.LockList
0046b337 +0f Server.exe Classes 3141 +1 TThreadList.Remove
0052a987 +23 Server.exe IdContext 142 +2 TIdContext.Destroy
00403c28 +08 Server.exe System 8393 +1 TObject.Free
0045b38f +1b Server.exe SysUtils 16274 +3 FreeAndNil
0052b89b +17 Server.exe IdThread 544 +1 TIdThreadWithTask.Destroy
00403c28 +08 Server.exe System 8393 +1 TObject.Free
00474d63 +93 Server.exe Classes 9382 +17 ThreadProc
00404ad8 +28 Server.exe System 11562 +33 ThreadWrapper
0044ea51 +0d Server.exe madExcept CallThreadProcSafe
0044eabb +37 Server.exe madExcept ThreadExceptFrame
Main ($85c):
7c90eb94 +000 ntdll.dll
KiFastSystemCallRet
7c90e9a9 +00a ntdll.dll
NtWaitForMultipleObjects
7c8094dc +000 kernel32.dll
WaitForMultipleObjectsEx
7e4195f3 +000 user32.dll
MsgWaitForMultipleObjectsEx
7e4196a3 +01a user32.dll
MsgWaitForMultipleObjects
004752d8 +054 Server.exe Classes 9716 +11 TThread.WaitFor
0052d29f +07b Server.exe IdTCPServer 664 +10
TIdTCPServer.TerminateListenerThreads
0052d44d +011 Server.exe IdTCPServer 730 +4 TIdTCPServer.Shutdown
0052d05d +049 Server.exe IdTCPServer 578 +12 TIdTCPServer.SetActive
00563495 +195 Server.exe ConfClass 315 +61 TConf.Destroy
00403c28 +008 Server.exe System 8393 +1 TObject.Free
00482681 +021 Server.exe Contnrs 304 +3 TObjectList.Notify
0046ab84 +08c Server.exe Classes 2779 +9 TList.Delete
0046b00e +026 Server.exe Classes 2910 +3 TList.Remove
004826aa +012 Server.exe Contnrs 310 +1 TObjectList.Remove
0056cb51 +015 Server.exe Users 1542 +1
TUserList.DestroyPrivateConf
00561f5b +01b Server.exe MainUnit 194 +1
TMainForm.DestroyPrivateConf
004b3377 +1df Server.exe Controls 4645 +53 TControl.WndProc
004b6f46 +18e Server.exe Controls 6342 +33 TWinControl.WndProc
004d0d9c +478 Server.exe Forms 3097 +103 TCustomForm.WndProc
004b6b18 +034 Server.exe Controls 6237 +3 TWinControl.MainWndProc
004771fc +014 Server.exe Classes 10966 +8 StdWndProc
7e4196c2 +00a user32.dll
DispatchMessageA
004d88b4 +0ac Server.exe Forms 6872 +13
TApplication.ProcessMessage
004d88fb +00f Server.exe Forms 6891 +1
TApplication.HandleMessage
004d8b96 +0a6 Server.exe Forms 6975 +16 TApplication.Run
0056e573 +053 Server.exe Server 29 +4 initialization
disassembling:
[...]
0046b300 3134 push ebp
0046b301 mov ebp, esp
0046b303 add esp, -8
0046b306 mov [ebp-4], eax
0046b309 3135 mov eax, [ebp-4]
0046b30c add eax, 8
0046b30f push eax
0046b310 > call -$63c85 ($407690) ;
Windows.EnterCriticalSection
0046b310
0046b315 3136 mov eax, [ebp-4]
0046b318 mov eax, [eax+4]
0046b31b mov [ebp-8], eax
0046b31e mov eax, [ebp-8]
0046b321 3137 pop ecx
0046b322 pop ecx
[...]
> What's the reason of exception below? I got this exception
> when I attemp to deactivate TIdTCPServer.
It is hard to say without seeing your actual server code. A stack trace is
not very helpful without seeing the code that triggered it, or what the code
does at each step along the way. Offhand, it looks like TIdTCPServer is
crashing internally when trying to remove a TIdContext pointer from its
internal list. Which version of Indy 10 are you actually using?
> For each client disconnected from server i set AContext to
> nil in OnDisconnect so Indy wont try to free it.
That will not stop TIdTCPServer from freeing the TIdContext object. You are
setting a variable that is local to the event handler only, not inside the
server. You shouldn't be setting TIdContext pointers to nil yourself
anyway.
Gambit
I just attemped to include anything that deal with Indy in my server. sorry
if the code is a little long
procedure TRoom.IdTCPServerConnect(AContext: TIdContext);
var
User: TRoomUser;
begin
User := TRoomUser.Create(Self);
User.Context := AContext;
User.LastIncomingPacketTime := Now;
User.IPAddress := AContext.Connection.Socket.Binding.PeerIP;
FUserListCriticalSection.Enter;
FUserList.Add(User);
FUserListCriticalSection.Leave;
AContext.Data := User;
end;
procedure TRoom.IdTCPServerDisconnect(AContext: TIdContext);
var
i: Integer;
UserLeaving: string;
TerminateTimeOut: Integer;
begin
FUserListCriticalSection.Enter;
if UserList.Count < 2 then
begin
FUserList.Delete(0);
AContext.Data := nil;
Terminate;
TerminateTimeOut := 0;
FUserListCriticalSection.Leave;
while not FThreadTerminated do
begin
Sleep(1);
if TerminateTimeOut>50 then
Exit; // should be handled later
end;
if IsPrivate then // destructure will be called from MainForm
PostMessage(MainForm.Handle,WCM_DestroyMePrvRoom,Integer(Self),0)
else
PostMessage(MainForm.Handle,WCM_DestroyMeRoom,Integer(Self),0);
Exit;
end;
for i := 0 to UserList.Count - 1 do
begin
if TRoomUser(UserList.Items[i]).Context = AContext then
begin
UserLeaving := TRoomUser(UserList.Items[i]).UserName;
if TRoomUser(UserList.Items[i]) = UserOnMic then
UserOnMic := nil;
if TRoomUser(UserList.Items[i]).RaiseHand then
Dec(FHandRaisesCount);
FUserList.Delete(i);
AContext.Data := nil;
UserLeftTheRoomNotify(UserLeaving);
break;
end;
end;
FUserListCriticalSection.Leave;
end;
procedure TRoom.IdTCPServerExecute(AContext: TIdContext);
begin
TRoomUser(AContext.Data).ProcessSocketOperation;
end;
procedure TRoomUser.ProcessSocketOperation;
var
TmpBuff: TBytes;
Buffer: Pointer;
Len: Integer;
begin
Sleep(5);
FSocketCriticalSection.Enter;
Len := FContext.Connection.IOHandler.InputBuffer.Size;
if Len>=SizeOf(TCommunicatorPacket) then
begin
FContext.Connection.IOHandler.ReadBytes(TmpBuff,Len);
if Cardinal(Len)+FPacketBufferPtr<USER_PACKET_BUFER_LENGTH then
begin
CopyMemory(Pointer(Cardinal(FPacketBuffer)+FPacketBufferPtr),@TmpBuff[0],Len);
FPacketBufferPtr := FPacketBufferPtr + Cardinal(Len);
SplitAndExecutePacketBuffer;
end
else
begin
MainForm.AddToLogFile('Unable to add data to packet buffer due buffer
overflow.');
MainForm.AddToLogFile('Room Name = '+TRoom(FOwner).RoomInfo.RoomName);
MainForm.AddToLogFile('Current Buffer Size =
'+IntToStr(FPacketBufferPtr));
MainForm.AddToLogFile('New recieved Buffer Size = '+IntToStr(Len));
MainForm.AddToLogFile('Username = '+UserName);
MainForm.AddToLogFile('IP = '+IPAddress);
MainForm.AddToLogFile('Current Buffer is:');
MainForm.AddBufToLogFile(FPacketBuffer,FPacketBufferPtr);
MainForm.AddToLogFile('New recieved Buffer is:');
MainForm.AddBufToLogFile(TmpBuff);
FNeedDisconnect := True;
end;
SetLength(TmpBuff,0);
end;
while OutputBufferList.Count>0 do
begin
Buffer := OutputBufferList.items[0];
Len := PCommunicatorPacket(Buffer).BufferSize;
SetLength(TmpBuff,Len);
CopyMemory(@TmpBuff[0],Buffer,Len);
FContext.Connection.IOHandler.Write(TmpBuff);
SetLength(TmpBuff,0);
//FContext.Connection.Socket.WriteBufferFlush;
FOutputCriticalSection.Enter;
OutputBufferList.Delete(0);
FOutputCriticalSection.Leave;
FreeMem(Buffer);
end;
try
if (MilliSecondsBetween(Now,LastIncomingPacketTime)>20000) or
FNeedDisconnect then
FContext.Connection.Disconnect(True);
except
end;
FSocketCriticalSection.Leave;
end;
constructor TRoom.Create(CreateSuspended: Boolean);
begin
inherited Create(True);
FUserListCriticalSection := TCriticalSection.Create;
FreeOnTerminate := False;
FUserList := TObjectList.Create;
FServer := TIdTCPServer.Create(nil);
if not CreateSuspended then
Resume;
end;
function TRoom.Open: Boolean;
begin
Result := True;
// FSocket.Port := FServerPort;
// FSocket.ServerType := stThreadBlocking;
// FSocket.OnGetThread := OnServerSocketGetThread;
// FSocket.OnThreadEnd := OnThreadEnd;
// FSocket.Open;
FServer.DefaultPort := FServerPort;
FServer.OnConnect := IdTCPServerConnect;
FServer.OnDisconnect := IdTCPServerDisconnect;
FServer.OnExecute := IdTCPServerExecute;
FServer.Active := True;
end;
destructor TRoom.Destroy;
var
i: Integer;
WaitTime: Integer;
begin
WaitTime := 0;
Terminate;
while not FThreadTerminated do
begin
Windows.Sleep(2);
Inc(WaitTime);
if WaitTime>20 then
begin
TerminateThread(Handle,0);
break;
end;
end;
FUserListCriticalSection.Free;
FServer.Active := False;
FServer.Free;
FUserList.Free;
inherited;
end;
////////////////////////////////////////// again stack trace
/////////////////////////////////////////////
exception class : EAccessViolation
exception message : Access violation at address 7C918FEA in module
'ntdll.dll'. Write of address 00000010.
thread $834:
7c918fea +5b ntdll.dll
RtlpWaitForCriticalSection
7c901046 +41 ntdll.dll RtlEnterCriticalSection
0046b310 +10 HiChatterServer.exe Classes 3135 +1 TThreadList.LockList
0046b337 +0f HiChatterServer.exe Classes 3141 +1 TThreadList.Remove
0052a987 +23 HiChatterServer.exe IdContext 142 +2 TIdContext.Destroy
00403c28 +08 HiChatterServer.exe System 8393 +1 TObject.Free
0045b38f +1b HiChatterServer.exe SysUtils 16274 +3 FreeAndNil
0052b89b +17 HiChatterServer.exe IdThread 544 +1
TIdThreadWithTask.Destroy
00403c28 +08 HiChatterServer.exe System 8393 +1 TObject.Free
00474d63 +93 HiChatterServer.exe Classes 9382 +17 ThreadProc
00404ad8 +28 HiChatterServer.exe System 11562 +33 ThreadWrapper
0044ea51 +0d HiChatterServer.exe madExcept CallThreadProcSafe
0044eabb +37 HiChatterServer.exe madExcept ThreadExceptFrame
Main ($85c):
7c90eb94 +000 ntdll.dll
KiFastSystemCallRet
7c90e9a9 +00a ntdll.dll
NtWaitForMultipleObjects
7c8094dc +000 kernel32.dll
WaitForMultipleObjectsEx
7e4195f3 +000 user32.dll
MsgWaitForMultipleObjectsEx
7e4196a3 +01a user32.dll
MsgWaitForMultipleObjects
004752d8 +054 HiChatterServer.exe Classes 9716 +11 TThread.WaitFor
0052d29f +07b HiChatterServer.exe IdTCPServer 664 +10
TIdTCPServer.TerminateListenerThreads
0052d44d +011 HiChatterServer.exe IdTCPServer 730 +4
TIdTCPServer.Shutdown
0052d05d +049 HiChatterServer.exe IdTCPServer 578 +12
TIdTCPServer.SetActive
00563495 +195 HiChatterServer.exe RoomClass 315 +61 TRoom.Destroy
00403c28 +008 HiChatterServer.exe System 8393 +1 TObject.Free
00482681 +021 HiChatterServer.exe Contnrs 304 +3
TObjectList.Notify
0046ab84 +08c HiChatterServer.exe Classes 2779 +9 TList.Delete
0046b00e +026 HiChatterServer.exe Classes 2910 +3 TList.Remove
004826aa +012 HiChatterServer.exe Contnrs 310 +1
TObjectList.Remove
0056cb51 +015 HiChatterServer.exe Users 1542 +1
TUserList.DestroyPrivateRoom
00561f5b +01b HiChatterServer.exe MainUnit 194 +1
TMainForm.DestroyPrivateRoom
004b3377 +1df HiChatterServer.exe Controls 4645 +53
TControl.WndProc
004b6f46 +18e HiChatterServer.exe Controls 6342 +33
TWinControl.WndProc
004d0d9c +478 HiChatterServer.exe Forms 3097 +103
TCustomForm.WndProc
004b6b18 +034 HiChatterServer.exe Controls 6237 +3
TWinControl.MainWndProc
004771fc +014 HiChatterServer.exe Classes 10966 +8 StdWndProc
7e4196c2 +00a user32.dll
DispatchMessageA
004d88b4 +0ac HiChatterServer.exe Forms 6872 +13
TApplication.ProcessMessage
004d88fb +00f HiChatterServer.exe Forms 6891 +1
TApplication.HandleMessage
004d8b96 +0a6 HiChatterServer.exe Forms 6975 +16
TApplication.Run
0056e573 +053 HiChatterServer.exe HiChatterServer 29 +4 initialization
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
how can i know whats my indy version?
i have these lines in file IdVers.inc in system folder
gsIdProductName = 'Indy'; {do not localize}
gsIdVersion = '10.0.52'; {do not localize}
so i think its 10.0.52
"Remy Lebeau (TeamB)" <no....@no.spam.com> wrote in message
news:4818ed53$3...@newsgroups.borland.com...
> FUserListCriticalSection.Enter;
> FUserList.Add(User);
> FUserListCriticalSection.Leave;
I would suggest changing FUserList to be a TThreadList instead. Then you
don't need to use your own critical section, as TThreadList uses one
internally for you.
> FUserList.Delete(0);
> AContext.Data := nil;
That is a memory leak, as you are not freeing the TRoomUser object.
> if IsPrivate then // destructure will be called from MainForm
> PostMessage(MainForm.Handle,WCM_DestroyMePrvRoom,Integer(Self),0)
> else
> PostMessage(MainForm.Handle,WCM_DestroyMeRoom,Integer(Self),0);
The TWinControl.Handle property is not thread-safe. Post to the
TApplication.Handle property instead. Or else create your own private HWND,
such as via the AllocateHWnd() function.
> Len := FContext.Connection.IOHandler.InputBuffer.Size;
You are never reading from the socket before checking the Size, so the
InputBuffer will never have any chance of receiving data. Call
CheckForDataOnSource() for that, ie:
Len := FContext.Connection.IOHandler.InputBuffer.Size;
if Len < SizeOf(TCommunicatorPacket) then
begin
if not FContext.Connection.IOHandler.CheckForDataOnSource(0) then
Exit;
Len := FContext.Connection.IOHandler.InputBuffer.Size;
if Len < SizeOf(TCommunicatorPacket) then Exit;
end;
> MainForm.AddToLogFile('Unable to add data to packet buffer due buffer
> overflow.');
> MainForm.AddToLogFile('Room Name =
> '+TRoom(FOwner).RoomInfo.RoomName);
> MainForm.AddToLogFile('Current Buffer Size =
> '+IntToStr(FPacketBufferPtr));
> MainForm.AddToLogFile('New recieved Buffer Size = '+IntToStr(Len));
> MainForm.AddToLogFile('Username = '+UserName);
> MainForm.AddToLogFile('IP = '+IPAddress);
> MainForm.AddToLogFile('Current Buffer is:');
> MainForm.AddBufToLogFile(FPacketBuffer,FPacketBufferPtr);
> MainForm.AddToLogFile('New recieved Buffer is:');
> MainForm.AddBufToLogFile(TmpBuff);
Unless those methods implement their own synchroniztion internally, then
they are not thread-safe.
> while OutputBufferList.Count>0 do
> begin
> Buffer := OutputBufferList.items[0];
You are not entering FOutputCriticalSection before entering the loop. You
need to do that so no other thread can alter the list while you are looping
through it. Entering the critical section only when calling Delete() is the
wrong thing to do.
> if WaitTime>20 then
> begin
> TerminateThread(Handle,0);
> break;
> end;
> end;
That is very dangerous. You should not be using TerminateThread() like
that, if at all. That is a brute force termination that does not allow the
thread to clean up after itself. For instance, if the thread has any locks
active, they will not be released correctly.
> FUserListCriticalSection.Free;
> FServer.Active := False;
There is likely the cause of your AV. You are freeing the critical section
too early. While the server is shutting down, any active clients are going
to try accessing that critical section during their cleanup operation. Free
the critical section after deactivating the server, not before.
> how can i know whats my indy version?
Right-click on any Indy component in the form designer. Or look in
IdVers.inc.
> so i think its 10.0.52
Then you are using a very old version and shold consider upgrading.
Gambit
FUserList is not TList thats TObjectList. So Deleting the Object will cause
to free it. Please just inform me again if you still think using TThreadList
is better than this so i'll do the changes.
> The TWinControl.Handle property is not thread-safe. Post to the
> TApplication.Handle property instead. Or else create your own private
> HWND, such as via the AllocateHWnd() function.
Yes, you are right. Thank you for pointing this problem.
> You are never reading from the socket before checking the Size, so the
> InputBuffer will never have any chance of receiving data. Call
> CheckForDataOnSource() for that, ie:
During about these 2 weeks i hadn't any problem with this. Every thing was
ok with reading sockets. But anyway i'll do this parallel with upgrading to
new version of indy.
> Unless those methods implement their own synchroniztion internally, then
> they are not thread-safe.
Log procedures all are threadsafe in main form.
Athough about critical section in loop i couldnt understand how it may cause
problem but i'll move critical sections outside of loop.
>> if WaitTime>20 then
>> begin
>> TerminateThread(Handle,0);
>> break;
>> end;
Its just for making sure my server will not hang while users connected and
using it. Because thats very important to me. I think this will never occur.
>> FUserListCriticalSection.Free;
>> FServer.Active := False;
>
> There is likely the cause of your AV. You are freeing the critical
> section too early. While the server is shutting down, any active clients
> are going to try accessing that critical section during their cleanup
> operation. Free the critical section after deactivating the server, not
> before.
Totaly you are right and i'll fix that. But no,thats not the reason of
exception because i got that exception while the only client disconnected.
which version of indy you suggest to upgrade. I'm going to download latest
snapshot.
-----------------------------
I found that you are from indy team. Many thanks in advance and best
respects to you.
Thanks again for spending time and looking carefully to the code.
> FUserList is not TList thats TObjectList. So Deleting the Object
> will cause to free it.
Only if you set the OwnsObjects property to True, which you are not doing in
the code you showed. The OwnsObjects property is False by default.
> Please just inform me again if you still think using TThreadList is
> better than this so i'll do the changes.
Yes, I do. TObjectList is not really gaining you anything in the code you
showed, and you are doing some manual work in the rest of the code that
TThreadList can do for you.
> During about these 2 weeks i hadn't any problem with this. Every
> thing was ok with reading sockets.
The first thing you are doing on a new connection is checking the
InputBuffer.Size property. There is no data in the InputBuffer initially.
You are only reading from the socket if the InputBuffer.Size is large enough
to hold a TCommunicatorPacket structure. But you are not doing anything in
ProcessSocketOperation() to fill the InputBuffer with bytes, so the
InputBuffer.Size property would always be 0 and any data the client may send
would never be processed. The only possibility for the InputBuffer to
receive any data in that situation is if you are calling Connected()
elsewhere in code that you did not show.
> Athough about critical section in loop i couldnt understand how it
> may cause problem but i'll move critical sections outside of loop.
Because you are not locking the list while accessing its Count or Items[]
properties, so others threads are still able to alter the list at the same
time your server thread is using it. That could cause the internal list's
memory to be re-allocated behind your back, causing all sorts of problems.
In other words, your list is not 100% thread-safe because you are misusing
the critical section for it.
> Its just for making sure my server will not hang while users
> connected and using it. Because thats very important to me.
> I think this will never occur.
Using TerminateThread() is still not a good idea in that situation.
> which version of indy you suggest to upgrade.
10.2.3
> I'm going to download latest snapshot.
That is 10.2.3.
Gambit
default is true.
function TIdCustomTCPServer.DoExecute(AContext: TIdContext): Boolean;
begin
if Assigned(OnExecute) then begin
OnExecute(AContext);
end;
Result := False;
if AContext <> nil then begin
if AContext.Connection <> nil then begin
Result := AContext.Connection.Connected;
end;
end;
end;