Java is a free-threaded environment. This means that any object can call any other object, at any time, from any thread. Special care must be taken when writing objects so that their methods are atomic and thread-safe.
There are several classes that benefit from being free-threaded, and WFC has provided the locking code to make these objects thread-safe. These classes are as follows:
On the other hand, any object that derives from com.ms.wfc.ui.Control is apartment-threaded because of the Win32 window that is tied to each control. Additionally, most other objects in the com.ms.wfc.ui package are not synchronized, so they should also be considered to be apartment threaded. Likewise, the com.ms.wfc.io, com.ms.wfc.html, and com.ms.wfc.util packages are not thread-safe.
WFC accesses native Win32 constructs (such as windows) from Java objects. The Win32 window manager is apartment-threaded, and Windows automatically marshals calls from one thread to another as needed. When a free-threaded object calls into an apartment-threaded object, the call must marshal to the object's apartment. This means that the free thread is blocked for a period of time while the apartment thread handles the request. Any other calls from free-threaded objects to the apartment call will block until the apartment call is free. Consequently, this can lead to deadlock situations.
So how do Java objects, which are inherently free thread work with WFC controls? Rather than hide when thread transitions occur, WFC makes it the programmer's responsibility to request the transition. The programmer can then design an algorithm in such a way as to prevent deadlocks. This can be done by invoking a delegate on the control's thread, which in turn calls the method specified in the delegate.
To execute a given delegate on the thread that created the control's window handle and contains the message loop, use the Control.invoke or Control.invokeAsync methods from the desired control. It is important to use the control's own thread in case the control needs to re-create its window handle for any reason. The invoke method causes the thread to call the specified callback method and wait for a return. The invokeAsync method causes the thread to call the callback method without waiting for reply. All exceptions on the invoked thread are passed on to the owning control in both cases.
You can also use the Control.createGraphics object to perform background painting and animation techniques on a ui.Graphics object. Whereas the Graphics object is apartment-threaded, the createGraphics call is entirely free-threaded. This allows one thread to create a graphics object for a control in another thread.
Free-threaded threads can be created using the standard Java method of implementing the java.lang.Runnable interface.
This sample shows a class that implements Runnable and takes two controls (a trackbar and a label) as parameters to its constructor. From the thread's run method, it transitions to the trackbar's thread by calling the trackbar's invokeAsync method. InvokeAsync passes a delegate called tDelegate (an instance of com.ms.app.MethodInvoker) that specifies a callback method called tCallBack. Inside that method, the control's thread can safely manipulate the control's properties, in this case, changing the trackbar's tick style. This causes the trackbar's window handle to be re-created. If the thread was not transitioned as demonstrated here, the trackbar would be re-created on the new thread rather than on the thread containing the message loop; in this case, the control trackbar wouldn't receive any new messages and would fail to respond.
import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
/**
* Runnable is the interface you need to implement to make a new
* java thread
*/
public class RunnableClass implements Runnable
{
final int SLEEP = 500;
Label l;
TrackBar tb;
/**
* This is the thread for our class.
*/
Thread thread;
/**
* Makes a special delegate so WFC can call it from the control's
* thread.
*/
MethodInvoker tDelegate = new MethodInvoker(tCallback);
/**
* Make a new Java thread; tell it to begin running via the
* start() method.
*/
public RunnableClass (TrackBar tb, Label l)
{
this.l = l;
this.tb = tb;
thread = new Thread(this, "RunnableClass thread");
thread.start();
}
public void run()
{
while (true)
{
/**
* Call the specified method from the label's thread.
*/
tb.invokeAsync (tCallback);
try
{
Thread.sleep (SLEEP);
}
catch (InterruptedException e)
{
}
}
}
int nCount = 0;
int nTickStyles[] = {TickStyle.BOTH,
TickStyle.BOTTOMRIGHT,
TickStyle.NONE,
TickStyle.TOPLEFT};
/**
* This code is executed on the trackbar's thread.
*/
private void tCallback()
{
int nIndex = nCount % (nTickStyles.length);
l.setText ("hello from tCallBack: " + nCount);
tb.setTickStyle (nTickStyles [nIndex]);
nCount++;
int nValue = tb.getValue();
if (nValue >= tb.getMaximum())
tb.setValue(0);
else
tb.setValue (nValue + 1);
}
public void stopThread()
{
thread.stop();
}
}
Exiting a thread in this case is just a matter of running the thread's stop method. In this example, the Form class that creates the RunnableClass object calls that object's stopThread method when it is disposed. The following code fragment demonstrates this.
...
import RunnableClass;
public class SimpleRunnable extends Form
{
/**
* This is the class that implements the Runnable interface.
*/
RunnableClass runnableClass;
public SimpleRunnable()
{
// Required for Visual J++ Forms Designer support.
initForm();
runnableClass = new RunnableClass (tb, l);
}
public void dispose()
{
runnableClass.stopThread();
super.dispose();
components.dispose();
}
Container components = new Container();
Edit eDescription = new Edit();
TrackBar tb = new TrackBar();
Label l = new Label();
private void initForm()
{
// Code to initialize the controls omitted ...
}
public static void main(String args[])
{
Application.run(new SimpleRunnable());
}
}
Alternately, to create a new application thread without having to implement the Java Runnable interface or extend java.lang.Thread, you can use the Application.createThread method. The createThread method takes a delegate as a parameter (MethodInvoker is often used, but any delegate can be used). In this case, all logic can be contained in one class, typically the Form-based class of the application. The following example code fragment shows how this works.
import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;
public class SimpleAppThread extends Form
{
final int SLEEP = 700;
Thread thread;
// Specify the thread context to run a method on.
MethodInvoker cbDelegate = new MethodInvoker( cbThrdCallback );
public SimpleAppThread()
{
initForm();
/**
* Creates a new thread and runs the methodInvoker method
* on the new thread. The returned thread object is needed
* so we can stop the thread when this form is closed (disposed).
* Note that thread.start() is called automatically.
*/
thread = Application.createThread (new MethodInvoker (this.methodInvoker));
}
private void methodInvoker()
{
while (true)
{
// cbThrdCallback is called on the check box's thread.
cb.invoke (cbDelegate);
try
{
Thread.sleep (SLEEP);
}
catch (InterruptedException e)
{
}
}
}
int nCount = 0;
/**
* Thread callback that sets check box alignment property.
* This code is to be executed on the check box's thread.
*/
private void cbThrdCallback()
{
cb.setText ("threadCallback loop: " + nCount++);
if (nCount % 2 == 0)
cb.setTextAlign (LeftRightAlignment.LEFT);
else
cb.setTextAlign (LeftRightAlignment.RIGHT);
}
public void dispose()
{
thread.stop();
super.dispose();
components.dispose();
}
private void cbSuspend_click(Object sender, Event e)
{
if (cbSuspend.getChecked())
{
cbSuspend.setText ("press to resume thread");
thread.suspend();
}
else
{
cbSuspend.setText ("press to suspend thread");
thread.resume();
}
}
Container components = new Container();
CheckBox cbSuspend = new CheckBox();
CheckBox cb = new CheckBox();
private void initForm()
{
// Code to initialize the controls here ...
}
public static void main(String args[])
{
Application.run(new SimpleAppThread());
}
}
Here the thread is stopped by calling the thread’s stop method from the form's dispose method. The thread is not in a separate class, so its methods can be called directly from the Form-based class.
The Application class also contains the Application.exitThread method, which closes the thread's message loop and shuts down all windows on the thread (note that it does not stop or exit the thread itself). By way of contrast, Application.exit closes message loops on all threads and closes all windows.
The WFC Application class provides support for Thread Local Storage (TLS). Each thread can allocate a slot of memory for storing data that is specific to the thread. Calling Application.allocThreadStorage returns an index to that slot. To set a value in TLS, call the Application.setThreadStorage with the index and a value you want to set. To retrieve that value, call Application.getThreadStorage. Remember to free any allocated thread storage with a call to Application.freeThreadStorage.
The com.ms.wfc.app class provides a ThreadExceptionDialog class, which is automatically displayed whenever an unhandled exception occurs in a thread. You can gain control of exceptions by using the Application.addOnThreadException method to specify your own thread exception handler. The addOnThreadException method takes a ThreadExceptionEventHandler delegate, which is constructed with your event handler method and the ThreadExceptionEvent class.
Typically, a thread exception event handler queries the exception field of the ThreadExceptionEvent object passed to it to determine the next course of action. From this thread exception handler, you can run the ThreadExceptionDialog and retrieve the dialog results in the same way as any other WFC dialog box: use the Form.showDialog method to launch the dialog box, and compare the returned results with the com.ms.wfc.ui.DialogResult class fields.