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?
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.
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.
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.
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.
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.
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:
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;
}
// 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With