Threading Code Design Standards

Top  Previous  Next


To allow Clarion to support preemptive threads some changes were required in the language. This was kept to a minimum so as to ease the migration of versions prior to Clarion 6 to our latest version.


THREAD attribute on Classes

Prior to Clarion 5.5 if you had a global variable that was an instance of a class the THREAD attribute was ignored. In Clarion 6 and higher any global instance of a class with the THREAD attribute will have separate data for each running thread. The constructor of the class will be called on thread initialization and the destructor will be called on thread termination.

Thread "initialization" and thread "termination" must be defined because it is not always obvious. In general, any DLL has some entry point called by the OS on attaching that DLL to the process, detaching it from the process, on the start and closing a thread. The Clarion RTL translates every such call, to chains of calls to initialize or cleanup data in all modules that have application-wide or thread-wide variables declared. But in certain situations the OS does not do calls to executables, for example, if it is an EXE rather than DLL, or if the DLL is loaded dynamically by the call to LoadLibrary. In the latter case, the OS calls that DLL's entry point for initializing application-wide data only. The CW RTL handles all special cases and calls the code to initialize and also to cleanup threaded data.

The order that class constructors are called is undefined. So do not assume that a different class instance has been constructed when your constructor is called

This feature gives you a lot of power to control things on a thread-by-thread basis. The ABC templates use this strategy to make sure the meta-information about your database is available on each thread.

See the section below "Using hand code" for an example of a global threaded class.


THREAD and EXTERNALEXTERNAL__set_defined_externally_

In versions of Clarion prior to version 6, a variable with the THREAD attribute was allocated a fixed memory location and the runtime library handled swapping the thread specific values of that variable in and out of that memory location. So if you had a file definition in a DLL you would declare it as



Then in your EXE you would declare the file as




A variable with the THREAD attribute has different memory allocated for each thread. The compiler generates code to make sure that the right memory is accessed regardless of which thread is running. For the compiler to generate the right code it needs to know if a variable has the THREAD attribute regardless of where the variable is defined. So in Clarion if you have a file definition in a DLL you declare it as




Then in your EXE you declare the file as




Notice the THREAD attribute is present in both declarations, unlike previous versions.


ADDRESS of a threaded variable

In version prior to Clarion 6, taking the ADDRESS() of a threaded variable would always return the same result regardless of what thread is running. Every thread has its own memory location for a threaded variable. So ADDRESS() no longer will return the same value. If you need a constant address for a threaded global variable you can use the new function INSTANCE() and pass a Thread Number of 0. For example, to get a unique identifier for a threaded FILE that you can be certain will be the same regardless of what thread you are on you used to do ADDRESS(MyFile). You now do INSTANCE(MyFile, 0)



Clarion works with preemptive threads rather than cooperative threads these functions do nothing by default. However, if you want to work in a cooperative threading environment LOCKTHREAD will call the SYSTEM{PROP:LockThreadHook} function, UNLOCKTHREAD will call the SYSTEM{PROP:UnlockThreadHook} function and THREADLOCKED will call the SYSTEM{PROP:ThreadLockedHook} function.


See Also: