Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is trying to open a TOpenDialog spawning a ton of threads?

I've got a very simple form with a TOpenDialog and a button on it. When I press the button, it calls Execute on the dialog. If I watch in the debugger, the act of opening the dialog box spawns something like 14 threads, and they don't go away when I close the dialog box either.

Anyone have any idea what's going on with that?

like image 634
Mason Wheeler Avatar asked Sep 10 '11 17:09

Mason Wheeler


1 Answers

Imagine you want to show your friends how beautiful the Pacific Northwest is. You decide to set off on a trip to snap a few photos of sunset over the Pacific. What you really care about is the image files making their way home, where they can be uploaded to the Facebook. In reality the camera, lenses and the tripod need to be hauled over the Olympics and back. You also need to bring the photographer (yourself) who will set the camera up and press the shutter. The photographer needs to be moved there and back in relative comfort, so you take a seat on which the photographer will rest while making the trip. This seat is enclosed in a shiny metal box with a bunch of other metal, glass and rubber parts some of which are turning and reciprocating. In the end, about two tons of stuff (and a living human being) taking a multi-hour trip, burning gallons of hydrocarbon liquid -- with the goal of moving a few bits of information from the shore to the internet.

Exactly the same thing happens with your application. When the user wants to open a file using "Open File" dialog box, the user expects to be able to:

  • navigate to the directory containing the file (The directory may be on local hard drive or CD/DVD/BR or network drive or archive, etc. The media may be encrypted or compressed, which needs to be displayed differently. The media may not be plugged in, for which the user might need to be prompted. The media may require user's credentials, which have to be asked);
  • connect to a new directory using its URI/UNC (map the drive);
  • search the directory for some keywords;
  • copy/delete/rename some files;
  • see the list of the files in that directory;
  • preview the content of each file in the directory;
  • select which file to open;
  • change his/her mind and decide not to open the file;
  • do many other file-related things.

The OS lets all this to happen by essentially giving your process most of the Windows Explorer functionality. And some of it has to happen in the background, otherwise the users will complain about how unresponsive the Open File dialog is. The obvious way to run some tasks in background is to run them on different threads. So that's what we see.

What about the threads left behind, you ask? Well, some of them are left there for the case the user will decide to open another file: it saves a lot of time, traffic and typing in this case. That custom authentication used for this one particular process last time? -- stored. The preview icons for those pesky PDFs? -- still there. The length and bitrate for every movie in the directory? -- still available, no need to re-parse them.

Of course the threads did not just magically appear by themselves. Check out how many DLLs have been mapped into the process. Looking at some of them one can get quite an interesting picture of what functionality has been added.

Another interesting way to look at it would be to dump the callstacks at the moment every thread gets created. This shows which DLL (and sometimes which object) created them. Here's how a x64 Win7 creates all the threads. One can find the Explorer frame's thread getting created; some OLE activity which will be used to instantiate file filters, some of which can generate preview icons, overlays and tooltips; few threads belonging to search subsystem; shell's device enumerator (so if the user plugs in a new device, it will automatically appear in the open dialog); shell network monitor (ditto) and other stuff.

The good news is it happens fast and doesn't add too much overhead to your process. Most of the threads spend most of the time waiting for some seldom events (like USB key being plugged in), so the CPU doesn't spend any time executing them. Each thread consumes 1MB of virtual address space in your process, but only few 4Kb pages of actual physical memory. And most, if not all of those DLLs did not use any disk bandwidth to be loaded: they were already in RAM, so they just got mapped into your process for almost free.

In the end the user got a whole lot of useful functionality in a snappy UI, while the process had to do very little to achieve all that.

like image 144
Rom Avatar answered Sep 22 '22 06:09

Rom