50 }
51 if (status != 0)
52 err_abort (status, 'Cond timedwait');
53 }
54 if (!expired)
55 alarm_insert (alarm);
56 } else
57 expired = 1;
58 if (expired) {
59 printf ('(%d) %s
', alarm->seconds, alarm->message);
60 free (alarm);
61 }
62 }
63 }
Part 4 shows the final section of alarm_cond.c, the main program. It is nearly identical to the main function from alarm_mutex.c.
38 Because the condition variable signal operation is built into the new alarm_ insert function, we call alarm_insert rather than inserting a new alarm directly.
¦ alarm_cond.c part 4 main
1 int main (int argc, char *argv[])
2 {
3 int status;
4 char line[128];
5 alarm_t *alarm;
6 pthread_t thread; 7
8 status = pthread_create (
9 &thread, NULL, alarm thread, NULL);
10 if (status != 0)
11 err_abort (status, 'Create alarm thread');
12 while (1) {
13 printf ('Alarm> ');
14 if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
15 if (strlen (line) <= 1) continue;
16 alarm = (alarm_t*)malloc (sizeof (alarm_t));
17 if (alarm == NULL)
18 errno_abort ('Allocate alarm');
19
20 /*
21 * Parse input line into seconds (%d) and a message
22 * (%64[^
]), consisting of up to 64 characters
23 * separated from the seconds by whitespace.
24 */
25 if (sscanf (line, '%d %64[^
]',
26 &alarm->seconds, alarm->message) < 2) {
27 fprintf (stderr, 'Bad command
');
28 free (alarm);
29 } else {
30 status = pthread_mutex_lock (&alarm_mutex);
31 if (status != 0)
32 err_abort (status, 'Lock mutex');
33 alarm->time = time (NULL) + alarm->seconds;
34 /*
35 * Insert the new alarm into the list of alarms,
36 * sorted by expiration time.
37 */
38 alarm_insert (alarm);
39 status = pthread_mutex_unlock (&alarm_mutex);
40 if (status != 0)
41 err_abort (status, 'Unlock mutex');
42 }
43 }
44 }
3.4 Memory visibility between threads
The moment Alice appeared, she was appealed to by all three to settle the question, and they repeated their arguments to her, though, as they all spoke at once, she found it very hard to make out exactly what they said.
In this chapter we have seen how you should use mutexes and condition variables to synchronize (or 'coordinate') thread activities. Now we'll journey off on a tangent, for just a few pages, and see what is really meant by 'synchronization' in the world of threads. It is more than making sure two threads don't write to the same location at the same time, although that's part of it. As the title of this section implies, it is about how threads see the computer's memory.
Pthreads provides a few basic rules about memory visibility. You can count on all implementations of the standard to follow these rules:
1. Whatever memory values a thread can see when it calls pthread_create can also be seen by the new thread when it starts. Any data written to memory after the call to pthread_create may not necessarily be seen by the new thread, even if the write occurs before the thread starts.
2. Whatever memory values a thread can see when it unlocks a mutex, either directly or by waiting on a condition variable, can also be seen by any thread that later locks the same mutex. Again, data written after the mutex is unlocked may not necessarily be seen by the thread that locks the mutex, even if the write occurs before the lock.
3. Whatever memory values a thread can see when it terminates, either by cancellation, returning from its start function, or by calling pthread_exit, can also be seen by the thread that joins with the terminated thread by calling pthread_join. And, of course, data written after the thread terminates may not necessarily be seen by the thread that joins, even if the write occurs before the join.
4. Whatever memory values a thread can see when it signals or broadcasts a condition variable can also be seen by any thread that is awakened by that signal or broadcast. And, one more time, data written after the signal or broadcast may not necessarily be seen by the thread that wakes up, even if the write occurs before it awakens.
Figures 3.5 and 3.6 demonstrate some of the consequences. So what should you, as a programmer, do?
First, where possible make sure that only one thread will ever access a piece of data. A thread's registers can't be modified by another thread. A thread's stack and heap memory a thread allocates is private unless the thread