Wednesday, May 20, 2009

 

FindFirst...FindClose in Linux


Last week a customer asked for one application we sell for sending a work list from a Radiology Information System to different equipment called Modalities in medical terminology, they are CT, CR, MR and so on.

The application is called Modality WorkList Server, and works as a Windows Service. It's function is reading XML files from a shared directory, convert to Dicom format and place in a specific directory. Also it opens a socket port for listening incoming requests from modalities, matches the Dicom files with the request and send the files to the origin. The final product of this process is a list containing patient data a radiology technician can see in the modality screen.

All went well, until our customer told us they use Linux!. Fortunately, the application was written taking care it should compile with FPC in the future, so, after a couple of IfDefs it compiled and started running on Linux.

The sad news, were, after ten minutes it stopped working.

What went wrong?

The first think I did was to add logs everywhere trying to catch the focus of the problem. And found it was stopping in a procedure similar to this:


if FindFirst(Edit1.Text, FileAttrs, sr) = 0 then
begin
repeat
...
...
until FindNext(sr) <> 0;
FindClose(sr);
end;


If you look at the FindFirst function in Delphi help, it has an example very similar to this, if FindFirst finds files that matches what I'm seeking, then a loop traverses the file list, and at the end, a FindClose must be issued, but only if FindFirst returned 0.

In Linux, on the other hand, FindClose must be called every time a FindFile is executed, it doesn't matter if it found anything or not. This is the code:


if FindFirst(Edit1.Text, FileAttrs, sr) = 0 then
begin
repeat
...
...
until FindNext(sr) <> 0;
end;
(* In Linux, FindClose must be called always after FindFirst *)
FindClose(sr);


Internals

To those who want to know why the FreePascal Linux version is different than Delphi/FPC windows counterpart, I'll try to explain here.

To interact with the Linux file system, FreePascal used to use an unit named OldLinux, which contains the function Glob(), in charge of finding files. This function returns a pointer to a glob structure (the file list buffer), it doesn't matter if there are found files or not, this pointer is always allocated. After calling Glob, always must be called GlobFree, to free the result of Glob call.

The Glob function of FPC, is a wrapper to the Glob function of GLibC's Glob function, that's why it is implemented this way, and it's correct.

In Linux, if you want to search for files, you use the "find" command, again, a wrapper to the GlibC Glob function. To implement FindFirst/FindNext/FindClose in FPC's RTL, the developers created them as wrappers to Glob, and again, this was a wise decision, at least to me. The only problem, is that it's usage is different in Linux than Windows, so, if you have to use this functions in a program that must run in both environments, just take care to write the call to FindClose in the right place for each operating system.
Comments:
> this was a wise decision

And why doesn't FPC for Linux call GlobFree in FindFirst if nothing was found? Wouldn't that have been an obvious implementation for FindFirst/FindNext?
That way FindFirst/FindNext would be work like it's Windows counterpart.
 
Well, until yesterday, aparently nobody faced this problem.

Michael Van Canneyt fixed it in the SVN version, with exactly what you said.
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?