You might have an application need, such as an Internet Server Application in the FoxIsap server sample, where you want to instantiate a form to use in mapping to equivalent HTML. The impact of eliminating a number of non-critical features by using the multithreaded Visual FoxPro run-time library will improve overall performance of your multithreaded server applications.
A common misconception about server choice is that an application will automatically scale well just by building the .dll to use the new run time. You can often do this, but you should also consider issues involving use of Visual FoxPro API calls, scalability, reentrancy, and resource contention.
Using Visual FoxPro API Calls
When using Visual FoxPro FLL or standard Windows DLL libraries that make Visual FoxPro API calls, there are some issues you need to be aware of when designing your servers. The Visual FoxPro API does not readily determine to which project (.dll) a particular call belongs. So, there is a potential, although rare, for an API call to be made against the wrong project (.dll - thread local storage). This may occur if your FLL/DLL library creates an instance from a different Visual FoxPro .dll server before making its API call. There are several possible solutions if your server should experience this problem:
Create a separate copy of the multithreaded Visual FoxPro run-time library file, and place the copy in the same folder as your .dll server. The server will always search first in your server folder before attempting to locate files in the Windows System folder or folder of your client process. By having its own run-time library servicing it, a server will not experience potential conflicts from other .dlls (assuming these other .dlls are not in the same folder). Since this behavior (automatic copying/renaming of run time) occurs by default with the main Visual FoxPro run time, the above API issues only manifest themselves in the mulithreaded Visual FoxPro run-time library.
Limit your application to a single .dll server. If you are using several .dll servers (projects), there may be potential for API conflicts. Remember that a project can contain many OLEPUBLICs, so you don't need to have a separate project for each OLEPUBLIC. Be aware that on shared computer servers, its possible that people may install other Visual FoxPro COM servers, which could potentially conflict with yours if running concurrently.
The multithreaded run time in Visual FoxPro allows for very scalable Visual FoxPro in-process servers. Though scalability of your servers will be handled automatically by the run time, certain tight code loops may not allow thread switching to occur as frequently. For example, a very tight DO WHILE loop may cause this. If you experience scalability problems, you can include code such as the following example which forces the processor to switch threads:
DECLARE Sleep IN Win32API INTEGER SLEEP(0)
Though you can use multithreading on a single-processor machine, consider that this may not provide quite the results you expect. For example, on a single-processor machine, multithreading results in a perceived performance improvement only with a mix of long and short tasks. Multithreaded method calls, as described in this example, may appear to be slower.
In the example, method A and method B are called at the same time. In a single-threaded component, the requests are serialized, so that B doesn't begin until A has finished. With multithreading, the two active threads duel for the processor's attention.
Not only does the perceived average completion time increase, but more processor time is spent switching between threads.
The problem is that method A and method B take about the same amount of time.
For example, if method B required only three time slices to complete, the user of the system would perceive a huge improvement in the responsiveness of method B вЂ” and only a slight degradation in the time required to execute method A.
The scenarios in which multithreading shows to best advantage are those in which most threads spend a substantial percentage of the time blocked вЂ” for example, waiting for file I/O вЂ” so that other threads can be executing code at any given time.
If you are considering an application on a single-processor machine with characteristics that provide minimal thread blocking, you may want to consider using the main Visual FoxPro run-time library. On multiprocessor machines and in most other applications, you should consider using the multithreaded Visual FoxPro run-time library for .dll servers.
In the apartment model, reentrancy refers to the following sequence of events:
An apartment's thread of execution enters an object's code, because a property or method has been invoked.
While the thread is in the property or method, another thread invokes a property or method of the object, and Automation serializes this request вЂ” that is, it queues the request until the thread that owns the object's apartment finishes the member it's currently executing.
Before the thread reaches the end of the member, it executes code that yields control of the processor.
Automation tells the thread to begin executing the serialized request, so that the thread reenters the object's code.
The new request may be for the member the thread was already executing вЂ” in which case the thread enters the member a second time вЂ” or it may be for another member. If the second member doesn't yield, it will finish processing before the first member. If it changes module-level data the first member was using, the result may be unfortunate.
By serializing property and method calls for each apartment, Automation protects you from reentrancy вЂ” unless your code yields control of the processor. Ways in which your code can yield control of the processor include:
Invoking the properties or methods of an object on another thread, or in another process. Setting the Application.AutoYield property to false (.F.) will suspend processing of intermittent events between lines of code executing.
Invoking a cross-thread or cross-process method from within a method.
Unless you've carefully written all of an object's code so that it doesn't matter whether two members are executing at the same time, you should not include code that yields control of the processor.
The multithreaded Visual FoxPro run-time library will protect objects from accessing each other's application data. This includes both declared and intrinsic environment globals. Some resources present potential problems of contention, including:
File I/O presents resource contentions that can be easily handled through code since file management is handled through the operating system. When using a file, your server can open (and lock) a file so that other objects see that the file is in use. A properly written server can handle this scenario by waiting for the file to become available, or otherwise gracefully handle the error. Files can also be opened shared in which case your server should be aware that other objects can potentially change values.
A common scenario is presented with INI files in which different objects can read or write values. The process of reading and then writing a value to an INI file requires two steps. So, there is a potential that the value read by one client is not necessarily the same one it overwrites later if another client alters it first, producing a need for conflict resolution.
As do INI files, the Registry stores settings, which can be accessed by multiple clients. Your applications should use common Windows API routines to access the Registry. Again, your server design should reflect that a potential exists for Registry key values to change between the time the key is read and the time it's written. The Windows API routines work to avoid the necessity for conflict resolution.
Data is a tricky resource to handle. Fortunately, the rich Visual FoxPro language available in the run-times provides all the information and functionality necessary to handle resource contention issues. For more information on handling shared data, see Programming for Shared Access.