Line data Source code
1 : /*
2 : * lwan - web server
3 : * Copyright (c) 2012, 2013 L. A. F. Pereira <l@tia.mat.br>
4 : *
5 : * This program is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU General Public License
7 : * as published by the Free Software Foundation; either version 2
8 : * of the License, or any later version.
9 : *
10 : * This program is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : * GNU General Public License for more details.
14 : *
15 : * You should have received a copy of the GNU General Public License
16 : * along with this program; if not, write to the Free Software
17 : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 : */
19 :
20 : #define _GNU_SOURCE
21 : #include <assert.h>
22 : #include <errno.h>
23 : #include <ioprio.h>
24 : #include <pthread.h>
25 : #include <sched.h>
26 : #include <stdbool.h>
27 : #include <stdlib.h>
28 : #include <sys/time.h>
29 : #include <unistd.h>
30 :
31 : #include "lwan-private.h"
32 : #include "lwan-status.h"
33 : #include "list.h"
34 :
35 : struct job {
36 : struct list_node jobs;
37 : bool (*cb)(void *data);
38 : void *data;
39 : };
40 :
41 : static pthread_t self;
42 : static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
43 : static bool running = false;
44 : static struct list_head jobs;
45 :
46 : static pthread_mutex_t job_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
47 : static pthread_cond_t job_wait_cond = PTHREAD_COND_INITIALIZER;
48 :
49 : static void
50 97 : timedwait(bool had_job)
51 : {
52 : static int secs = 1;
53 : struct timeval now;
54 :
55 97 : if (had_job)
56 2 : secs = 1;
57 95 : else if (secs <= 15)
58 95 : secs++;
59 :
60 97 : gettimeofday(&now, NULL);
61 :
62 97 : struct timespec rgtp = { now.tv_sec + secs, now.tv_usec * 1000 };
63 97 : pthread_cond_timedwait(&job_wait_cond, &job_wait_mutex, &rgtp);
64 5 : }
65 :
66 92 : void lwan_job_thread_main_loop(void)
67 : {
68 : /* Idle priority for the calling thread. Magic value of `7` obtained from
69 : * sample program in linux/Documentation/block/ioprio.txt. This is a no-op
70 : * on anything but Linux. */
71 92 : ioprio_set(IOPRIO_WHO_PROCESS, 0, IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 7));
72 :
73 92 : lwan_set_thread_name("job");
74 :
75 92 : if (pthread_mutex_lock(&job_wait_mutex))
76 0 : lwan_status_critical("Could not lock job wait mutex");
77 :
78 97 : while (running) {
79 97 : bool had_job = false;
80 :
81 97 : if (LIKELY(!pthread_mutex_lock(&queue_mutex))) {
82 : struct job *job;
83 :
84 291 : list_for_each(&jobs, job, jobs)
85 194 : had_job |= job->cb(job->data);
86 :
87 97 : pthread_mutex_unlock(&queue_mutex);
88 : }
89 :
90 97 : timedwait(had_job);
91 : }
92 :
93 0 : if (pthread_mutex_unlock(&job_wait_mutex))
94 0 : lwan_status_critical("Could not lock job wait mutex");
95 0 : }
96 :
97 92 : void lwan_job_thread_init(void)
98 : {
99 92 : assert(!running);
100 :
101 92 : lwan_status_debug("Initializing low priority job thread");
102 :
103 92 : list_head_init(&jobs);
104 :
105 92 : self = pthread_self();
106 92 : running = true;
107 :
108 : #ifdef SCHED_IDLE
109 92 : struct sched_param sched_param = {
110 : .sched_priority = 0
111 : };
112 92 : if (pthread_setschedparam(self, SCHED_IDLE, &sched_param) < 0)
113 0 : lwan_status_perror("pthread_setschedparam");
114 : #endif /* SCHED_IDLE */
115 92 : }
116 :
117 0 : void lwan_job_thread_shutdown(void)
118 : {
119 0 : lwan_status_debug("Shutting down job thread");
120 :
121 0 : if (LIKELY(!pthread_mutex_lock(&queue_mutex))) {
122 : struct job *node, *next;
123 : int r;
124 :
125 0 : list_for_each_safe(&jobs, node, next, jobs) {
126 0 : list_del(&node->jobs);
127 0 : free(node);
128 : }
129 0 : running = false;
130 :
131 0 : pthread_cond_signal(&job_wait_cond);
132 :
133 0 : r = pthread_join(self, NULL);
134 0 : if (r) {
135 0 : errno = r;
136 0 : lwan_status_perror("pthread_join");
137 : }
138 :
139 0 : pthread_mutex_unlock(&queue_mutex);
140 : }
141 0 : }
142 :
143 192 : void lwan_job_add(bool (*cb)(void *data), void *data)
144 : {
145 192 : assert(cb);
146 :
147 192 : struct job *job = calloc(1, sizeof(*job));
148 192 : if (!job)
149 0 : lwan_status_critical_perror("calloc");
150 :
151 192 : job->cb = cb;
152 192 : job->data = data;
153 :
154 192 : if (LIKELY(!pthread_mutex_lock(&queue_mutex))) {
155 192 : list_add(&jobs, &job->jobs);
156 192 : pthread_mutex_unlock(&queue_mutex);
157 : } else {
158 0 : lwan_status_warning("Couldn't lock job mutex");
159 0 : free(job);
160 : }
161 192 : }
162 :
163 0 : void lwan_job_del(bool (*cb)(void *data), void *data)
164 : {
165 : struct job *node, *next;
166 :
167 0 : assert(cb);
168 :
169 0 : if (LIKELY(!pthread_mutex_lock(&queue_mutex))) {
170 0 : list_for_each_safe(&jobs, node, next, jobs) {
171 0 : if (cb == node->cb && data == node->data) {
172 0 : list_del(&node->jobs);
173 0 : free(node);
174 : }
175 : }
176 0 : pthread_mutex_unlock(&queue_mutex);
177 : }
178 0 : }
|