-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathalarm_mutex.c
175 lines (164 loc) · 5.46 KB
/
alarm_mutex.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
* alarm_mutex.c
*
* This is an enhancement to the alarm_thread.c program, which
* created an "alarm thread" for each alarm command. This new
* version uses a single alarm thread, which reads the next
* entry in a list. The main thread places new requests onto the
* list, in order of absolute expiration time. The list is
* protected by a mutex, and the alarm thread sleeps for at
* least 1 second, each iteration, to ensure that the main
* thread can lock the mutex to add new work to the list.
*/
#include <pthread.h>
#include <time.h>
#include "errors.h"
/*
* The "alarm" structure now contains the time_t (time since the
* Epoch, in seconds) for each alarm, so that they can be
* sorted. Storing the requested number of seconds would not be
* enough, since the "alarm thread" cannot tell how long it has
* been on the list.
*/
typedef struct alarm_tag {
struct alarm_tag *link;
int seconds;
time_t time; /* seconds from EPOCH */
char message[64];
} alarm_t;
pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
alarm_t *alarm_list = NULL;
/*
* The alarm thread's start routine.
*/
void *alarm_thread (void *arg)
{
alarm_t *alarm;
int sleep_time;
time_t now;
int status;
/*
* Loop forever, processing commands. The alarm thread will
* be disintegrated when the process exits.
*/
while (1) {
status = pthread_mutex_lock (&alarm_mutex);
if (status != 0)
err_abort (status, "Lock mutex");
alarm = alarm_list;
/*
* If the alarm list is empty, wait for one second. This
* allows the main thread to run, and read another
* command. If the list is not empty, remove the first
* item. Compute the number of seconds to wait -- if the
* result is less than 0 (the time has passed), then set
* the sleep_time to 0.
*/
if (alarm == NULL)
sleep_time = 1;
else {
alarm_list = alarm->link;
now = time (NULL);
if (alarm->time <= now)
sleep_time = 0;
else
sleep_time = alarm->time - now;
#ifdef DEBUG
printf ("[waiting: %d(%d)\"%s\"]\n", alarm->time,
sleep_time, alarm->message);
#endif
}
/*
* Unlock the mutex before waiting, so that the main
* thread can lock it to insert a new alarm request. If
* the sleep_time is 0, then call sched_yield, giving
* the main thread a chance to run if it has been
* readied by user input, without delaying the message
* if there's no input.
*/
status = pthread_mutex_unlock (&alarm_mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
if (sleep_time > 0)
sleep (sleep_time);
else
sched_yield ();
/*
* If a timer expired, print the message and free the
* structure.
*/
if (alarm != NULL) {
printf ("(%d) %s\n", alarm->seconds, alarm->message);
free (alarm);
}
}
}
int main (int argc, char *argv[])
{
int status;
char line[128];
alarm_t *alarm, **last, *next;
pthread_t thread;
status = pthread_create (
&thread, NULL, alarm_thread, NULL);
if (status != 0)
err_abort (status, "Create alarm thread");
while (1) {
printf ("alarm> ");
if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
if (strlen (line) <= 1) continue;
alarm = (alarm_t*)malloc (sizeof (alarm_t));
if (alarm == NULL)
errno_abort ("Allocate alarm");
/*
* Parse input line into seconds (%d) and a message
* (%64[^\n]), consisting of up to 64 characters
* separated from the seconds by whitespace.
*/
if (sscanf (line, "%d %64[^\n]",
&alarm->seconds, alarm->message) < 2) {
fprintf (stderr, "Bad command\n");
free (alarm);
} else {
status = pthread_mutex_lock (&alarm_mutex);
if (status != 0)
err_abort (status, "Lock mutex");
alarm->time = time (NULL) + alarm->seconds;
/*
* Insert the new alarm into the list of alarms,
* sorted by expiration time.
*/
last = &alarm_list;
next = *last;
while (next != NULL) {
if (next->time >= alarm->time) {
alarm->link = next;
*last = alarm;
break;
}
last = &next->link;
next = next->link;
}
/*
* If we reached the end of the list, insert the new
* alarm there. ("next" is NULL, and "last" points
* to the link field of the last item, or to the
* list header).
*/
if (next == NULL) {
*last = alarm;
alarm->link = NULL;
}
#ifdef DEBUG
printf ("[list: ");
for (next = alarm_list; next != NULL; next = next->link)
printf ("%d(%d)[\"%s\"] ", next->time,
next->time - time (NULL), next->message);
printf ("]\n");
#endif
status = pthread_mutex_unlock (&alarm_mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
}
}
}