Multithreading and Yoctopuce libraries

Multithreading and Yoctopuce libraries

We take great care of our programming libraries, in particular of their ease of use. However, there is one spot where our libraries can be tricky: using our libraries from several threads. This week, we clarify and improve the situation.





We designed our API so that you can efficiently use our libraries from a single thread, with the aim that novice developers can use our API easily and efficiently without having to manage threads.

It's for this reason that our libraries weren't "thread safe", that is that if several threads used our library at the same time, it could happen that our code didn't work properly. Our idea was that if developers correctly know how to use threads, they also know how to avoid concurrent access to the Yoctopuce library.

For some languages, in particular C++, C#, or Java, the use of threads is very widespread and some of the developers prefer using several threads rather than a state machine to perform several tasks. In this case, the developers had to use mutex to serialize accesses to the Yoctopuce library. To ease the use of our API, we decided to make "thread safe" the libraries for these three languages.

Some theory


The issue with multithread programming is linked to concurrency. Indeed, if several threads use the same resource at the same time (variable, file, data base, access to a peripheral device, and so on) without taking precautions, it is highly likely that something goes wrong. Let's take the example of the following function performing a withdrawal on a bank account:

1: bool withdraw(int withdrawal) 2: { 3: if (balance >= withdrawal) { 4: balance = balance - withdrawal; 5: return true; 6: } 7: return false; 8: }



The withdraw function checks that the balance is sufficient before subtracting the amount of the withdrawal from the amount in the account. This code works perfectly if only one thread uses it at a time. But if several threads call this function at the same time, the result can be incorrect. Let's imagine that the balance is $100. The first thread performs a $60 withdrawal and the second thread a $70 withdrawal. If both threads execute line 3 at the same time, both withdrawals are authorized while the addition of the two withdrawals goes above the $100 balance.

Fortunately, there is a mechanism to solve this issue: mutual exclusion or mutex. Without going into details, mutex is a mechanism enabling us to guarantee that a resource is used by a single thread at a time. All the programming languages have a mutual exclusion mechanism.

Unfortunately, mutex are complex to use and can be the source of other bugs. The most well known case is the bug on the Mars Pathfinder probe.

The impact on our libraries


In our case, the shared resource that needs protection is the Yoctopuce library. If two threads call our library simultaneously, it's possible that the library doesn't work correctly. The code of our library being much longer than that of our previous example, the probability that two threads use simultaneously the same part of the code is smaller. However, on an application working 24/7, sooner or later Murphy's law is going to apply.

For this reason, developers had to implement their own mutex mechanism to ensure that there was no simultaneous calls to the Yoctopuce library. This work being laborious and complex, we decided to include a protection directly in the library.

The new C++, C#, and Java thread safe libraries


We publish today a new build making thread safe the C++, C#, Java, and Android libraries. This means that, from now on, the library supports being called simultaneously by several threads.

Internally, the library protects itself against concurrent accesses with mutex. The interest is twofold. First, this simplifies the work of the developers because they don't have to write their own protection mechanism. Second, the code is more efficient because only the problematic parts of the code are protected.

Example


There are two ways to implement the same application.

The first one without using threads:

static void deviceArrival(YModule m)
{
    Console.WriteLine("Device arrival : " + m.get_serialNumber());
}

static void deviceRemoval(YModule m)
{
    Console.WriteLine("Device removal : " + m.get_serialNumber());
}

static void Main(string[] args)
{
    string errmsg = "";
    if (YAPI.RegisterHub("usb", ref errmsg) != YAPI.SUCCESS)
    {
        Console.WriteLine("RegisterHub error: " + errmsg);
        Environment.Exit(0);
    }

    YAPI.RegisterDeviceArrivalCallback(deviceArrival);
    YAPI.RegisterDeviceRemovalCallback(deviceRemoval);
    YTemperature temp = YTemperature.FirstTemperature();
    Console.WriteLine("Hit Ctrl-C to Stop ");
    while (true)
    {
        YAPI.UpdateDeviceList(ref errmsg);
        YAPI.Sleep(3000, ref errmsg);
        Console.WriteLine("Temp : " + temp.get_currentValue()
                                    + " " + temp.get_unit());
        }
    }
}




The second version uses a second thread to display the temperature:

static void deviceArrival(YModule m)
{
    Console.WriteLine("Device arrival : " + m.get_serialNumber());
}

static void deviceRemoval(YModule m)
{
    Console.WriteLine("Device removal : " + m.get_serialNumber());
}

static void PollingThread()
{
    while (true)
    {
        YTemperature temp = YTemperature.FirstTemperature();
        while (true)
        {
            Console.WriteLine("Temp : " + temp.get_currentValue()
                                    + " " + temp.get_unit());
            System.Threading.Thread.Sleep(3000);
        }
    }
}

static void Main(string[] args)
{
    string errmsg = "";
    if (YAPI.RegisterHub("usb", ref errmsg) != YAPI.SUCCESS)
    {
        Console.WriteLine("RegisterHub error: " + errmsg);
        Environment.Exit(0);
    }

    Console.WriteLine("Start second thread that will log the temperature");
    Thread pollThread = new Thread(new ThreadStart(PollingThread));
    pollThread.Start();

    YAPI.RegisterDeviceArrivalCallback(deviceArrival);
    YAPI.RegisterDeviceRemovalCallback(deviceRemoval);
    Console.WriteLine("Hit Ctrl-C to Stop ");
    while (true)
    {
        YAPI.UpdateDeviceList(ref errmsg);
        YAPI.Sleep(1000, ref errmsg);
    }
}



Calls to the library are identical and you don't need to protect the calls to the Yoctopuce library with a mutex.

Conclusion


Using threads is a very common practice in the C++, C#, and Java languages. This is why we modified these libraries. Form now on, you don't need to protect these libraries from concurrent access. However, keep in mind that using threads is not always the most efficient way to solve a problem. If your application has 15 threads which use the Yoctopuce library, it may be time to think about a more efficient way to write your code.

Note : Special thanks to Rob Krakora who helped us to identify and fix some nasty race conditions.

Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.