Related
This is a really long question due to the code snippets and the detailed explanations. TL;DR, are there issues with the macros shown below, is this a reasonable solution, and if not then what is the most reasonable way to solve the issues presented below?
I am currently writing a C library which deals with POSIX threads, and must be able to handle thread cancellation cleanly. In particular, the library functions may be called from threads that were set to be cancellable (either PTHREAD_CANCEL_DEFFERED or PTHREAD_CANCEL_ASYNCHRONOUS canceltype) by the user.
Currently the library functions that interface with the user all begin with a call to pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate), and at each return point, I make sure that a call to pthread_setcancelstate(oldstate, &dummy) is made to restore whatever cancellation settings the thread had previously.
This basically prevents the thread from being cancelled while in the library code, thus ensuring that the global state remains consistent and resources were properly managed before returning.
This method unfortunately has a few drawbacks:
One must be sure to restore the cancelstate at every return point. This makes it somewhat hard to manage if the function has nontrivial control flow with multiple return points. Forgetting to do so may lead to threads that don't get cancelled even after return from the library.
We only really need to prevent cancellations at points where resources are being allocated or global state is inconsistent. A library function may in turn call other internal library functions that are cancel-safe, and ideally cancellations could occur at such points.
Here is a sample illustration of the issues:
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
int mylib_function(size_t len)
{
char *buffer;
int oldstate, oldstate2;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
buffer = malloc(len);
if (buffer == NULL) {
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
do_some_long_computation(buffer, len);
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return -1;
}
write(fd, buffer, len); /* Normally also do error-check */
close(fd);
free(buffer);
pthread_setcancelstate(oldstate, &oldstate2);
return 0;
}
Here it is not so bad because there are only 3 return points. One could possibly even restructure the control flow in such a way as to force all paths to reach a single return point, perhaps with the goto cleanup pattern. But the second issue is still left unresolved. And imagine having to do that for many library functions.
The second issue may be resolved by wrapping each resource allocation with calls to pthread_setcancelstate that will only disable cancellations during resource allocation. While cancellations are disabled, we also push a cleanup handler (with pthread_cleanup_push). One could also move all resource allocations together (opening the file before doing the long computation).
While solving the second issue, it is still somewhat hard to maintain because each resource allocation needs to be wrapped under these pthread_setcancelstate and pthread_cleanup_[push|pop] calls. Also it might not always be possible to put all resource allocations together, for instance if they depend on the results of the computation. Moreover, the control flow needs to be changed because one cannot return between a pthread_cleanup_push and pthread_cleanup_pop pair (which would be the case if malloc returns NULL for example).
In order to solve both issues, I came up with another possible method that involves dirty hacks with macros. The idea is to simulate something like a critical section block in other languages, to insert a block of code in a "cancel-safe" scope.
This is what the library code would look like (compile with -c -Wall -Wextra -pedantic):
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include "cancelsafe.h"
static void do_some_long_computation(char *buffer, size_t len)
{
(void)buffer; (void)len;
/* This is really, really long! */
}
static void free_wrapper(void *arg)
{
free(*(void **)arg);
}
static void close_wrapper(void *arg)
{
close(*(int *)arg);
}
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
CANCELSAFE_INIT();
CANCELSAFE_PUSH(free_wrapper, buffer) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
CANCELSAFE_BREAK(buffer);
}
}
do_some_long_computation(buffer, len);
CANCELSAFE_PUSH(close_wrapper, fd) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
CANCELSAFE_BREAK(fd);
}
}
write(fd, buffer, len);
CANCELSAFE_POP(fd, 1); /* close fd */
CANCELSAFE_POP(buffer, 1); /* free buffer */
CANCELSAFE_END();
return rc;
}
This resolves both issues to some extent. The cancelstate settings and cleanup push/pop calls are implicit in the macros, so the programmer only has to specify the sections of code that need to be cancel-safe and what cleanup handlers to push. The rest is done behind the scenes, and the compiler will make sure each CANCELSAFE_PUSH is paired with a CANCELSAFE_POP.
The implementation of the macros is as follows:
#define CANCELSAFE_INIT() \
do {\
int CANCELSAFE_global_stop = 0
#define CANCELSAFE_PUSH(cleanup, ident) \
do {\
int CANCELSAFE_oldstate_##ident, CANCELSAFE_oldstate2_##ident;\
int CANCELSAFE_stop_##ident;\
\
if (CANCELSAFE_global_stop)\
break;\
\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_push(cleanup, &ident);\
for (CANCELSAFE_stop_##ident = 0; CANCELSAFE_stop_##ident == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_##ident = 1, pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident))
#define CANCELSAFE_BREAK(ident) \
do {\
CANCELSAFE_global_stop = 1;\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
goto CANCELSAFE_POP_LABEL_##ident;\
} while (0)
#define CANCELSAFE_POP(ident, execute) \
CANCELSAFE_POP_LABEL_##ident:\
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_##ident);\
pthread_cleanup_pop(execute);\
pthread_setcancelstate(CANCELSAFE_oldstate_##ident, &CANCELSAFE_oldstate2_##ident);\
} while (0)
#define CANCELSAFE_END() \
} while (0)
This combines several macro tricks that I have encountered before.
The do { } while (0) pattern is used to have a multiline function-like macro (with semicolon required).
The CANCELSAFE_PUSH and CANCELSAFE_POP macros are forced to come in pairs by the use of the same trick as the pthread_cleanup_push and pthread_cleanup_pop using unmatched { and } braces respectively (here it is unmatched do { and } while (0) instead).
The usage of the for loops is somewhat inspired by this question. The idea is that we want to call the pthread_setcancelstate function after the macro body to restore cancellations after the CANCELSAFE_PUSH block. I use a stop flag that is set to 1 at the second loop iteration.
The ident is the name of the variable that will be released (this needs to be a valid identifier). The cleanup_wrappers will be given its address, which will always be valid in a cleanup handler scope according to this answer. This is done because the value of the variable is not yet initialized at the point of cleanup push (and also doesn't work if the variable is not of pointer type).
The ident is also used to avoid name collisions in the temporary variables and labels by appending it as a suffix with the ## concatenation macro, giving them unique names.
The CANCELSAFE_BREAK macro is used to jump out of the cancelsafe block and right into the corresponding CANCELSAFE_POP_LABEL. This is inspired by the goto cleanup pattern, as mentioned here. It also sets the global stop flag.
The global stop is used to avoid cases were there might be two PUSH/POP pairs in the same scope level. This seems like an unlikely situation, but if this happens then the content of the macros is basically skipped when the global stop flag is set to 1. The CANCELSAFE_INIT and CANCELSAFE_END macros aren't crucial, they just avoid the need to declare the global stop flag ourselves. These could be skipped if the programmer always does all the pushes and then all the pops consecutively.
After expanding the macros, we obtain the following code for the mylib_function:
int mylib_function(size_t len)
{
char *buffer;
int fd;
int rc;
rc = 0;
do {
int CANCELSAFE_global_stop = 0;
do {
int CANCELSAFE_oldstate_buffer, CANCELSAFE_oldstate2_buffer;
int CANCELSAFE_stop_buffer;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_push(free_wrapper, &buffer);
for (CANCELSAFE_stop_buffer = 0; CANCELSAFE_stop_buffer == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_buffer = 1, pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer)) {
buffer = malloc(len);
if (buffer == NULL) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
goto CANCELSAFE_POP_LABEL_buffer;
} while (0);
}
}
do_some_long_computation(buffer, len);
do {
int CANCELSAFE_oldstate_fd, CANCELSAFE_oldstate2_fd;
int CANCELSAFE_stop_fd;
if (CANCELSAFE_global_stop)
break;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_push(close_wrapper, &fd);
for (CANCELSAFE_stop_fd = 0; CANCELSAFE_stop_fd == 0 && CANCELSAFE_global_stop == 0; CANCELSAFE_stop_fd = 1, pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSTATE_oldstate2_fd)) {
fd = open("results.txt", O_WRONLY);
if (fd < 0) {
rc = -1;
do {
CANCELSAFE_global_stop = 1;
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
goto CANCELSAFE_POP_LABEL_fd;
} while (0);
}
}
write(fd, buffer, len);
CANCELSAFE_POP_LABEL_fd:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_fd);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_fd, &CANCELSAFE_oldstate2_fd);
} while (0);
CANCELSAFE_POP_LABEL_buffer:
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &CANCELSAFE_oldstate_buffer);
pthread_cleanup_pop(1);
pthread_setcancelstate(CANCELSAFE_oldstate_buffer, &CANCELSAFE_oldstate2_buffer);
} while (0);
} while (0);
return rc;
}
Now, this set of macros is horrendous to look at and it is somewhat tricky to understand how they work exactly. On the other hand, this is a one-time task, and once written, they can be left and the rest of the project can benefit from their nice benefits.
I would like to know if there are any issues with the macros that I may have overlooked, and whether there could be a better way to implement similar functionality. Also, which of the solutions proposed do you think would be the most reasonable? Are there other ideas that could work better to resolve these issues (or perhaps, are they really non-issues)?
Unless you use asynchronous cancellation (which is always very problematic), you do not have to disable cancellation around malloc and free (and many other POSIX functions). Synchronous cancellation only happens at cancellation points, and these functions are not.
You are abusing the POSIX cancellation handling facilities to implement a scope exit hook. In general, if you find yourself doing things like this in C, you should seriously consider using C++ instead. This will give you a much more polished version of the feature, with ample documentation, and programmers will already have experience with it.
Is there a way to access a variable initialized in one code from another code. For eg. my code1.c is as follows,
# include <stdio.h>
int main()
{
int a=4;
sleep(99);
printf("%d\n", a);
return 0;
}
Now, is there any way that I can access the value of a from inside another C code (code2.c)? I am assuming, I have all the knowledge of the variable which I want to access, but I don't have any information about its address in the RAM. So, is there any way?
I know about the extern, what I am asking for here is a sort of backdoor. Like, kind of searching for the variable in the RAM based on some properties.
Your example has one caveat, set aside possible optimizations that would make the variable to dissapear: variable a only exists while the function is being executed and has not yet finished.
Well, given that the function is main() it shouldn't be a problem, at least, for standard C programs, so if you have a program like this:
# include <stdio.h>
int main()
{
int a=4;
printf("%d\n", a);
return 0;
}
Chances are that this code will call some functions. If one of them needs to access a to read and write to it, just pass a pointer to a as an argument to the function.
# include <stdio.h>
int main()
{
int a=4;
somefunction(&a);
printf("%d\n", a);
return 0;
}
void somefunction (int *n)
{
/* Whatever you do with *n you are actually
doing it with a */
*n++; /* actually increments a */
}
But if the function that needs to access a is deep in the function call stack, all the parent functions need to pass the pointer to a even if they don't use it, adding clutter and lowering the readability of code.
The usual solution is to declare a as global, making it accessible to every function in your code. If that scenario is to be avoided, you can make a visible only for the functions that need to access it. To do that, you need to have a single source code file with all the functions that need to use a. Then, declare a as static global variable. So, only the functions that are written in the same source file will know about a, and no pointer will be needed. It doesn't matter if the functions are very nested in the function call stack. Intermediate functions won't need to pass any additional information to make a nested function to know about a
So, you would have code1.c with main() and all the functions that need to access a
/* code1.c */
# include <stdio.h>
static int a;
void somefunction (void);
int main()
{
a=4;
somefunction();
printf("%d\n", a);
return 0;
}
void somefunction (void)
{
a++;
}
/* end of code1.c */
About trying to figure out where in RAM is a specific variable stored:
Kind of. You can travel across function stack frames from yours to the main() stack frame, and inside those stack frames lie the local variables of each function, but there is no sumplementary information in RAM about what variable is located at what position, and the compiler may choose to put it wherever it likes within the stack frame (or even in a register, so there would be no trace of it in RAM, except for push and pops from/to general registers, which would be even harder to follow).
So unless that variable has a non trivial value, it's the only local variable in its stack frame, compiler optimizations have been disabled, your code is aware of the architecture and calling conventions being used, and the variable is declared as volatile to stop being stored in a CPU register, I think there is no safe and/or portable way to find it out.
OTOH, if your program has been compiled with -g flag, you might be able to read debugging information from within your program and find out where in the stack frame the variable is, and crawl through it to find it.
code1.c:
#include <stdio.h>
void doSomething(); // so that we can use the function from code2.c
int a = 4; // global variable accessible in all functions defined after this point
int main()
{
printf("main says %d\n", a);
doSomething();
printf("main says %d\n", a);
return 0;
}
code2.c
#include <stdio.h>
extern int a; // gain access to variable from code1.c
void doSomething()
{
a = 3;
printf("doSomething says %d\n", a);
}
output:
main says 4
doSomething says 3
main says 3
You can use extern int a; in every file in which you must use a (code2.c in this case), except for the file in which it is declared without extern (code1.c in this case). For this approach to work you must declare your a variable globally (not inside a function).
One approach is to have the separate executable have the same stack layout as the program in question (since the variable is placed on the stack, and we need the relative address of the variable), therefore compile it with the same or similar compiler version and options, as much as possible.
On Linux, we can read the running code's data with ptrace(PTRACE_PEEKDATA, pid, …). Since on current Linux systems the start address of the stack varies, we have to account for that; fortunately, this address can be obtained from the 28th field of /proc/…/stat.
The following program (compiled with cc Debian 4.4.5-8 and no code generator option on Linux 2.6.32) works; the pid of the running program has to be specified as the program argument.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
void *startstack(char *pid)
{ // The address of the start (i. e. bottom) of the stack.
char str[FILENAME_MAX];
FILE *fp = fopen(strcat(strcat(strcpy(str, "/proc/"), pid), "/stat"), "r");
if (!fp) perror(str), exit(1);
if (!fgets(str, sizeof str, fp)) exit(1);
fclose(fp);
unsigned long address;
int i = 28; char *s = str; while (--i) s += strcspn(s, " ") + 1;
sscanf(s, "%lu", &address);
return (void *)address;
}
static int access(void *a, char *pidstr)
{
if (!pidstr) return 1;
int pid = atoi(pidstr);
if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) return perror("PTRACE_ATTACH"), 1;
int status;
// wait for program being signaled as stopped
if (wait(&status) < 0) return perror("wait"), 1;
// relocate variable address to stack of program in question
a = a-startstack("self")+startstack(pidstr);
int val;
if (errno = 0, val = ptrace(PTRACE_PEEKDATA, pid, a, 0), errno)
return perror("PTRACE_PEEKDATA"), 1;
printf("%d\n", val);
return 0;
}
int main(int argc, char *argv[])
{
int a;
return access(&a, argv[1]);
}
Another, more demanding approach would be as mcleod_ideafix indicated at the end of his answer to implement the bulk of a debugger and use the debug information (provided its presence) to locate the variable.
I need to use an atomic variable in C as this variable is accessed across different threads. Don't want a race condition.
My code is running on CentOS. What are my options?
C11 atomic primitives
http://en.cppreference.com/w/c/language/atomic
_Atomic const int * p1; // p is a pointer to an atomic const int
const atomic_int * p2; // same
const _Atomic(int) * p3; // same
Added in glibc 2.28. Tested in Ubuntu 18.04 (glibc 2.27) by compiling glibc from source: Multiple glibc libraries on a single host Later also tested on Ubuntu 20.04, glibc 2.31.
Example adapted from: https://en.cppreference.com/w/c/language/atomic
main.c
#include <stdio.h>
#include <threads.h>
#include <stdatomic.h>
atomic_int acnt;
int cnt;
int f(void* thr_data)
{
(void)thr_data;
for(int n = 0; n < 1000; ++n) {
++cnt;
++acnt;
// for this example, relaxed memory order is sufficient, e.g.
// atomic_fetch_add_explicit(&acnt, 1, memory_order_relaxed);
}
return 0;
}
int main(void)
{
thrd_t thr[10];
for(int n = 0; n < 10; ++n)
thrd_create(&thr[n], f, NULL);
for(int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("The atomic counter is %u\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
Compile and run:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c -pthread
./main.out
Possible output:
The atomic counter is 10000
The non-atomic counter is 8644
The non-atomic counter is very likely to be smaller than the atomic one due to racy access across threads to the non atomic variable.
Disassembly analysis at: How do I start threads in plain C?
If you are using GCC on your CentOS platform, then you can use the __atomic built-in functions.
Of particular interest might be this function:
— Built-in Function: bool __atomic_always_lock_free (size_t size, void *ptr)
This built-in function returns true if objects of size bytes always generate lock free atomic instructions for the target architecture. size must resolve to a compile-time constant and the result also resolves to a compile-time constant.
ptr is an optional pointer to the object that may be used to determine alignment. A value of 0 indicates typical alignment should be used. The compiler may also ignore this parameter.
if (_atomic_always_lock_free (sizeof (long long), 0))
I am going to toss in my two cents in case someone benefits. Atomic operations are a major problem in Linux. I used gatomic.h at one time only to find it gone. I see all kinds of different atomic options of either questionable reliability or availability -- and I see things changing all the time. They can be complex with tests needed by O/S level, processor, whatever. You can use a mutex -- not only complex by dreadfully slow.
Although perhaps not ideal in threads, this works great for atomic operations on shared memory variables. It is simple and it works on every O/S and processor and configuration known to man (or woman), dead reliable, easy to code, and will always work.
Any code can me made atomic with a simple primitive -- a semaphore. It is something that is true/false, 1/0, yes/no, locked/unlocked -- binary.
Once you establish the semaphore:
set semaphore //must be atomic
do all the code you like which will be atomic as the semaphore will block for you
release semaphore //must be atomic
Relatively straight forward except the "must be atomic" lines.
It turns out that you easily assign your semaphores a number (I use a define so they have a name like "#define OPEN_SEM 1" and "#define "CLASS_SEM 2" and so forth.
Find out your largest number and when your program initializes open a file in some directory (I use one just for this purpose). If not there create it:
if (ablockfd < 0) { //ablockfd is static in case you want to
//call it over and over
char *get_sy_path();
char lockname[100];
strcpy(lockname, get_sy_path());
strcat(lockname, "/metlock");
ablockfd = open(lockname, O_RDWR);
//error code if ablockfd bad
}
Now to gain a semaphore:
Now use your semaphore number to "lock" a "record" in your file of length one byte. Note -- the file will never actually occupy disk space and no disk operation occurs.
//sem_id is passed in and is set from OPEN_SEM or CLASS_SEM or whatever you call your semaphores.
lseek(ablockfd, sem_id, SEEK_SET); //seeks to the bytes in file of
//your semaphore number
result = lockf(ablockfd, F_LOCK, 1);
if (result != -1) {
//got the semaphore
} else {
//failed
}
To test if the semaphore is held:
result = lockf(ablockfd, F_TEST, 1); //after same lseek
To release the semaphore:
result = lockf(ablockfd, F_ULOCK, 1); //after same lseek
And all the other things you can do with lockf -- blocking/non-blocking, etc.
Note -- this is WAY faster than a mutex, it goes away if the process dies (a good thing), simple to code, and I know of no operating system with any processor with any number of them or number of cores that cannot atomically lock a record ... so simple code that just works. The file never really exists (no bytes but in directory), seems to be no practical limit to how many you may have. I have used this for years on machines with no easy atomic solutions.
Consider the following snippet of C code:
int flag = 0;
/* Assume that the functions lock_helper, unlock_helper implement enter/leave in
* a global mutex and thread_start_helper simply runs the function in separate
* operating-system threads */
void worker1()
{
/* Long-running job here */
lock_helper();
if (!flag)
flag = 1;
unlock_helper();
}
void worker2()
{
/* Another long-running job here */
lock_helper();
if (!flag)
flag = 2;
unlock_helper();
}
int main(int argc, char **argv)
{
thread_start_helper(&worker1);
thread_start_helper(&worker2);
do
{
/* doing something */
} while (!flag);
/* do something with 'flag' */
}
Questions:
Is it it possible that 'flag' will always be 0 for the main thread(and it
becomes stuck in the do/while loop) due to some compiler optimization?
Will the 'volatile' modifier make any difference?
If the answer is 'depends on a feature provided by the compiler', is there any
way I can check for this 'feature' with a configuration script at
compile-time?
The code is likely to work as is, but is somewhat fragile. For one thing, it depends on the reads and writes to flag being atomic on the processor being used (and that flag's alignment is sufficient).
I would recommend either using a read lock to read the value of flag or use functionality of whatever threading library you are using to make flag properly atomic.
Since you can assume that the loading of an aligned int is an atomic operation, the only danger with your code is the optimizer: your compiler is allowed to optimize away all but the first reads of flag within main(), i. e. to convert your code into
int main(int argc, char **argv)
{
thread_start_helper(&worker1);
thread_start_helper(&worker2);
/* doing something */
if(!flag) {
while(1) /* doing something */
}
//This point is unreachable and the following can be optimized away entirely.
/* do something with 'flag' */
}
There are two ways you can make sure that this does not happen: 1. make flag volatile, which is a bad idea because it includes quite a bit of unwanted overhead, and 2. introduce the necessary memory barriers. Due to the atomicity of reading an int and the fact that you only want to interprete the value of flag after it has changed, you should be able to get away with just a compiler barrier before the loop condition like this:
int main(int argc, char **argv)
{
thread_start_helper(&worker1);
thread_start_helper(&worker2);
do
{
/* doing something */
barrier();
} while(!flag)
/* do something with 'flag' */
}
The barrier() used here is very lightweight, it is the cheapest of all barriers available.
This is not enough if you want to analyze any other data that is written before flag is raised, because you might still load stale data from memory (because the CPU decided to prefetch the value). For a comprehensive discussion of memory fences, their necessity, and their use, see https://www.kernel.org/doc/Documentation/memory-barriers.txt
Finally, you should be aware, that the other writer thread may modify flag at any time after the do{}while() loop exits. So, you should immediately copy its value to a shadow variable like this:
int myFlagCopy;
do
{
/* doing something */
barrier();
} while(!(myFlagCopy = flag))
/* do something with 'myFlagCopy' */
It is possible that the while is executed before the threads... you have to wait the execution of thread before, using pthread_join()
I have a main.c with a global variable called int countboards. In the main() I start a pthread, that listens to ONE TCP-Connection and runs that through (progserver.c). Means, this thread will never return. In the main() I enter the function rmmain(...) which is in the rm.c (RM=Ressource Manager). In rm.c I read countboards, in the progserver.c in the pthread I write to this variable (both are made accessible by extern int countboards).
So the problem is, when I write to countboards in the pthread and I want to access this variable after it's been written to in the rm.c, it still has the old value (in this case 0 instead of for example 10). Why?
main.c:
int countboards;
int main(int argc, char** argv) {
countboards = 0;
pthread_t thread;
pthread_create(&thread, NULL, startProgramserver, NULL);
rmmain();
return 0;
}
rm.c:
extern int countboards;
int rmmain(vhbuser* vhbuserlist, int countvhbuser,
userio* useriolist, int countios, int usertorm, int rmtosslserver, int sslservertorm) {
while(1) {
int n;
n=read(usertorm,buf,bufc); // blocks until command comes from the user
...
board* b = findAFreeBoard(boardlist, countboards, usagelist); // here countboards should be >0, but it isn't
...
}
}
programserver.c:
extern int countboards;
void* startProgramserver(void*) {
...
sock = tcp_listen();
...
http_serve(ssl,s, sslpipes);
}
static int http_serve(SSL *ssl, int s, void* sslpipes) {
...
countboards = countboards + countboardscommands;
...
// here countboards has the new value
}
You're seeing a cached copy in each thread. I would suggest declaring it volatile int countboards except that's really not a good way to go about things.
Globals are kinda evil. You'd be better served by passing a pointer to each thread and synchronizing with a mutex.
Edit: To expand on this since I was in a hurry last night ...
http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/
As KasigiYabu mentions in the comments below, creating a "context" structure that contains all the information you want to share between the threads and passing that in to pthread_create as the last arg is a sound approach and is what I do as well in most cases.