Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does managed languages lock flush and reload variables of native libraries?

Tags:

java

c++

c

c#

locking

When we use locks in managed languages like C# and Java, we can always be sure we are dealing with the latest data.

Specifically in Java memory model, they have a guarantee called Happens-before relationship. But I'm not sure what will happen with native libraries.

Say, I have C functions like this :

static int sharedData;      // I'm not declaring this as volatile on purpose here.

void setData(int data) {
    sharedData = data;      // Not using any mutex or the like.
}

int getData() {
    return sharedData;
}

I also have C# code like this :

// Thread 1
while( true )
    lock( key )
        setData( ++i );     // Calling a native C function using P/Invoke.

// Thread 2
while( true )
    lock( key )
        DoSomeJob( getData() );

As you see, if sharedData from C side is not declared as volatile, then is there still a guarantee that Thread 2 can always get the latest value set by Thread 1?

Does the same apply to Java using JNI too?

like image 392
Jenix Avatar asked Jun 27 '19 08:06

Jenix


People also ask

What is Read_buffer_size in MySQL?

read_buffer_size Is also used to determine the memory block size for Memory tables. The read_rnd_buffer_size variable is also used mainly for MyISAM reads from tables. Also, consider InnoDB or MariaDB's Aria storage engines. For the past decade, the default values of these buffers have remained the same.

How do I permanently set a global variable in MySQL?

To persist a global system variable to the mysqld-auto. cnf option file in the data directory, precede the variable name by the PERSIST keyword or the @@PERSIST. qualifier: SET PERSIST max_connections = 1000; SET @@PERSIST.

What is Net_read_timeout in MySQL?

When the server is reading from the client, net_read_timeout is the timeout value controlling when to abort. However, MySQL does not abort connections if the read (waiting for data) takes more than the value specified in this variable.

Where are system variables stored in MySQL?

In the mysqld-auto. cnf option file, the names and values of sensitive system variables are stored in an encrypted format, along with a generated file key to decrypt them. The generated file key is in turn encrypted using a master key ( persisted_variables_key ) that is stored in a keyring.


1 Answers

As you see, if sharedData from C side is not declared as volatile, then is there still a guarantee that Thread 2 can always get the latest value set by Thread 1?

No, and marking it volatile has no effect on threading either way.

Does the same apply to Java using JNI too?

Yes, and it also applies to PHP, Lua, Python, and any other language that can pull in a C library in that way.

To elaborate on your first question, the volatile keyword in C is not used for threading, it is used to tell the compiler not to optimize on that variable.

Take for example the following code:

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

static bool run; // = false

void do_run(void)
{
    unsigned long v = 1;
    while (run) {
        if (++v == ULONG_MAX) run = false;
    }
    printf("v = %lu\n", v);
}

void set_run(bool value)
{
    run = value;
}

int main(int argc, char** argv)
{
    set_run(true);
    do_run();
    return 0;
}

With optimizations turned on, the compiler might see a lot of areas to remove unnecessary code without side effect, and do so; for instance, the compiler could see that unsigned long v will always be ULONG_MAX in the do_run function and opt instead to simply return ULONG_MAX.

And in fact when I run gcc -O3 on the above code, that's exactly what happens, with the do_run function returning immediately and printing v = 18446744073709551615.

If you were to mark run as volatile, then the compiler can't optimize that variable, which usually means it cannot optimize the areas of code with that variable in certain ways.

To wit, when I change run to static volatile bool run; and then compile using gcc -O3, my program now stalls waiting for the loop to iterate through 18446744073709551615 times.


That aside, when you make a call into an external library, the only thread-safety you have is that provided by the language utilized in that library.

For C, you must explicitly specify thread safety in functions. So for your code, even though you are utilizing the lock context in the managed code, it is only locking for the managed code and the C code itself is still not thread safe.

Take for example the following code:

C code

static volatile int sharedData;
static volatile bool doRun;
static pthread_t addThread;

void* runThread(void* data)
{
    while (doRun) {
        ++sharedData;
    }
    return NULL;
}

void startThread(void)
{
    doRun = true;
    pthread_create(&addThread, NULL, &runThread, NULL);
}

void stopThread(void)
{
    doRun = false;
}

void setData(int data)
{
    sharedData = data;
}

int getData(void)
{
    return sharedData;
}

C# code

// Thread 1
startThread();
while (true) {
    lock (key) {
        setData(++i);
    }
}

// Thread 2
while (true) {
    lock (key) {
        i = getData();
    }
}
stopThread();

In this code, when lock (key) is called, the only guarantee you have is that i will be protected in the C# code. However, since the C code is also running a thread (because thread 1 called startThread), then you do not have any guarantee that the C# code will be properly synchronized.

To make the C code thread-safe, you will have to specifically add a mutex or semaphore to suit your needs:

static int sharedData;
static volatile bool doRun;
static pthread_t addThread;
static pthread_mutex_t key;

void* runThread(void* data)
{
    while (doRun) {
        pthread_mutex_lock(&key);
        ++sharedData;
        pthread_mutex_unlock(&key);
    }
    return NULL;
}

void startThread(void)
{
    doRun = true;
    pthread_mutex_init(&key, NULL);
    pthread_create(&addThread, NULL, &runThread, NULL);
}

void stopThread(void)
{
    doRun = false;
    pthread_mutex_lock(&key);
    pthread_mutex_unlock(&key);
    pthread_mutex_destroy(&key);
}

void setData(int data)
{
    pthread_mutex_lock(&key);
    sharedData = data;
    pthread_mutex_unlock(&key);
}

int getData(void)
{
    int ret = 0;
    pthread_mutex_lock(&key);
    ret = sharedData;
    pthread_mutex_unlock(&key);
    return ret;
}

In this way, the underlying library calls are protected appropriately and any number of processes sharing the memory of this library will also then be thread-safe.

I should note that the above utilizes POSIX for thread synchronization, but the WinAPI, or the C11 standard mutex could also be utilized depending on your target system.

I hope that can help.

like image 133
txtechhelp Avatar answered Sep 25 '22 23:09

txtechhelp