Rules and Restrictions

Keep in mind of the following rules and restrictions when programming multithreading application:

Avoid sharing resources (variables or objects) among thread that may change in threading operation. It will cause unexpected errors. Use TInterlocked or TThread.Synchronize when necessary.

VCL or FMX libraries are not thread safe. Most GUI updates performed by thread shall invoke TThread.Synchronize or TThread.Queue.

System.Classes.TThread

In most situation, operation that want to perform in thread shall define in TThread descendant and override TThread.Execute method.

TThread.CreateAnonymousThread

TThread.CreateAnonymousThread is a class method allow the creation of simple task embed in an anonymous method to run in thread without the hazard to define a TThread descendant.

TThread.ProcessorCount

TThread.ProcessorCount is a property that return the number of virtual CPU cores of the runtime process in operating system. It may serves as a base measurement for application to determine total number of simultaneous running threads in a process.

TThread.Synchronize

TThread.Synchronize execute codes in main thread if thread safe manner is a concern. TThread.Synchronize is blocked until finish execution in the thread.

TThread.Queue

TThread.Queue works similar to TThread.Synchronize in thread safe manner with blocking execution in the executing thread.

System.Threading.pas

TParallel

TParallel.&For method allow us to run a thread method in loop manner by a range of low and high bound.

TTask

In addition to TParallel, class TTask can be invoke for a more diversified job. One TTask.Run for one job to run in parallel. System shall take care of resource allocation when a significant number of TTask.Run was invoked.

Each TTask job return a ITask reference. If sequence of executed task is important for later operation, use TTask.WaitForAll or TTask.WaitForAny to check the task status first.

TThreadPool

The number of executing thread tasks is determined by available number of virtual CPU core (TThread.ProcessorCount). The behaviour may alter by introducing a new TThreadPool instance with new MaxWorkerThreads and MinWorkerThreads value to TTask or TParallel methods.

Use TThreadPool.SetMaxWorkerThreads and TThread.SetMinWorkerThreads method to adjust both worker values. TThreadPool.SetMaxWorkerThreads shall invoke prior to TThreadPool.SetMinWorkerThreads to avoid the restriction enforced in the method:

TInterlocked class

TInterlocked implements various common atomic opererations for the purpose of ensuring “thread” or “multi-core” safety when modifying variables that could be accessed from multiple threads simultaneously. The TInterlocked class is not intended to be instantiated nor derived from. All the methods are “class static” and are merely defined in a class as a way to group their like-functionality

Freeze when use TThread.Synchronize with TParallel or TTask.WaitForAll

It is a common practice to update GUI control from a running thread to update status periodically using TThread.Synchronize method when the GUI controls are not thread-safe (e.g.: VCL or FMX controls).

Both TParallel and TTask.WaitForAll are blocked and wait for a list of tasks to finish, invoke TThread.Synchronize that blocked natively in thread will make the process freeze forever. For example:

The following answer explain the mechanism works behind that cause the frozen:

When you call TThread.Synchronize the thread and method pointer are added to a global SyncList: TList in Classes.pas. In the main exe’s TApplication.Idle routine calls CheckSynchronize, which checks the SyncList, … End result, your synchronized methods are never called.

Using TThread.Synchronize with TTask.WaitForAll

If using blocking TThread.Synchronize is necessary in thread (e.g.: Waiting response from end user), using TTask.WaitForAll will freeze the application. Consider using CheckSynchronize() in a timeout TTask.WaitForAll loop to process TThread.Synchronize request:

Passing dynamic resources to parallel tasks

Dynamic resources refer to variables, class instances, records or other means that are to be decided at runtime.

Due to the class design of TParallel.&For and TTask.Run. It is almost impossible to pass complex resources to the task for parallel execution. TParallel.&For shed light on this problem by an Integer index the task defined in TProc<Integer>. However, this is not enough for complex problem domain that are difficult to decide the lower and upper bounds. For example, execute task in parallel for each rows in a uni-directional TDataSet with unknown record count:

The code attempt to create an equal numbers of database connection dataset record. It is expensive and impractical to design in this approach.

There is a solution for the problem. Since tasks running in parallel is limited by TThreadPool.MinWorkerThreads, we can define a resource pool that is large enough and consume in round-robin manner.

For example, there are 100 tasks to be executed in parallel but at any one time no more than 4 tasks are executing due to limited CPU cores. We may define 8 or more resources in a pool and each task will pick a resource for execution:

The scenario assumes these tasks run smoothly in sequence ideally. However, it is not the case in real runtime environment. The operating system cannot guarantee threaded tasks run or finish in the order it queue.

Let’s try to design a simple workflow:

Define a pool work like queue to hold resources

Enqueue resources to the queue pool

For each running task, dequeue a resource for consumption

For each running task, enqueue the resource back to queue pool for next consumption

There is a significant different between TParallel.&For and TTask.Run handing exception.

If exception happens in the middle of TParallel.&For, it stop immediately without queuing more threaded task. it works in the manner is due to TParallel.&For is blocked during execution.

TTask.Run, on the other hand doesn’t block during execution. Exception occurs in a particular ITask instance doesn’t stop other ITask instance. The best spot to capture exceptions from ITask reference is via TTask.WaitForAll(...);

