A customer asked
1) Is it better to set it to 0 (Auto) or some "larger" number, such as -16 (1024kb). I haven't found any information in the help files as to where the tradeoff lies between these, but presumably the default for MTDLLs of 128kb is deliberately small to prevent each thread requiring too much RAM.
2) The second thing is that when I query the current PROGCACHE setting (using SYS(3065) for the DLL without any CONFIG.FPW, it sometimes returns -2 (the stated default) and sometimes 0. I suspect this could be why the problem seems to be intermittent, with a page refresh often causing the second request to work. Could this be a VFP bug or something to do with the configuration of the webserver and how it is calling the COM object?
The ProgCache setting is used to set how much memory VFP will set aside for the program cache. When you execute some code, it gets loaded and stored into this cache. When a program gets loaded, the binary compiled file gets loaded and fixups are applied. Such fixups are things like names in the compiled code are mapped to names in the current name table. For VFP versions prior to 9, the cache was set to about10 megs, and the user could not change it. For multiple threads, each thread would create its own cache, which means 20 threads would use 200 megs of memory. Actually, the memory isn’t really used until necessary: it’s VirtualAlloc’ed with the MEM_RESERVE parameter, which reserves 200 megs of virtual address space out of the available 2^32 = 4 gigs.
For VFP9, I added the PROGCACHE setting that user’s can set in config.fpw. Setting it to 0 means that no cache will be used: programs will be loaded into dynamically allocated memory, rather than a prereserved cache.
To query the ProgCache setting, use SYS(3065)
Keep in mind that “without any CONFIG.FPW” should be confirmed via querying both internal and external CONFIG.FPW files (sys(2019))
Try running the code below, varying the progcache setting and the size of the generated program (n) to see the effects. It generates a VFP COM server with a particular ProgCache setting, and then tries to run a very large program. Try causing a failure with various ProgCache settings.
See also: Collecting garbage at the wrong time
Using Very large programs
For more about the program cache, see Windows Security and how it affects running generated code
For more about memory usage, see Inspect your memory image and see fragmentation
#define cProgName "testPCCache"
TEXT TO cStr NOSHOW textmerge
EXTERNAL FILE config.fpw && comment this line in/out
DEFINE CLASS c1 as session olepublic
func MyEval(cExpr as string, p2 as Variant, p3 as Variant, p4 as Variant, p5 as Variant) as variant helpstring "this is the Eval help"
func MyDoCmd(cCmd as stRing, p2 as Variant, p3 as Variant, p4 as Variant, p5 as Variant) helpstring "this is the docmd help"
*progcache=0 && uncomment this line to see the results
BUILD PROJECT (cProgName) FROM (cProgName)
BUILD MTDLL (cProgName) FROM (cProgName)
n=578 && 578 passes, 579 fails w/ -2
SET TEXTMERGE TO tt2.prg on noshow
\y='a' && this is used in a CASE statement *way* down there
\Case Y = 'a'
FOR i = 1 TO n
\ y = "a"
\set compatible on
\IF y="b" then
\ cret= "DOCASE:PASS "+TRANSFORM(FSIZE("tt2.prg"))
\ cret= "DOCASE:FAIL "+TRANSFORM(FSIZE("tt2.prg"))
\set compatible off
SET TEXTMERGE to
CATCH TO oex
IF FILE(cProgName+".dll") && cleanup
DECLARE integer DllUnregisterServer IN (cProgName)