SocketTools and Multi-threading

One of the errors that developers occasionally ask about is the "Handle not owned by the current thread error"; what it actually means and the situations in which it can occur. This doesn't tend to be an error message that developers using the SocketTools ActiveX controls encounter. It is almost exclusively an issue related to the .NET and Library Editions. The reason this error exists, an explanation of the threading model used and how things have changed involves a bit of a history and the origins of SocketTools.

Historical Overview

The very first release of SocketTools consisted of two versions, a 16-bit version for the Windows 3.1 platform, and a 32-bit version for the Windows 95 platform, which was relatively new at the time. Visual Basic 4.0 had been released, and with it came the new OLE controls which were later renamed to ActiveX. Initially, SocketTools was only available as controls (16-bit VBXs and 32-bit OCXs) and their code was largely shared between the two versions. When the decision was made to release a Library Edition which consisted of standard Windows DLLs, we had to go back to the drawing board to some extent.

There were a fair number of assumptions in the core networking code that worked well when it was in a control targeted for a single language. On the other hand, porting that code to standard DLLs presented a problem because a DLL can be used with virtually any Windows programming language. The solution was to redesign that core code from the ground up, focusing on implementing the DLL interfaces first, and then building the ActiveX controls on top of those APIs. All of the "inside knowledge" the ActiveX controls depended on had to be redesigned (the decision was made early on that we didn't want to create undocumented functions specifically for the controls.) Rather than creating our own shortcuts, it forced us to use our own code much as any other developer would.

Multi-threading

As a result of this re-development, there were two central issues that we had to address. One was support for Unicode, and the other was multi-threading. Unicode support was a fairly easy decision to make. We already had to support it internally because 32-bit ActiveX controls use Unicode. And while it wasn't considered to be a significant issue years ago, we knew that it would be in the future. Multi-threading support was a bit more problematic. We knew that it was important in a general sense, however Visual Basic was (for all intents and purposes) a single-threaded language and ActiveX controls use single-threaded apartments. In essence, COM made sure that there would only be one object per thread and that fit nicely with the Windows Sockets API which allows only one outstanding blocking socket operation per thread. With a DLL, we couldn't make any assumptions about the language it would be used with, how threads would be managed, or if worker threads would be created at all.

We decided on two principal design goals when it came to the SocketTools API. One was to make sure it could be used in both a single-threaded and multi-threaded program without requiring any special coding on the part of the developer. Every function had to be designed to be thread-safe, but this introduced another fundamental problem: sockets are not inherently thread-safe. In other words, when you have two threads reading and writing on the same socket, there's no guarantee which thread is actually going to read the data, or in what order the data is going to be written. The developer has to use some synchronization mechanism to ensure that thread A is not reading data that thread B expects to read. That tied into the second design goal, which was to make sure the SocketTools API wouldn't introduce any unnecessary complexity that could result in unexpected behavior.

Thread Affinity

This lead to the decision to implement a threading model in the DLLs that was functionally similar to how COM apartment threading works, but with a bit more flexibility. Whenever a handle to a client session is created, it is associated with the thread that created it. The handles that are used with the SocketTools API are really a kind of pseudo-handle that references some internal data structures, along with I/O buffers and the actual socket handle. Whenever the handle is passed to a function, it is dereferenced internally and certain checks are performed depending on the type of function being called. One of those checks is whether the thread that is calling the function is the same as the thread that created the handle. If they don't match, the error ST_ERROR_NOT_HANDLE_OWNER is returned to the caller.

This check isn't actually performed on all functions; however, any function that deals with I/O on the socket will check thread ownership. Because it's possible that the developer would want to have one thread initially create the session, but have another thread do the actual work, a mechanism was added where a thread could assume ownership of a handle created by another thread; this is done using the AttachThread method and related functions. In many ways, this provided the best of both worlds. Developers could pass client handles to other threads, but they had to explicitly establish ownership which would prevent inadvertent errors in their code that could prove very difficult to debug.

Free-Threading Model

Over time, we have had some developers express some frustration that handles (or class instances, in the case of the .NET classes) cannot be freely passed between threads. Multi-threaded languages have become the rule rather than the exception, and it's really only the legacy languages like Visual Basic 6.0 which don't have native support for threading. With that in mind, for SocketTools we decided to revisit the issue of the thread ownership model that we use. In the end, we have decided that in the interest of making things safer for developers, our implementation was overly restrictive. If the programmer wants to take responsibility for thread synchronization (which would typically have to be done in most cases anyway), then he should be able to without the additional bookkeeping involved in tracking what thread owns a particular client handle at that point in time.

In SocketTools, when you create a connection you will be able to continue to use the same threading model used in previous versions, or you can choose to specify a "free-threading" option that tells the class or library that any thread can use that session handle. It is important to keep in mind that if you use this option, you are responsible for synchronizing access to the session yourself. For example, if two threads call the FtpClient.Command method for the same class instance, or the call FtpCommand function using the same handle, you will have no control over which command is actually sent to the server first. Unless you synchronize those method calls, it can lead to logical errors in the program which can be very difficult to find and reproduce in a large program. With the default threading model, you would catch this during testing because one of those threads would get an error indicating that it's not the handle's owner. With the free-threading model, that responsibility falls to you. It's an advanced option we feel will be of benefit to developers; however we recommend that you continue to use the default threading model whenever possible.

Shopping Cart
Scroll to Top