Each WaitForAll batch of 100,000 tasks (IFuture<Integer>) shall keep in a list (Batches: TList<IFuture<Integer>>) first. Remember there should have balance tasks in TaskList not able to group in 100,000 per batch after the lengthy for loop.

Finally, query the each batch’s value (Batch.Value) to make sure all tasks ended properly.

Sunday, October 14, 2012

Introduction

When setup a WiFi network using network appliances, an account credential is always required to logon to network services provided by ISP (Internet Service Provider). For example, using modem router to access ISP’s broadband internet service.

The ISP account credentials is usually enter once during configuration at first time and it should persist in the device’s RAM is ready to work for next power on. Compare ISP account credential with the other account credentials like your email account or desktop OS account that use every day, the user tend to forget or lost the ISP account credential easily. This happens when we upgrade to new WiFI device or hard reset the device due to some technical issues.

Monday, August 27, 2012

Introduction

Windows 7 introduces a new virtual driver for WiFi network that create a virtual WiFi AP to share network / Internet connection for any WiFi device. The network adapter is named as Microsoft Virtual Wifi miniport adapter.

Before Windows 7, Adhoc wireless connection is a common WiFi connection that may only connect to one WiFi device only. The Windows Virtual WiFi connection may connect to up 8 WiFi devices.

Check support of Virtual Wifi

The virtual network adapter should install automatically in Device Manager once your WiFi adapter is activated:

A new Wireless Network Connection (e.g.: Wireless Network Connection 2 in the following example) should configure as well:

Everything is ready up to this stage, continue the configuration to turn on the virtual WiFi AP and start sharing your network connection.

Please note that there is no GUI tools to configure virtual WIFI connection. All commands should type in command line console under Administrator privilege.

Configure Virtual WiFi connection

Choose a SSID to identify your virtual WIFI AP and set a password for it. Type the following command in command line console running as administrator to start configure:

C:\Windows\system32>netsh wlan set hostednetwork mode=allow ssid=MyWifi key=password keyUsage=persistent
The hosted network mode has been set to allow.
The SSID of the hosted network has been successfully changed.
The user key passphrase of the hosted network has been successfully changed.

Make sure the physical Wifi adapter is enabled before start the Virtual WiFi connection:

Stop virtual WiFi connection

Run the command to stop connection:

C:\>netsh wlan stop hostednetwork
The hosted network stopped.

Uninstall virtual WiFi adapter

It is not necessary to uninstall virtual WiFi adapter as the uninstall is not permanent. The virtual WiFi adapter will be installed once your reboot machine. If you mean to uninstall the virtual WiFi adapter for current session, try this:

C:\>netsh wlan set hostednetwork mode=disallow
The hosted network mode has been set to disallow.

Thursday, May 24, 2012

Introduction

SELINUX may cause some confusion and difficulties when configuring Linux. If any weird problems encounter while configure any of the Linux services (e.g.: Samba, Firewall, ...), we may disable the SELINUX first to check if the problems are related to it.

Fedora 16

In Fedora 16, selinux no longer mount to /selinux. It has move to /sys/fs/selinux.

Wednesday, May 02, 2012

Introduction

It is straight forward to install a fresh new Windows 7 instance on iSCSI target. However, there few tricks to migrate a Windows 7 instance to iSCSI target. Migrating an existing Windows 7 instance is a time consuming process especially for large partition size. Doing it right will save lot of time.

Prepare Windows 7 disk volume for migration

If a Windows 7 instance has larger partition size, e.g.: Few hundred Giga bytes or Tera bytes, migrate this instance will spend lot of time transfer Windows 7 instance to iSCSI target. Before start migrate the instance, try shrink or extend the volume size suitable for usage in near future. Use Extend Volume… and Shrink Volume… function in Disk Management to perform the task:

Update Windows 7 network driver

The iSCSI operation rely heavily on the network device. Update the network driver to latest version is not always necessary but it is advisable to do so. Some booting process of iSCSI operation may slow down due to network driver’s problem.

Disable LightWeight Filter (LWF)

Disable LightWeight Filter (LWF) is a crucial step to make sure the migration work. This step must perform or else the SAN boot will fail in later stage.

There is a quick solution to disable LWF by changing some registry setting:

Identify the description of Network Adapter use for iSCSI network operation in later stage:

Start RegEdit in administrator account.

Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ Class\{4D36E972-E325-11CE-BFC1-08002BE10318}. There are many subkeys underneath, find and open the subkey where the DriverDesc match the NIC’s description. e.g.: 0013

In above example, the windows 7 partition is /dev/sdb1. The sector start from 209920 and end at 309299199. However, sector from 1 to 2099199 is necessary too as it contain the MBR code to make Windows 7 boots properly. The total size to of Windows 7 image should start from sector 1 to 309299199. Each sector has size 512 bytes.

# dd if=/dev/sdb of=win7.img bs=512 count=309299199

The block size of 512 bytes of above example may be slow to image the partition. Try switch the bs and count value may accelerate the imaging process:

# dd if=/dev/sdb of=win7.img bs=309299199 count=512

Transfer win7.img to iSCSI target and perform necessary setup. The Windows 7 instance has successfully migrate to iSCSI target. The iSCSI target is ready to SAN boot now.

Boot iSCSI target

Once the iSCSI target is setup, use iPXE or gPXE to SAN boot the iSCSI target: