1. PI-SDK Background Threads

The PI-SDK uses several background threads (separate concurrent execution paths) to enhance a program's performance. Some of these will be running in most any program and some are particular to the type of calls in-use. When trouble-shooting programs that use the PI-SDK, some understanding of these threads can be helpful, for example when a call stack references one of these threads.  

Communication threads

Much of the communication that goes on under the hood in the PI-SDK is managed by the PI Session Manager (PIsessionmgr) which is code shared by PI Server subsystems and the PI-SDK. It is responsible for managing communication between a program and the local PINet Manager subsystem (pinetmgr). The local pinetmgr service handles communications with other local subsystems as well as pinetmgr instances on remote PI server nodes. The PIsessionmgr is responsible for two of the background threads used by the PI-SDK. The three communication threads listed below are started as soon as a program accesses the Servers collection.

Read Thread - PIsessionmgr Thread

The Read thread monitors "the wire" - sockets or pipes - looking for incoming data. As data is received it is put in one of an array of "streams" that are managed by the PIsessionmgr object.

Message Thread - PIsessionmgr Thread

The Message thread accesses the streams populated by the read thread. It handles completed "transactions" - calls in flight to the server - using the callback supplied by the caller - either a general callback used for synchronous calls or in the case of PI-SDK asynchronous calls a PI-SDK specific callback that moves results to a queue where they are handled by the PI-SDK Asynch thread (described below). In a subsystem that offers its own RPCs, the thread is also used to send RPC results and update transaction lists.

Yield Thread - PI-SDK Thread

The Yield thread provides periodic processing of incoming messages from the server. It is present in the PI-SDK to allow response to periodic "health check" messages sent by a server to manage its connection resources. If the client does not respond to multiple health check messages, its connection is closed.

Processing threads

Event Retrieval Thread - PI-SDK thread

This thread monitors signups with PI server update managers and when events are available retrieves them, creates PIEventObjects to contain them and distributes them to the appropriate EventPipe. In PI-SDK versions and earlier, the thread is only present while the program holds an EventPipe object. In later versions, the thread is started when a program first acquires an EventPipe but remains running until the controlling PISDK application object is released.

Reconnect Thread (PI-SDK SDKQueueThread)

This thread attempts to reestablish communication when the connection to a server has been lost. Because the attempt typically requires waiting for a timeout period, reconnection attempts tie up a program's main thread repeatedly when a server becomes unavailable making the user interface unresponsive. Note in the case of a high availability server, reconnection attempts are not normally routed to a background thread (unless all members are unavailable) as another connection point should be available.

Asynch Thread (PI-SDK SDKQueueThread)

When PI-SDK calls are placed that take a PIAsynchStatus object as an argument and this is not passed as a NULL, the call is placed and returns immediately before results are generated. The Asynch thread handles callbacks from the server when the data is ready, fills up the result containers pointed to by the PIAsynchStatus object and lets the caller know the data is available.
Windows Event Firing Thread (PI-SDK SDKQueueThread
This PI-SDK thread is designed to avoid potential deadlock situations. When signing up for a Windows event produced by one of the PI-SDK objects (for example the OnDisconnect event from the Server object), the caller builds an interface handler in code and passes it to the PI-SDK to be called when the event occurs. This is made simpler by many languages but this is still what is done at a lower level. When the PI-SDK calls the methods of this interface, it has to wait for the method to return before proceeding. Because some of these events are called while the PI-SDK is holding internal locks (for example the OnDisconnect event is fired when during call execution it is determined the server is unavailable) and these same locks could be required by actions programmed into an event response by an interface handler already holding other locks, there is a potential for deadlock. By queuing events that need to be fired (other than those fired from the main thread) and firing them in a separate thread, deadlock is avoided.

Registry Monitor Thread (PI-SDK Thread)

The PI-SDK stores configuration information in the registry which can affect the behavior of a running program. To allow configuration changes by one program to affect another running program and to monitor these changes efficiently, the PI-SDK uses a separate thread that watches for events raised when monitored areas of the registry are changed by any program.

What they look like in a debugger

When debugging a program in Visual Studio, Windbg, or other debuggers, you can examine the thread table to see what threads are running. The name and location fields of the thread typically give are good identifiers. The display below taken from Visual Studio shows several SDK threads. This particular example was taken while running AboutPI-SDK and doing a TagSearch, which is done asynchronously.

ID Name  Location
5644  TagSearch::Show tsSearch::ShowTagSearchDialog
2884 Win32 Thread _ZwWaitForMultipleObjects@20
5464 Win32 Thread  _ZwRemoveIoCompletion@20
6400 Win32 Thread  _NtDelayExecution@8
4684 thdChangeMonitor thdChangeMonitor
5060 PIsessionmgrMessageThread WaitForMultipleSemaphores
7132 PIsessionmgrreadthread PIw32_stream::recv
2648 SDKYieldThread SDKYieldThread
6648 SDKWorkThread PISDKAsynchHandler::ProcessCallBack
 5804  Win32 Thread  _ZwRemoveIoCompletion@20

Here the main thread, 5644, is running the TagSearch. The next three threads (2884,5464, and 6400) are system threads. Thread 4684 is the PI-SDK's registry monitor thread; 5060 is the Message thread, 7132 is the Read thread, 2648 is the Yield thread, 6648 is the Asynch thread, and 5804 is another system thread.

Here's a line from the debugger when running the EventPipe sample program from the PI-SDK Help that shows the Event Retrieval thread in action.

7048 EventRetrievalThreadRoutine EventRetrievalThreadRoutine

Here's one from debugging a test program that does reconnection testing. Note the name is the same as for the Asynch thread in the table above but the location is more informative showing us this is the Reconnect thread. The reason for the similar name is the same code is used to manage both threads.

4196 SDKWorkThread ReconnectionData::ProcessCallBack

One more line from the thread table of the same program, this time showing the Windows Event Firing thread. The "cp" in the name stands for Connection Point which is the formal way you hook up to windows events. Again this is done by the SDKWorkThread code but is a separate thread.

6392 SDKWorkThread SDKcpQueueItem::ProcessCallBack

For the examples that use the SDKWorkThread code, breakpoints were inserted within the code so the thread location was demonstrative. A more typical display would look like:

4196 SDKWorkThread SDKQueueThread::ProcessQueue
6392 SDKWorkThread SDKQueueThread::ProcessQueue

Here, the generic location isn't very helpful. You only know it is one of the three SDKWorkThread implementations. Each of these three threads consults a queue of work. Other threads post tasks in the queue for the thread to process.



Enabling Operational Intelligence