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.
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.
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.
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.
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.
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 184.108.40.2065 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.
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.
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.
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.
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.
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.